本文共 7317 字,大约阅读时间需要 24 分钟。
ARM 和汇编语言基础
ARM 基础
位、字节、字是计算机数据存储的单位。 位是最小的存储单位,每一个位存储一个1位的二进制码,一个字节由8位组成。 而字通常为16、32或64个位组成。ARM 处理器是 32 位设计的。
ATPCS 规则
ATPCS即ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)的简称。
PCS规定了应用程序的函数可以如何分开地写,分开地编译,最后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。
在实际工程应用中 ARM 汇编常常与 C 语言混合编程,为了实现 ARM 汇编与 C 语言程序的相互调用,子程序需要遵循 ATPCS 规则,基本的 ATPCS 规定有:
- 各寄存器的使用规则及其相应的名称;
- 数据栈的使用规则;
- 参数传递的规则。
简而言之,如果函数有不多于四个参数,使用 ARM 寄存器 R0-R3 进行传递,若参数多于四个则借助栈,函数返回值通过 R0 返回。在 C 程序中声明函数原型,并使用 extern 标记为外部实现,在 C 语言主程序中即可调用该函数。
交叉编译过程:略
寄存器基础
- 程序计数器 pc:保存着当前CPU执行指令的地址。不能用作算数指令的源或目的地以及用作加载或存储指令。
- 堆栈指针:
- sp:指向堆栈的顶部。
- fp:帧指针,指向当前frame的栈底,也就是高地址。
- 链接寄存器:lr,存储着函数的返回地址。
- 程序状态寄存器:
- CPSR:程序状态寄存器(current program status register) CPSR 在用户级编程时用于存储条件码。例如,‘C’ 位用于加法或减法的进位操作。‘Z’ 位用于
TST、CMP、TEQ
等判断指令。 - SPSR:当发生异常时,cpsr 会存入 spsr 直到异常恢复再复制回 cpsr。
- CPSR:程序状态寄存器(current program status register) CPSR 在用户级编程时用于存储条件码。例如,‘C’ 位用于加法或减法的进位操作。‘Z’ 位用于
数据处理指令
数据处理指令语法:
<操作{>}{ S}> 操作{>, , <操作码> <目标寄存器rd> <第一操作寄存器rn> <第二操作数operand2> ;第一个位置必须是寄存器,第二操作数可以是寄存器,也可以是立即数 第二操作数operand2> 第一操作寄存器rn> 目标寄存器rd> 操作码>
MOV(数据传送指令)
指令格式:MOV{条件}{S} 目的寄存器,源操作数
简要说明:MOV 指令是数据传送指令,用于将一个数据从源地址传送到目标地址(寄存器间的数据传送本质上也是一样的)。 其特点是不破坏源地址单元的内容。其中S选项为指令的操作结果是否操作CPSR中的条件标志位,当没有S选项时指令不更新CPSR中的条件标志位结果。
指令示例:
mov r1, #0x1 ;r1 = 0x1 0x1 是立即数mov r2, r1 ;r2 = r1 mvn r3, r2 ;r3 = ~r2mov r1, 0xffffff00 ;0xffffff00 不是立即数,只是编译器在编译阶段对其进行了替换mvn r1, 0x000000ff ;替换的指令
ADD(加法指令)
ADD(加法指令)
指令示例:
;加法指令执行时,若没有进位 CPSR 'C' 位置 0mov r0, #1mov r1, #1add r2, r1, r0 ;r2 = r1 + r0add r2, r1, #2 ;r2 = r1 + 2
ADDS(带标志位的加法指令)
简要说明:默认情况下,数据处理指令不影响条件码标志位,但可以选择通过添加“S”来影响标志位。
指令示例:
;默认情况下,数据处理指令不影响条件码标志位,但可以选择通过添加“S”来影响标志位。mov r1, #0# mov r2, #-1adds r3, r1, r2
ADC(带进位的加法指令)
检验说明:使用 32 位寄存器进行 64 位的数据处理时需要借助 ADDS
和 ADC
来保存和使用进位标志位。
指令示例:
;两个64位数相加,第一个64位的低32位放在 r0,高位放到 r1,第二个64位数的低32位放在 r2 高32位放在 r3;编写代码实现两个64位数的和,结果的低32位放在 r4 高32位放在 r5mov r0, #0xfffffffe ;第一个数的低32位mov r1, #1 ;第一个数的高32位mov r2, #0x5 ;第二个数的低32位 mov r3, #1 ;第二个数的高32位adds r4, r0, r2 ;保存进位标志位到 CPSRadc r5, r1, r3 ; ADC 运算的实质是 r5 = r1 + r3 + 'C','C'位为 CPSR 进位标志
SUB(减法指令)
SUB(减法指令)
注意:加法没有进位时 CPSR ‘C’ 位置 0,而减法没有借位时 CPSR ‘C’ 位置 1,因为减法的本质是加法的逆运算
指令示例:
;减法指令执行时,没有借位时 CPSR 'C' 位置 1mov r0, #5mov r1, #3sub r2, r0, r1 ;r2 = r0 - r1
SUBS & SBC(带标志位和借位的减法指令)
指令示例:
mov r0, #1 ;第一个数的低32位mov r1, #3 ;第一个数的高32位mov r2, #3 ;第二个数的低32位mov r3, #1 ;第二个输的高32位subs r4, r0, r2 sbc r5, r1, r3
RSB(逆向减法)
指令示例:
mov r0, #3rsb r1, r0, #5 ;r1 = 5 - r0
MUL(乘法指令)
MUL(乘法指令)
指令示例:
;为了提高效率,任何乘法指令不可以使用立即数mov r0, #3mov r1, #5mov r2, r0, r1 ;r2 = r0 * r1
MLA(乘累加指令)
指令示例:
mla r3 ,r0, r1, r2 ;r3 = (r0 * r1) + r2
逻辑运算指令
AND(逻辑与指令)
指令示例:
mov r0, #0xf0mov r1, #0x0fand r2, r0, r1 ;r2 = r0 & r1
ORR(逻辑或指令)
指令示例:
mov r0, #0xf0mov r1, #0x0forr r2, r0, r1 ;r2 = r0 | r1
BIC(位清零指令)
指令格式:BIC{cond}{S} Rd,Rn,operand2
简要说明:BIC 指令将 Rn 的值与操作数 operand2 的反码按位逻辑”与”,结果存放到目的寄存器 Rd 中。
指令示例:
BIC R0, R0, #0x0F ;将R0最低4位清零,其余位不变。mov r0, #0xffbic r0, r0, #0xf ;第二个操作数的每一位为 1 就把第一个操作数对应的位清零
TST(位测试指令)
指令示例:
;实质是与运算 常用于用来测试某一位或某几位是 0 还是 1,结果通过 CPSR 的 'Z' 位判断tst r0, #0x3
CMP(比较指令)
简要说明:为第一个操作减去第二个操作数,但不影响第两个操作数的值,它影响 CPSR flag的 CF,ZF,OF,AF,PF
如何判断大小:
- ZF:ZF=1 则说明两个数相等,因为zero为1说明结果为0。
- CF:
- 当无符号时:
- CF=1 则说明了有进位或借位,cmp是进行的减操作,故可以看出为借位,所以,此时oprd1<oprd2
- CF=0 则说明了无借位,但此时要注意ZF是否为0,若为0,则说明结果不为0,故此时oprd1>oprd2
- 当有符号时:
- 若SF=0,OF=0 则说明了此时的值为正数,没有溢出,可以直观的看出,oprd1>oprd2
- 若SF=1,OF=0 则说明了此时的值为负数,没有溢出,则为oprd1<oprd2
- 若SF=0,OF=1 则说明了此时的值为正数,有溢出,可以看出oprd1<oprd2
- 若SF=1,OF=1则说明了此时的值为负数,有溢出,可以看出oprd1>oprd2
- 当无符号时:
如何判断溢出:
- 两数同为正,相加,值为负,则说明溢出。
- 两数同为负,相加,值为正,则说明溢出。
- 两数相减,同号,则不溢出;两数为异号,结果与减数符号相同,则溢出。
指令示例:
;实质是一条减法指令;没有目标register,用来比较两个数是否相等,结果放到 CPSR 的 'Z' 位判断mov r0, #2mov r1, #1cmp r0, r1
TEQ(相等测试指令)
简要说明:实质是异或运算,测试两个数是否相等,两个数相等时异或结果位 0,通过 CPSR 的 ‘Z’ 位判断
指令示例:
;实质是异或运算,测试两个数是否相等,两个数相等时异或结果位 0,通过 CPSR 的 'Z' 位判断teq r0, r1
移位运算指令
指令格式:op{S}{cond} Rd, Rm, Rs
简要说明:如果指定了 S,则这些指令将会根据结果来更新 N 和 Z 标记。如果移位值为 0,则不会影响 C 标记。否则,C 标记会更新为移出的最后一位。
- LSL 逻辑左移,高位移出,低位补零
- ASL 算术左移,和 LSL 是等同的,可以自由互换。
- LSR 逻辑右移,低位移出,高位补零
- ASR 算术右移,低位移出,高位补符号位
- ROR 循环右移,低位移出,高位补低位移出位
- RRX 带扩展的循环右移
ASL
指令示例:
mov r0, #0xffmov r1, r0, lsl #4 ;与mov配合使用,将 r0 逻辑左移 4 位放入 r1 中LSLS r1, r2, r3 ;单独使用
加载/存储指令
ARM 微处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。
- LDR 和 STR 用于 Int 类型
- LDRB 和 STRB 用于 Char 类型
- LDRH 和 STRH 用于 Short 类型
LDR(加载指令)
LDR(字数据加载指令)
指令格式:LDR{条件} 目的寄存器,<存储器地址>
简要说明:LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。
指令示例:
LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0。LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。LDR R0,[R1,R2] ! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。LDR R0,[R1,#8] ! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。LDR R0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
LDRB(字节数据加载指令)
指令格式:LDR{条件}B 目的寄存器,<存储器地址>
简要说明:LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。
指令示例:
LDRB R0,[R1] ;将存储器地址为R1的字节数据读入寄存器R0,并将R0的高24位清零。LDRB R0,[R1,#8] ;将存储器地址为R1+8的字节数据读入寄存器R0,并将R0的高24位清零。
LDRH(半字数据加载指令)
指令格式:LDR{条件}H 目的寄存器,<存储器地址>
简要说明:LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。
指令示例:
LDRH R0,[R1] ;将存储器地址为R1的半字数据读入寄存器R0,并将R0的高16位清零。LDRH R0,[R1,#8] ;将存储器地址为R1+8的半字数据读入寄存器R0,并将R0的高16位清零。LDRH R0,[R1,R2] ;将存储器地址为R1+R2的半字数据读入寄存器R0,并将R0的高16位清零。
STR(存储指令)
STR(字数据存储指令)
指令格式:STR{条件} 源寄存器,<存储器地址>
简要说明:STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。
指令示例:
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
STRB(字节数据存储指令)
指令格式:STR{条件}B 源寄存器,<存储器地址>
简要说明:STRB指令用于从源寄存器中将一个8位的字节数据传送到存储器中。该字节数据为源寄存器中的低8位。
指令示例:
STRB R0,[R1] ;将寄存器R0中的字节数据写入以R1为地址的存储器中。STRB R0,[R1,#8] ;将寄存器R0中的字节数据写入以R1+8为地址的存储器中。
STRH(半字数据存储指令)
指令格式:STR{条件}H 源寄存器,<存储器地址>
简要说明:STRH指令用于从源寄存器中将一个16位的半字数据传送到存储器中。该半字数据为源寄存器中的低16位。
指令示例:
STRH R0,[R1] ;将寄存器R0中的半字数据写入以R1为地址的存储器中。STRH R0,[R1,#8] ;将寄存器R0中的半字数据写入以R1+8为地址的存储器中。
堆栈操作指令
PUSH 和 POP
指令格式:
PUSH {reglist[,LR]}
POP {reglist[,PC]}
简要说明:寄存器入栈及出栈指令。实现低寄存器和可选的 LR 寄存器入栈寄存器和可选的 PC 寄存器出栈操作,堆栈地址由 SP 寄存设置,堆栈是满递减堆栈.
指令示例:
PUSH { R4,LR} ;将低寄存器R4入栈,LR也入栈。POP { R4,PC} ;将堆栈中的数据弹出到低寄存器 R4 及 PC 中PUSH { R0-R7,LR} ;将低寄存器 R0~R7 全部入栈,LR 也入栈POP { R0-R7,PC} ;将堆栈中的数据弹出到低寄存器 R0~R7 及 PC 中
STMF 和 LDMF
堆栈是特定顺序进行存取的存储区,操作顺序分为“后进先出”和“先进后出”。
堆栈寻址时隐含的,它使用一个专门的寄存器(堆栈指针)指向一块存储区域(堆栈),指针所指向的存储单元就是堆栈的栈顶。存储器堆栈可分为两种:
- 向上生长:向高地址方向生长,称为递增堆栈
- 向下生长:向低地址方向生长,称为递减堆栈
根据堆栈指针又可以分成两种:
- 堆栈指针指向最后压入的堆栈的有效数据项,称为满堆栈
- 堆栈指针指向下一个要放入的空位置,称为空堆栈
这样就有 4 中类型的堆栈表示递增和递减的满堆栈和空堆栈的各种组合:
-
满递增:堆栈通过增大存储器的地址向上增长,堆栈指针指向内含有效数据项的最高地址。
- 指令如 LDMFA,STMFA 等。
-
空递增:堆栈通过增大存储器的地址向上增长,堆栈指针指向堆栈上的第一个空位置。
- 指令如 LDMEA,STMEA 等。
-
满递减:堆栈通过减小存储器的地址向下增长,堆栈指针指向内含有效数据项的最低地址。
- 指令如 LDMFD,STMFD 等。
-
空递减:堆栈通过减小存储器的地址向下增长,堆栈指针指向堆栈下的第一个空位置。
- 指令如 LDMED,STMED 等。
跳转指令
- B:在32M空间内的相对跳转指令,label 满足条件立即跳转到Label指定的地址执行
- BEQ:相等则跳转(Branch if EQual)
- BNE:不相等则跳转(Branch if Not Equal)
- BGE:大于或等于跳转(Branch if Greater than or Equa)
- BGT:大于跳转(Branch if Greater Than)
- BL:带链接的相对跳转指令,带链接的跳转。 首先将当前指令的下一条指令地址保存在LR寄存器,然后跳转的label。通常用于调用子程序,可通过在子程序的尾部添加
mov pc, lr
返回。 - BLE:小于或等于跳转(Branch if Less than or Equal)
- BLEQ:带链接等于跳转(Branch with Link if EQual)
- BLLT:带链接小于跳转(Branch with Link if Less Than)
- BLT:小于跳转(Branch if Less Than)
- BX:切换跳转,带状态切换的跳转。最低位为1时,切换到Thumb指令执行,为0时,解释为ARM指令执行。
- BLX:带链接的切换跳转,带链接和状态切换的跳转。结合了BX与BL功能。
指令示例:
LDR r0,=0X3LDR r1,=0X8FCMP r0,r1BGE a_labelSUBS r1,r1, #0XC9
其他指令
NOP(空指令)
有时候,我们不得不需要修改BL或者Jl之类的指令,改为什么也不做,这个NOP指令就派上用场了。
转载地址:https://mortal.blog.csdn.net/article/details/110136325 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!