03 ARM 指令系统
文章目录
部分参考:
ARM64指令简易手册 - 简书 (jianshu.com)
指令格式
ARM 指令的基本格式
ARM指令基本格式
<opcode>{<cond>}{S} <Rd>,<Rn>,{<Op2>}
<>必须
{}可选
<opcode>
指令助记符 必需
{<cond>}
指令执行条件 可选。不写->无条件执行
ARM指令格式说明
项目 | 含义 | 备注 |
---|---|---|
opcode | 指令的操作码 | 指令助记符,eg LDR,STR |
cond | 条件域,满足条件才执行指令 | 可不加条件 |
S | 指令执行时是否更新CPSR(程序状态寄存器) | 可省略 |
Rd | 目标寄存器 | Rd为任意通用寄存器 |
Rn | 第一个操作数 | Rn为任意通用寄存器 |
Op2 | 第二个操作数 | 可为#immed_8r、寄存器Rm及任意移位的寄存器 |
; | 注释符号 | 加注释 |
Op2备注
#immed_8r
常数表达式,该常数必须对应8位位图,即一个8位的常数通过循环右移偶数位得到的32位常数
寄存器Rm
在寄存器方式下,操作数即为寄存器的数值
eg
SUB R1,R1,R2 ; R1-R2→R1
MOV PC,R0 ; PC=R0,程序跳转到指定地址
寄存器Rm 移位
将寄存器的移位结果作为操作数,Rm 值保持不变。
ADD R1,R1,R1,LSL#3 ; R1=R1+R1*8=9R1,
SUB R1,R1,R2,LSR#2 ; R2 逻辑右移2 位,最后结果是R1=R1-(R2/4)
汇编语言的一般格式
一个完整的ARM 汇编由两部分组成:声明和实际代码段。
声明
声明代码段
用AREA 指令定义一个段,说明所定义段的相关属性(段的名字和段的属性等)
AREA指令是一个伪指令,用于段定义。
声明ARM 指令
用CODE来声明程序为ARM指令
声明程序入口
用ENTRY 指令标识程序的入口点。在程序完成后要用END 指令声明程序结束。每一个汇编程序段都必须有一条END 指令,指示代码段的结束。
段
段的定义
在ARM 汇编语言程序中,以程序段为单位组织代码。段是相对独立的指令或数据序列,具有特定的名称。
段的分类
一个汇编程序至少有一个代码段。如果程序较长时,可以分割为多个代码段和数据段。多个段在程序编译连接时最终形成一个可执行的映像文件。
代码段
代码段的内容为执行代码
数据段
数据段存放代码运行时需要用到的数据。
ARM 指令编码
eg
AREA ARMex,CODE,READONLY ;代码块命名为ARMex
ENTRY ;声明程序入口
start
MOV r0,#10 ;设置参数
MOV r1,#3
ADD r0,r0,r1 ;r0 = r0 + r1
stop
MOV r0,#0x18 ;
LDR r1,=0x20026 ;将存储器地址为0x20026的字数据读入寄存器R0
SVC #0x123456 ;生成编号为#0x123456的SVC(supervisor call)
条件码
{<cond>}
表示一个可选的条件码。使用条件码“cond”可以实现高效的逻辑操作,提高代码效率
只有在CPSR 中条件码对应的标志满足指定条件时,带条件码的指令才被执行,否则指令被忽略
条件码位于ARM 指令的最高4 位[31:28]
指令的条件码
条件码 | 助记符后缀 | 标识 | 含义 |
---|---|---|---|
0000 | EQ | Z置位 | 相等 |
0001 | NE | Z清零 | 不相等 |
0010 | CS | C置位 | 无符号数大于或等于 |
0011 | CC | C清零 | 无符号数小于 |
0100 | MI | N置位 | 负数 |
0101 | PL | N清零 | 正数或零 |
0110 | VS | V置位 | 溢出 |
0111 | VC | V清零 | 未溢出 |
1000 | HI | C置位Z清零 | 无符号数大于 |
1001 | LS | C清零Z置位 | 无符号数小于或等于 |
1010 | GE | N等于V | 带符号数大于或等于 |
1011 | LT | N不等于V | 带符号数小于 |
1100 | GT | Z清零且(N等于V) | 带符号数大于 |
1101 | LE | Z置位或(N不等于V) | 带符号数小于或等于 |
1110 | AL | 无条件执行 |
1111系统保留,暂不使用
NZCV标志位总结
N
N=1表示运算的结果为负数;N=0表示运算的结果为正数或零
Z
Z=1表示运算的结果为零,Z=0表示运算的结果非零
C
加法运算(包括CMN):当运算结果产生了进位时(无符号数溢出),C=1,否则C=0
减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1
对于包含移位操作的非加/减运算指令,C为溢出值的最后一位。
对于其它的非加/减运算指令,C的值通常不会改变。
V
对于加减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出
对于其它的非加/减运算指令,V的值通常不会改变。
ARM 指令的寻址方式
8 种基本的寻址方式
立即寻址
立即寻址也叫作立即数寻址。立即寻址方式的目的是将操作数紧跟在操作码后面,与操作码一起放在指令代码段中。在程序运行时,程序直接调用该操作数,而不需要到其他地址单元中去取相应的操作数
ADD R1,R0,#1 ;R0 <- R0 + 1
MOV R0,#0xFF00 ;R0 <- 0xFF00
寄存器寻址
寄存器寻址指的是操作数的值在寄存器中,指令中的地址码字段给出的是寄存器编号,指令执行时直接取出寄存器值来操作。
ADD R0,R1,R2 ;R0 <- R1 + R2
MOV R1,R2 ;R1 <- R2
指令MOV R1,R2
寄存器移位寻址
寄存器移位寻址的操作数是由寄存器做相应移位而得到的,移位的方式在指令中以助记符的形式给出,而移位的位数可用立即数或寄存器寻址方式表示。
MOV R0,R2,LSL#3 ;R2的值逻辑左移三位 R0 <- R2*8
ANDS R1,R1,R2,LSL R3 ;R2的值逻辑左移R3位,然后和R1相与存入R1
寄存器间接寻址
寄存器间接寻址指令中的地址码给出的是一个通用寄存器的编号,所需的操作数保存在存储器指定地址的存储单元中,即寄存器是操作数的地址指针。用于**间接寻址的寄存器必须用[ ]**括起来。
STR R0,[R1] ;将R0的值传送到以Rq的值作为地址的存储器中
LDR R1,[R2] ;将以R2的值作为地址的寄存器中的数据传送搭配R1中
指令LDR R1,[R2]
基址寻址
基址寻址方式就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到操作数的有效地址。寄存器间接寻址是偏移量为0的基址加偏移寻址。
用基址访问同在一个存储区域的某一存储单元的内容。
LDR R0,[R1,#4] ;R0 <- [R1 + 4]
LDR R2,[R3,#0x0C] ;R2 <- [R3 + 0x0C]
LDR R2,[R3,#0x0C]
带自动变址的前变址寻址
“!”符号表明指令在完成数据传送后应该更新基址寄存器。ARM 的这种自动变址不消耗额外时间。
LDR R0,[R1,#4]! ;R0 <- [R1 + 4],R1 <- R1 + 4
这条指令是将寄存器R1 的内容加上位移量4 形成操作数的有效地址,从该地址取的操作数存入寄存器R0 中,然后将R1 的内容自增4 字节。
后变址寻址
基址加偏移寻址。基址不带偏移作为传送的地址,传送后自动变址。
LDR R0,[R1],#4 ;R0 <- [R1],R1 <- R1 + 4
这里没有“!”符号,只使用立即数偏移作为基址寄存器的修改量。这条指令是将寄存器R1 的内容作为操作数的有效地址,从该地址取得操作数存入寄存器R0 中,然后将R1 的内容自增4 字节。
指令指定一个基址寄存器,再指定另一个寄存器(变址),其值作为位移与基址相加形成存储器地址
LDR R0,[R1,R2] ;R0 <- [R1 + R2]
相对寻址
相对寻址和基址变址寻址方式类似,以程序计数器PC 的当前值作为基地址,指令中的地址标号作为偏移
量,将两者相加之后得到操作数的有效地址
跳转指令利用相对寻址
多寄存器寻址
多寄存器寻址可以实现一条指令完成多个寄存器值的传送,允许一条指令传送 16 个寄存器的任何子集或所有寄存器。
LDMIA R0,{R1,R2,R3,R4} ;R1 <- [R0]
;R2 <- [R0 + 4]
;R3 <- [R0 + 8]
;R4 <- [R0 + 12]
该条指令的后缀IA 即表示在每次执行完加载/存储操作之后,R0 按字长度(即4 字节)增加。因此指令可以将连续多个存储单元的值传送到R1~R4 中。
LDR R1!,{R2- R4,R6}
R2- R4:R2到R4
堆栈寻址
堆栈使用一个叫作堆栈指针的专用寄存器指示当前操作位置,堆栈指针总是指向栈顶。存储器堆栈可分为如下两种方式
存储器堆栈方式
向上生长
当堆栈由低地址向高地址生成时,称为递增堆栈(Ascending Stack)
向下生长
当堆栈由高地址向低地址生成时,称为递减堆栈(Descending Stack)
堆栈工作方式
在ARM 指令中,数据的进栈和出栈通过Load/Store 指
令实现,指令STM 向堆栈写数据项,指令LDM 从堆栈读
数据项。