ARMV7架构下常用汇编指令
简介
ARMv7架构是一个32位处理器架构。它也是一种加载/存储体系结构,这意味着数据处理指令只对通用寄存器中的值进行操作。只有加载和存储指令才能访问内存。通用寄存器也是32位的。在本书中,当我们提到一个字时,我们指的是32位。因此双字是64位宽,半字是16位宽。尽管ARMv7体系结构是32位体系结构,但单个处理器实现不一定对所有块和互连都具有32位宽度。例如,可以为指令获取或数据访问提供64位或更宽的路径。大多数ARM处理器支持两种甚至三种不同的指令集,而有些(例如Cortex-M3处理器)实际上并不执行原始的ARM指令集。ARM处理器至少可以使用两种指令集。
Arm指令集为32位指令是原始指令集。
Thumb指令集最初是在ARM7TDMI处理器中添加的,它只包含16位指令,以牺牲一些性能为代价提供了更小的程序(在较小的嵌入式系统中,内存占用可能是一个主要问题)。最近的处理器,包括Cortex-A系列,都支持Thumb-2技术,该技术扩展了Thumb指令集,以提供16位和32位指令的混合。这提供了两全其美的效果,性能与ARM指令集相似,代码大小与Thumb指令相似。由于Thumb-2的大小和性能优势,编译或组装所有代码以利用Thumb-2技术的做法越来越普遍。
ARMV7架构提供16个32位通用寄存器(R0-R15)供软件使用。其中15个(R0-R14)可用于通用数据存储,而R15是程序计数器,其值随着核心执行指令而改变。软件对R15的显式写入将改变程序流程。软件还可以访问CPSR,以及从先前执行模式中保存的CPSR副本,称为已保存的程序状态寄存器(SPSR)。
1.数据处理指令
ARM内核只能在寄存器上执行数据处理,而不能直接在内存上执行。数据处理指令(大多数情况下)使用一个目标寄存器和两个源操作数。基本格式可以认为是操作码,可选地后跟条件码,可选地后跟S(设置标志),如下所示
操作码{可选条件}-S(设置标志) (Rd目标寄存器) 操作数1(Rn) 操作数2(OP)
1.1算数运算
- ADC 进位加
/*格式:*/ADC Rd,Rn,OP2 /*Rd = Rn + Op2 + C*/
ADC r0,r1,#0x2
/*如果r1的值为0x2,那么r0的值就为r1+0x2+1也就是0x2+0x2+0x1=5*/
- ADD 加法
/*格式:*/ADD Rd, Rn, Op2 /*Rd = Rn + Op2*/
ADD r0,r1,#0x3
/*r0 = r1+3*/
- MOV 赋值(转移)
/*格式:*/MOV Rd, Op2 /* Rd = Op2*/
MOV r0,#0x3
/*r0 = 0x3*/
movl
mov long : 传送字长 : 32位;把32位立即数放到寄存器
movw
mov word:传送字 :16位; 把 16 位立即数放到寄存器的底16位,高16位清0
movb
mov byte:传送字节 :8位;把8位立即数放到寄存器
movs
s(s标志)功能不变,影响CPSR标志位
movs r0, #0 默认结果为零但不影响CPSR的Z位,加上s以后会影响CPSR标志位
- MVN (取非赋值)
/*格式:*/MVN Rd, Op2 /* Rd = ~Op2*/
MVN r0,#0x3
/*r0 = ~0x3*/
- RSB (反向相减)
/*格式:*/RSB Rd, Rn, Op2 /* Rd = Op2 – Rn*/
rsb r0,r1,#0x3
/*r0 = 0x3-r1*/
- RSC (反向相减携带)
/*格式:*/RSC Rd, Rn, Op2 /* Rd = Op2 – Rn - !C*/
rsc r0,r1,#0x3
/*r0 = 0x3-r1 -0*/
- SBC (相减携带)
/*格式:*/SBC Rd, Rn, Op2 /* Rd =Rn-Op2 - !C*/
sbc r0,r1,#0x3
/*r0 =r1- 0x3-0*/
- SUB(相减)
/*格式:*/SUB Rd, Rn, Op2 /* Rd = Rn – Op2*/
sub r0,r1,#0x3
/*r0 = r1-0x3*/
- 乘法运算
指令 | 格式 | 含义 | 实例 |
---|---|---|---|
MLS | Rd, Rn, Rm, Ra | 乘法与减法结合 | Rd = Ra - (Rm × Rn) |
MLA | Rd, Rn, Rm, Ra | 乘法与加法结合 | Rd = Ra + (Rn × Rm) |
MUL | Rd, Rn, Rm | 乘法 | Rd = Rn × Rm |
SMLAL | RdLo, RdHi, Rn, Rm | 32位符号数乘以一个64位数 | RdHiLo += Rn × Rm |
SMULL | RdLo, RdHi, Rn, Rm | 有符号64位数相乘 | RdHiLo = Rn × Rm |
UMLAL | RdLo, RdHi, Rn, Rm | 无符号64位数累计和 | RdHiLo += Rn × Rm |
UMULL | RdLo, RdHi, Rn, Rm | 无符号64位乘法 | RdHiLo = Rn × Rm |
1.2逻辑运算与标志指令
- AND(与运算)
/*格式:*/AND Rd, Rn, Op2/* Rd = Rn & Op2*/
and r0,r1,#0x3
/*r0 = r1&0x3*/
- BTC(位清除)
/*格式:*/BIC Rd, Rn, Op2/* Rd = Rn & ~ Op2*/
bic r0,r1,#(0x1 << 12)
/*将r1寄存器第12位清零并赋值给r0寄存器*/
- EOR(异或)
/*格式:*/EOR Rd, Rn, Op2/* Rd = Rn ^ Op2*/
eor r0,r1,#0x1
/*r0 = r1^1*/
-
ORR(或运算)
/*格式:*/ORR Rd, Rn, Op2/* Rd = Rn | Op2*/ orr r0,r1,#0x1 /*r0 = r1|1*/
ARMV7架构(CPSR)程序当前状态寄存器如下所示
-
N 表示运算结果为假
-
Z 表示运算结果为0
-
C 表示运算结果进位
-
V 表示运算结果溢出
-
CMP (比较)
/*格式:*/CMP Rn, Op2/* Rn – Op2*/ CMP r0,r1 /*r0-r1*/
CMP结果 ZF CF 目的操作数 < 源操作数 0 1 目的操作数 > 源操作数 0 0 目的操作数 = 源操作数 1 0 上为无符号数比较
下为有符号比较
CMP结果 标志位 目的操作数 < 源操作数 SF ≠ OF 目的操作数 > 源操作数 SF = OF 目的操作数 = 源操作数 ZF = 1 - CMN(比较取负的值)
/*格式:*/CMN Rn, Op2/* Rn + Op2*/ CMP r0,r1 /*r0+r1*/
- TEQ(测试等价)
/*格式:*/TEQ Rn, Op2/* Rn ^ Op2*/ teq r0,r1 /*r0^r1*/
- TST(测试)
/*格式:*/TST Rn, Op2/* Rn & Op2*/ tst r0,r1 /*r0&r1*/
-
1.3 操作数2的灵活性
add r0,r1,#1 /*r0=r1+1*/
add r0,r1,r2 /*r0=r1+r2*/
add r0,r1,r2,LSL #4 /*r0=r1+r2<<4*/
add r0,r1,r2,LSL r3 /*r0=r1+r2<<r3*/
2.内存访问
2.1 LDR指令
LDR (Load Register)将一个值从内存加载到ARM寄存器,可以选择更新用于给出地址的寄存器。
格式如下
LDR{type}{T}{cond} Rt, [Rn {, #offset}]
LDR{type}{cond} Rt, [Rn, #offset]!
LDR{type}{T}{cond} Rt, [Rn], #offset
LDR{type}{cond} Rt, [Rn, +/-Rm {, shift}]
LDR{type}{cond} Rt, [Rn, +/-Rm {, shift}]!
LDR{type}{T}{cond} Rt, [Rn], +/-Rm {, shift}
type字段可以为
• B – 无符号数(32位)
• SB – 有符号数(32位)
• H – 无符号数半字节(32位数高16或低16)
• SH – 有符号数半字节. (32位数高16或低16)
寻址方式实例
(1) LDR R0, [R1] 加载R1指向的地址到R0
(2) LDR R0, [R1, R2] 加载R1+R2指向的地址到R0
(3) LDR R0, [R1, R2, LSL #2] 加载R1+(R2<<2)指向的地址到R0
(4) LDR R0, [R1, #32]! 加载 R1+32 指向的地址然后 R1:=R1 + 32
(5) LDR R0, [R1], #32 从R1所指向的地址读取R0 然后R1:=R1 + 32
3.流控制
3.1 B
直接跳转不返回,对于简单的相对分支(相对于当前地址的偏移量),使用B指令。
3.2 BL
跳转并返回到连接地址,一般用于函数调用。调用子程序时,如果返回,地址必须存储在链接寄存器中,则使用BL指令
3.3BLX与BX
如果你想改变指令集(从ARM到Thumb或Thumb到ARM),使用BX或BLX。
4.系统(协处理器、调试、模式更改等)
协处理器协处理器指令占用部分ARM指令集。最多可实现16个协处理器,编号为0到15 (CP0, CP1…CP15)
- Coprocessor 15是一个内置的协处理器,提供对许多核心功能的控制,包括缓存和MMU。
- Coprocessor14是一个内置的协处理器,它控制核心的硬件调试设施,比如断点单元。
- 协处理器10和11可以访问系统中的浮点和NEON硬件。
tips
如果执行了协处理器指令,但是系统中没有相应的协处理器,则会发生未定义指令异常.
协处理器指令有五类
- CDP 启动协处理器数据处理操作。
- MRC 从协处理器寄存器移到ARM寄存器。
- MCR -从ARM寄存器移到协处理器寄存器
- LDC 从内存加载协处理器寄存器
- STC 从协处理器寄存器存储到存储器。
tips
我们操作一些特殊寄存器时例如CPSR寄存器时我们一般使用MRC将CPSR读取到arm寄存器,操作完后用MCR写回到
CPSR寄存器。
5.其他
简单介绍几个ARM架构下的主要寄存器
堆栈指针R13(SP)
每一种异常模式都有其自己独立的r13,它通常指向异常模式所专用的堆栈,当ARM进入异常模式的时候,程序就可以把一般通用寄存器压入堆栈,返回时再出栈,保证了各种模式下程序的状态的完整性。
连接寄存器R14(LR)
保存子程序返回地址。使用BL或BLX时,跳转指令自动把返回地址放入r14中;
子程序通过把r14复制到PC来实现返回,通常用下列指令:MOV PC, LR;BX LR;
当异常发生时,异常模式的R14用来保存异常返回地址,将R14如栈可以处理嵌套中断。
程序计数器R15(PC)
保存当前程序执行指令的首地址。
- PUSH(入栈)
主要用于保存一些临时的变量用于调用。
- POP(出栈)
主要用于读取入栈的变量的值。
- CPS
主要用于在特权模式下直接修改CPSR寄存器的值,不需要使用MRC和MCR指令操作。
- STR
主要用于把源寄存器的值放到存储器中去。指令格式可以参考LDR指令
GNU汇编与arm区别
通常用下列指令:MOV PC, LR;BX LR;
当异常发生时,异常模式的R14(LR链接寄存器)用来保存异常返回地址(PC),将R14如栈可以处理嵌套中断。
程序计数器R15(PC)
保存当前程序执行指令的首地址。
- PUSH(入栈)
主要用于保存一些临时的变量用于调用。
- POP(出栈)
主要用于读取入栈的变量的值。
- CPS
主要用于在特权模式下直接修改CPSR寄存器的值,不需要使用MRC和MCR指令操作。
- STR
主要用于把源寄存器的值放到存储器中去。指令格式可以参考LDR指令