ARM汇编(基于树莓派3B)

  1. 开始
  2. 数据加载与加法
  3. 有用的工具

寄存器

A program on an ARM processor in user mode has access to 16 registers:
• R0 to R12: 通用寄存器
• R13: 栈指针寄存器
• R14: 连接寄存器。R13 and R14 are used in the context of calling functions, and we’ll explain these in more detail when we cover subroutines.
• R15: 程序计数器。 The memory address of the currently executing instruction.
• Current Program Status Register (CPSR): This 17th register contains bits of information on the last
instruction executed. More on the CPSR when we cover branch instructions (if statements).

Hello world

.global _start

_start: mov R0, #1
        ldr R1, =helloworld
        mov R2, #13
        mov R7, #4
        svc 0
        mov R0, #0
        mov R7, #1
        svc 0

.data
helloworld: .ascii "Hello World!\n"

.global_start定义为一个全局标号, 这样链接器(the ld command in our build file)才可以访问它. 汇编程序把包含 _start的标号作为程序的入口, then the linker can find it because it has been defined as a global variable. All our programs will contain this somewhere.
• 我们的程序可以有很多个.s 文件, but only one can contain _start.

汇编指令

We only use three different Assembly language statements in this example:

  1. MOV which moves data into a register. In this case, 我们使用了一个以“#”开头的立即数. So “MOV R1, #4” means move the number 4 into R1. In this case, 4是指令的一部分而不会存储在内存中的某个位置. 在源程序中, 操作数可以是大写的也可以是小写的; I tend to prefer lowercase in my program listings.
  2. LDR R1, =helloworld” statement which loads register 1 with the address of the string we want to print.
  3. SVC 0执行0号软件中断.它将控制权交给Linux内核中的中断处理程序,该处理程序解释我们在各个寄存器中设置的参数并完成实际工作。

数据(Data)

Next, we have .data which indicates the following instructions are in the data section of the program:
• In this, we have a label “helloworld” followed by an .ascii statement and then the string we want to print.
.ascii语句告诉汇编程序只需将我们的字符串放入数据部分,然后就可以像在LDR语句中一样通过标号访问它。稍后我们将讨论文本如何用数字表示,此处的编码方案称为ASCII。
• The last “\n” character is how we represent a new line. If we don’t include this, you must press return to see the text in the terminal window.

Linux系统调用

This program makes two Linux system calls to do its work. The first is the Linux write to file command (#4). Normally, we would have to open a file first before using this command, but when Linux runs a program, it opens three files for it:

  1. stdin (input from the keyboard)
  2. stdout (output to the screen)
  3. stderr (also output to the screen)

当在Linux shell使用>,<和|时将会发生重定向。 对于任何Linux系统调用,根据需要,将参数放入寄存器R0–R4中。然后返回码存放在R0中。每个系统调用都通过将其功能编号放入R7来指定。
执行软件中断而不是分支或子程序调用的原因是,我们可以调用Linux,而无需知道该程序在内存中的位置。这意味着随着Linux的更新以及其程序在内存中的移动,我们无需更改程序中的任何地址。
软件中断的另一个好处是提供了一种标准的机制来切换优先级。我们将在第7章“ Linux操作系统服务”中讨论Linux系统调用。

ARM指令格式

在这里插入图片描述
Condition: Allows the instruction to execute depending on the bits in the CPSR. We’ll examine this in detail when we get to branching instructions.
Operand type: 指定19–0位上的操作数类型. We could have specified some of these bits, since we used two registers and an immediate operand in this example.
Opcode: Which instruction are we performing, like ADD or MUL.
Set condition code:这条指令是否更改CPSR. If we don’t want the result of this instruction to affect following branch instructions, we would set it to 0.
Operand register: One register to use as input.
Destination register: Where to put the result of whatever this instruction does.
Immediate operand: 通常这是一小部分数据,可以在指令中直接指定。因此,如果要将1加到一个寄存器中,则可以将其设为1,而不是将1放入另一个寄存器中并将两个寄存器相加。该字段的格式非常复杂,需要较大的部分来解释所有细节,但这是基本思想。

大小端

ARM CPU之所以称为bi-endian,是因为它可以使用大小端的任一种。 CPSR中有一个程序状态标志,指出要使用的字节序。默认情况下,Raspbian和你的程序使用小端存储(类似于Intel处器)。

小端的优势

优点是无需任何地址运算就能更改整数的存储大小。如4字节的数变成1字节表示,取第1个字节即可;变成2字节表示,取前2个字节即可。
尽管Raspbian使用的是小端,但互联网上使用的许多协​​议(如TCP / IP)使用了大端,因此在将数据从树莓派迁移到外界时需要进行转换。

MOV/MVN

In this section, we are going to look at several forms of the MOV instruction:

  1. MOV RD, #imm16
  2. MOVT RD, #imm16
  3. MOV RD, RS
  4. MOV RD, operand2
  5. MVN RD, operand2

我们已经见过了第一种情况,将一小部分存入寄存器。这里的立即数可以是任何16位的数,它会被放在指定寄存器的低16位中。这种形式的MOV指令非常简单。因此,我们经常使用它。

MOVT

这条指令用于将16位的立即数加载到指定寄存器的高16位而不会影响它的低16位。例如,如果我们想要将0x4F5D6E3A加载到寄存器R2,我们可以这样做:
MOV R2, #0x6E3A
MOVT R2, #0x4F5D

Operand2

There are two formats for Operand2:

  1. A register and a shift
  2. A small number and a rotation

Register and Shift

MOV R1, R2, LSL #1
把R2逻辑左移1位的结果保存在R1中。
可以简写成:
LSL R1, R2, #1

Small Number and Rotation

这个比较难理解。回顾一下上面的指令格式,立即数操作数使用了11-0共计12位,这12位由两部分组成:

  1. 一个字节大小的数,需要8位
  2. 一个表示循环右移位数为0,2,4,6,8 … 30的偶数,需要4位(4位2进制数的值*2)

我们要做的,首先是把这一个字节大小的“小”数扩展为32位,然后循环右移,然后我们会得到另一个数,比如:
• 0 - 255 [0 - 0xff ]
• 256,260,264,…,1020 [0x100-0x3fc, 步长 4, 通过0x40-0xff 循环右移30位得到]
• 1024,1040,1056,…,4080 [0x400-0xff0, 步长 16, 通过0x40-0xff 循环右移28位得到]
• 4096,4160, 4224,…,16320 [0x1000-0x3fc0, 步长 64, 通过0x40-0xff 循环右移26位得到]

这意味着,很多32位的立即数可以通过一个8位的数+4位的循环位数来表示,也就是说,有些情况下我们把一个32位的立即数通过MOV指令送给寄存器也是被允许的,例如:

@ 超过了16位立即数的范围但可以被表示
MOV R1, #0xAB000000

这个是被允许的,因为0xAB000000可以通过AB循环右移8位得到

@ 超过了16位立即数的范围且不能被表示
MOV R1, #0xABCDEF11

因为汇编器找不到这样一个字节数和循环位数的组合来表示它,所以会报错:
Error: invalid constant (abcdef11) after fixup
要加载它到寄存器,需要搭配使用MOV / MOVT。
仔细思考不难发现,当一个数中1的个数超过8位时,它不能被表示(或取反,往下看)。当然,还有其它条件,这些留给你们慢慢思考。

MVN

和MOV类似,只不过它将源数据取反码后加载到目的寄存器中。
MVN是一个独特的操作码,而不是另一个带有隐秘参数的指令的别名。 ARM32指令集只有16个操作码,因此这是一条重要的指令,具有三个主要用途:

  1. 计算反码。它有其用途,但是否保证有自己的操作码?
  2. 乘以–1。我们看到,通过移位操作,我们可以乘以或除以2的幂。这条指令使我们可以乘以–1。记住,数字的负数是数字的补码或反码加一。这意味着我们可以通过执行此指令将其乘以–1,然后加一。我们为什么要这样做而不是使用乘法(MUL)指令?位移也一样,为什么要这样做而不是使用MUL?原因是乘法指令太慢了,要花费几个时钟周期来完成。位移指令仅仅需要一个时钟周期,并且使用MVN和ADD,我们乘-1只需要2个时钟周期。乘-1是很常见的操作,现在我们可以更快的完成它。
  3. 通过额外的位(13位相比于12位),可以获得的值的数量是原来的两倍。事实证明,对于MVN和MOV,使用字节数和偶数移位获得的所有数字都不同。这意味着如果汇编器发现指定的数无法在MOV指令中表示,则它将尝试将其更改为MVN指令,反之亦然。因此,你实际上有13位立即数据,而不是12位。注意:它仍然可能无法表示你的数字,可能仍需要使用MOV / MOVT对。

ADD和ADC

ADD R0, R1, #1
把R1和1相加的结果载入R0

ADD R1, #1等价于ADD R1, R1, #1

ADC:带进位的加法

ADDS:允许这条指令改变CPSR
其它指令也一样,在后面加上S即可改变CPSR。

GDB

r 执行程序
l 看十行代码
disassemble _start 看实际的二进制编码
b _start 断点
s 单步执行
i r 查看寄存器
c 继续执行直到下一断点或程序结束
i b 查看所有断点的号码
delete num 删除num号断点
x /Nfu addr 查看内存
q 退出GDB
where
N is the number of objects to display
• f is the display format where some common ones are
• t for binary
• x for hexadecimal
• d for decimal
• i for instruction
• s for string
u is unit size, and is any of
• b for bytes
• h for halfwords (16 bits)
• w for words (32 bits)
• g for giant words (64 bits)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值