ARM汇编基础
整体框架
;文件名:TEST1.S
;功能:实现两个寄存器相加
;说明:使用ARMulate软件仿真调试
AREA Example1,CODE,READONLY ;声明代码段Example1
ENTRY ;标识程序入口
CODE32 ;声明32位ARM指令
START MOV R0,#0 ;设置参数
MOV R1,#10
LOOP BL ADD_SUB ;调用子程序ADD_SUB
B LOOP ;跳转到LOOP
ADD_SUB
ADDS R0,R0,R1 ;R0 = R0 + R1
MOV PC,LR ;子程序返回
END ;文件结束
说明:
1.注释以“;”开头
2.标号(以上代码中的“START”“LOOP”“ADD_SUB”)定格写
3.以“END”结尾
4.尖括号(“<>”)包含的内容是必须的,花括号(“{}”)包含的内容是可选的
寻址方式
寻址方式是根据指令中给出的地址码字段来实现寻找真实操作数地址的方式。ARM处理器具有9种基本寻址方式。
-
寄存器寻址
操作数的值在寄存器中,指令中的地址码字段指出的是寄存器编号,指令执行时直接取出寄存器值来操作。
对寄存器空间本身进行操作。
直接用R1
、R2
等即可。 -
立即寻址
立即寻址指令中的操作码字段后面的地址码部分即是操作数本身,也就是说,数据就包含在指令当中,取出指令也就取出了可以立即使用的操作数(这样的数称为立即数)。
用#
开头,后面写数值,该数值即操作数本身,有前缀0x
则为十六进制,没有前缀默认为十进制。
如#100
、#0xFE76
。 -
寄存器移位寻址
寄存器移位寻址是ARM指令集特有的寻址方式。当第2个操作数是寄存器移位方式时,第2个寄存器操作数在与第1个操作数结合之前,选择进行移位操作。
寄存器存储的值本身就是操作数,但要进一步移位后得到需要的操作数,例如R2,LSL #3
。 -
寄存器间接寻址
寄存器间接寻址指令中的地址码给出的是一个通用寄存器的编号,所需的操作数保存在寄存器指定地址的存储单元中,即寄存器为操作数的地址指针。
寄存器空间只是保存了实际操作空间的地址。
用[]
包含一个储存地址值的寄存器,代表该地址所指向的位置中的操作数。 -
基址寻址
基址寻址就是将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址。基址寻址用于访问基址附近的存储单元,常用于查表、数组操作、功能部件寄存器访问等。
寄存器空间只是保存了实际操作空间的地址。
例如[R3,#0x0C]
表示地址为R3+0x0c
位置内的操作数,[R0,#-4]
表示地址为R0-4
位置内的操作数。 -
多寄存器寻址
多寄存器寻址一次可传送几个寄存器值,允许一条指令传送16个寄存器的任何子集或所有寄存器。 -
堆栈寻址
堆栈是一个按特定顺序进行存取的存储区,操作顺序为“后进先出” 。堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针)指向一块存储区域(堆栈),指针所指向的存储单元即是堆栈的栈顶。存储器堆栈可分为两种:
向上生长:向高地址方向生长,称为递增堆栈
向下生长:向低地址方向生长,称为递减堆栈 -
相对寻址 相对寻址是基址寻址的一种变通。由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。
单条指令格式
<opcode> {<cond>} {S} <Rd> ,<Rn> {,<operand2>}
<指令助记符> {<执行条件>} {是否影响CPSR寄存器的值} <目标寄存器> ,<第一个操作数的寄存器> {,<第二个操作数>}
<operand2> 第二个操作数
<operand2>有如下三种形式:
- 常数表达式形式
- 寄存器形式
- 寄存器移位形式
常数表达式形式(#immed_8r)
该常数必须对应8位位图,即必须是一个8位的常数通过循环右移偶数位可以得到的数。
移位前:00000000 00000000 00000000 xxxxxxxx
(除了最后8位都是0)
将这个32位数向右循环移位任意偶数位(但要在1轮以内),即得到一个新的32位数(称为8位图立即数
),将这个32位二进制数写入汇编代码中。
这样,新的32位数就可以用8位数值位
和4位移位数
组成的12位二进制编码表示。由于用12位二进制编码来表示32位二进制数,因此有些32位二进制数无法被表示。
参考资料:CSDN
寄存器形式
在寄存器方式下,操作数即为寄存器的数值。
寄存器移位方式
将寄存器的移位结果作为操作数,但所提到的寄存器本身的值保持不变。
移位方式:
操作码 | 说明 |
---|---|
ASR #n | 算术右移n位 |
LSL #n | 逻辑左移n位 |
LSR #n | 逻辑右移n位 |
ROR #n | 循环右移n位 |
RRX | 带扩展的循环右移1位 |
Type Rs | Type为移位的一种类型,Rs为偏移量寄存器,低8位有效 |
示例:
ADD R1,R1,R1,LSL #3 ;R1=R1+R1*8=9R1 ;“R1,LSL # 3”为一个整体
SUB R1,R1,R2,LSR R3 ;R1=R1-(R2/2^R3) ;“R2,LSR R3”为一个整体
条件码
绝大部分的ARM指令都可以条件执行,而Thumb指令只有B(跳转)指令具有条件执行 功能。如果指令不标明条件代码,将默认为无条件(AL)执行。
操作码 | 条件助记符 | 标志 | 含义 |
---|---|---|---|
0 | EQ | Z=1 | 相等 |
1 | NE | Z=0 | 不相等 |
10 | CS/HS | C=1 | 无符号数大于或等于 |
11 | CC/LO | C=0 | 无符号数小于 |
100 | MI | N=1 | 负数 |
101 | PL | N=0 | 正数或零 |
110 | VS | V=1 | 溢出 |
111 | VC | V=0 | 没有溢出 |
1000 | HI | C=1,Z=0 | 无符号数大于 |
1001 | LS | C=0,Z=1 | 无符号数小于或等于 |
1010 | GE | N=V | 有符号数大于或等于 |
1011 | LT | N!=V | 有符号数小于 |
1100 | GT | Z=0,N=V | 有符号数大于 |
1101 | LE | Z=1,N!=V | 有符号数小于或等于 |
1110 | AL | 任何 | 无条件执行(指令默认条件) |
1111 | NV | 任何 | 从不执行(不要使用) |
ARM指令种类
- 存储器访问指令
- 数据处理指令
- 乘法指令
- ARM分支指令
- 协处理器指令
- 杂项指令
- 伪指令
存储器访问指令
ARM处理器是典型的RISC处理器,对存储器的访问只能使用加载和存储指令实现。
存储器访问指令分为单寄存器操作指令和多寄存器操作指令。
LDR
/STR
指令用于对内存变量的访问、内存缓冲区数据的访问、查表、外围部件的控制操作等。若使用LDR
指令加载数据到PC寄存器,则实现程序跳转功能,这样也就实现了程序散转。
单寄存器
所有单寄存器加载/存储指令可分为“字和无符号字节加载存储指令”和“半字和有符号字节加载存储指令”。
LDR <目标寄存器>,<源地址> ;装载指令(存储器->寄存器)
STR <源寄存器>,<目标地址> ;存储指令(寄存器->存储器)
LDR/STR指令搭配不同的后缀实现不同方式的单寄存器存取操作:
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
LDR Rd,addressing | 加载字数据 | Rd←[addressing],addressing索引 | LDR{cond} |
LDRB Rd,addressing | 加载无符号字节数据 | Rd←[addressing],addressing索引 | LDR{cond}B |
LDRT Rd,addressing | 以用户模式加载字数据 | Rd←[addressing],addressing索引 | LDR{cond}T |
LDRBT Rd,addressing | 以用户模式加载无符号 | Rd←[addressing],addressing索引 | LDR{cond}BT |
LDRH Rd,addressing | 加载无符号半字数据 | Rd←[addressing],addressing索引 | LDR{cond}H |
LDRSB Rd,addressing | 加载有符号字节数据 | Rd←[addressing],addressing索引 | LDR{cond}SB |
LDRSH Rd,addressing | 加载有符号半字数据 | Rd←[addressing],addressing索引 | LDR{cond}SH |
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
STR Rd,addressing | 存储字数据 | [addressing]←Rd,addressing索引 | STR{cond} |
STRB Rd,addressing | 存储字节数据 | [addressing]←Rd,addressing索引 | STR{cond}B |
STRT Rd,addressing | 以用户模式存储字数据 | [addressing]←Rd,addressing索引 | STR{cond}T |
STRBT Rd,addressing | 以用户模式存储字节数据 | [addressing]←Rd,addressing索引 | STR{cond}BT |
STRH Rd,addressing | 存储半字数据 | [addressing]←Rd,addressing索引 | STR{cond}H |
规律:
- 有
B
为“字节”数据,有H
的是“半字”数据,都没有则为“字”数据 - 有
T
则以用户模式加载/存储,否则以特权模式加载/存储 - 有
S
则为有符号数据,否则为无符号数据 -
LDR
/STR
最前,条件码在中,后缀在后
多寄存器
多寄存器加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM为加载多个寄存器;STM为存储多个寄存器。允许一条指令传送16个寄存器的任何子集或所有寄存器。它们主要用于现场保护、数据复制、常数传递等。
LDM <源地址>,<目标寄存器列表> ;装载指令(存储器->寄存器)
STM <目标地址>,<源寄存器列表> ;存储指令(寄存器->存储器)
LDM/STM指令搭配不同的后缀实现不同方式地址增长方式:
- IA: 每次传送后地址加4
- IB: 每次传送前地址加4
- DA:每次传送后地址减4
- DB:每次传送前地址减4
数据处理指令
数据传送指令
- MOV
- MVN
MOV
指令将8位图立即数或寄存器传送到目标寄存器(Rd),可用于移位运算等操作。
同类型的指令还有MVN
,它可以实现数据的非传递,即把操作数取反后送至目标寄存器。
MOV 目标寄存器,操作数
; 目标寄存器 <-- 操作数
算术逻辑运算指令
算术逻辑运算指令包括“加/减”以及“与/或/异或”等指令,它们的格式如下:
OpCode 结果寄存器,运算寄存器,第二操作数
OpCode种类:
- 算术运算指令
- ADD:加法
- ADC:带进位加法
- SUB:剑法
- RSB:逆向减法
- SBC:带进位减法
- RSC:带进位逆向减法
- 逻辑运算指令
- AND:逻辑“与”
- ORR:逻辑“或”
- EOR:逻辑“异或”
- BIC:位清除
比较指令
比较指令将两个数值进行的特定运算,根据运算结果影响CPSR的相关标志位,用于后面程序的条件执行,但是运算结果不予保存。
OpCode 运算寄存器,操作数
OpCode:
- CMP:数值比较
- CMN:负数比较
- TST:位测试
- TEQ:相等测试
乘法指令
ARM7TDMI具有三种乘法指令,分别为:
- 32×32位乘法指令;
- 32× 32位乘加指令;
- 32× 32位结果为64位的乘/乘加指令。
MUL 目标寄存器,运算寄存器,第二操作数
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
MUL Rd,Rm,Rs | 32位乘法指令 | Rd<-Rm*Rs (Rd≠Rm) | MUL{cond}{S} |
MLA Rd,Rm,Rs,Rn | 32位乘加指令 | Rd<-Rm*Rs+Rn (Rd≠Rm) | MLA{cond}{S} |
UMULL RdLo,RdHi,Rm,Rs | 64位无符号乘法指令 | (RdLo,RdHi)<-Rm*Rs | UMULL{cond}{S} |
UMLAL RdLo,RdHi,Rm,Rs | 64位无符号乘加指令 | (RdLo,RdHi)<-Rm*Rs+(RdLo,RdHi) | SMLAL{cond}{S} |
SMULL RdLo,RdHi,Rm,Rs | 64位有符号乘法指令 | (RdLo,RdHi)<-Rm*Rs | SMULL{cond}{S} |
SMLAL RdLo,RdHi,Rm,Rs | 64位有符号乘加指令 | (RdLo,RdHi)<-Rm*Rs+(RdLo,RdHi) | SMLAL{cond}{S} |
ARM分支指令
在ARM中有两种方式可以实现程序的跳转:
- 直接向PC寄存器赋值实现跳转;
例:MOV PC,R14
- 使用分支指令直接跳转。
OpCode 跳转目标
OpCode:
- B:分支指令
- BL:带链接的分支指令
- BX:带状态切换的分支指令
协处理器指令
ARM内核支持协处理器操作,协处理器的控制要通过协处理器命令实现。
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
CDP coproc,opcode1,CRd,CRn,CRm{,opcode2} | 协处理器数据操作指令 | 取决于协处理器 | CDP{cond} |
LDC{L} coproc, CRd,<地址>地址>地址> | 协处理器数据读取指令 | 取决于协处理器 | LDC{cond}{L} |
STC{L} coproc, CRd,<地址>地址>地址> | 协处理器数据写入指令 | 取决于协处理器 | STC{cond}{L} |
MCR coproc,opcode1,Rd,CRn,CRm{,opcode2} | ARM寄存器到协处理器寄存器的数据传送指令 | 取决于协处理器 | MCR{cond} |
MRC coproc,opcode1,Rd,CRn,CRm{,opcode2} | 协处理器寄存器到ARM寄存器到的数据传送指令 | 取决于协处理器 | MCR{cond} |
杂项指令
在ARM指令集中杂项指令共有3条,它们非常重要,特别是与操作系统的使用息息相关:
- 软件中断产生指令:SWI
- 程序状态寄存器读指令:MRS
- 程序状态寄存器写指令:MSR
伪指令
ARM伪指令不属于ARM指令集中的指令,是为了编程方便而定义的。伪指令可以像其它ARM指令一样使用,但在编译时这些指令将被等效的ARM指令代替。
ARM伪指令有四条:
- 小范围地址读取指令:ADR
- 中等范围地址读取指令:ADRL
- 大等范围地址读取指令:LDR
- 空操作指令:NOP
地址的后缀和前缀
S后缀
S后缀的含义是:使用S后缀时,指令执行后程序状态寄存器的条件标志位将刷新;不使用S后缀,指令执行后程序状态寄存器的条件标志位将不发生变化。
ADD R1,R2,R3 ;R1<-R2+R3,没有使用S后缀,条件标志位不刷新
ADDS R1,R2,R3 ;R1<-R2+R3,使用S后缀,条件标志位刷新
!后缀
!
后缀的含义是:在指令的地址表达式中含有!后缀时,指令执行后,基址寄存器中的地址将发生变化,变化的结果如下:
LDR R2,[R1,#02] ;没有!后缀,结果是把R1加2作为地址指针存储的数据赋给R2,R1值不变
LDR R2,[R1,#02]! ;有!后缀,果是把R1加2作为地址指针存储的数据赋给R2,R1加2的结果送到R1中
B后缀和H后缀
B后缀的含义是:指令所涉及的数据是一个8位字节,不是一个字或半字。
H后缀的含义是:指令所涉及的数据是一个16位半字,不是一个字或字节。
LDR R2,[R0,#20] ;R2<-[R0+0x20]传送一个32位字
LDRB R2,[R0,#20] ;R2<-[R0+0x20]传送一个8位字节
LDRH R2,[R0,#20] ;R2<-[R0+0x20]传送一个半字