因为处理器架构不一样,指令集有所不同。所以一种架构,对应一种指令集。
指令是让计算机做某个操作的具体命令,这个命令计算机能识别,每一个指令有它自己的操作意义。
ARM指令在计算机中是32位的二进制数表示,所以占用4个字节。
如ARM中有一条指令如下:
ADDEQS R0,R1,#85
所以指令其实程序员与计算机之间的交互语言,以上面这条指令为例,如果你要让计算机把R1+85之后的值再赋给R0,那你必须严格按照这个指令格式去书写代码,如果不按这个格式,cpu是无法识别的。
<opcode>{<cond>}{S} <Rd>,<Rn>{,<operand2>}
注:
ADDS R1,R1,#1 @加法指令,R1 = R1+1影响CPSR 寄存器,带有S
条件码(31~28共4位表示):
条件域本质上是根据CPSR寄存的条件状态位(N,Z,C,V)来判断
所有指令都可以条件执行。
分为六大类,数据处理指令,数据加载指令和存储指令,分支指令,程序状态寄存器(CPSR)处理指令,协处理器指令和异常产生指令六大类。
ARM指令的寻址方式
ARM指令的寻址方式分为8类:立即数寻址,寄存器寻址,寄存器间接寻址,寄存器移位寻址,基址变址寻址,多寄存器寻址,相对寻址,堆栈寻址
举例:
MOV R0,#15 ;立即数15放入寄存器R0中
ADD R0,R1,R2 ;R0 <= R1+R2
LDR R0,[R4] ;R0 <= [R4](R4中存放的是一个地址值,[ ]表示取该地址值指向的内容)
ADD R0,R1,R2,LSL #1 ;R0 <= R1+R2(R2左移一位后的值)
MOV R0,R1,LSL R3 ;R0 <= R1(R1左移R3位后)
LDR R0,[R1,#4] ;R0 <= [R1+4]
LDR R0,[R1,#4]! ;R0 <= [R1+4],R1 <= R1+4。同时更新基址
LDR R0,[R1],#4 ;R0 <= [R1],R1 <= R1+4
LDR R0,[R1,R2] ;R0 <= [R1+R2]
LDMIA R0!,{R1 - R4} ;R1 <= [R0]、R2 <= [R0+4]、R3 <= [R0+8]、R4 <= [R0+12]
BL proc ;跳转到子程序proc处执行,执行完毕后返回(L标记,带返回的跳转)。
STMFD R13!,{R0 - R4} ;R0-R4压栈,FD为满栈递减,地址从高到低,R13为SP堆栈指针
LDMFD R13!,{R0 - R4} ;R0-R4出栈,FD为满栈递减
- cond:指令执行的条件码
- I:用于区别第二操作数是立即数(I=1)还是寄存器移位(I=0)
- opcode:数据处理指令操作码 S:用于设置条件码,S=0时,条件码不改变,S=1时,条件码根据具体指令的结果修改,本质上是修改CPSR寄存器状态位,条件码根据CPSR位来判断
- Rn:第一操作数寄存器 Rd:目标寄存器
- Op2:第二操作数,该数可以是立即数或寄存器移位数
mov r0,#1 @ r0=1 #1 立即数
mov r1,r0 @ r1=r0
mov r2,r1,lsl #1 @将寄存器r1的值左移1位之后再赋给r2
mov r0,#1 @ r0=1
mvn r1,r0 @ r1 = ~r0;
mvn r1,#2 @ r1 = ~2;
mvn r2,r1,lsr #1 @r1的值右移一位之后,取反之后给r2
第一操作数寄存器可以和目的寄存器Rd相同。
逻辑移位
LSL(或ASL)操作
LSL(或ASL)操作的格式为:
通用寄存器,LSL(或ASL)操作数
LSL(或ASL)可完成对通用寄存器中的内容进行逻辑(或算术)的左移操作,按操作数所指定的数量向左移位,低位用零来填充。
其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
操作示例
MOV R0, R1, LSL#2 ;将R1中的内容左移两位后传送到R0中。
LSR操作
LSR操作的格式为:
通用寄存器,LSR 操作数
MOV R0, R1, LSR#2 ;将R1中的内容右移两位后传送到R0中,左端用零来填充。
算术移位
ASR操作
ASR操作的格式为:通用寄存器,ASR操作数
ASR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用第31位的值来填充。
其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
操作示例:
MOV R0, R1, ASR#2 ;将R1中的内容右移两位后传送到R0中,左端用第31位的值来填充。
ROR操作
ROR操作的格式为:
通用寄存器,ROR 操作数
ROR可完成对通用寄存器中的内容进行循环右移的操作,按操作数所指定的数量向右循环移位,左端用右端移出的位来填充。
其中,操作数可以是通用寄存器,也可以是立即数(0~31)。显然,当进行32位的循环右移操作时,通用寄存器中的值不改变。
操作示例:
MOV R0, R1, ROR#2 ;将R1中的内容循环右移两位后传送到R0中。
加法(ADD)
add r0,r0,#1 @ r0 = r0+1
add r0,r0,r1 @ r0 = r0+r1
add r0,r0,r1,lsr #1 @ r0 = r0 + (r1 >> 1)
减法(SUB RSB)
subR0,R1,R2 @r0 = r1 - r2
subR0,R1,#1 @r0 = r1 - 1
subR0,R1,R2,lsl #1 @R1与寄存器R2左移一位后的值相减,得到的结果传送到R0
RSB R0,R1,R2 @ r0 = r2 - r1 ,注意是R2-R1,方向相反
RSB R0,R1,#6 @ r0 = 6 - r1
RSB R0,R1,R2,LSL #1 @ r0 = (r2 << 1) - r1
以上加减运算指令第一操作数寄存器和目的寄存器可以相同。
乘法(MUL MULS MLA UMULL UMLAL SMULL SMLAL)
mul r0,r1,r2 @ r0 = r1r2,低32位赋给
muls r0,r1,r2 @ r0 = r1r2,同时设置CPSR中的N位和Z位
注意:
乘法运算指令Rd和Rn不能相同。
另外第二操作数也只能是寄存器,不能是立即数或者位移指令。
mla{cond}{S} ,,,
将Rm和Rs相乘之后再加上Rn的值,最后将结果的低32位赋给Rd
注意:
Rd,Rm,Rs,Rn必须都是寄存器
Rd,Rm两个寄存器不能相同
mla r0,r1,r2,r3 @r0 = r1*r2+r3,结果最低32位保存到r0中
umull{cond}{S} ,,, 无符号长乘
将Rm和Rs的值做无符号数相乘,结果低32位保存到Rdl,结果高32位保存到Rdh。
umull r0,r1,r2,r3 @ (r1,r0) = r2*r3
umlal{cond}{S} ,,, 无符号长乘累加
<=> = + Rm*Rs
将Rm和Rs的值做无符号相乘之后,64位乘积与(Rdh,Rdl)组成的值进行相加,结果的低32位放Rdl,结果的高32位放到Rdh。
umlal r0,r1,r2,r3 @ (r1,r0) = (r1,r0)+r2*r3
smull{cond}{S} ,,, 有符号长乘
smlal{cond}{S} ,,, 有符号长乘累加
与运算 AND
and{cond}{S} <Rd>,<Rn>,<oprand2>
and r0,r0,r1 @r0 = r0&r1
and r0,r0,#5 @r0 = r0&5
and r0,r0,r1,lsl #1 @r0 = r0 & (r1 << 1)
或运算 ORR EOR
orr r0,r0,r1 @r0 = r0|r1
orr r0,r0,#5@r0 = r0|5
orr r0,r0,r1,lsl #1 @r0 = r0 | (r1 << 1)
eor r0,r0,r1 @r0 = r0^r1
eor r0,r0,#5 @r0 = r0^5
eor r0,r0,r1,lsl #1 @r0 = r0 ^ (r1 << 1)
bic{cond}{S} <Rd>,<Rn>,<oprand2>
将寄存器Rn的值与第二操作数oprand2的值的反码做逻辑与操作,结果保存到Rd中。
bic r0,r0,r1 @r0 = r0 & (~r1)
bic r0,r0,#5 @r0 = r0 & (~5)
bic r0,r0,r1,lsl #1 @r0 = r0 & (~(r1 << 1))
CMP : 比较(Compare)
CMP{条件}{P} , status = op_1 - op_2
Cmp r0,#1 == >cpsr
CMP 允许把一个寄存器的内容如另一个寄存器的内容或立即值进行比较,更改状态标志来允许进行条件执行。它进行一次减法,但不存储结果,而是正确的更改标志。标志表示的是操作数 1 比操作数 2 如何(大小等)。如果操作数 1 大于操作操作数 2,则此后的有 GT 后缀的指令将可以执行。明显的,你不需要显式的指定 S 后缀来更改状态标志。
CMN : 比较取负的值(Compare Negative)
CMN{条件}{P} , status = op_1 - (- op_2)
CMN 同于 CMP,但它允许你与取负值(操作数 2 的取负的值)进行比较,比如难于用其他方法实现的用于结束列表的 -1。这样与 -1 比较将使用:
CMN R0, #1 ; 把 R0 与 -1 进行比较
详情参照 CMP 指令。
TST : 测试位
(Test bits)
TST{条件}{P} , Status = op_1 AND op_2
TST指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位与运算,并根据运算结果更新CPSR中的条件标志位的值,操作数1是要测试的数据,操作数2是一个位掩码,根据测试结果设置相应标志位。当位与结果为0时,EQ位被设置为1。测试寄存器某些位是否为0,如果为0,则CPSR中Z位置1。
像 CMP 那样,你不需要指定 S 后缀。
TST R0, #1 ; 测试在 R0 中bit0是否为 0。
跳转指令用于实现程序流程的跳转。相当于C语言的goto。
在arm中,实现程序流程跳转的方式主要有两种:
使用专门的跳转指令,只能实现当前指令前后32M范围内的跳转。
修改PC寄存器值,实现全范围4G的范围内任意跳转。
任意跳转,在跳转前结合使用MOV LR,PC 。保存将来返回来的地址。
B
格式 :B{cond} 目标地址 @立即跳转到目标地址执行
BL
格式 :BL{cond} 目标地址
@跳转到目标地址,并且将PC值存放到LR中。
处理程序状态寄存器的读写指令,不能用通用寄存器读写指令mov,而有专门的操作指令MRS和MSR。
MRS R0, CPSR 读取CPSR的内容写入R0
MSR CPSR, R0 读取R0的内容写入CPSR
MSR CPSR_f, R0 读取R0的24-31的内容写入CPSR的24-31bit
MSR CPSR_c, R0 读取R0的0-7的内容写入CPSR的0-7bit
MSR CPSR_fc, R0
MSR CPSR_f, #0xf0000000 将0xf写入CPSR的24-31bit
注意:所有的CPSR可以替换成SPSR
如何禁止CPU中断?代码如下:
MRS R0,CPSR
MOV R1,#1
orr r0,r0,R1,LSL#7
Msr cpsr,r0
内存和寄存器之间传递数据。
Ldr{cond} 目的寄存器 <存储器的地址>
Ldr r0,[r1] 将存储器地址为r1的字数据存入寄存器r0
Ldr r0,[r1,r2] 将存储器地址为r1+r2的字数据存入寄存器r0
Ldr r0,[r1,#8]! 将存储器地址为r1+8的字数据存入寄存器r0,更新 r1 = r1+8
Ldr r0,[r1,r2]! 将存储器地址为r1+r2的字数据存入寄存器r0,并将新的r1+r2的值写入r1
LDRB{cond} 目的寄存器 <存储器的地址>
将一个8位的字节数据从存储器传送到目的寄存器。同时将寄存器的高24位清零。
LDRH{cond} 目的寄存器 <存储器的地址>
将一个16位的字节数据从存储器传送到目的寄存器。同时将寄存器的高16位清零。
LDRSH 从内存读取16bit的(有符号)数据到寄存器
LDRSB 从内存读取8bit的(有符号)数据到寄存器
带符号和不带符号的区别
运行下面指令后R1寄存器的值如下:
LDRB R1, [R0] (0x81) 0x00 00 00 81
LDRSB R1, [R0] (0x81) 0xFF FF FF 81 0b10000001
LDRSB R1, [R0] (0x71) 0x00 00 00 71 0b01110001
LDRH R1, [R0] (0x82 81) 0x00 00 82 81
LDRSH R1, [R0] (0x82 81) 0xFF FF 82 81
LDRSH R1, [R0] (0x72 81) 0x00 00 72 81
STR{cond}源寄存器,<存储器地址>
将源寄存器中32位数传送到存储器地址指定存储器内。
Str r0,[r1],#8 将r0寄存器的值存入存储器的r1地址处。同时r1=r1+8
Str r0,[r1,#8] 将r0寄存器的值存入存储器的r1+8地址处。
STRH 保存寄存器的(低16bit)数据到内存
STRB 保存寄存器的(低8bit)数据到内存
需要考虑有无符号的指令
比较大小, 条件助记符中需要区分有(GE/GT/LE/LT)无(CS/HI/LS/CC)符号
右移运算, 需要区分有(ASR)无(LSR)符号
从内存中读取(8bit和16bit)数据时, 需要区分有(SB/SH)无(B/H)符号
批量内存操作 ldm stm
Ldm 从由基地址寄存器指定的一片连续的存储器到寄存器列表所指示的多个寄存器之间传递
Stm 将寄存器的数据传到内存中
cond 条件助记符
Rb 基址寄存器
! 是否更新基址寄存器的值
<寄存器 list> 跟内存交换数据的寄存器
{R0}
{R0, R1}
{R0 - R8, R15}
例子:从内存的0x40000008 地址处 读取 16个字节 ?
Ldr r0,=0x40000008
ldmfd r0 {r0-r3}
addressing_mode:
- IA(Increment After transmit) 传送后地址加4
- IB(Increment Before) 传送前地址加4
- DA(Decrement After) 传送后地址减4
- DB(Decrement Before) 传送前地址减4
- FD(Full stack Decrement) 满递减堆栈
- FA (full stack add) 满递增堆栈
- ED 空递减堆栈
- EA 空递增堆栈
关于堆栈的一些概念:
满堆栈(full stack,“F”)是指堆栈指针指向堆栈的最后一个已使用的地址或者满位置(也就是sp指向堆栈最后一个数据项位置)。
空堆栈(empty stack,“E”)是指sp指向堆栈的第一个没有使用的地址或者空位置(也就是说sp指向堆栈最后一个数据项的下一个位置)。
示意图样例解释:
Ldmfd sp!,{r4-r12,lr} //load
以满栈递减的方式将堆栈内存加载到 寄存器r4~r12, lr中,出栈。
Stmfd sp!,{r4-r7,lr} //store
以满栈递减的方式将寄存器r4-r7,lr数据存入堆栈内,入栈。