第四章 指令系统
数据传送类指令
寄存器到寄存器传送:MOV 指令、MVN指令
MOV R8, R3; R8 = R3
MVN R8, R3; R8 = -R3
学过微机原理的都应记得,x86中一条MOV 指令存储器和寄存器间的任意传送。ARM 中是不行的,这也是CISC和RISC 内核的一个比较明显的区别。
存储器到寄存器传送:LDRx 指令、LDMxy指令
寄存器到存储器:STRx 指令、STMxy指令
LDRx 指令的x可以是B(byte)、H(half word)、D(Double word)或者省略(word),具体的用法如下:
示例 | 功能描述 |
LDRB Rd, [Rn, #offset] | 从地址Rn+offset处读取一个字节送到Rd |
LDRH Rd, [Rn, #offset] | 从地址Rn+offset处读取一个半字送到Rd |
LDR Rd, [Rn, #offset] | 从地址Rn+offset处读取一个字送到Rd |
LDRD Rd1, Rd2, [Rn, #offset] | 从地址Rn+offset处读取一个双字(64位整数)送到Rd1(低32位)和Rd2(高32位)中。 |
STRx 指令的x同样可以是B(byte)、H(half word)、D(Double word)或者省略(word),具体的用法如下:
示例 | 功能描述 |
STRB Rd, [Rn, #offset] | 把Rd中的低字节存储到地址Rn+offset处 |
STRH Rd, [Rn, #offset] | 把Rd中的低半字存储到地址Rn+offset处 |
STR Rd, [Rn, #offset] | 把Rd中的低字存储到地址Rn+offset处 |
STRD Rd1, Rd2, [Rn, #offset] | 把Rd1(低32位)和Rd2(高32位)表达的双字存储到地址Rn+offset处 |
LDRx和STRx指令还有一种带预索引的格式,下面举个例子(注意语句中的“!”):
LDR.W R0,[R1, #20]! ;预索引
上面语句的意思是先把地址R1+offset处的值加载到R0,然后,R1 ßR1+ 20
还有一种后索引形式,注意与上面的预索引的区别(还要注意语句中没有“!”):
STR.W R0, [R1], #-12 ;把R0的值存储到地址R1处。完毕后, R1ßR1+(-12)
LDMxy指令和STMxy指令可以一次传送更多的数据。
X可以为要I或D,I表示自增(Increment),D表示自减(Decrement)。
Y可以为A或B,表示自增或自减的时机是在每次访问前(Before)还是访问后(After)。
另外,指令带有“.W”后缀表示这条指令是32位的Thumb-2指令,否则是16位的指令。
示例 | 功能描述 |
LDMIA Rd!, {寄存器列表} | 从Rd处读取多个字,并依次送到寄存器列表中的寄存器。每读一个字后Rd自增一次,16位指令 |
LDMIA.W Rd!, {寄存器列表} | 从Rd处读取多个字,并依次送到寄存器列表中的寄存器。每读一个字后Rd自增一次 |
STMIA Rd!, {寄存器列表} | 依次存储寄存器列表中各寄存器的值到Rd给出的地址。每存一个字后Rd自增一次,16位指令 |
STMIA.W Rd!, {寄存器列表} | 依次存储寄存器列表中各寄存器的值到Rd给出的地址。每存一个字后Rd自增一次 |
LDMDB.W Rd!, {寄存器列表} | 从Rd处读取多个字,并依次送到寄存器列表中的寄存器。每读一个字前Rd自减一次 |
STMDB.W Rd!, {寄存器列表} | 存储多个字到Rd处。每存一个字前Rd自减一次 |
这里需要特别注意!的含义,它表示要自增(Increment)或自减(Decrement)基址寄存器Rd的值,时机是在每次访问前(Before)或访问后(After)。比如:
假设 R8=0x8000,则
STMIA.W R8!, {R0-R3} ; R8值变为0x8010
STMIA.W R8, {R0-R3} ; R8值不变
上面两行代码都是将R0-R3共16个字节的数据存储到从0x8000开始的16个字节空间中,唯一的区别是第一条指令执行完后R8被更新为0x8010,而第二条指令不更新R8。
立即数的加载
MOV支持8位立即数加载,比如:
MOV R0, #0x12
32位指令MOVW(加载到寄存器的低16位)和MOVT(加载到寄存器的高16位)可以支持16位立即数加载。如果要加载32位的立即数,必须先使用MOVW,再使用MOVT,因为MOVW会清零高16位。
LDR 和ADR的区别
LDR和ADR都是伪指令,都可以用来加载一个立即数(也可以是一个地址),如果加载的是程序地址,LDR会自动地把LSB置位,ADR则不会:
LDR R0, =address1 ; R0= 0x4000 | 1
ADR R1, address1 ; R1= 0x4000。注意:没有“=”号
…
address1
0x4000: MOV R0, R1
特殊功能寄存器只能用MSR/MRS指令访问:
MRS <gp_reg>, <special_reg> ;读特殊功能寄存器的值到通用寄存器
MSR <special_reg>, <gp_reg> ;写通用寄存器的值到特殊功能寄存器
下面是两个例子:
MRS R0, PRIMASK ; 读取PRIMASK到R0中
MSR BASEPRI, R0 ;写入R0到BASEPRI中
但是需要注意大多数的特殊功能寄存器都只能在特权级下访问,非特权级下只能访问APSR