type
status
date
slug
summary
tags
category
icon
password
这一章介绍的都是有对应机器码的指令,而不是伪指令

3.1 ARM指令集简介

3.1.0 ARM指令集概述(*)

ARM微处理器的ARM指令集 ,所有的指令长度都是32位 ,并且大多数指令都在一个单独指令周期内执行。
  • ARM指令集主要特点(在第二章介绍指令系统版本时提过ARM处理器指令集的特点。这里可以补充那里的)
    • 指令是条件执行的
    • ARM微处理器的指令集是加载/存储型的
    • 在多寄存器操作指令中一次最多可以完成16个寄存器的数据传送

3.1.1 ARM指令格式(**)

3.1.1.1 ARM指令一般格式(**)

  • 用助记符表示的ARM指令一般格式
  • 如下:
    • 其中< >的内容必不可少,{ }中的内容可省略。故除了<opcode>其余域都可以选择使用。
      下面对每一个部分进行解释:
    • <opcode>表示操作码,如ADD表示算术加法。
    • {<cond>}表示指令执行的条件域,如EQ、NE等
    • {S}决定指令的执行结果是否影响CPSR的值,使用该后缀则指令执行的结将果影响CPSR的值,否则不影响。
    • <Rd>表示目标或源寄存器
    • <Rn>表示第一个操作数,为寄存器
    • <op2>表示第二个操作数,可以是立即数、寄存器和寄存器移位操作数

3.1.1.2 ARM指令格式示例

  • 指令ADDEQS R0,R1,#8
    • 操作码为ADD
    • 条件域cond为EQ
    • S表示该指令的执行影响CPSR寄存器的值
    • 目的寄存器Rd为R0
    • 第一个操作数寄存器Rn为R1
    • 第二个操作数OP2为立即数#8
    • 执行结果:R0 = R1+ 8
  • 指令LDR R3,[R6]:将R6寄存器内容所指向的存储单元内容读出,保存在R3中,执行条件AL(AL表示所有条件都执行,为条件域的缺省值
  • 指令BEQ NEXT:分支指令B,加上后缀EQ,变为BEQ,表示“满足条件相等则跳转
  • 使用场景如下图:
    • notion image
  • 指令ADD R0,R1,#2:加法指令ADD,R0=R1+2,不带S,计算结果不影响CPSR寄存器。
  • 指令SUBNES R0,R1,#0X03:条件执行减法运算(NE),R0=R1-0X03,影响CPSR寄存器

3.1.1.3 指令的机器码

从形式上看,ARM指令在机器中的表示格式是用32位的二进制数表示(ARM是32位指令,Thumb是16位指令)
  • ARM指令机器码组成:ARM指令代码一般可以分为5个域
    • 第1个域是4位[31:28]的条件码域,4位条件码共有16种组合
    • 第2个域是指令代码域[27:20],除了指令编码外,还包含几个很重要的指令特征和可选后缀的编码(感叹号也在这里?)
    • 第3个域是第1个操作数寄存器Rn,是4位[19:16],为R0~R15共16个寄存器编码
    • 第4个域是目标或源寄存器Rd,是4位[15:12],为R0~R15共16个寄存器编码
    • 第5个域是第二个操作数[11:0]
  • 如下图:
    • notion image
      下面给出一个示例:指令ADDEQS R0, R1, #8
      其机器码(二进制码)为:
      notion image

3.1.1.4 指令的可选后缀(**)

  • S后缀:指令中使用S后缀时,指令执行后程序状态寄存器的条件标志位将被刷新不使用S后缀时,指令执行后程序状态寄存器CPSR的条件标志将不会发生变化
  • 需要额外注意的一点是,当指令中加上了S后缀并且目的寄存器为PC时会将SPSR的值恢复到CPSR中
  • !后缀:如果指令地址表达式中不含!后缀,则基址寄存器中的地址值不会发生变化(注意是基址寄存器不会变化)。指令中的地址表达式中含有!后缀时,指令执行基址寄存器中的地址值将发生变化,变化的结果为:基址寄存器中的值(指令执行后)=指令执行前的值+地址偏移量(地址偏移量可正可负)
    • 使用!后缀的注意事项
      • !后缀必须紧跟在地址表达式(可以是一个寄存器,也可以是一个[])后面,而地址表达式要有明确的地址偏移量(显式变址或者隐性偏移)
      • !后缀不能用于R15(PC)的后面
      • 当用在单个地址寄存器后面时,必须确信这个寄存器有隐性的偏移量,例如“STMIA R7!, {R0 – R3}”此时地址基址寄存器R7的隐性偏移量是16字节(四个寄存器16字节)。如果R7的初始值为 0X40000000,则该语句结束后为0X40000010
  • B后缀:B后缀表示指令所涉及的数据是一个字节,不是一个字或半字
  • 如:
    • LDR R4,[R0]:R4=[R0],指令传送一个字
    • LDRB R4,[R0]:R4=[R0],指令传送一个字节(R4寄存器高位清0。如果是STR就不影响存储器高位)
    • LDREQB R4,[R0]:如果相等则执行,R4=[R0],指令传送一个字节(R4寄存器高位清0)

3.1.2 ARM指令的条件码(***)

3.1.2.0 ARM指令条件码概述

当处理器工作在ARM状态时,几乎所有的指令均能根据CPSR中的条件标志位的状态和指令的条件域有条件地执行。当指令的执行条件满足时,指令被执行,否则指令被忽略(条件后缀只是影响指令是否执行,不影响指令的内容)

3.1.2.1 ARM 指令的条件码与助记符(***)

notion image
notion image
  • 条件后缀和S后缀的关系
    • 如果既有条件后缀又有S后缀,则书写时S排在后面(就是S写在最后)。
    • 这里举一个例子:ADDEQS R1,R0,R2。
      • 该指令在Z=1时执行,将R0+R2的值放入R1,同时刷新条件标志位。
    • 条件后缀是要测试条件标志位,而S后缀是要刷新条件标志位
    • 条件后缀要测试的是执行前的标志位(条件执行当前指令),而S后缀依据指令的结果改变条件标志

3.1.3 ARM指令分类

ARM 指令可以分为:分支指令数据处理指令存储访问指令协处理器指令杂项指令五类。

3.1.3.1 分支指令

分支指令用于控制程序的执行流程、实现ARM代码与Thumb代码之间进行切换(BX指令)

3.1.3.2 数据处理指令

数据处理指令在通用寄存器上执行计算,主要分为5种:算术运算指令、逻辑运算指令、数据传送指令、比较指令、测试指令

3.1.3.3 存储访问指令

用于加载/存储存放于MCU片外存储系统中的数据。加载指令用于从内存中读取数据放入寄存器中(LDR,LDM),存储指令用于将寄存器中的数据保存到内存中(STR,STM)

3.1.3.4 ARM协处理器指令

ARM协处理器指令用于控制外部的协处理器。包括
  • 数据处理指令:启动一个协处理器专用的内部操作。
  • 数据转移指令:使数据在协处理器和存储器之间进行转移。
  • 寄存器转移指令:协处理器值转移到ARM寄存器或ARM寄存器的值转移到协处理器。

3.1.3.5 杂项指令

包括状态寄存器转移指令(MSR、MRS)和异常中断产生指令(SWI、BKPT)
  • 状态寄存器转移指令将CPSR或SPSR的内容转移到一个通用寄存器或者反过来将通用寄存器的内容写入CPSR或SPSR寄存器
  • ARM有两条异常中断产生指令,分别为 软中断指令SWI 和 断点中断指令BKPT
具体的指令在3.3中会介绍

3.2 ARM指令的寻址方式(***)

3.2.1 立即数寻址

3.2.1.0 立即数寻址简介

立即数寻址也叫立即寻址,操作数本身就在指令中给出(在指令机器码的低12位。详见上面的指令机器码),取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即数寻址
立即寻址中的立即数需要受到八位位图规则限制

3.2.1.1 立即数格式(*)

立即数要求以“#”为前缀没有以#为前缀的就不是立即数,进而就不用满足八位位图了
  • 对于以十六进制表示的立即数,还要求在“#”后加上“0x”或“&”;
  • 对于以二进制表示的立即数,要求在“#”后加上“0b”
  • 对于以十进制表示的立即数,要求在“#”后加上“0d”或缺省

3.2.1.2 立即数的构造(***)

在指令格式中,第二个操作数(也就是立即数所在的位置)有12位:
notion image
因此有效立即数immediate可以表示成:<immediate>=immed_8 循环右移(2×rot)。(immediate循环左移2×rot,或者循环右移32-2×rot得到机器码
4位移位因子 (0-15)乘2,得到一个范围在0-30,步长为2的移位值(即机器码中最后8位移动偶数位得到立即数)
因此,将ARM中的立即数称为8位位图
  • 立即数的合法性:只有能够通过此构造方法(特别注意一定要移动偶数位)得到的才是合法的立即数。
    • 合法立即数:0xFF;0x104(8位图为0x41);0xFF0;0xFF00
    • 非法立即数:0x101;0x102;0xFF1
  • 立即数的规范化(或者说汇编编译器如何生成立即数)
    • 当立即数数值在0到0xFF范围时,令immed_8=<immediate>,rot=0。
    • 其它情况下,汇编编译器选择使rot数值最小的编码方式(也就是立即数左移最少的位数,或者说立即数右移最多的位数。同样需要注意,一定要移动偶数位)
  • 举一个例子:0x3F0——其规范化立即数为0x3F(右移最多的偶数位数4

3.2.2 寄存器寻址

3.2.2.1 寄存器寻址简介

寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。如:

3.2.3 寄存器移位寻址(***)

3.2.3.1 寄存器移位寻址

  • 寄存器移位寻址形式:当第二操作数为寄存器型时(也就是指令中的最后一个操作数),在执行寄存器寻址操作时,也可以对第二操作数寄存器进行移位。
  • ADD Rd, Rn, Rm,{<shift>}
    • 其中:
    • Rm:称为第二操作数寄存器
    • <shift> :用来指定移位类型和移位位数,有两种形式:
      • 5位立即数 (其值小于32
      • 寄存器(用Rs表示) (其值小于32
    • 可见移位的范围为0-31(下面还会介绍)
  • 寄存器移位寻址执行过程:在指令执行时,将寄存器移位后的内容,作为第二操作数参与运算
  • 例如指令:
    • 需要注意的是,这里移位指令与移动位数之间没有逗号

3.2.3.2 第二操作数移位方式(***)

共有6种移位方式:LSL(逻辑左移)、LSR(逻辑右移)、ASL(算术左移)、ASR( 算术右移)、ROR( 循环右移)、RRX(带扩展的循环右移)
需要注意的是,在非加减法指令(包括比较指令CMN与CMP)的移位操作中,如果指令带上了S后缀,那么C会被置为最后移出的位
  • LSL:逻辑左移,空出的最低有效位用0填充
  • 如下图:
    • notion image
      指令示例:
  • LSR:逻辑右移,空出的最高有效位用0填充
  • 如下图:
    • notion image
      指令示例:
  • ASL:算术左移,由于左移空出的有效位用0填充,因此它与LSL同义
  • 指令示例:
    • ASR:算术右移,算术移位的对象是带符号数,移位过程中必须保持操作数的符号不变如果源操作数是正数,空出的最高有效位用0填充,如果是负数用1填充
    • 如下图:
      • notion image
        指令示例:
    • ROR:循环右移,移出的最低有效位依次填入空出的最高有效位
    • 如下图:
      • notion image
        指令示例:
    • RRX:带进位位的循环右移。将寄存器的内容循环右移1位空位用原来C标志位填充
    • 如下图:
      • notion image
        指令示例:

    3.2.3.3 第二操作数的移位位数

    • 移位位数形式及范围:移位位数可以用立即数方式(所以要满足八位位图。但是他只能是5位,就一定是满足八位位图的了)或者寄存器方式给出,其值均小于32,应为0---31
    • 下面给出几个移位指令的示例:
      • 指令执行效果:寄存器R1的内容分别逻辑右移2位、R4位,再与寄存器R2的内容相加,结果放入R3中。

    3.2.4 寄存器间接寻址

    • 寄存器间接寻址:寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中
    • 示例代码如下:
      • 该指令的执行过程为:
        notion image

    3.2.5 基址变址寻址(***)

    就是变址寻址

    3.2.5.1 变址寻址

    • 变址寻址简介:也叫基址变址寻址,将基址寄存器的内容与指令中给出的地址偏移量相加,得到操作数所在的存储器的有效地址。
    • 基址变址寻址使用场景:常用于访问某基地址附近的地址单元。(4K范围的偏移)
    • 使用基址变址寻址时,指令的第二操作数部分有所变化(这里是立即数偏移的指令格式):
      • notion image
        可以发现最低12位都作为了地址偏移量,而不是4位移位因子,8位位图。所以在进行基址变址寻址时的立即数就不需要考虑是否合法了,只要是12位能表示的数即可(不使用八位位图也是合理的,因为寻址总不能是不连续的吧)。而12位对应的地址空间为2^12,即为4K(所以是一个12位无符号数。但实际上的寻址空间为8K,因为在基址变址寻址的时候可以选择加上或者减去这个偏移量,这是由操作码中的U)

    3.2.5.2 偏移地址方式(***)

    有三种加偏址的方式:前变址自动变址后变址寻址方式
    • 前变址(不修改基址寄存器):先基址+偏址,生成操作数地址,再做指令指定的操作。也叫前索引偏移
    • 指令示例:
      • 该指令执行流程如下图:
        notion image
    • 自动变址:先基址+偏移,生成操作数地址,做指令指定的操作。然后自动修改基址寄存器
    • 指令示例:
      • !后缀表示自动更新基址寄存器,其中[R1,#4]是一个地址表达式
    • 后变址寻址:基址寄存器不加偏移作为操作数地址,然后修改基地址(基址+偏移),也叫后索引偏移。
    • 指令示例:
      • 需要特别注意的是,这里并没有使用!后缀(其实也没地方用。。),但是基址寄存器的值还是被修改了
        该指令执行流程如下:
        notion image

    3.2.5.3 偏移地址形式(**)

    • 立即数偏移(常用
    • 指令示例:
      • 寄存器偏移
      • 指令示例:
        • 寄存器移位偏移
        • 指令示例:

          3.2.6 多寄存器寻址(***)

          3.2.6.1 多寄存器寻址简介

          采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。这种寻址方式是多寄存器传送指令LDM/STM的寻址方式,这种寻址方式中用一条指令最多可传送16个通用寄存器的值连续的寄存器间用“-”连接,否则用“,”分隔
          • 指令示例
          • LDMIA R0!,{R1-R4} ;R1←[R0]、R2←[R0+4]、R3←[R0+8]、R4←[R0+12]
            • 需要注意的是,这里的R0带了后缀!,所以会自动修改R0中的基址值
              该指令的执行流程如下图:
              notion image

          3.2.6.2 多寄存器寻址操作(***)

          • LDMIA/STMIA Increment After(先传送,后地址加4)
          • LDMIB/STMIB Increment Before(先地址加4 ,后传送)
          • LDMDA/STMDA Decrement After(先传送,后地址减4)
          • LDMDB/STMDB Decrement Before (先地址减4,后传送)
          上述四种多寄存器寻址操作效果如下图:
          notion image

          3.2.6.3 多寄存器寻址注意事项(***)

          对于所有LDM/STM指令而言,寄存器序号低的,在低地址单元,序号大的在高地址单元!与书写顺序无关!

          3.2.7 堆栈寻址

          3.2.7.1 堆栈寻址简介

          堆栈是一种数据结构,按后进先出(Last In First Out, LIFO)的方式工作,使用一个称作堆栈指针(SP)的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶

          3.2.7.2 栈的工作方式(**)

          • 栈的增长方式
            • 向上生长:向高地址方向生长,称为递增堆栈(入栈地址增加)
            • 向下生长:向低地址方向生长,称为递减堆栈(入栈地址减小)
          • 栈的分类——按数据位置
            • 满堆栈:堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈
            • 空堆栈:堆栈指针指向下一个待压入数据的空位置,称为空堆栈
          • 堆栈工作方式:根据上面的增长方式和分类排列组合
            • 满递增堆栈FA(Full Ascending):堆栈指针指向最后压入的数据,且由低地址向高地址生长
            • 满递减堆栈FD(Full Descending):堆栈指针指向最后压入的数据,且由高地址向低地址生长
            • 空递增堆栈EA(Empty Ascending):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生长
            • 空递减堆栈ED(Empty Descending):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生长
          • 以上的四个工作方式的缩写都可以作为多寄存器传送指令的后缀

          3.2.7.3 堆栈寻址注意事项(***)

          堆栈的四种类型的“递增” 和“递减” 都是相对入栈操作(STM)而言的
          如下图:
          notion image

          3.2.8 相对寻址

          与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。(也就是标号寻址就是一个PC相对寻址)
          相对寻址指令举例如下:

          3.3 ARM指令集(***)

          就是上面ARM指令集分类中分出的五类

          3.3.1 分支指令(***)

          3.3.1.0 分支指令概述

          • 程序跳转的两种方式(不算异常的硬件跳转)
            • 使用分支转移指令直接跳转
            • 直接向PC寄存器赋值来实现跳转
          • ARM的分支指令分类:ARM的分支转移指令,也被称为程序转移指令,可以从当前指令向前或向后32MB的地址空间跳转。
          • 分支指令的分类如下图:
            • notion image
              需要注意的是这里的BX指令,放入PC的值只有高30位,最低两位由于指令对齐要求在放入PC时被清零;同时会将跳转地址的最低位写入T以实现修改处理器状态

          3.3.1.1 跳转指令B及带连接的跳转指令BL(***)

          • B 指令与 BL 指令的编码格式(与上面的通用的编码有点不同
          • 如下图:
            • notion image
              从编码中看到L控制了PC与LR寄存器之间的开关。当L=0 时,该开关断开,指令为B指令;当L=1时,该开关接通,指令为BL指令。(所以B指令和BL指令只有一位的区别)
              其中Signedimmed24表示24位有符号立即数(偏移量)(这里也不用考虑八位位图。并且与变址寻址中的不同,变址寻址中的是12位无符号数
          • B 指令与 BL 指令的助记符格式(就是指令格式)
          • 如下:
            • cond表示指令执行条件
            • target表示跳转地址,可以是一个标号,也可以是一个绝对地址,如:
            • B 0x1234 ;(注: B #0x1234是错误的 )
              • 编译器会把该绝对地址转换为相对地址放入指令
          • B 指令与 BL 指令功能:跳转到指定地址执行,地址范围限制在当前PC寄存器所指向的指令地址的±32MB范围(是一个PC相对寻址)
          • ±32MB是因为B/BL指令的机器码中偏移地址为24位有符号数,而ARM指令集要求低两位为0,所以24位有符号数能表示±2^25MB的指令存储器空间,即为±32MB(下面会详细介绍signedimmed24的计算)
          • 关于signedimmed24地址偏移量:signedimmed24 间接提供目标地址(就是间接提供相对地址),真正的目标地址是由处理器根据这个有符号数和当前的PC值计算出来的
          • signedimmed24处理步骤如下:
            • 先将 signedimmed24 左移两位,得到一个26位偏移量(左移两位是因为ARM指令集要求4字节对齐)
            • 将26位偏移量扩展为32位有符号数(高位补符号位)
            • 将这32位有符号数与 PC 的当前值相加,得到实际的跳转地址。
            • 因此B 和 BL 指令转移的偏移量为 26 位,即转移的跨度为前后 32MB 地址空间
          • B&BL指令使用示例
          • 指令示例如下:
            • B和BL的区别:BL在跳转之前会把BL指令的下一条指令地址(断点地址)保存到连接寄存器 LR(R14),因此程序在必要的时候可以通过将 LR 的内容加载到 PC 中,使程序返回到跳转点。因此BL 指令经常被用来调用一个子程序
            • BL指令执行流程如下图:
              • notion image

            3.3.1.2 带状态切换的跳转指令BX(***)

            • BX 指令的格式为: BX{<cond>} Rm
            • 其中:
              • cond表示指令执行条件
              • Rm寄存器,值是绝对地址值,不是偏移量,在指令执行后,Rm中的地址值与#0XFFFF FFFE 进行 “与” 运算,再被复制到程序计数器PC。
              • Rm[0]为1时,强制程序从ARM指令状态跳转到Thumb指令状态
              • Rm[0]为0时,强制程序从Thumb 指令状态跳转到ARM指令状态
              • 上面两条在说的事情就是,将BX跳转指令的最低位写入CPSR的T位(T<-Rm[0])
            • BX 指令功能:跳转到指令中所指定的目标地址,并实现状态切换。(T<-Rm[0])
            • BX指令编码格式
            • 如下图:
              • notion image
                其中,Rm为目标地址寄存器(这里只能提供寄存器,不能提供标号或者绝对地址
            • BX指令示例:见一些重点中的程序部分

            3.3.1.3 带连接和状态切换的跳转指令 BLX(**)

            • BLX指令格式:BLX指令的格式有以下两种
              • BLX <target>
              • 功能:把程序跳转到指令中所指定的目标地址继续执行,并同时将处理器的工作状态从ARM状态切换到Thumb状态,并将下一条的地址保存到寄存器 LR 中。
              • BLX{<cond>} Rm
              • 功能:以 Rm 方式提供目标地址的 BLX 指令,除了跳转和下一条的地址保存到LR之外,也可进行状态切换,但其切换的依据是Rm最低位的值。如果值为0 ,则目标地址处应为 ARM指令,如果值为1 ,则目标地址处应为 Thumb 指令
            • BLX指令示例:见一些重点的程序部分
            • BLX指令注意事项:某些型号的CPU不支持BLX指令,比如S3C2440

            3.3.1.4 PC赋值跳转(*)

            另一种实现指令跳转的方式是通过直接向 PC 寄存器中写入目标地址值,实现在 4GB (就是32位)地址空间中任意跳转,这种跳转又称为长跳转
            如果在长跳转指令之前使用“MOV LR,PC” 等指令,可以保存将来返回的地址值,也就实现了在 4GB 的地址空间中的子程序调用

            3.3.2 数据处理指令(***)

            3.3.2.1 数据处理指令概述(*)

            • ARM数据处理指令的功能:主要完成寄存器中数据的算术和逻辑运算操作(还有乘法操作比较操作测试操作
            • ARM数据处理指令的特点
              • 操作数来源:所有的操作数要么来自寄存器,要么来自立即数,不会来自存储器(因为存储器读写需要单独的指令)
              • 操作结果:如果有结果,则结果一定是为32位宽、或64位宽(长乘法指令),并且放在一个或两个寄存器中,不会写入存储器(因为存储器读写需要单独的指令)
              • 第二个操作数(除了乘法指令) Operand2 :切记其三种形式:立即数、寄存器、寄存器移位(如果带上了S后缀就会影响C)
              • 在下面就不考虑移位影响C,而只考虑指令本身对CPSR的影响
              • 乘法指令的操作数:全部是寄存器(所以乘法指令的第二个操作数就没有上面的三种形式了)
            • ARM数据处理指令分类:22条可分为5类
              • 算术运算指令:ADD ADC SUB SBC RSB RSC MUL MLA UMULL UMLAL SMULL SMLAL
              • 逻辑运算指令:AND ORR EOR BIC
              • 数据传送指令:MOV MVN
              • 比较指令:CMP CMN
              • 测试指令:TST TEQ
            • 上述指令只能对寄存器操作不能针对存储器(存储器读写需要单独的指令)。下面会逐个介绍
            • 数据处理指令对程序状态寄存器CPSR的影响
              • 指令中可以选择s后缀,以影响状态标志。但是比较指令(CMP和CMN)和测试指令(TST和TEQ)不需要后缀S它们总会直接影响CPSR中的状态标志。
              • 关于恢复CPSR原值问题:
                • 在异常模式下(异常模式才有SPSR寄存器),如果指令带有S后缀(除了比较指令以外),同时又以PC为目标寄存器进行操作,则操作的同时从SPSR恢复CPSR(运算结果就不会影响CPSR了)。
                • 如:
                  • 在user或者system模式下这样操作会产生不可预料的结果,因为在这两种模式下没有SPSR
                • 在出栈操作中加入"^",则在装入PC时会从SPSR恢复CPSR
            • 数据处理指令编码格式(这里是一个通式,对特定的指令如乘法指令会有变化)
            • 如下图:
              • notion image
                其中:
              • opcode为数据处理指令操作码
              • I用于区别立即数(I为1)或寄存器移位(I为0)(这里的立即数受到8位位图限制
              • S用于设置条件码(结果是否影响CPSR
              • Rn为第一操作数寄存器
              • Rd为目标寄存器
              • operand2为第二个操作数(三种形式:立即数、寄存器、寄存器移位
              • 若指令不需要全部的可用操作数时(如MOV指令的Rn),不用的寄存器域应设置为0(由编译器自动完成)。
              • 对于比较指令测试指令,b20位固定为1(默认修改CPSR)

            3.3.2.2 算术运算指令(***)

            主要是加减乘运算
            • 加减运算指令
              • ADD——加法运算指令
                • 指令格式:ADD{cond}{S} Rd,Rn,operand2
                • ADD指令把第1源操作数寄存器Rn和第2源操作数operand2相加后,将结果存放到目的寄存器 Rd。
                  • ADC通常用来实现字长大于32位的加法运算
                • 指令流程
                • 如下图:
                  • notion image
                • 受影响的标志位(需要注意的是这里只有加上了S后缀才会影响)
                • 如下图:
                  • notion image
                    再提醒一次,CV是有可能同时置位的,只不过置位逻辑不一样。C是将操作数当成无符号数;而V是将操作数当成有符号数
                • 指令示例
                • ADD R0,R1,R2 ;R0←(R1)+(R2) ADD R0,R1,#255 ;R0 ←(R1)+ 255,不能是0x256,这样就不满足8位位图了 ADD R0,R2,R3,LSL#1 ;R0 ←(R2) +(R3<<1)
              • ADC——带进位加法指令
                • 指令格式:ADC{cond}{S} Rd,Rn,operand2
                • ADC指令将operand2的数据与Rn的值相加,再加上CPSR中的C条件标志位(所以常用于无符号数),结果保存到Rd寄存器
                • 指令流程
                • 如下图:
                  • notion image
                • 受影响的标志位:只修改 N、Z、C、V这4个标志位
                • 指令示例:见一些重点的程序部分
              • SUB——减法运算指令
                • 指令格式:SUB{cond}{S} Rd,Rn,operand2
                • SUB指令用寄存器Rn减去operand2,结果保存到Rd中。
                • 指令流程
                • 如下图:
                  • notion image
                • 受影响的标志位
                • 如下图:
                  • notion image
                    需要注意的就是上面圈出来的,如果有借位C是置0而不是置1
                • 指令示例
                • SUB R0,R1,R2 ;R0←(R1)-(R2) SUB R0,R1,#256 ;R0←(R1)- 256 SUB R0,R2,R3,LSL#1 ;R0←(R2)-(R3<<1)
              • SBC——带进位减法指令
                • 指令格式:SBC{cond}{S} Rd,Rn,operand2
                • SBC指令用寄存器Rn减去operand2,再减去CPSR中的C条件标志位的反码(所以常用于无符号数),结果保存到Rd中。
                  • 该指令主要用于字长大于 32 位的数据的减法运算
                • 指令流程
                • 如下图:
                  • notion image
                • 受影响的标志位:同SUB
                • 指令示例:见一些重点的程序部分
              • RSB——逆(反)向减法指令
                • 指令格式:RSB{cond}{S} Rd,Rn,operand2
                • RSB指令用operand2减去寄存器Rn,结果保存到Rd中。
                • 指令流程:与上面的运算指令大同小异
                • 指令示例
                • RSB R3,R1,#0xFF00 ;R3=0xFF00-R1 RSB R0,R1,#256 ;R0←256 -(R1) RSBS R1,R2,R2,LSL #2 ;R1=R2<<2-R2 即 (R1 =R2×3)
              • RSC——带进位反向减法指令
                • 指令格式:RSC{cond}{S} Rd,Rn,operand2
                • RSC 指令用寄存器operand2减去Rn,再减去CPSR中的C条件标志位的反码(所以常用于无符号数),结果保存到Rd中
                • 指令流程:与上面的运算指令大同小异
                • 指令示例:见一些重点的程序部分
            • 乘法指令
              • 乘法指令的分类:
                • 32位的乘法指令:乘法操作的结果为32位
                • 64位的乘法指令:即乘法操作的结果为64位
              • MUL——32位乘法指令
                • 指令格式:MUL{cond}{S} Rd,Rm,Rs
                • MUL指令将Rm和Rs中的值相乘,结果的低32位保存到Rd中。注意这里第二操作数只能是寄存器,并且Rd ≠ Rm,即指令中的前两个寄存器不能相同(但是1、3可以相同)
                • 指令流程
                • 如下图:
                  • notion image
                • 受影响的标志位
                • 如下图:
                  • notion image
                    非加减法指令对C、V标志通常没有影响(移位操作也可能会影响C标志位)
                • 指令示例
                • MUL R1,R2,R3 ;R1=R2×R3 MULS R0,R3,R7 ;R0=R3×R7,设置CPSR的N位和Z位
              • MLA——32位乘加指令
                • 指令格式:MLA{cond}{S} Rd,Rm,Rs,Rn
                • 指令将Rm和Rs中的值相乘,再将乘积加上第3个操作数,结果的低32位保存到Rd中。同样,第二操作数必须是寄存器Rd ≠ Rm
                • 指令流程:MUL大同小异
                • 受影响的标志位:同 MUL
                • 指令示例
                • MLA R0,R1,R2,R3 ;R0←(R1)X(R2)+(R3) MLAS R0,R1,R2,R3 ;R0←(R1)X(R2)+(R3),并更新CPSR标志位
              • UMULL—64位无符号乘法指令(到64位开始分有没有符号了,U代表无符号,MUL代表乘法指令,L代表长乘法)
                • 指令格式:UMULL{cond}{S} RdLo,RdHi,Rm,Rs
                • UMULL指令将Rm和Rs中的值作无符号数相乘,结果的低32位保存到RdLo中,高32位保存到RdHi中
                • 指令流程:这里的指令流程肯定跟上面的不太一样了,ppt上也没给出,如果问到了就去改改上面的。后面就不再写指令流程这一项了
                • 受影响的标志位
                  • notion image
                    需要注意的是,对N的修改是复制RdHi的最高位
                • 指令示例
                • UMULL R0,R1,R5,R8 ;(R1,R0)←R5×R8
              • UMLAL—64位无符号乘加指令
                • 指令格式:UMLAL{cond}{S} RdLo,RdHi,Rm,Rs
                • 受影响的标志位:同 UMULL
                • 指令示例
                • UMLAL R0,R1,R5,R8 ;(R1,R0)←R5×R8+(R1,R0)
              • SMULL—64位有符号乘法指令
                • 指令格式:SMULL{cond}{S} RdLo,RdHi,Rm,Rs
                • 受影响的标志位
                • 如下图:
                  • notion image
                • 指令示例
                • SMULL R2,R3,R7,R6 ;(R3,R2)←R7×R6
              • SMLAL—64位有符号乘加指令
                • 指令格式:SMLAL{cond}{S} RdLo,RdHi,Rm,Rs
                • 受影响的标志位:同SMULL
                • 指令示例
                • SMLAL R2,R3,R7,R6 ;(R3,R2)←R7×R6+(R3,R2)
              • 乘法指令的特点
                • 不支持第2操作数为立即数(只能为寄存器,移位操作也没见过)
                • 结果寄存器不能与第一源寄存器(Rm)相同
                  • Rd、RdHi、RdLo不能与Rm为同一寄存器。
                  • RdHi和RdLo不能为同一寄存器。
                • 对标志位的影响
                  • N标志位:若结果是32位指令形式,Rd的第31位是标志位N;对于产生长结果的指令形式,RdHi的第31位是标志位(无论如何,总是结果的最高位
                  • Z标志位:如果Rd或RdHi、RdLo为0,则标志位Z置位(无论如何,结果为0就置位)
                  • V标志位:乘法指令不影响V标志位
                  • C标志位: ARM v5及以上的版本不影响C标志位; ARM v5以前的版本,C标志位数值不确定。
                • 指令编码格式
                • 如下图:
                  • notion image
                  • opcode为乘法指令操作码
                  • S为设置条件码
                  • Rm为被乘数寄存器(第一操作数)
                  • Rs为乘数的寄存器(第二操作数,一定是寄存器)
                  • Rn/RdLo用于MLA指令相加的寄存器或64位乘法指令的目标寄存器(低32位)
                  • Rd/RdHi用于目标寄存器或64位乘法指令的目标寄存器(高32位)
                  • 若指令不需要全部的可用操作数时(如MUL指令的Rn),不用的寄存器域应设置为0(由编译器自动完成)。
                • 避免将R15定义为任一操作数或结果寄存器。
                • 早期的ARM处理器仅支持32位乘法指令。ARM7版本和后续的在名字中有M的处理器才支持64位乘法指令。

            3.3.2.3 逻辑运算指令(***)

            主要是逻辑运算
            或者称为按位逻辑操作指令。包括:逻辑与操作指令AND、逻辑或操作指令ORR、逻辑异或操作指令EOR 以及 位清除指令BIC
            • AND——逻辑“与”操作指令
              • 指令格式:AND{cond}{S} Rd,Rn,operand2
              • AND指令将operand2的值与寄存器Rn的值按位逻辑“与”操作,结果保存到Rd中(这里就没有要求一定要是寄存器了,所以可以是三种形式:立即数、寄存器、寄存器移位)
                • AND指令可用于提取寄存器中某些位的值,也可以用于把指定位清0
              • 指令流程
              • 如下图:
                • notion image
              • 受影响的标志位
              • 如下图:
                • notion image
                  非加减法指令通常不影响CV(移位操作也可能)
              • 指令示例
              • ANDS R0,R0,#0x01 ;R0=R0&0x01,取出最低位数据 AND R2,R1,R3 ;R2=R1&R3
            • ORR——逻辑“或”操作指令
              • 指令格式: ORR{cond}{S} Rd,Rn,operand2
              • ORR指令将operand2的值与寄存器Rn的值按位逻辑“或”操作,结果保存到Rd中。同样第二操作数没有限制
              • 指令流程:与AND相似,后面的指令流程也不在显式说明了,如果没有写的话就跟最近的上面的某个指令相似
              • 受影响的标志位:同AND
              • 指令示例
              • ORR R0,R0,#0x0F ;将R0的低4位置1
            • EOR——逻辑“异或”操作指令
              • 指令格式:EOR{cond}{S} Rd,Rn,operand2
              • EOR指令将operand2的值与寄存器Rn的值按位逻辑“异或”操作,结果保存到Rd中。
                • EOR指令可用于将寄存器中某些位的值取反。将某一位与0异或,该位值不变与1异或,该位值被求反
              • 受影响的标志位:同AND
              • 指令示例
              • EOR R1,R1,#0x0F ;将R1的低4位取反 EORS R0,R5,#0x01 ;R0<-R5异或0x01,并影响标志位
            • BIC——位清除指令
              • 指令格式:BIC{cond}{S} Rd,Rn,operand2
              • BIC指令将寄存器Rn的值与operand2的值的反码按位逻辑“与”操作,结果保存到Rd中。即将某一位与1 做BIC操作,该位值被清除为0 ;将某一位与0做BIC操作,该位值不变
              • 受影响的标志位:同AND
              • 指令示例
              • BIC R1,R1,#0x0F ;将R1的低4位清0,其它位不变

            3.3.2.4 数据传送指令(***)

            • MOV——数据传送指令
              • 指令格式:MOV{cond}{S} Rd,operand2
              • MOV指令将operand2传送到目标寄存器Rd中。这里第二操作数也没有做限制,所以可以是三种形式:立即数、寄存器、寄存器移位
              • 指令流程
              • 如下图:
                • notion image
              • 受影响的标志位
              • 如下图:
                • notion image
                  这里C=0可以理解为对C没有影响(除非是移位操作)
              • 指令示例
              • MOV R2,#0x7E ;将立即数0x7E传送到寄存器R2中 MOVS R1,R0,LSL#3 ;将寄存器R0 * 8传送到寄存器R1,并影响标志位 MOV PC,LR ;PC←LR,子程序返回 MOVS PC,LR ;PC←LR,异常模式下返回,CPSR←SPSR
              • MOV指令的功能总结
                • 寄存器之间传送。
                • 立即数传送到寄存器中。(8位立即数位图。所有的数据处理指令都需要满足8位位图)
                • 实现单纯的移位操作。MOV Rd,Rd,LSL,#3(如果带上了S后缀,移位操作将影响C标志位)
                • 实现子程序调用、从子程序中返回。当PC寄存器作为目标寄存器时可以实现程序跳转。
                • 实现异常模式的返回,并把当前处理器模式的SPSR寄存器内容复制到CPSR中。
            • MVN——数据求反传送指令
              • 指令格式:MVN{cond}{S} Rd,operand2
              • MVN指令将operand2按位取反后传送到目标寄存器Rd中。
              • 受影响的标志位:同MOV
              • 指令示例
              • MVN R1,#0xFF ;R1←0xFFFFFF00 MVN R1,R2 ;Rl← R2取反

            3.3.2.5 比较指令(***)

            • CMP——比较指令
              • 指令格式:CMP{cond} Rn,operand2
              • CMP指令将寄存器Rn的值减去operand2的值,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位(所以会像减法操作一样影响状态寄存器标志位),以便后面的指令根据相应的条件标志来判断是否执行
              • 受影响的标志位
              • 如下图:
                • notion image
                  注意会影响C、V标志位(是根据CMP时进行的减法操作置位的)
              • 指令示例
              • CMP R1,R0 ;(R1)-(R0);根据结果设置CPSR的标志位。 CMP R1,#100 ;(R1)-100;根据结果设置CPSR的标志位
                • 注意:比较类指令本身带有更新 CPSR的功能,故在该指令中不能使用后缀 S
            • CMN——负数比较指令
              • 指令格式:CMN{cond} Rn,operand2
              • CMN指令将寄存器Rn的值减去operand2的负数(即加上operand2的值),但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位(所以会像加法操作一样影响CPSR标志位),以便后面的指令根据相应的条件标志来判断是否执行。
              • 受影响的标志位:同CMP
              • 指令示例
              • CMN R0,#1 ;R0+1,判断R0是否为-1。

            3.3.2.6 测试指令(**)

            • TST——位测试指令
              • 指令格式:TST{cond} Rn,operand2
              • TST指令将寄存器Rn的值与operand2的值按位逻辑“与”操作(比较指令是做减法),但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位。该指令一般用来检测是否设置了特定的位
                • 由于指令执行的是按位与操作,所以不会影响CV标志位。并且第二操作数没有限制,可以是立即数、寄存器、寄存器移位三种形式
              • 受影响的标志位:只影响NZ标志位
              • 指令示例
              • TST R0,#0x01 ;判断R0的最低位是否为0,为0则Z位置位 TST Rl,#0x0F ;判断R1的低4位是否为0,为0则Z位置位
            • TEQ——测试相等指令
              • 指令格式:TEQ{cond} Rn,operand2
              • TEQ指令将寄存器Rn的值与operand2的值按位逻辑“异或”操作,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行
                • 由于指令执行的是按位异或操作,所以不会影响CV标志位。并且第二操作数没有限制,可以是立即数、寄存器、寄存器移位三种形式
              • 受影响的标志位:只影响NZ标志位
              • 指令示例
              • TEQ R0,R1 ;比较R0与R1是否相等,(不影响V位和C位)

            3.3.3 存储器访问指令(***)

            3.3.3.0 存储器访问指令简介

            ARM微处理器用加载/存储指令访问存储器,实现在寄存器和存储器之间传送数据(是因为ARM是精简指令集)
            由于ARM处理器对外设寄存器、I/O映射空间与存储器统一编址,因此,对外围设备的I/O操作也用此类指令(没有专用的IO指令)
            • ARM基本的加载/存储指令分类
              • LDR和STR,单寄存器加载/存储指令
              • LDM和STM,多寄存器加载/存储指令
              • SWP,寄存器和存储器数据交换指令

            3.3.3.1 单寄存器的存取指令(***)

            单寄存器加载/存储指令是ARM在寄存器和存储器间传送单个字节和字的最灵活方式
            • 单寄存器的存取指令分类——按传送数据的类型分类
              • 单字无符号字节的加载/存储指令
              • 半字有符号字节的加载/存储指令
            • 单字和无符号字节的加载/存储指令
              • 指令简介
                • LDR:指令从内存中取32位字或8位无符号字节数据放入寄存器;
                • STR:指令将寄存器中的32位字或8位无符号字节数据保存到存储器中。
              • 无符号字节加载(LDR)时,用0将8位的操作数扩展到32位(即使用LDR指令获取字节时,因为是无符号字节,所以高位补0
              • 指令格式
              • 如下:
                • T后缀
                  • T为可选后缀,若指令有T,存储系统将访问看成是处理器是在用户模式下
                  • 用于存储器保护
                  • 不能与后变址模式、自动变址模式一起使用(即不能改变基址寄存器值)。
                  • T在用户模式下无效
                • 操作数寻址方式:LDR/STR指令为基址变址寻址(或寄存器间接寻址),由两部分组成:
                  • 基地址部分:为一个基址寄存器,可以为任一个通用寄存器(包括PC)
                  • 偏移地址部分:这一部分非常灵活,实际就类似第二个操作数(这里说的是类似第二操作数所以只有第二操作数的立即数才会受到8位位图的限制),可以有以下3种格式:立即数、寄存器、寄存器移位
                    • 立即数:12位立即数是一个无符号的数值。这个数据可以加到基址寄存器,也可以从基址寄存器中去这个数值(所以是8K的寻址空间,在寻址方式中也有介绍)
                    • 指令示例:
                      • 寄存器:寄存器中的数值(无符号数)可以加到基址寄存器,也可以从基址寄存器中去这个数值。
                      • 指令示例:
                        • 寄存器移位:寄存器移位后的值(无符号数)可以加到基址寄存器,也可以从基址寄存器中去这个数值
                        • 指令示例:
                          • 注意:移位位数只能是5位的立即数,不能使用寄存器指定移位位数(与上面的移位寻址不同,移位寻址还可以使用寄存器提供移位位数。但是移位位数的限制都是一样的0-31
                    • PC(即R15)使用的几个问题
                      • 使用PC作为基址时,使用的数值是当前执行指令的地址加8个字节(取指与执行相差8个字节。流水线问题)
                      • PC不能用做偏移寄存器,也不能用于任何变址寻址模式
                      • 把一个字加载到PC,将使程序转移到所加载的地址,这是一个公认的实现跳转的方法。但是应当避免将一个字节加载到PC
                      • 把PC存到存储器的操作在不同体系结构的处理器中产生不同的结果,应尽可能避免
                    • 单字和无符号字节的加载/存储指令编码格式
                    • 如下图:
                      • notion image
                      • I为0时,偏移量为12位立即数;I为1时,偏移量为寄存器移位(寄存器也算是寄存器移位)
                      • P表示前/后变址
                      • U表示加/减(相当于将12位无符号数扩展为13位有符号数)
                      • W表示回写(!后缀)。
                      • L用于区别加载(L为1时)或存储(L为0时)。
                      • B用于区别字节访问(B为1时)或字访问(B为0时)。
                      • Rn为基址寄存器,Rd为源/目标寄存器。
                  • 半字和有符号字节的加载/存储指令:这类LDR/STR指令可实现半字(有符号和无符号)、有符号字节数据的传送
                    • 半字和有符号字节的加载/存储指令特点(这就是与上面的指令的区别了)
                      • 偏移量格式寻址方式与加载/存储字和无符号字节指令基本相同
                      • 立即数偏移量限定在8位,寄存器偏移量不可经过移位得到(这个就是主要区别了。在上面的指令中立即数偏移量是12位,并且偏移地址可以通过移位得到
                    • 指令格式
                    • 如下:
                      • 存储有符号数据和无符号数据之间没有差别(只不过机器是根据我们的指令来解释数据罢了。所以使用STR指令的时候是不会出现S后缀的)
                    • 两点说明
                      • 符 号 位——有符号字节或有符号半字的加载,用“符号位”扩展到32位;无符号半字传送是用0扩展到32位
                      • 地址对齐——对半字传送的地址必须为偶数。非半字对齐的半字加载将使Rd内容不可靠;非半字对齐的半字存储将使指定地址的2字节存储内容不可靠(说白了就是非半字对齐会使最终数据存放的位置数据不可靠)
                    • 指令编码格式
                    • 如下图:
                      • notion image
                      • 当I为0时,偏移量为8位立即数;当I为1时,偏移量为寄存器偏移(不能是寄存器移位)
                      • P表示前/后变址
                      • U表示加/减(8位无符号立即数扩展为9位有符号数)
                      • W表示回写(!后缀)
                      • L用于区别加载(L为1)或存储(L为0)
                      • S用于区别有符号访问(S为1)和无符号访问(S为0)
                      • H用于区别半字访问(H为1)或字节访问(H为0)
                      • Rn为基址寄存器
                      • Rd为源/目标寄存器。
                    • 指令示例
                    • LDRSB R1,[R0,R3] ;将R0+R3地址上的字节数据读到R1,高24位用符号位扩展。 LDRSH R1,[R9] ;将R9地址上的半字数据读出到R1,高16位用符号位扩展。 LDRH R6,[R2],#2 ;将R2地址上的半字数据读出到R6,高16位用零扩展,然后修改R2=R2+2。 STRH R1,[R0,#2]! ;R1的数据保存到R0+2地址中,只存储低2字节数据,并且修改R0=R0+2。

                  3.3.3.2 多寄存器的存取指令(***)

                  • LDM&STM多寄存器的存取指令:LDM和STM指令可以实现在一组寄存器和一块连续的内存单元之间存/取数据。
                  • LDM加载多个寄存器;STM存储多个寄存器。这两条指令,允许传送16个寄存器R0---R15的任何子集或所有寄存器(也就是传送所有的通用寄存器
                    • 指令格式
                    • LDM{cond} <模式> Rn{!},<reglist>{^} STM{cond} <模式> Rn{!},<reglist>{^}
                      • 关于指令格式的说明:
                      • Rn:表示基址寄存器,装有传送数据的初始地址,Rn不允许为R15(即PC)在单寄存器存取中允许PC为基址
                      • Rn后缀“!”:表示最后的地址写回到Rn中。
                      • Reglist:表示寄存器列表,其中包含一个或多个寄存器。当寄存器不连续时,中间使用“,”隔开。
                      • 格式例子:{R1,R2,R6-R9}
                      • 列表寄存器和存储器地址的关系规则:编号低的寄存器对应于存储器中低地址单元,编号高的寄存器对应于存储器中高地址单元
                      • 后缀“^”说明
                        • 寄存器列表不包含PC:使用后缀“^”进行数据传送时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器
                        • 寄存器列表包含有PC:除了正常的多寄存器传送外,还要将SPSR拷贝到CPSR中。该用法可用于异常处理返回。
                        • 禁用情况:后缀“^”不允许在用户模式或系统模式下使用,因为它们没有SPSR
                      • Rn在寄存器列表中且使用后缀“!”
                        • 对于STM指令,若Rn为寄存器列表中的最低序号的寄存器,则会将Rn的初值保存
                        • 其它情况下编译无法通过
                      • 地址字对齐:这些指令寻址是字对齐的,即忽略地址位[1:0]
                    • 指令编码格式
                    • 如下图:
                      • notion image
                      • registerlist为寄存器列表,b0位与R0对应,b15位与R15对应
                      • P表示前/后变址(before/after)
                      • U表示地址增/减(I/D),对应了后面的后缀
                      • W表示回写(!后缀)
                      • S用于恢复CPSR强制用户位(^后缀):当PC寄存器包含在LDM指令的reglist中,且S为1时,则当前模式的SPSR将被复制到CPSR,成为一个原子的返回和恢复状态指令;若reglist不包含PC寄存器,且S为1时,则加载/存储的是用户模式的寄存器(在前面指令格式中也有介绍)
                      • L用于区别加载(L为1)或存储(L为0)。Rn为基址寄存器。
                    • 关于模式项:模式项本来应该在指令格式中介绍的,但是比较重要又比较多,就拿出来写
                    • LDM/STM的主要用途是现场保护、数据复制和参数传送等。其模式有如下8种(前面4种用于数据块的传输(为存储操作), 后面4种是堆栈操作)
                      • 数据块传输
                        • notion image
                      • 堆栈操作
                        • notion image
                          需要注意的是堆栈操作中的堆栈类型都是针对入栈操作而言的。并且堆栈操作常常用于保存现场和恢复现场,如下:
                      • 堆栈操作与批量数据传输的对应关系
                      • 如下图:
                        • notion image

                  3.3.3.3 单寄存器交换指令(SWP)

                  • SWP——单寄存器交换
                  • SWP指令用于将一个存储单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该存储单元中。即Rm->[Rn]->Rd
                    • 交换指令是一个原子操作,也就是说,在连续的总线操作中读/写一个存储单元,在操作期间阻止其它任何指令对该存储单元的读写 。
                    • 指令格式:SWP{cond}{B} Rd,Rm,[Rn]
                      • B为可选后缀,若有B,则交换无符号字节,否则交换32位字;
                      • Rd为被加载的寄存器;
                      • Rm的数据用于存储到Rn所指的地址中,若Rm与Rd相同,则为寄存器与存储器内容进行交换
                      • Rn为要进行数据交换的存储器地址,Rn不能与Rd和Rm相同
                    • 指令功能:将一个内存单元[Rn]的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中。即Rm->[Rn]->Rd
                    • 指令编码格式
                    • 如下图:
                      • notion image
                      • B用于区别无符号字节(B为1)或字(B为0)。
                      • Rm为源寄存器
                      • Rd为目标寄存器
                      • Rn为基址寄存器。
                    • 指令示例
                    • SWP R1,R1,[R0];将R1的内容与R0指向的存储单元的内容进行交换。 ;从R0指向的存储单元读取1字节数据到R1中(高24位清零),将R2的内容写入到该内存单元中(最低字节有效) SWPB R1,R2,[R0]

                  3.3.4 协处理器指令(-)

                  3.3.4.0 ARM协处理器简介

                  • ARM协处理器简介:ARM支持16个协处理器,用于各种协处理器操作,最常使用的协处理器是用于控制片上功能的系统协处理器,例如控制高速缓存和存储器的管理单元,浮点ARM协处理器等,还可以开发专用的协处理器。
                  • ARM协处理器指令分类
                    • 协处理器数据操作指令。
                    • ARM寄存器与协处理器寄存器的数据传送指令。
                    • 协处理器寄存器和内存单元之间数据存/取指令。
                  • ARM协处理器指令
                    • CDP:协处理器数操作指令 coprocessor
                    • LDC:协处理器数据加载指令
                    • STC:协处理器数据存储指令
                    • MCR:ARM处理器寄存器到协处理器寄存器的数据传送指令
                    • MRC:协处理器寄存器到ARM处理器寄存器的数据传送指令

                  3.3.4.1 CDP——协处理器数据操作指令

                  ARM处理器通过CDP指令通知ARM协处理器执行特定的操作。协处理器数据操作完全是协处理器内部的操作,用于初始化ARM协处理器,完成协处理器寄存器的状态改变。
                  • 指令格式:CDP{<cond>} <CP#>,<Cop1>,CRd,CRn,CRm {,<Cop2>}
                    • CP#:指令操作的协处理器名。标准名为pn,n为0~15。
                    • Cop1:协处理器的特定操作码。
                    • CRd:作为目标寄存器的协处理器寄存器。
                    • CRn:存放第1个操作数的协处理器寄存器。
                    • CRm:存放第2个操作数的协处理器寄存器。
                    • Cop2:可选的协处理器特定操作码。
                  • 指令示例
                  • CDP p7,0,c0,c2,c3,0 ;协处理器7执行操作码1为0和可选操作码2为0的操作。 CDP p6,1,c3,c4,c5 ;协处理器6执行;操作码为1的操作
                  • 指令编码格式
                  • 如下图:
                    • notion image
                      其中,cp_num为协处理器编号。
                  • 指令特点
                    • 该操作由协处理器完成,即对命令参数的解释与协处理器有关,指令的使用取决于协处理器。
                    • 若协处理器不能成功地执行该操作,将产生未定义指令异常中断。

                  3.3.4.2 LDC/STC——协处理器数据取/存指令

                  协处理器数据取/存指令从存储器读取数据装入协处理器寄存器,或将协处理器寄存器的数据存入存储器。
                  • LDC——协处理器数据读取指令
                  • LDC指令从某一连续的内存单元将数据读取到协处理器的寄存器中。进行协处理器的数据传送时,由协处理器来控制传送的字数。若协处理器不能成功地执行该操作,将产生未定义指令异常中断
                    • 指令格式:LDC{cond}{L} <CP#>,CRd, <地址>
                      • L:可选后缀,指明是长整数传送。
                      • CP#:指令操作的协处理器名。标准名为pn,n为0~15。
                      • CRd:作为目标寄存的协处理器寄存器。
                      • <地址> :指定的内存地址。
                    • 指令举例
                    • LDC p5,c2,[R2,#4] ;读取R2+4指向的内存单元的数据,传送到协处理器p5的c2寄存器中。 LDC p6,c2,[R1] ;读取R1指向的内存单元的数据,传送到协处理器p6的c2寄存器中。
                    • 指令编码格式
                    • 如下图:
                      • notion image
                      • cp_num为协处理器编号
                      • 8bitword_offset为8位立即数偏移(单寄存器的第二类也只有8位立即数)
                      • P表示前/后变址
                      • U表示加/减(偏移量是加还是减)
                      • W表示回写(!后缀)
                      • N表示数据的大小(依赖于协处理器)
                  • STC——协处理器数据存入指令
                  • 将协处理器的寄存器数据存入到某一连续的内存单元中,由协处理器来控制写入的字数。若协处理器不能成功地执行该操作,将产生未定义指令异常中断
                    • 指令格式:STC{cond}{L} CP#,CRd,<地址>
                    • 格式说明同LDC指令
                    • 指令示例
                    • STC p5,c1,[R0] STC p5,c1,[R0,#0x04]
                    • 指令编码格式
                    • 如下图:
                      • notion image
                      • cp_num为协处理器编号
                      • 8bitword_offset指8位立即数偏移(单寄存器存取也是8位无符号立即数偏移)
                      • P表示前/后变址
                      • U表示加/减
                      • W表示回写
                      • N表示数据大小(依赖于协处理器)。

                  3.3.4.3 ARM寄存器与协处理器寄存器的数据传送指令

                  • MCR ——ARM寄存器到协处理器寄存器的数据传送指令
                  • MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。若协处理器不能成功地执行该操作,将产生未定义指令异常中断
                    • 指令格式:MCR{cond} CP# , Cop1, Rd, CRn, CRm{, <Cop2>}
                      • Cop1:协处理器的特定操作码. 对于CP15寄存器来说,Cop1=0
                      • Rd:源ARM寄存器,其值将被传送到协处理器寄存器中。
                      • CRn:目标寄存器的协处理器寄存器,其编号是C~C15。
                      • CRm:协处理器中附加的目标寄存器操作数寄存器。如果不需要设置附加信息,将CRm设置为c0,否则结果未知
                      • Cop2:可选的协处理器特定操作码。(用来区分同一个编号的不同物理寄存器,当不需要提供附加信息时,指定为0
                    • 指令示例
                    • MCR p14,0,Rn,c1,c0 ;把ARM寄存器Rn中的值,写入到协处理器P14的C1寄存器中。 MCR p15,0,r3,c2,c0 ;把ARM寄存器R3中的值,写入到协处理器P15的C2寄存器中。
                    • 指令编码格式
                      • notion image
                        其中,cp_num为协处理器编号
                  • MRC——协处理器寄存器到ARM寄存器的数据传送指令
                  • MRC指令将协处理器寄存器中的数据传送到ARM处理器的寄存器中。若协处理器不能成功地执行该操作,将产生未定义指令异常中断
                    • 指令格式:MRC{cond} CP# , Cop1, Rd,CRn,CRm{, <Cop2>}
                    • 其中格式说明同MCR指令
                    • 指令示例
                    • MRC p15, 0, <Rd>, c0, c0, 0 ;返回协处理器C0寄存器中的32位的设备ID号到ARM Rd中。 MRC p15, 0, <Rd>, c0, c0, 1 ;返回协处理器C0寄存器中的caches的信息到ARM Rd中。 MRC p15, 0, <Rd>, c2, c0, 0 ;返回协处理器C2寄存器中的值到ARM Rd中。
                      • P15 有2个C0寄存器:ID号寄存器 和 缓存类型寄存器。这里是通过Cop2区分的
                    • 指令编码格式
                    • 如下图:
                      • notion image
                        其中,cp_num为协处理器编号。

                  3.3.5 杂项指令(**)

                  3.3.5.0 杂项指令简介

                  • 杂项指令分类
                    • 状态寄存器操作指令
                      • MRS:读程序状态寄存器指令
                      • MSR:写程序状态寄存器指令
                    • 异常中断操作指令
                      • SWI: 软件中断指令(可以由用户模式进入管理模式)
                      • BKPT:断点指令(v5T体系)
                      • CLZ: 前导0计数(v5T体系)

                  3.3.5.1 程序状态寄存器处理指令(**)

                  ARM指令中有两条指令,用于在状态寄存器和通用寄存器之间传送数据。修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。
                  • MRS——读状态寄存器指令
                  • 在ARM处理器中,只有MRS指令可以将状态寄存器CPSR或SPSR读出到通用寄存器中
                    • 指令格式:MRS{cond} Rd,psr
                    • 把状态寄存器psr(CPSR或SPSR)的内容传送到目标寄存器中(后移动到前)。
                    • Rd:目标寄存器。Rd不允许为R15(即要是一般通用寄存器)
                      • psr:CPSR或SPSR
                    • 指令编码格式
                    • 如下图:
                      • notion image
                        其中,R用于区别CPSR(R为0)或SPSR(R为1)
                    • 指令示例
                    • MRS R1,CPSR ;R1<- CPSR MRS R2,SPSR ;R2 <-SPSR
                  • MSR——写状态寄存器指令
                  • 在ARM处理器中,只有MSR指令可以直接设置状态寄存器CPSR或SPSR
                    • 指令格式
                    • MSR{cond} psr_fields,#immed MSR{cond} psr_fields,Rm
                      • psr:CPSR或SPSR。
                      • immed:要传送到状态寄存器指定域的8位立即数
                      • Rm:要传送到状态寄存器指定域的数据的源寄存器。
                      • fields :指定传送的区域。fields可以是以下的一种或多种(顺序不重要。指定后只会写入对应的域
                        • c 控制域 (psr[7…0])
                        • x 扩展域(psr[15…8]);(暂未用)
                        • s 状态域 (psr[23…16]);(暂未用)
                        • f 标志位域 (psr[31…24])
                      • 如下图:
                        • notion image
                        从指令格式中可以发现,MSR指令可以向状态寄存器中写一个立即数或者通用寄存器
                    • 指令编码格式
                      • 操作数为立即数的指令编码格式
                        • notion image
                      • 操作数为寄存器的指令编码格式
                        • notion image
                      • 指令编码格式解释
                        • R用于区别CPSR(R为0)或SPSR(R为1)
                        • field_mask表示域屏蔽
                        • rotate_imm即8位位图中的rot。该指令中,这4个bit总是为0
                        • 8bitimmediate为8位立即数
                        • Rm为操作数寄存器。
                    • 指令示例
                    • MSR CPSR_cxsf,R0 ;传送R0的内容到CPSR MSR SPSR_cxsf,R0 ;传送R0的内容到SPSR MSR CPSR_c, R0 ;传送R0的内容到CPSR,但仅仅修改CPSR中的控制位域 MSR CPSR_cfxs,R0 ;传送R0的内容到CPSR,修改所有域
                  • MRS/MSR使用注意事项
                    • 控制域的修改问题:只有在特权模式下才能修改状态寄存器的控制域[7:0],以实现处理器模式转换,或设置开/关异常中断 。
                    • T控制位的修改问题:程序中不能通过MSR指令,直接修改CPSR中的T控制位来实现ARM状态/Thumb状态的切换,必须使用BX指令完成处理器状态的切换。
                    • 用户模式下能够修改的位:在用户模式只能修改“标志位域”,不能对CPSR[23:0]做修改。
                    • S后缀的使用问题:在MRS/MSR指令中不可以使用S后缀

                  3.3.5.2 异常中断产生指令

                  • SWI——软件中断指令
                  • 软件中断指令SWI产生软件异常中断,用来实现用户模式到特权模式的切换
                    • SWI作用
                      • 用于在用户模式下对操作系统中特权模式的程序的调用(即在用户程序调用操作系统的API,作用相当于trap)
                      • 它将处理器置于管理(svc)模式,中断矢量地址为0x08
                    • 指令格式:SWI {<cond>} <24位立即数>
                      • 24位立即数用于指定用户程序调用系统例程的类型,相关参数通过寄存器传递,当指令中24位立即数被忽略时(立即数为0),用户程序调用系统例程的类型由通用寄存器R0决定,同时参数通过其它寄存器传递
                      • 另外需要注意的是这里的立即数没有#
                    • 操作系统API参数传递方法:从上面的指令格式介绍中其实就能看出来了
                      • 指令中的24bit立即数指定API号其它参数通过寄存器传递
                      • 忽略指令中的24bit立即数,r0指定API号其它参数通过其它寄存器传递
                    • 指令编码格式
                      • notion image
                    • 指令示例
                    • ;软中断号在指令中,不传递其它参数: SWI 10 ;中断类型号为10(注:没有#号) SWI 0x123456 ;中断类型号为0x123456 ;软中断号在指令中,其它参数在寄存器中传递: MOV R0,#34 ;准备参数 SWI 12 ;调用12号软中断 ;不用指令中的立即数,软中断类型号和其它参数都在寄存器中传递: MOV R0,#12 ;准备中断类型号 MOV R1,#34 ;准备参数 SWI 0 ;进入软中断。
                  • BKPT——断点指令(-)
                  • 断点中断指令BKPT用于产生软件断点,供调试程序用。它使处理器停止执行正常指令而进入相应的调试程序。v5T及以上体系使用。
                    • 指令格式: BKPT { immed_16}
                      • immed_16:16位的立即数。该立即数被调试软件用来保存额外的断点信息
                  • CLZ——前导0计数指令(-)
                  • 前导0计数指令CLZ 对Rm中的前导0的个数进行计数,结果放到Rd中。 v5T及以上体系使用。
                    • 指令格式:CLZ{<cond>} Rd, Rm
                    • 指令示例
                    • MOV R2, #0X17C00 ;R2=0b0000 0000 0000 0001 0111 1100 0000 0000 CLZ R3, R2 ;R3=15
                  Paper2PX4
                  Loading...
                  Noah
                  Noah
                  永远年轻,永远热泪盈眶
                  公告
                  ❗❗复习笔记问题❗❗
                  由于兼容性问题
                  导入md文件可能导致了一些格式错误
                  🌹如发现格式错误,请联系我~🌹
                  🌹如博客内容有误也欢迎指出~🌹