ARM 指令集的基础指令
发布日期:2021-07-01 04:06:18 浏览次数:4 分类:技术文章

本文共 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 规定有:

  1. 各寄存器的使用规则及其相应的名称;
  2. 数据栈的使用规则;
  3. 参数传递的规则。

简而言之,如果函数有不多于四个参数,使用 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。

数据处理指令

数据处理指令语法:

<操作{>
}{
S}>
,
,
<操作码>
<目标寄存器rd>
<第一操作寄存器rn>
<第二操作数operand2>
;第一个位置必须是寄存器,第二操作数可以是寄存器,也可以是立即数

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 位的数据处理时需要借助 ADDSADC 来保存和使用进位标志位。

指令示例:

;两个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

如何判断大小:

  1. ZF:ZF=1 则说明两个数相等,因为zero为1说明结果为0。
  2. 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

如何判断溢出:

  1. 两数同为正,相加,值为负,则说明溢出。
  2. 两数同为负,相加,值为正,则说明溢出。
  3. 两数相减,同号,则不溢出;两数为异号,结果与减数符号相同,则溢出。

指令示例:

;实质是一条减法指令;没有目标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 微处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。

  1. LDR 和 STR 用于 Int 类型
  2. LDRB 和 STRB 用于 Char 类型
  3. 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

堆栈是特定顺序进行存取的存储区,操作顺序分为“后进先出”和“先进后出”。

堆栈寻址时隐含的,它使用一个专门的寄存器(堆栈指针)指向一块存储区域(堆栈),指针所指向的存储单元就是堆栈的栈顶。存储器堆栈可分为两种:

  1. 向上生长:向高地址方向生长,称为递增堆栈
  2. 向下生长:向低地址方向生长,称为递减堆栈

根据堆栈指针又可以分成两种:

  1. 堆栈指针指向最后压入的堆栈的有效数据项,称为满堆栈
  2. 堆栈指针指向下一个要放入的空位置,称为空堆栈

这样就有 4 中类型的堆栈表示递增和递减的满堆栈和空堆栈的各种组合:

  1. 满递增:堆栈通过增大存储器的地址向上增长,堆栈指针指向内含有效数据项的最高地址。

    • 指令如 LDMFA,STMFA 等。
  2. 空递增:堆栈通过增大存储器的地址向上增长,堆栈指针指向堆栈上的第一个空位置。

    • 指令如 LDMEA,STMEA 等。
  3. 满递减:堆栈通过减小存储器的地址向下增长,堆栈指针指向内含有效数据项的最低地址。

    • 指令如 LDMFD,STMFD 等。
  4. 空递减:堆栈通过减小存储器的地址向下增长,堆栈指针指向堆栈下的第一个空位置。

    • 指令如 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Go 生产者-消费者:交替输出奇数和偶数(通过接力棒协作)
下一篇:读书笔记:《苏世民:我的经验与教训》

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月08日 05时50分05秒