STM32
直播中

自我清欢

10年用户 1050经验值
擅长:可编程逻辑 模拟技术 EMC/EMI设计 光电显示 存储技术
私信 关注
[问答]

ARM汇编指令以及伪指令的知识点汇总,不看肯定后悔

ARM汇编指令以及伪指令的知识点汇总,不看肯定后悔

回帖(1)

徐慧

2021-11-30 10:03:46
ARM寄存器

  
  

  

  


  • R13:栈指针寄存器(SP)
  • R14:连接寄存器(LR)
  • R15:程序计数器(PC)

  

  • ARM采用流水线机制,当读取PC值时,该值为当前指令地址值+8个字节
  • 当使用STR/STM保存R15时,保存可能是当前地址值+8字节,也有可能保存当前指令地址+12字节(取决芯片的具体设计)
  • 由于ARM指令是字对齐,PC值的第0位和第1位总为0

  


  • CPSR:程序状态寄存器

  

  • 可以在任何处理器模式下访问,包含条件标志位、中断禁止位、当前处理器模式标志,以及其他一些控制和状态位
  • 每种模式下,都有一个专用的物理状态寄存器(SPSR),当异常发生时,SPSR存放当前的程序状态寄存器,异常退出时,可以用SPSR保存的值回复CPSR
  • 由于用户模式和系统模式不是异常模式,所有没有SPSR,如果在用户模式或系统模式访问SPSR时,将发生不可预知的结果

  


  • CPSR和SPSR的格式

      



  

  • 条件标志位

  [tr]标志位含义[/tr]
N当两个补码表示的有符号整数运算时,N=1表示运算的结果为负数;N=0表示结果为整数或零
ZZ=1表示运算的结果为0;Z=0表示运算的结果不为零,对应CMP指令,Z=1表示进行比较的两个数大小相等
C在加法指令中(包括比较指令CMP),当结果产生进位,则C=1,表示无符号数运算发生上溢,否则C=0;在减法指令中(包括比较指令CMP),当运算发生借位,则C=0,表示无符号数运算发生下溢,否则C=0;对于包含位移操作的非加/减法运算指令,C包含最后一次被溢出的位数值
V用于加/减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出
以下指令会影响CPSR中的条件标志位
1、比较指令,如CMP、CMN、TEQ、TST等
2、当一些算术运算指令和逻辑指令的目标寄存器不是R15(PC)时,将会影响CPSR中的条件标志位
3、MSR指令可以向CPSR/SPSR写入新值
4、MRC指令将R15作为目标寄存器时,可以把写处理器产生的条件标志位的值传给ARM处理器
5、一些LDM指令的变种指令可以将SPSR的值赋值到CPSR中,例如从异常中断程序返回
6、一些带“位设置”的算术和逻辑指令的变种指令,也可以将SPSR的值复制到CPSR中,例如从异常中断程序返回
  

  • Q标志位,在ARMv5的E系列处理器中,主要用于只是增强的DSP指令是否发生溢出
  • CPSR的控制位,在特权的处理器模式下,软件可以修改这些控制位
    1、 中断禁止位,当I=1时禁止IRQ中断;当F=1时禁止FIQ中断
    2、T控制位,用于控制指令执行的状态,即说明本指令时ARM指令,还是Thumb指令
    3、M控制位,控制处理器模式

      


  • CPSR其他位用于将来ARM版本的扩展

  ARM指令的一般编码格式

  典型的ARM指令语法格式:
{}{S} ,,

  

  

其中的符号及参数说明:
  


  • opcode:指令操作符编码
  • cond:指令执行的条件编码
  • S:决定指令的操作是否影响CPSR的条件标志位
  • Rd:目标寄存器编码
  • Rn:包含第1个操作数的寄存器编码
  • shifter_operand:表示第2个操作数

  ARM指令的条件域

  


  • 多数ARM指令都可以有条件执行,根据CPSR的条件标志位决定是否执行该指令。当条件满足时执行,条件不满足时改指令被当作一条NOP指令。
  • 可条件执行的指令可以在其指令助记符的扩展域加上条件码助记符,从而在特定的条件下执行。

  [tr]条件码条件码助记符含义CPSR条件标志位[/tr]
0000EQ相等Z=1
0001NE不相等Z=0
0010CS/HS无符号数大于/等于C=1
0011CC/LO无符号数小于C=0
0100MI负数N=1
0101PL非负数N=0
0110VS上溢出V=1
0111VC没有上溢出V=0
1000HI无符号数大于C=1 且 Z=0
1001LS无符号数小于等于C=0 或Z=1
1010GE带符号数大于等于N=1 且 V=1 或 N= 0 且 V=0
1011LT带符号数小于N=1 且 V=0 或 N= 0 且 V=1
1100GT带符号数小于Z=0 且 N=V
1101LE带符号数小于/等于Z=1 或 N !=V
1110AL无条件执行
1111NV该指令从不执行ARMv3之前
1111未定义该指令执行结果不可预知ARMv3及ARMv4
1111AL该指令无条件执行ARMv5及以上版本
ARM寻址方式

  数据处理指令的操作数寻址方式

  通常数据处理指令格式:
{}{S} ,,
  


  • opcode:指令操作符编码
  • cond:指令执行的条件编码
  • S:决定指令的操作是否影响CPSR的条件标志位
  • Rd:目标寄存器编码
  • Rn:包含第1个操作数的寄存器编码
  • shifter_operand:表示第2个操作数

  通常有3种格式
  

  • 立即数方式。每个立即数由一个8位的常数循环右移偶数位得到,其中循环右移的位数由一个4位二进制的二倍表示。如果立即数记作,8位常数记作immed_8,4位的循环右移值记作rotate_imm,则有:=immed_8 循环右移 (2*rotate_imm)。并不是每一个32位的常数都是合法的立即数,只有通过构造方法得到的才是合法的立即数,如0x101、0x102、oxFF1不是合法的立即数。而且一个合法的立即数可能有多种编码方法,并且立即数构造包含循环移位操作,从而影响CPSR的条件标志位,因此ARM汇编编译器按照下面的规则生成立即数的编码:
    1、当立即数数值在0和0xFF范围时,令immed_8=,rotate_imm=0
    2、其他情况下,汇编编译器选择使rotate_imm数值最小的编码方式
  • 寄存器方式,在寄存器寻址方式下,操作数即为寄存器的数值,如:MOV R3,R2
  • 寄存器移位方式,操作数位寄存器的数值做相应的移位而等到,移位的位数可以用立即数或寄存器方式表示
    1、ASR:算术右移
    2、LSL:逻辑左移
    3、LSR:逻辑右移
    4、ROR:循环右移
    5、RRX:扩展的循环右移
    MOV R0,R1,LSL #3 ;RO=R1<<3
    MOV R0,R1,ROR R2 ;RO=R1循环右移R2位

  立即数寻址方式

  指令编码格式

  

  

  


  • 指令的操作数即为立即数#
  • 当ratate_imm=0时,循环器的进位值为CPSR的C位条件标志位;当rotate_imm!=0时,循环器的进位值为操作数的最高位bit[31]
    示例:
    MOV R0,#0xFC0;令R0的数值为0xFC0

  寄存器寻址方式

  指令编码格式

  

  

  寄存器移位寻址方式

  

  • ,LSL #的指令编码格式

      


  • ,LSL 的指令编码格式

      


  • ,LSR #的指令编码格式

      


  • ,LSR 的指令编码格式

      


  • ,ASR #的指令编码格式

      


  • ,ASR 的指令编码格式

      


  • ,ROR #的指令编码格式

      


  • ,ROR 的指令编码格式

      


  • ,RRX的指令编码格式

      



  字或无符号字节的Load/Store指令的寻址方式

  LDR指令格式

  

  

  


  • cond为指令执行的条件编码
  • I、P、U、B、W、L标志位
  • Rd为目标寄存器编码
    1、I标志位指示address_mode是否使用基址寄存器,当I=1时,address_mode使用基址寄存器,当I=0时,则使用偏移量
    2、P标志位
    3、U标志位指示偏移量,当U=1时,正偏移,当U=0时,负偏移
    4、B标志位用于控制操作的数据类型,当B=1时,指令访问无符号字节数据,当B=0时,指令访问的是字数据
    5、W标志位用更新基址寄存器的值,当W=1时,访问完成后更新寄存器值,当W=0时,访问完成不更新
    6、L标志位用于控制内存操作的方向,当L=1时。指令执行Load操作,当L=0时,指令执行Store操作
  • Rn与Address_mode一起构成第二个操作数的内存地址
  • 当R15用作基址寄存器时,内存基地址为当前指令地址+8字节偏移量

  LDR指令的语法格式:
LDR{}{B}{T} ,
  其中,表示第2个操作数的内存地址,共有9种格式
  

  • [, #+/-] 指令编码格式

      

      

    内存地址address为寄存器的值加上/减去偏移量offset_12
    示例:LDR R0, [R1,#4];将内存单元R1+4中的字读取到R0寄存器中
  • [, #+/-] 指令编码格式

      

      

    内存地址address为寄存器的值加上/减去索引寄存器Rm的值
  • [, #+/-, #] 指令编码格式

      

      

    内存地址address为基址寄存器Rn的值加上/减去一个地址偏移量
    示例:LDR R0, [R1,R2,LSL #2];将地址单元(R1+R2*4)中的数据读取到R0中
  • [, #+/-]! 指令编码格式

      

      

    内存地址address为寄存器的值加上/减去偏移量offset_12,当指令执行的条件满足时,生成的地址值将跟新到基址寄存器Rn中
    示例:LDR R0, [R1,#4]!;将内存单元R1+4中的字读取到R0寄存器中,同时R1=R1+4
  • [, #+/-]! 指令编码格式

      

      

    1、内存地址address为寄存器的值加上/减去索引寄存器Rm的值,当指令执行的条件满足时,生成的地址值将跟新到基址寄存器Rn中
    2、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
    3、当Rn和Rm时同一个寄存器时,会产生不可预知的结果
  • [, #+/-, #]! 指令编码格式

      

      

    1、内存地址address为基址寄存器Rn的值加上/减去一个地址偏移量,当指令执行的条件满足时,生成的地址值将跟新到基址寄存器Rn中
    2、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
    3、当Rn和Rm时同一个寄存器时,会产生不可预知的结果
    示例:LDR R0, [R1,R2,LSL #2];将地址单元(R1+R24)中的数据读取到R0中,同时R1=R1+R24
  • [], #+/- 指令编码格式

      

      

    1、指令使用基址寄存器Rn的值作为实际内存访问的地址
    2、当指令执行的条件满足时,将基址寄存器的值加上/减去偏移量,生成新的地址值
    3、最后将新的地址值写入基址寄存器Rn中
    4、当R15用作基址寄存器Rn或Rm时,会产生不可预知的结果
    示例:LDR R0, [R1]+4;将地址为R1的内存单元数据读取到R0中,然后R1=R1+4
  • [], #+/- 指令编码格式

      

      

    1、指令使用基址寄存器Rn的值作为实际内存访问的地址
    2、当指令执行的条件满足时,将基址寄存器的值加上/减去偏移量Rm,生成新的地址值
    3、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
    4、当Rn和Rm时同一个寄存器时,会产生不可预知的结果
  • [], #+/-, # 指令编码格式

      

      

    1、指令使用基址寄存器Rn的值作为实际内存访问的地址
    2、当指令执行的条件满足时,将基址寄存器的值加上/减去偏移量Rm,生成新的地址值
    3、当R15用作基址寄存器Rn和Rm时,产生不可预知的结果
    4、当Rn和Rm时同一个寄存器时,会产生不可预知的结果

  杂类Load/Store指令的寻址方式

  操作数为半字(无符号数或带符号数)数据的Load/Store指令,这类指令的语法格式为:
LDR | STR{}H | SH | SB | D ,
其中是指令内存单元的寻址方式,具体有6种格式:
  

  • [, #+/-]
  • [, #+/-]
  • [, #+/-]!
  • [, #+/-]!
  • [], #+/-
  • [], #+/-]

  批量Load/Store指令的寻址方式

  批量Load/Store指令可以实现在一组寄存器和一块连续的内存单元之间传输数据,这类指令的语法格式为:
LDM | STM{} {!}, {^}
  


  • 指令中寄存器和内存单元的对应关系规则:编号低的寄存器对应与内存中的低地址单元,编号高的寄存器对应与内存中的高地址单元
  • 存放地址块的最低地址值
  • 栈指针指向栈顶元素(即最后一个入栈的数据元素)时,称为Full栈;栈指针指向与栈顶元素相邻的一个可用数据单元时称为Empty栈
  • 当数据栈向内存地址减少的方向增长时,称为Descending栈;当数据栈向内存地址增加的方向增长时,称为Ascending栈

  其中表示通常地址的变化方式,具体有4种格式:
  

  • IA:事后递增方式
  • IB:事先递增方式
  • DA:事后递减方式
  • DB:事先递减方式

  其中表示数据栈地址的变化方式,具体有4种格式
  

  • FD:Full Descending
  • ED:Empty Descending
  • FA:Full Ascending
  • EA:Empty Ascending

  协处理器Load/Store指令的寻址方式

  一条协处理器Load/Store指令可以在ARM处理器和协处理器之间传输批量数据,其语法格式如下:
{}{L} ,,
其中表示地址的变化方式有以下4种格式:
  

  • [, #+/-*4]
  • [, #+/-*4]!
  • [], #+/-*4
  • [],
  ARM指令集

  跳转指令

  ARM跳转指令可以从当前指令向前或向后32MB的地址空间跳转
  

  • B:跳转指令
  • BL:带返回的跳转指令
  • BX:带状态切换的跳转指令
  • BLX:带返回和状态切换的跳转指令

  


  • BL指令的编码格式

      

      

    指令的语法格式
    B{L}{}
  • BLX(1)指令从ARM指令集跳转到指令中指定的目的地址,并将程序状态切换为Thumb状态,该指令同时将PC寄存器的内容复制到LR寄存器中,指令编码格式

      

      

    指令的语法格式
    BLX
  • BLX(2)指令从ARM指令集跳转到指令中指定的目的地址,目标地址的指令可以是ARM指令,也可以是Thumb指令,目标地址放在指令中的寄存器,该地址的bit[0]为0,目标地址处的指令类型由CPSR中的T位决定,该指令同时将PC寄存器的内容复制到LR寄存器中,指令编码格式

      

      

    指令的语法格式
    BLX{}
  • BX指令从ARM指令集跳转到指令中指定的目的地址目标地址处的指令可以是ARM指令。也可以是Thumb指令,目标地址值为指令的值和0xFFFFFFFE与操作的结果,目标地址处的指令类型由寄存器的bit[0]决定,当bit[0]=0时,目标地址处的指令为ARM指令,当bit[0]=1时,目标地址处的指令为Thumb指令,指令编码格式

      

      

    指令的语法格式
    BX{}

  数据处理指令

  MOV

  MOV指令将shifter_operand表示的数据传送到目标寄存器Rd中,并根据操作结果跟新CPSR
指令的编码格式

  

  

指令的语法格式
MOV{}{S} ,
  


  • 将数据从一个寄存器传送到另一个寄存器中
  • 将一个常数传送到一个寄存器中
  • 实现单纯的移位操作,左移操作可以实现将操作数乘以2^n
  • 当PC寄存器作为目标寄存器时,可以实现程序跳转
  • 当PC寄存器作为目标寄存器且指令中S位被设置时,指令在执行跳转操作的同时,将当前处理器模式的SPSR寄存器内容复制到CPSR中,这样指令“MOVS PC,LR”可以实现从某些中断中返回

  MVN

  MVN指令将shifter_operand表示的数据反码传送到目标寄存器Rd中,并根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
MVN{}{S} ,
  


  • 向寄存器传送一个负数
  • 生成位掩码
  • 求一个数的反码

  ADD

  ADD指令将shifter_operand表示的数据与寄存器Rn中的值相加,并把结果保存到目标寄存器Rd中,同时根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
ADD{}{S} , ,
  


  • 实现两个操作数相加

  ADC

  ADC指令将shifter_operand表示的数据与寄存器Rn中的值相加,再加上CPSR中的C条件标志位的值,并把结果保存到目标寄存器Rd中,同时根据结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
ADC{}{S} , ,
  


  • ADC指令和ADD指令联合使用,可以实现两个64位的操作数相加

  SUB

  SUB指令从寄存器Rn中减去shifter_operand表示的数值,并把结果保存到目标寄存器Rd中,根据结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
SUB{}{S} , ,
  


  • SUB指令实现两个操作数相减
  • 当SUBS指令与跳转指令联合使用时,可以实现程序中循环,就不需要CMP指令了
  • 在SUBS指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

  SBC

  SBC指令从寄存器Rn中减去shifter_operand表示的数值,再减去寄存器CPSR中C条件标志位的反码,并把结果保存到目标寄存器Rd中,同时根据操作的结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
SBC{}{S} , ,
  


  • SBC指令和SUBS指令联合使用,可以实现两个64位的操作数相减
  • 在SBCS指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

  RSB

  RSB指令从shifter_operand表示的数值减去寄存器Rn值,并把结果保存到目标寄存器Rd中,同时根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
RSB{}{S} , ,
  


  • RSB指令实现两个操作数相减
  • 在RSB指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

  RSC

  RSC指令将shifter_operand表示的数值减去寄存器Rn的值,再减去寄存器CPSR中C条件标志位的反码,并把结果保存到目标寄存器Rd中,根据操作的结果跟新CPSR
  指令的编码格式

  

  

指令的语法格式
RSC{}{S} , ,
  


  • 在RSCS指令中,如果发生了借位操作,CPSR寄存器中的C标志位设置成0,如果没有发生借位操作,CPSR寄存器的C标志位设置成1

  AND

  AND指令将shifter_operand表示的数值与寄存器Rn的值按位与操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
AND{}{S} , ,
  ORR

  ORR指令将shifter_operand表示的数值与寄存器Rn的值按位或操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
ORR{}{S} , ,
  EOR

  ORR指令将shifter_operand表示的数值与寄存器Rn的值按位异或操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
EOR{}{S} , ,
  BIC

  BIC指令将shifter_operand表示的数值与寄存器Rn的值的反码按位与操作,并把结果保存到目标寄存器Rd中,根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
BIC{}{S} , ,
  


  • BIC指令可用于将寄存器中某些位的值设置成0,将某一位与1做BIC操作,该位值被设置成0;将某一位与0做BIC操作,该位不变

  CMP

  CMP指令从寄存器Rn中减去shifter_operand表示的数值,根据操作结果更新CPSR,后面的指令可以根据CPSR中的条件标志位判断是否执行
  指令的编码格式

  

  

指令的语法格式
CMP} ,
  


  • CMP指令不保存操作结果

  CMN

  CMN指令将寄存器Rn中的值加上shifter_operand表示的数值,根据操作结果更新CPSR,后面的指令可以根据CPSR的条件标志位判断是否执行
  指令的编码格式

  

  

指令的语法格式
CMN} ,
  


  • CMN根据加法操作结果设置CPSR条件标志位,不保存操作结果

  TST

  TST指令将shifter_operamd表示的数值与寄存器Rn的值按位与操作,根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
TST} ,
  


  • TST指令通常用于测试寄存器中某些位是1还是0

  TEQ

  TEQ指令将shifter_operand表示的数值与寄存器Rn的值按位异或操作,根据操作结果更新CPSR
  指令的编码格式

  

  

指令的语法格式
TEQ} ,
  


  • TEQ指令通常用于比较两个数是否相等,这种操作通常不影响CPSR寄存器中的V位和C位
  • TEQ指令也可以用于比较两个操作数符号是否相同,该指令执行后,CPSR寄存器中的N位位两个操作数符号位异或操作的结果

  乘法指令

  MUL

  MUL指令实现两个32位的数(可以为无符号数,也可以是符号数)的乘积,并将结果放到一个32位的寄存器中,根据运算结果设置CPSR
  指令的编码格式

  

  

指令的语法格式
MUL}{S} ,
  


  • Rd寄存器为目标寄存器
  • Rm寄存器为第1个乘数所在的寄存器
  • Rs寄存器为第2个乘数所在的寄存器
  • 由于两个32位数相乘结果为64位,而MUL指令仅仅保存64位结果的低32位,对于带符号数和无符号的操作数来说,MUL指令执行的结果相同
  • 对于ARMv5以上,MULS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,MULS指令对CPSR的C标志位数值不确定
  • 寄存器Rm、Rn、Rd为R15时,指令执行的结果不可预测

  MLA

  MLA指令实现两个32位的数(可以为无符号数,也可以是符号数)的乘积,再将乘积加上第3个操作数,并把结果存放到一个32位的寄存器中,根据运算结果设置CPSR
  指令的编码格式

  

  

指令的语法格式
MLA}{S} , , ,
  


  • Rd寄存器为目标寄存器
  • Rm寄存器为第1个乘数所在的寄存器
  • Rs寄存器为第2个乘数所在的寄存器
  • Rn寄存器为第3个操作数所在的寄存器,该操作数时一个加数
  • 由于两个32位数相乘结果为64位,而MLA指令仅仅保存64位结果的低32位,对于带符号数和无符号的操作数来说,MLA指令执行的结果相同
  • 对于ARMv5以上,MLAS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,MLAS指令对CPSR的C标志位数值不确定
  • 寄存器Rm、Rn、Rd为R15时,指令执行的结果不可预测

  SMULL

  SMULL指令实现两个32位的有符号数的乘积,乘积结果的高32位存放到一个32位的寄存器的RdHi中,乘积结果的低32位存放到另一个32位的寄存器RdLo中,根据运算结果设置CPSR
  指令的编码格式

  

  

指令的语法格式
SMULL}{S} , , ,
  


  • RdHi寄存器存放乘积结果的高32位数据
  • RdLo寄存器存放乘积结果的低32位数据
  • 对于ARMv5以上,SMULLS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,SMULLS指令对CPSR的C标志位数值不确定
  • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

  SMLAL

  SMLAL指令将两个32位有符号数的64位乘积结果与RdHi和RdLo中的64位数相加,乘加法结果的高32位存放到RdHi,乘加法结果的低32位存放到RdLo中,根据运算结果设置CPSR
  指令的编码格式

  

  

指令的语法格式
SMLAL}{S} , , ,
  


  • RdHi寄存器再指令执行前存放64位加数的高32位,指令执行后存放结果的高32位数据
  • RdLo寄存器再指令执行前存放64位加数的低32位,指令执行后存放结果的低32位数据
  • 对于ARMv5以上,SMLALS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,SMLALS指令对CPSR的C标志位数值不确定
  • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

  指令操作的伪代码
  if cond then         RdLo = (Rm * Rs)[31:0] + RdLo         RdHi = (Rm * Rs)[63:32 + RdHi + CPSR[C]         if S = 1 then                 CPSR[N] = RdHi[31]                 CPSR[Z] = if((RdHi == 0) and (RdLo == 0)) then 1 else 0                 CPSR[C] = unaffected                 CPSR[V] = unaffected   UMULL

  UMULL指令实现两个32位的无符号数的乘积,乘积结果的高32位存放到一个32位的寄存器的RdHi中,乘积结果的低32位存放到另一个32位的寄存器RdLo中,根据运算结果设置CPSR
  指令的编码格式

  

  

指令的语法格式
UMULL}{S} , , ,
  


  • RdHi寄存器存放乘积结果的高32位数据
  • RdLo寄存器存放乘积结果的低32位数据
  • 对于ARMv5以上,UMULLS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,UMULLS指令对CPSR的C标志位数值不确定
  • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

  UMLAL

  UMLAL指令将两个32位有符号数的64位乘积结果与RdHi和RdLo中的64位数相加,乘加法结果的高32位存放到RdHi,乘加法结果的低32位存放到RdLo中,根据运算结果设置CPSR
  指令的编码格式

  

  

指令的语法格式
UMLAL}{S} , , ,
  


  • RdHi寄存器再指令执行前存放64位加数的高32位,指令执行后存放结果的高32位数据
  • RdLo寄存器再指令执行前存放64位加数的低32位,指令执行后存放结果的低32位数据
  • 对于ARMv5以上,UMLALS指令不影响CPSR寄存器的C条件标志位,ARMv5以下,UMLALS指令对CPSR的C标志位数值不确定
  • 寄存器Rm、Rn、RdHi、RdLo为R15时,指令执行的结果不可预测

  指令操作的伪代码
  if cond then         RdLo = (Rm * Rs)[31:0] + RdLo         RdHi = (Rm * Rs)[63:32 + RdHi + CPSR[C]         if S = 1 then                 CPSR[N] = RdHi[31]                 CPSR[Z] = if((RdHi == 0) and (RdLo == 0)) then 1 else 0                 CPSR[C] = unaffected                 CPSR[V] = unaffected   杂类算术指令

  在ARMv5及以上的版本中,包含一条特殊的指令CLZ,用于计算操作数最高端0的个数
  CLZ

  CLZ指令用于计算寄存器中操作数最高端0的个数,如果操作数的bit[31]为1,则指令返回0;如果操作数为0,则指令返回32
  指令的编码格式

  

  

指令的语法格式
CLZ}{S} ,
  


  • cond为指令执行的条件码,当cond忽略时,指令为无条件执行
  • Rd为目标寄存器
  • Rm寄存器为第1个乘数所在的寄存器
  • Rs为源操作数寄存器,当Rd为R15时,指令执行结果不可预知

  指令操作的伪代码
  if Rm == 0         Rd = 32 else         Rd = 31 - (bit position of most significant '1' in Rm)   状态寄存器访问指令

  


  • 状态寄存器中,未定义位是当前未使用
  • 程序不能通过直接修改CPSR中的T控制位直接将程序状态切换到Thumb状态,必须通过BX等指令完成程序状态的切换
  • 通常修改状态寄存器是通过“读取-修改-写回”的操作序列来实现

  MRS

  MRS指令用于将状态寄存器的内容传送到通用寄存器中
  指令的编码格式

  

  

指令的语法格式
MRS{} ,CPSR
MRS{} ,SPSR
  


  • cond为指令执行的条件码,当cond忽略时,指令为无条件执行
  • Rd为目标寄存器
  • MRS指令用于将状态寄存器的内容读取到通用寄存器中
  • 当异常中断允许嵌套时,需要在进入异常中断之后,嵌套中断发生之前保存当前处理器模式对应的SPSR,这时需要先通过MRS指令读取SPSR的值,然后用其他指令将SPSR的值保存起来
  • 在进程切换时也需要保存当前状态寄存器值

  MSR

  MSR指令用于将通用寄存器内容或一个立即数传送到状态寄存器中
  指令的编码格式
(1) 指令的源操作数为通用寄存器时

  

  

(2) 指令的源操作数为立即数时

  

  

指令的语法格式
MSR{} CPSR_, #
MSR{} CPSR_,
MSR{} SPSR_, #
MSR{} SPSR_,
  


  • CPSR_(CPSR_fsxc、CPSR_f、CPSR_s、CPSR_x、CPSR_c)的fields是设置状态寄存器中需要操作的位。状态寄存器的32位可以分为4个8位的域:bits[31:24]为条件标志位域,用f表示;bits[23:16]为状态位域,用s表示;bits[15:8]为扩展位域,用x表示;bits[7:0]为控制位域,用c表示
  • immediate为将要传送到状态寄存器中的立即数
  • Rm寄存器包含将要传送到状态寄存器中的数据
  • MSR指令通常用于恢复状态寄存器的内容或者修改状态寄存器的内容

  指令操作的伪代码
  if cond then         if opcode[25] == 1                 operand = 8_bit_immediate << (rotate_imm * 2)         else                  operand = Rm                  if R == 0 then                 if field_mask[0] == 1 and InaprivilegeMode() then                          CPSR[7:0] = operand[7:0]                 if field_mask[1] == 1 and InaprivilegeMode() then                          CPSR[15:8] = operand[15:8]                 if field_mask[2] == 1 and InaprivilegeMode() then                          CPSR[23:16] = operand[23:16]                 if field_mask[3] == 1 and InaprivilegeMode() then                          CPSR[31:24] = operand[31:24]         else                  if field_mask[0] == 1 and CurrentModeHasSPSR() then                          SPSR[7:0] = operand[7:0]                 if field_mask[1] == 1 and urrentModeHasSPSR()then                          SPSR[15:8] = operand[15:8]                 if field_mask[2] == 1 and urrentModeHasSPSR()then                          SPSR[23:16] = operand[23:16]                 if field_mask[3] == 1 and urrentModeHasSPSR()then                          SPSR[31:24] = operand[31:24]   内存访问指令

  LDR

  LDR指令用于从内存中将一个32位的字读取到指令中的目标寄存器中,如果指令中寻址方式确定的地址不是字对齐的,则从内存中读取的数值要进行循环右移操作,移位的位数为寻址方式确定的地址的bits[1:0]的8倍。
  指令的编码格式

  

  

指令的语法格式
LDR{} ,
  


  • cond为指令执行的条件码,当cond忽略时,指令为无条件执行
  • Rd为目标寄存器
  • addressing_mode为指令的寻址方式
  • 用于从内存读取32位字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
  • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能,当PC被作为LDR指令的目标寄存器时,指令从内存中读取的字节数据将被当作目标地址值,指令执行后,程序将从目标地址处开始执行。在ARMv5及以上版本中,地址值的bit[0]用来确定目标地址处的程序状态,当bit[0]=1时,目标地址指令为Thumb指令;当bit[0]=0时,目标地址指令为ARM指令

  指令操作伪代码
  if cond then         if address[1:0] == 0b00 then                 value = Memory[address,4]         else if address[1:0] == 0b01 then                 value = Memory[address, 4] Rotate_Right 8         else if address[1:0] == 0b10 then                 value = Memory[address, 4] Rotate_Right 16         else                 value = Memory[address, 4] Rotate_Right 24 if (Rd is R15) then         if (architecture version 5 or above) then                 PC = value AND 0xFFFFFFFE                 CPSR[T] = value[0]         else                 PC = value AND 0xFFFFFFFC else          Rd = value   LDRB

  LDRB指令用于从内存中将一个8位的字节数据读取到指令中的目标寄存器中,并且将寄存器的高24位清零
  指令的编码格式

  

  

指令的语法格式
LDR{}B ,
  


  • 用于从内存读取8位字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
  • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

  LDRBT

  LDRBT指令用于从内存中将一个8位的字节数据读取到指令中的目标寄存器中,并将寄存器的高24位清零,当在特权模式下使用本指令时,内存系统将该操作当作一般用户模式下的内存访问操作
  指令的编码格式

  

  

指令的语法格式
LDR{}BT ,
  


  • 异常中断程序是在特权模式下执行的,此时如果需要按照用户模式的权限访问内存,可以使用LDRBT指令

  LDRH

  LDRH指令用于从内存中将一个16位的半字数据读取到指令中的目标寄存器中,并将寄存器的高16位清零,如果指令中的内存地址不是半字对齐的,指令会产生不可预知的结果
  指令的编码格式

  

  

指令的语法格式
LDR{}H ,
  


  • 用于从内存中读取16位半字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
  • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

  LDRSB

  LDRSB指令用于从内存中将一个8位字节数据读取到指令中的目标寄存器中,并将寄存器的高24位设置成该字节数据的符号位的值(即将该8位字节数据进行符号位扩展,生成32位字数据)
  指令的编码格式

  

  

指令的语法格式
LDR{}SB ,
  


  • 用于从内存中读取8位字节数据到通用寄存器中,然后可在该寄存器中对数据进行操作
  • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

  LDRSH

  LDRSH指令用于从内存中将一个16位的半字数据读取到指令中的目标寄存器中,并将寄存器的高12位设置成该半字数据发惹符号位的值,如果指令的内存地址不是半字对齐,指令产生不可预知的结果
  指令的编码格式

  

  

指令的语法格式
LDR{}SH ,
  


  • 用于从内存中读取16位半字数据到通用寄存器中,然后可在该寄存器中对数据进行操作
  • 当PC作为指令中的目标寄存器时,指令可以实现程序跳转功能

  LDRT

  LDR指令用于从内存中将一个32位的字读取到指令中的目标寄存器中,如果指令中寻址方式确定的地址不是字对齐的,则从内存中读取的数值要进行循环右移操作,移位的位数为寻址方式确定的地址的bits[1:0]的8倍。当在特权模式下使用本指令时,内存系统将该操作当作一般用户模式下的内存访问操作
  指令的编码格式

  

  

指令的语法格式
LDR{}T ,
  


  • 异常中断程序在特权模式下执行时,如果需要按照用户模式的权限访问内存,可以使用LDRT指令

  STR

  STR指令用于将一个32位的字数据写入到指令中指定的内存单元
  指令的编码格式

  

  

指令的语法格式
STR{} ,
  


  • cond为指令执行的条件码,当cond忽略时,指令无条件执行
  • Rd为目标寄存器
  • addressing_mode为指令的寻址方式

  示例
  STR R0, [R1, #0x100];将R0中的字数据保存到内存单元[R1+0x100]中   STRB

  STRB指令用于将一个8位字节数据写入到指令中指定的内存单元,该字节数据为指令中存放源操作数的寄存器的低8位
  指令的编码格式

  

  

指令的语法格式
STR{}B ,
  STRH

  STRH指令用于将一个16位的半字数据写入到指令中指定的内存单元,该半字数据为指令中存放源操作数的寄存器的低16位,如果指令中的内存地址不是半字对齐的,指令会产生不可预知的结果
  指令的编码格式

  

  

指令的语法格式
STR{}H ,
  STRT

  STRT指令用于将一个32位的字数据写入到指令中指定的内存单元,当在特权模式下执行该指令时,内存系统将该操作当作一般用户下的内存访问操作
  指令的编码格式

  

  

指令的语法格式
STR{}T ,
  STRBT

  STRBT指令用于将一个8位的字节数据写入到指令中指定的内存单元,当在特权模式下执行该指令时,内存系统将该操作当作一般用户模式下的内存访问操作
  指令的编码格式

  

  

指令的语法格式
STR{}BT ,
  批量Load/Store内存访问指令

  批量Load/Store内存访问指令的语法格式
LDM | STM {} Rn{!}, {^}
其中表示通常地址的变化方式,具体有4种格式:
  

  • IA:事后递增方式
  • IB:事先递增方式
  • DA:事后递减方式
  • DB:事先递减方式

  其中表示数据栈地址的变化方式,具体有4种格式
  

  • FD:Full Descending
  • ED:Empty Descending
  • FA:Full Ascending
  • EA:Empty Ascending

  LDM(1)(批量内存字数据读取指令)

  


  • LDM指令将数据从连续的内存单元中读取到指令中寄存器列表中的各寄存器中,主要用于块数据的读取、数据栈操作以及子程序中返回的操作
  • 当PC包含在LDM指令的寄存器列表时,指令从内存中读取的字数据将被当作目标地址值,指令执行后,程序将从目标地址处开始执行,即实现跳转操作
  • 在ARMv5及以上版本中,地址值的bit[0]用来确定目标地址处的程序状态,当bit[0]=1时,目标地址处的指令为Thumb指令;当bit[0]=0时,目标地址处的指令为ARM指令

  指令的编码格式

  

  

指令的语法格式
LDM{} Rn{!},
  


  • cond为指令执行的条件码
  • Rn为指令寻址模式中的基址寄存器,存放地址块的最低地址值
  • !设置指令中的W位,使指令执行后将操作数的内存地址写入基址寄存器Rn中
  • addressing_mode为指令的寻址方式
  • registers为寄存器列表,其中寄存器和内存单元的对应关系满足:编号低的寄存器对应于内存中的低地址单元,编号高的寄存器对应内存中的高地址单元
  • 如果指令中的基址寄存器Rn在寄存器列表register中,而且指令中寻址方式指定指令执行后跟新基址寄存器Rn值,则指令执行产生不可预知结果

  指令操作伪代码
  if cond then         address = start_address         for i = 0 to 14                 if register_list == 1 then                         Ri = Memory[address, 4]                         address = address + 4                 if register_list[15] == 1 then                         value = Memory[address, 4]                         if (architecture version 5 or above) then                                 pc = value AND 0xFFFFFFFE                                 CPSR[T] = value[0]                         else                                 pc  = value AND 0xFFFFFFFC                         address = address + 4                 assert end_address = address - 4   LDM(2)(用户模式的批量内存字数据读取指令)

  

  • LDM指令将数据从连续的内存单元中读取到指令中寄存器列表中的各寄存器中,主要用于块数据的读取、数据栈操作以及子程序中返回的操作
  • PC寄存器不能包含在LDM指令的寄存器列表中
  • 当特权模式下执行该指令,内存系统将该操作当作一般用户模式下的内存访问操作

  指令的编码格式

  

  

指令的语法格式
LDM{} , ^
  

  • ^在寄存器列表中不含PC寄存器,指示指令中所用的寄存器为用户模式下的寄存器
  • 在本指令的后面不能紧跟访问备份寄存器的指令,最好跟一条NOP指令
  • 指令中的基址寄存器是指令执行时的当前处理器模式对应的物理寄存器,而不是用户模式对应的寄存器
  • 本指令忽略指令中内存地址的低2位
  • 异常中断程序在特权模式下执行,如果需要按照用户模式的权限访问内存,可以使用该指令
  • 在用户模式和系统模式下使用本指令会产生不可预知的结果

  指令操作的伪代码
  if cond then         address = start_address         for i = 0 to 14                 if register_list == 1 then                         Ri = Memory[address, 4]                         address = address + 4                 assert end_address = address - 4   LDM(3)(带状态寄存器的批量内存字数据读取指令)

  

  • LDM指令将数据从连续的内存单元中读取指令中寄存器列表中的各寄存器中,同时将当前处理器模式的SPSR寄存器内容复制到CPSR寄存器中
  • 当PC包含在LDM指令的寄存器列表时,指令从内存中读取的字数据将被当作目标地址值,指令执行后,程序将从目标地址处开始执行,实现跳转操作
  • 在ARMv5及以上版本和T系列的ARMv4版本中,SPSR寄存器的T位将复制到CPSR寄存器的T位,该位决定目标地址处的程序状态

  指令的编码格式

  

  

指令的语法格式
LDM{} {!}, ^
  

  • registers_and_pc为寄存器列表,在本格式指令中寄存器列表必须包含PC寄存器,其
  • ^指示指令执行时将当前处理器模式下的SPSR值赋值到CPSR中,若指令的寄存器列表不包含PC寄存器,则该指令为一条LDM(2)格式指令
  • 如果指令中基址寄存器Rn在寄存器列表registers中,而且指令中的寻址方式指定指令执行后更新寄存器Rn的值,则指令执行会产生不可预知的结果
  • 本指令主要用于从异常中断模式下返回,如果在用户模式或系统模式下使用该指令,会产生不可预知结果

  STM(1)(批量内存字数据写入指令)

  STM指令将指令中寄存器列表中的各寄存器数据写入到连续的内存单元中,主要用于块数据的写入、数据栈操作、以及进入子程序时保存相关的寄存器操作
  指令的编码格式

  

  

指令的语法格式
STM{} Rn{!},
  

  • Rn为指令寻址模式中的基址寄存器,用于存放地址块的最低地址,如果R15被作为Rn,指令产生不可预知结果
  • 如果指令基址寄存器Rn在寄存器列表registers中,而且指令中寻址方式指定指令执行后更新基址寄存器Rn的值,则当Rn是register中编号最小的寄存器时,指令将Rn的初始值保存到内存中,否则,指令执行后产生不可预知的结果

  STM(2)(用户模式的批量内存字数据写入指令)

  STM指令将指令中寄存器列表中的各寄存器(用户模式对应的寄存器)数值写入连续的内存单元中,主要用于块数据的写入、数据栈操作以及进入子程序时保存相关的寄存器操作
  指令的编码格式

  

  

指令的语法格式
STM{} , ^
  

  • Rn为指令寻址模式中的基址寄存器,存放地址块的最低地址值,如果R15被作为Rn,指令会产生不可预知的结果
  • ^指示指令中所用的寄存器为用户对应的寄存器
  • 本指令主要用于从异常中断模式下返回,如果在用户模型或系统模式下使用该指令,会产生不可预知的结果
  • 在本指令的后面不能紧跟访问备份寄存器的指令,最好跟一条NOP指令
  • 指令中的基址寄存器是指令执行时的当前处理器模式对应的物理寄存器,而不是用户模式对应的寄存器

  信号量操作指令

  信号量用于进程间的同步和互斥,对信号量的操作通常要求是一个原子操作,即在一条指令中完成信号量的读取和修改操作
  SWP(交换指令)

  SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中,当Rd和Rm为同一个寄存器时,指令交换该寄存器和内存单元的内容
  指令的编码格式

  

  

指令的语法格式
SWP{} , , []
  

  • cond为指令执行的条件码
  • Rd为目标寄存器
  • Rm寄存器包含将要保存到内存中的数值
  • Rn寄存器中包含将要访问的内存地址
  • 本指令主要用于实现信号量操作

  指令操作伪代码
  if cond then         if Rn[1:0] == 0b00 then                 temp = Memory[Rn, 4]         else if Rn[1:0] == 0b01 then                 temp = Memory[Rn, 4] Rotate_Right 8         else if Rn[1:0] == 0b10 then                 temp = Memory[Rn, 4]  Rotate_Right 16         else                 temp = Memory[Rn, 4]  Rotate_Right 24         Memory[Rn,4] = Rm         Rd = temp   示例
  SWP R1, R2, [R3];将R3指示的内存单元字数据读取到R1寄存器中,同时将R2寄存器的数据写入R3指示的内存单元中   SWPB(字节交换指令)

  SWPB指令用于将一个内存字节单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,寄存器Rd的高24位设置为0,同时将另一个寄存器Rm的低8位数值写入该内存单元中,当Rd和Rm为同一个寄存器时,指令交换该寄存器的低8位和内存字节单元的内容
  指令的编码格式

  

  

指令的语法格式
SWPB{} , , []
  异常中断产生指令

  ARM有两条异常中断产生指令
  

  • SWI:软中断指令,用于产生SWI异常中断,ARM通过该机制实现在用户模式中对操作系统特权模式的程序调用
  • BKPT:断点中断指令,在ARMv5及以上的版本引入,主要用于产生软件断点,供调试程序使用

  SWI(软中断指令)

  SWI指令用于产生软中断
  指令的编码格式

  

  

指令的语法格式
SWI{}
  

  • cond为指令执行的条件码
  • immed_24为24位的立即数,该立即数***作系统用来判断用户程序请求的服务类型
  • 用于用户程序调用操作系统服务,在SWI的异常中断处理程序中提供相关的系统服务,并定义参数传递方法:1、指令中24位的立即数指定用户请求的服务类型,参数通过通用寄存器传递;2、指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的数值决定,参数通过其他的通用寄存器传递

  指令操作的伪代码
  if cond then         R14_svc = address of next instruction after the SWI instruction         SPSR_svc = CPSR         CPSR[4:0] = 0b10011 #Enter Supervisor mode         CPSR[5] = 0 #Execute in ARM state         #CPSR[6] is unchange         CPSR[7] = 1 #Disable normal interrupts         if high vectors configured then                 PC = 0xFFFF0008         else                 PC = 0x00000008   BKPT(断点中断指令)

  BKPT指令用于产生软件断点中断,软件调试程序可以使用该中断,当系统使用硬件调试部件时,可以忽略该中断
  指令的编码格式

  

  

指令的语法格式
BKPT
  

  • immediate为16位的立即数,这个立即数被调试软件用来保存额外的断点信息
  • 主要供软件调试程序使用

  指令操作的伪代码
  if (not overridden by debug hardware)         R14_abt = address of BKPT instruction + 4         SPSR_abt = CPSR         CPSR[4:0] = 0b10111         CPSR[5] = 0 #使程序处于ARM状态         #CPSR[6] is unchanged         CPSR[7] = 1 #禁止正常中断         if high vectors configured then                 PC = 0xFFFF000C         else                  PC = 0x0000000C   ARM协处理器指令

  

  • ARM支持16个协处理器,在程序执行的过程中,每个协处理器忽略属于ARM处理器和其他协处理器的指令
  • 当一个协处理器硬件不能执行属于它的协处理器指令时,将产生未定义指令异常中断,在该异常中断处理程序中,可以通过软件模拟该硬件操作,比如系统中不包含向量浮点运算器,则可以选择浮点软件模拟包,支持向量的浮点运算
  • ARM协处理器可以部分地执行一条指令,然后产生异常中断,如除法运算除数为0情况,所有操作都由ARM协处理器操作,ARM处理器不参与操作

  ARM协处理器指令包括以下3类:
  

  • 用于ARM处理器初始化ARM协处理器地数据处理操作
  • 用于ARM处理器地寄存器和ARM协处理器地寄存器地数据传送操作
  • 用于在ARM协处理器地寄存器和内存单元之间地传送数据

  协处理器指令
  

  • CDP:协处理器数据操作指令
  • LDC:协处理器数据读取指令
  • STC:协处理器数据写入指令
  • MCR:ARM寄存器到协处理器寄存器地数据传送指令
  • MRC:协处理器寄存器到ARM寄存器地数据传送指令

  CDP(协处理器数据操作指令)

  CDP指令让ARM处理器能够通知ARM协处理器执行特定地操作,该操作由协处理器完成,如果协处理器不能成功地执行该操作,将产生未定义地指令异常中断
  指令地编码格式

  

  

指令地语法格式
CDP {} , , , , ,
CDP2 , , , , ,
  

  • cond为指令执行地条件码
  • CDP2格式中,cond为0b1111,指令为无条件执行指令
  • coproc为写处理器地编码
  • opcode_1为协处理器将执行地操作地操作码
  • CRd作为目标寄存器地协处理器寄存器
  • CRn为存放第1个操作数的协处理器寄存器
  • CRm为存放第2个操作数的协处理器寄存器
  • opcode_2为协处理器将执行的操作的操作码
  • 该指令让ARM处理器能够通知ARM协处理器执行特定的操作,且该操作不涉及ARM寄存器和内存单元

  示例
  CDP p5, 2, c12, c10, c3,4 #协处理器p5的操作初始化 #操作码1为2,操作码2为4 #目标寄存器为C12 #源操作数寄存器为C10和C3   LDC(协处理器数据读取指令)

  LDC指令从一系列连续的内存单元将数据读取到协处理器的寄存器中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断
  指令的编码格式

  

  

指令的语法格式
LDC {}{L} , ,
LDC2{L} , ,
  

  • LDC2格式中,cond为0b1111,指令为无条件执行指令
  • L指示指令为长读取操作,比如用于双精度的数据传送
  • addressing_mode为指令的寻址方式

  示例
  LDC p6, CR4, [R2, #4] #R2为ARM寄存器,指令读取内存单元(R2+4)的字数据,传送到协处理器p6的CR4寄存器中   STC(协处理器数据写入指令)

  STC指令将协处理器的寄存器中的数据写入到一系列连续的内存单元中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断
  指令的编码格式

  

  

指令的语法格式
STC {}{L} , ,
STC2{L} , ,
  

  • L指示指令为长读取操作,比如用于双精度的数据传送

  示例
  STC p8, CR4, [R2, #4] #R2为ARM寄存器,指令将协处理器p8的CR8寄存器中的字数据写入到内存单元(R2+4)中,执行后R2=R2+4   MCR(ARM寄存器到协处理器寄存器地数据传送指令)

  MCR指令将ARM处理器的寄存器的数据传送到协处理器的寄存器中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断
  指令的编码格式

  

  

指令的语法格式
MCR {}{L} , , , , {, }
MCR2{L} , , , , {, }
  

  • MCR2格式中,cond为0b1111,指令为无条件执行指令
  • Rd为ARM寄存器,其值将被传送到的协处理器的寄存器中
  • CRn为目标寄存器的协处理器寄存器
  • CRm为附加的目标寄存器或者源操作数寄存器

  示例
  MCR p14, 3, R7, c7, c11, 6 #指令从ARM寄存器中将数据传送到协处理器p14的寄存器中 #其中R7为ARM寄存器,存放源操作数 #c7和c11为协处理器的寄存器,为目标寄存器 #操作码1为3,操作码2为6   MRC(协处理器寄存器到ARM寄存器地数据传送指令)

  MRC指令将协处理器寄存器中的数值传送到ARM处理器的寄存器中,如果协处理器不能成功执行该操作,将产生未定义的指令异常中断
  指令的编码格式

  

  

指令的语法格式
MRC {}{L} , , , , {, }
MRC2{L} , , , , {, }
  

  • cond为0b1111,指令为无条件执行指令
  • Rd为目标寄存器的ARM寄存器
  • CRn为协处理器的寄存器,存放第1个源操作数
  • CRm为附加的目标寄存器或源操作数寄存器

  示例
  MRC p15, 2, R5, c0, c2, 6 #指令将协处理器p15寄存器中的数据传送到ARM寄存器中 #其中R5为ARM寄存器,是目标寄存器 #c0和c2为协处理器的寄存器,存放源操作数 #操作码1为2,操作码2为4   汇编代码段示例

  长跳转

  通过直接向PC寄存器读取字数据,实现¥ GB的地址空间的任意跳转
  ADD LR, PC, #4;将子程序function的返回地址设置为当前指令地址后字节处,即return_here处 LDR PC, [PC, #-4];从下一条指令(即DCD function)中读取跳转的目标地址,即function  DCD function;DCD伪操作保存跳转的目的地址 return_here;子程序function的返回地址   多路跳转

  通过函数地址实现多路跳转,其中maxiindex为跳转的最大索引号,R0为跳转的索引号
  CMP R0, #maxiindex;判断索引号是否超出最大索引号 LDRLO PC, [PC, R0, LSL #2] ;如果没有超过,跳转到相应的子程序处 B IndexOutOfRange ;如果超过,跳转错误处理程序处 DCD Handler0 ;子程序0的地址 DCD Handler1 ;子程序1的地址 DCD Handler2 ;子程序2的地址 DCD Handler3 ;子程序3的地址 ...   简单的块复制

  程序一次将48个字节数据从R12首地址的一段连续的内存单元复制到仪R13为首地址的一段连续的内存单元中,R14为源数据区的末地址
  loop LDMIA R12!, {R0-R11}; STMIA R13!, {R0-R11} CMP R12, R14; BLO loop;   信号量指令的应用

  

  • 代码中用进程标识符标识各信号量的所有者,代码执行前进程的标识符保存在R1中,信号量的地址保存在R0中,当信号量[R0]==0时,表示与该信号量相关的临界区可用;当信号量[R0]==-1时,表示当前由进程正在查看该信号量的值
  • 如果当前进程查看的信号量正忙,当前进程将一直等待该信号量,为了避免当前进程的查询操作阻塞操作系统的进度调度,可以在下一次查询之前完成操作系统中的系统调度,使当前进程休眠一段时间

  MVN R2, #0;//将-1保存到R2中  //lock spinin SWP R3, R2, [R0];//将信号量值读取到R3中,同时将其值设置成-1 CMN R3, #1;//判断读取到的信号量值是否为-1,即是否有其他进程正在访问该信号量 ... //如果有其他进程正在访问该信号量,则使当前进程休眠一段时间,以保证操作系统能够进行任务调度 ... BEQ spinin;//如果有其他进程正在访问该信号量,跳转到spinin处执行,即循环查询信号量是否可用  CMP R3, #0;//判断当前信号量是否可用,即内存单元[R0]是否为0,当不为0时,表示其他进程正拥有该信号量 STRNE R3, [R0];//此时恢复该信号量的值,即该信号量拥有者的进程标识符 ... //如果该信号量正在被别的进程占用,则使当前进程休眠一段时间,以保证操作系统能够完成任务调度 ... BEN spinin;//进程重新尝试获取该信号量 STR R1, [R0];//当前进程得到该信号量,将自己的进程标识符写入到内存单元[R0]处 ... //该信号量所包保护的临界区数据 ...  //unlock spinout SWP R3, R2, [R0];//将信号量值读取到R3中,同时将其值设置成-1 CMN R3, #1;//判断读取到的信号量值是否为-1,即是否有其他进程正在访问该信号量 ... //如果有其他进程正在访问该信号量,则使当前进程休眠一段时间,以保证操作系统能够完成任务调度 ... BEQ spinout;//如果有其他进程正在访问该信号量,跳转spinout处执行 CMP R3, R1;//判断是否当前进程拥有该信号量 BNE CorruptSemaphore;//如果当前进程不拥有该信号量,则系统出现错误 MOV R2, #0;//如果当前进程拥有该信号量,重新将该信号量设置为可用,即0 STR R2, [R0];   SWI中断处理程序示例

  

  • 当SWI指令执行时,通常完成下面的工作

  if cond then         R14_svc = address of next instruction after the SWI instruction         SPSR_svc = CPSR         CPSR[4:0] = 0b10011 #Enter Supervisor mode         CPSR[5] = 0 #Execute in ARM state         CPSR[7] = 1 #Disable normal interrupts         if high vectors configured then                 PC = 0xFFFF0008         else                 PC = 0x00000008   

  • SWI指令包含24位立即数,用于指定指令请求的具体SWI服务;对于Thumb指令而言。指令包含8位立即数指定指令请求的具体SWI服务
  • 当程序执行到SWI指令时,程序跳转到0x00000008处执行,由于该处是一条跳转指令,程序直接跳转到下面的代码段的首地址处开始执行,在代码段中,程序保存相关的寄存器,接着提取SWI指令中的立即数,以确认SWI指令请求的具体服务,跳转到相应的代码处执行
  • 下面代码段中,仅仅保存寄存器R0~R3、R12和LR,如果实际需要,可以修改代码中的保存和恢复指令中的寄存器列表

  SWIHandler //SWI中断服务程序 STMFD sp!, {R0-R3,R12,LR}; //保存相关的寄存器 MRS R0, SPSR;//将SPSR内容传送到R0中 TST R0, #0x20;//判断程序状态是否为ARM状态 /* 通过获取返回地址的执行指令上一条执行指令,就可以获取SWI的执行指令数据 通过SPSR[T]的判断确认立即数是24位,还是8位 */ LDRNEH R0, [LR, #-2];//如果是Thumb状态,获取[LR-2]地址处的执行指令,即SWI指令 BICNE R0, R0, #0xFF00;//提取SWI指令中相应的8位立即数 LDREQ R0, [LR, #-4]; //如果是ARM状态,获取[LR-4]地址处的执行指令,即SWI指令 BICEQ R0, R0, #0xFF000000;//提取SWI指令中相应的8位立即数 CMP R0, #MaxSWI;//判断指令请求的服务序号是否超出合法范围 LDRLS pc, [pc, R0, LSL #2];//如果没有超出合法范围,跳转到相应的服务程序执行 B SWIOutOfRange; //如果超出了合法范围,跳转到相应的服务程序执行 Switable //服务程序的函数地址表 DCD do_swi_0;//服务程序do_swi_0的首地址,该服务对于SWI指令中的立即数为0 DCD do_swi_1;//服务程序do_swi_1的首地址,该服务对于SWI指令中的立即数为1 ...  do_SWI_0 //服务程序do_swi_0的代码 ... //服务程序执行代码 ... LDMFD sp!, {R0-R3, R12, pc}^; //从服务程序中返回  do_swi_1 //服务程序do_swi_1的代码 ...   IRQ中断处理程序示例

  

  • 在ARM中,外部中断管理器或外设通过使能ARM处理器中的IRQ输入管脚产生IRQ异常中断
  • CPSR寄存器中的I控制位设置为1时,禁止ARM处理器响应IRQ中断请求;CPSR寄存器中的I控制位设置为0时,ARM处理器在指令边界处检查是否有IRQ中断请求
  • ARM处理器响应IRQ中断请求时,完成以下工作

  R14_irq = 当前指令地址 + 8 SPSR_irq = CPSR CPSR[4:0] = 0b10010 #Enter Supervisor mode CPSR[5] = 0 #Execute in ARM state CPSR[7] = 1 #Disable normal interrupts if high vectors configured then         PC = 0xFFFF0018 else         PC = 0x00000018   

  • 通常IRQ中断向量存放内存单元0x00000018处,该地址处放一条跳转指令,其目标地址为下面代码段的首地址
  • 外围中断管理硬件将所有的IRQ异常中断请求按优先级排队,并把优先级最高的IRQ异常中断的相关信息保存到寄存器中,IRQ中断处理程序读取信息,并跳转响应的代码处执行

  //保存工作寄存器数据、返回地址和当前程序现场 SUB R14, R14, #4; //调整R14值,使其指向发生IRQ中断的指令的下一条指令 STMFD R13!, {R12, R14}; //保存返回地址和相关的寄存器数据,R13为栈指针  MRS R12, SPSR; //保存SPSR STMFD R13!, {R12};//读取当前优先级最高的IRQ请求的相关信息 MOV R12, #InterBase;//读取中断控制器的基地址 LDR R12, [R12, #IntLevel];//读取优先级最高的IRQ的中断号IntBase[IntLevel]                                                 //存放优先级最高的IRQ的中断号的寄存器的偏移地址修改CPSR中的控制位,重新允许IRQ中断  MRS R14, CPSR; //读取CPSR BIC R14, R14, #0x80; //清除中断禁止位 MSR CPSR_c, R14;//将R14的值写入CPSR  LDR PC, [PC, R12, LSL #2];//跳转到当前IRQ对应的中断处理程序 BOP;//插入空指令,保证跳转正确执行  IntBase DCD Priority0Handler;//Priority0Handler的地址 DCD Priority1Handler;// ...  Priority0Handler STMFD R13!, {R0-R11};//保存工作寄存器组 ... //中断处理程序主体 ... MRS R12, CPSR;//修改CPSR的相关位,禁止响应中断 ORR R12, R12, #0x80; MSR CPSR_c, R12; //注意这里不要使用R14,否则发生中断时,R14的内容会被破坏  LDMFD R13!, {R0-R12};//恢复工作寄存器和SPSR MSR SPSR_cxsf, R12; LDMFD R13!, {R12, PC}^;//恢复所有寄存器,并返回  Priority0Handler //Priority1Handler程序体 ...   进程切换

  

  • 进程是操作系统中任务调度的基本单位,每个进程由一个进程控制块PCB表示,进程控制块PCB中包含了进行相关的信息
  • 进程间切换就是通过某种方式保存当前进程的PCB,恢复新进程的PCB内容到处理器中
  • 这里讨论用户模式的进程间切换,切换过程是通过IRQ中断处理程序完成的
  • 比如在进程1执行到特定时机时,希望切换到进程2,此时系统产生IRQ中断,首先执行常规的中断处理操作,然后判断是返回被中断的进程1,还是切换到新的进程2执行,这里仅仅讨论用户模式的进程间切换,如果在特权模式下发生IRQ中断,中断处理程序一定返回被中断的进程
  • 这里假设IRQ中断处理程序仅仅保存寄存器R0~R3、R12及R14,使用R13作为栈指针,栈的类型为FD,其他寄存器保存不变,在中断处理程序中始终禁止中断,也不进行处理器模式切换
  • 这里假设进程控制块格式为从低地址到高地址依次为下列寄存器:CPSR、返回地址、R0~R4

  下面分3部分介绍进程间切换的过程
  

  • 在进入IRQ中断处理程序时,首先计算返回地址,并保存相关的寄存器

  SUB R14, R14, #4;//使R4指向发生IRQ中断的指令的下面一条指令 STMFD  R13!, {R0~R3, R12, R14}; //保存R0~R3,R123,R14   

  • 如果IRQ中断处理程序返回被中断的进程,则执行下面的指令,该指令从数据栈中恢复寄存器R0~R3及R12的值,将返回地址传送到PC中,并将SPSR_irq值赋值到CPSR中

  LDMFD R3!, {R0~R3, R12, PC}^;   

  • 如果IRQ切换到新的进程,则要保存被中断的进程的PCB,然后恢复新进程的PCB到处理器中

  //保存被中断的进程的PCB,该PCB存放在R0所指向的连续的内存单元中 MRS R12, SPSR;//读取被中断的进程的CPSR STR R12, [R0], #8;//将其保存到R0指向的内存单元,并更新R0的值,R0=R0+8  LDMFD R13!, {R2,R3};//读取被中断进程的R2和R3 STMIA R0!, {R2,R3};//将其保存到R0指向的内存单元,并更新R0值 LDMFD R13!, {R2, R3, R12, R14};//读取栈中的其他数据 STR R14, [R0, #-12];//将返回地址值R14保存在PCB中的第2个字单元,即CPSR之后  STMIA R0, {R2~R14}^; //保存其他所有的寄存器 //将新进程的PCB中的内容恢复到处理器中,其中R1指向进程的PCB LDMIA R1!, {R12, R14};//恢复CPSR及R14 MSR SPSR_fsxc, R12; LDMIA R1, {R0~R14}^;//恢复R0~R14 NOP;//因为在用户模式的LDM指令后不能立即操作备份寄存器,故插入该指令  MOVS PC, R14;//切换到新进程执行,同时更新CPSR   伪操作

  伪操作不是在计算机运行期间由机器执行,是在汇编程序对源程序汇编期间由汇编程序处理的
  符号定义伪操作

  符号定义伪操作作用于定义ARM汇编程序中的变量,对变量进行赋值以及定义寄存器名称
  GBLA、GBLL、GBLS全局变量

  GBLA、GBLL、GBLS伪操作用于声明一个ARM程序中的全局变量,并且对其进行初始化,如果这些伪操作重新声明已经声明过的变量,则变量的值将被初始化成后一次声明语句中的值
  

  • GBLA伪操作声明一个全局的算术变量,并将其初始化成0
  • GBLL伪操作声明一个全局的逻辑变量,并将其初始化成{FALSE}
  • GBLS伪操作声明一个全局的串变量,并将其初始化成空串""

  GBLA objectsize;//声明一个全局的算术变量 objectsize SETA 0xFF;//向该变量赋值 SPACE objectsize;//引用该变量  GBLL status;//声明一个全局的逻辑变量 status SETL {TRUE};// 向该变量赋值   LCLA、LCLL、LCLS局部变量

  LCLA、LCLL、LCLS伪操作用于声明一个ARM程序中的局部变量,并对其进行初始化,如果这些伪操作重新声明已经声明过的变量,则变量的值将被初始化成后一次声明语句中的值,局部变量的作用范围为包含该局部变量的宏代码
  

  • LCLA伪操作声明一个全局的算术变量,并将其初始化为0
  • LCLL伪操作声明一个局部的逻辑变量,并将其初始化成{FALSE}
  • LCLS伪操作声明一个局部的串变量,并将其初始化成空串""

  MACRO //声明一个宏 $ label message $a //宏的原型 LCLS err //声明一个局部变量 err SETS "error no:" //向该变量赋值 $label //代码 INFO 0,"err":CC::STR:$a //使用该字符串 MEND //宏定义结束   SETA、SETL、SETS给变量赋值

  SETA、SETL、SETS伪操作用于给一个ARM程序中的变量赋值,在向变量赋值前,必须先声明该变量
  

  • SETA伪操作给一个算术变量赋值
  • SETL伪操作给一个逻辑变量赋值
  • SETS伪操作给一个串变量赋值

  语法格式
variable expr
  

  • setx是以下伪操作之一:SETA、SETL或SETS
  • variable使用GBLA、GBLL、GBLS或LCLA、LCLL、LCLS声明的变量名称
  • expr为表达式,即赋予变量的值

  RLIST

  RLIST伪操作用于为一个通用寄存器列表定义名称,可以在LDM或STM指令中使用;在LDM或STM指令中,寄存器列表中的寄存器访问次序总是先访问编号低的寄存器,再访问编号高的寄存器,而不管寄存器列表中各寄存器的排列顺序
  语法格式
name RLIST {list-of-registers}
  

  • name是寄存器列表的名称
  • {list-of-registers}为通用寄存器列表

  context RLIST {R0-R6, R8, R10-R12, R15} //将寄存器列表名称定义为context   CN给协处理器的寄存器定义名称

  CN伪操作用来给一个协处理器的寄存器定义名称
  语法格式
name CN expr
  

  • name是该寄存器的名称
  • expr为协处理器的寄存器的编号,数值范围为0~15

  CP给协处理器定义名称

  CP伪操作用来给一个协处理器定义名称
  语法格式
name CP expr
  

  • name是该协处理器的名称
  • expr为协处理器的编号,数值范围0~15

  DN、SN为VFP的寄存器定义名称

  DN伪操作用来给一个双精度的VFP寄存器定义名称
SN伪操作用来给一个单精度的VFP寄存器定义名称
  语法格式
name DN expr
name SN expr
  

  • name是该VFP寄存器的名称
  • expr为FVP双精度寄存器编号(0~15),或者VFP单精度寄存器编号(0~31)

  FN为FPA的浮点寄存器定义名称

  FN为一个FPA浮点寄存器定义名称
  语法格式
name FN expr
  

  • name是该浮点寄存器的名称
  • expr为浮点寄存器的编号,数值范围为0~7

  数据定义伪操作

  

  • LTORG声明一个数据缓冲池的开始
  • MAP定义一个结构化的内存表的首地址
  • FIELD定义结构化的内存表中一个数据域
  • SPACE分配一块内存单元,并用0初始化
  • DCB分配一段字节的内存单元,并用指定的数据初始化
  • DCD及DCDU分配一段字的内存单元,并用指定的数据初始化
  • DCDO分配一段字的内存单元,并将各单元的内存初始化成该单元相对于静态基值寄存器的偏移量
  • DCFD及DCFDU分配一段双字的内存单元,并用双精度的浮点数据初始化
  • DCFS及DCFSU分配一段字的内存单元,并用单精度的浮点数据初始化
  • DCI分配一段字节的内存单元,用指定的数据初始化,指定内存单元中存放的是代码,而不是数据
  • DCQ及DCQU分配一段双字的内存单元,并用64位的整数数据初始化
  • DCW及DCWU分配一段半字的内存单元,并用指定的数据初始化
  • DATA在代码中使用数据,现不再使用,仅用于保持向前兼容

  LTORG

  LTORG用于声明一个数据缓冲区的开始
  AREA Example, CODE, READONLY start BL funcl funcl //子程序 ...//子程序代码 LDR R1, =0x55555555 //LDR R1, [PC, #offset to Literal Pool 1] ...//子程序代码 MOV PC, LR //子程序结束  LTORG //定义数据缓冲池&55555555 data SPACE 4200 //从当前位置开始分配4200字节的内存单元 END   

  • 通常,ARM汇编编器器把数据缓冲池放在代码段的最后面,即下一个代码段开始之前,或END伪操作之前
  • 当程序中使用LDFD之类指令时,数据缓冲池的使用可能越界,这时可以使用LTORG伪操作定义数据缓冲池,防止越界发生,通常,大的代码段可以使用多个数据缓冲池
  • LTORG伪操作通常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器就不会错误将数据缓冲池的数据当作指令来执行

  MAP

  MAP用于定义一个结构化的内存表的首地址,此时,内存表的位置计数器{VAR}设置成该地址,^是MAP的同义词
  语法格式
MAP expr(, base-register}
  

  • expr位数字表达式或者程序的标号,当指令没有base-register时,expr即为结构化内存表的首地址,此时内存表的位置计数器{VAR}设置成该地址值,当expr位程序中的标号时,该标号必须是已经定义过的
  • base-register为一个寄存器,当指令包含这一项时,结构化内存表的首地址为expr和base-register寄存器值的和
  • MAP伪操作和FIELD伪操作配合使用,来定义结构化的内存表结构

  MAP 0x80, R9   FIELD

  FIELD用于定义一个结构化内存表中的数据域,#是FILED的同义词
  语法格式
{label} FIELD expr
  

  • {lable}为可选,当指令中包含这一项时,label的值为当前内存表的位置计数器{VAR}的值,汇编编译器处理该FIELD伪操作后,内存表计数器的值将加上expr
  • expr表示本数据域在内存表中所占的字节数
  • MAP伪操作和FILED伪操作配合使用,来定义结构化的内存表结构,MAP伪操作定义内存表的首地址,FIELD伪操作定义内存表中各数据域的字节长度,并可以为每一个数据域指定一个标号,其他指令可以引用该标号
  • MAP伪操作中的base-register寄存器值对于其后所有的FIELD伪操作定义的数据域时默认使用,直至遇到新的包含base-register项的MAP伪操作
  • MAP伪操作和FIELD伪操作仅仅定义数据结构,并不实际分配内存单元

  

  • 基于绝对地址的内存表

  //首地址为固定地址0x1000 //该内存表包含5各数据域 //consta长度为4个字节 //constb长度为4个字节 //x的长度为8个字节 //y的长度为8个字节 //string的长度为256个字节 //这种内存表称为基于绝对地址的内存表 MAP 4096 //内存表的首地址为4096(0x1000) consta FIELD 4 //consta的长度为4个字节,相对位置为0 constb FIELD 4 //constb的长度为4个字节,相对位置为5000 x FIELD 8 //x的长度为8个字节,相对位置为5004 y FIELD 8 //y的长度为8个字节,相对位置为5012 string FIELD 256 //string的长度为256字节,相对位置为5020  LDR R6,consta //在指令中引用内存表中的数据域   

  • 基于相对地址的内存表

  MAP 0//内存表的首地址为0 consta FIELD 4 //consta的长度为4个字节,相对位置为0 constb FIELD 4 //constb的长度为4个字节,相对位置为5000 x FIELD 8 //x的长度为8个字节,相对位置为5004 y FIELD 8 //y的长度为8个字节,相对位置为5012 string FIELD 256 //string的长度为256字节,相对位置为5020  MOV R9, #4096 LDR R6, [R9, constb] //将内存表中的数据域constb读取到R5中   内存表中个数据的实际内存地址不是基于一个固定的地址,而是基于LDR指令执行时R9寄存器的内容,通过上面方法定义内存表结构可以在程序中有多个实际(通过LDR指令中指定不同的基址寄存器实现)
  

  • 基于相对地址的内存表

  MAP 0, R9 //内存表的首地址为0与R9寄存器值的和 consta FIELD 4 //consta的长度为4个字节,相对位置为0 constb FIELD 4 //constb的长度为4个字节,相对位置为5000 x FIELD 8 //x的长度为8个字节,相对位置为5004 y FIELD 8 //y的长度为8个字节,相对位置为5012 string FIELD 256 //string的长度为256字节,相对位置为5020  ADR R9, DATASTART LDR R5, constb //相当于LDR R5, [R9, #4]   内存表中各数据域的实际内存地址不是基于一个固定的地址,而是基于LDR指令执行时R9寄存器中的内容,通过上面方法定义的内存表结构可以在程序中有多个实例(通过LDR指令前指定不同的基址寄存器R9值来实现)
  

  • 基于PC的内存表

  DATASTUCT SPACE 280 //分配280字节的内存单元 MAP DATASTUCT //内存表的首地址为DATASTUCT内存单元 consta FIELD 4 //consta的长度为4个字节,相对位置为0 constb FIELD 4 //constb的长度为4个字节,相对位置为5000 x FIELD 8 //x的长度为8个字节,相对位置为5004 y FIELD 8 //y的长度为8个字节,相对位置为5012 string FIELD 256 //string的长度为256字节,相对位置为5020  LDR R5, constb //相当于LDR R5, [PC, offset]   内存表中各数据域的实际内存地址不是基于一个固定的地址,而是基于PC寄存器的值,在使用LDR指令访问内存表中的数据域时,不必使用基值寄存器
  

  • 范围超出检查
    当FIELD伪操作中的操作数为0时,其中的标号即为当前内存单元的地址,由于其中操作数为0,汇编编译器处理该伪操作后,内存表的位置计数器的值并不改变,可以利用该技术判断当前内存的使用是否超过程序分配的可用内存

  startofmem EQU 0x1000 //分配的内存首地址 endofmem EQU 0x2000 //分配的内存未地址  MAP startofmem //内存表的首地址为DATASTUCT内存单元 consta FIELD 4 //consta的长度为4个字节,相对位置为0 constb FIELD 4 //constb的长度为4个字节,相对位置为5000 x FIELD 8 //x的长度为8个字节,相对位置为5004 y FIELD 8 //y的长度为8个字节,相对位置为5012 string FIELD 256 //string的长度为256字节,相对位置为5020 endofstru FIELD 0  ASSERT endofstru <= endofmem   SPACE

  SPACE伪操作用于分配一块内存单元,并用0初始化,%时SPACE的同义词
语法格式
{label} SPACE expr
  

  • {label}为可选的
  • expr表示本伪操作分配的内存字节数

  Datastruc SPACE 280   DCB、DCD及DCDU、DCDO、DCFD及DCFDU、DCFS及DCFSU、DCI、DCQ及DCQU、DCW及DCWU

  

  • DCB伪操作用于分配一段字节内存单元,并用语法格式中的expr初始化,=是DCB的同义词
    {label} DCB expr{,expr} …
    {label}为可选的
    expr可以为-128~255的数值或者字符串

  Nullstring DCB "Null string",0 //构造一个以NULL结尾的字符串   

  • DCD伪操作作用于分配一段内存单元(分配的内存都是字对齐的),并用伪操作中的expr初始化,&是DCD的同义词,DCDU与DCD的不同之处在于DCDU分配的内存并不严格字对齐
    {label} DCD expr{,expr} …
    {label}为可选的
    expr可以为数字表达式或者为程序中的标号

  

  • DCD伪操作可以在分配的第一个内存单元前插入填补字节以保证分配的内存是字对齐的
  • DCDU分配的内存单元则不需要字对齐

  data1 DCD 1, 5, 20 //其值分别为1、5和20 data2 DCD memaddr + 4 //分配一个字单元,其值为程序中的标号memaddr加4个字节   

  • DCDO伪操作用于分配一段字内存单元(分配的内存都是字对齐的),并将各字单元的内容初始化为xexpr标号基于静态基址寄存器R9的偏移量
    {label} DCDO expr{,expr} …

  IMPORT externsym DCDO externsym //32位的字单元,其值位标号externsym基于R9的偏移量   

  • DCFD用于双精度的浮点数分配字对齐的内存单元,并将字单元的内容初始化为fpliteral表示的双精度浮点数,每个双精度的浮点数占据两个字单元,DCFD与DCFDU的不同之处在于DCFDU分配的内存单元并不严格字对齐
    {label} DCFD{U} fpliteral{,fpliteral} …
    {label}为可选的
    fpliteral为双精度的浮点数

  

  • DCFD伪操作可能在分配的第一个内存单元前插入填补字节,以保证分配的内存是字对齐的
  • DCFDU分配的内存单元则不需要字对齐
  • 如果将fpliteral转换内存单元的内部表示形式是由浮点数运算单元控制的

  DCFD 1E308, -4E-100 DCFDU 10000, -.1, 3.1E26   

  • DCFS伪操作用来为单精度的浮点数分配字对齐的内存单元,并将字单元的内容初始化成fpliteral表示的单精度浮点数,每个单精度的浮点数占据1个字单元,DCFS与DCFSU的不同之处在于DCFSU分配的内存单元并不严格字对齐
    {label} DCFS{U} fpliteral{,fpliteral} …
  • DCI,在ARM代码中,用于分配一段字内存单元(分配的内存都是字对齐的),并用伪操作中的expr将其初始化,在Thumb代码中,用于分配一段半字单元(分配的内存都是半字对齐的),并用伪操作中的expr将其初始化
    {label} DCI expr{,expr} …

  

  • DCI伪操作和DCD伪操作非常类似,不同之处在于DCI分配的内存中数据被标识为指令,可用于通过宏指令来定义处理器指令系统不支持的指令
  • 在ARM代码中,DCI可能在分配的第一个内存单元前插入最多3个字节的填补字节以保证分配的内存是字对齐的,在Thumb代码中,DCI可能在分配第一个内存单元前插入1个字节的填补字节以保证分配的内存是半字对齐的

  MACRO //这个宏指令将指令newinstr Rd, Rm定义为相应的机器指令 newinstr $Rd,$Rm DCI oxe16f0f10 :OR: ($Rd:SHL:12) :OR: $Rm //这里存放的是指令MEMD   

  • DCQ伪操作用于分配一段以8个字节为单位的内存(分配的内存都是字对齐的),并用literal初始化,DCQU与DCQ的不同之处在于DCQU分配的内存单元并不严格字对齐
    {label} DCQ{U} {-}literal{, {-}literal} …
    {label}为可选的
    literal为64位的数字表达式,其取值范围为0~2^64 -1,当在literal前加上"-"符号时,literal的取值范围为-2^63-1。在内存中,2^64-n与-n具有相同的表达形式

  

  • DCQ伪操作可能在分配的第一个内存单元前插入多达3个字节的填补字节以保证分配的内存是字对齐的
  • DCQU分配的内存单元则不需要字对齐

  AREA MiscData, DATA, READWRITE data DCQ -225, 2_101 //2_1-1指的是二进制的101 DCQU number + 4 //number必须是已经定义过的数字表达式   

  • DCW伪操作用于分配一段半字内存单元(分配的内存都是半字对齐的),并且使用expr初始化,DCWU与DCW的不同之处在于DCWU分配的内存单元并不严格半字对齐
    {label} DCW expr{,expr} …
    {label}为可选的
    expr为数字表达式,其取值范围为-32768~65535

  

  • DCW伪操作可能在分配的第一个内存单元前插入1字节的填补字节以保证分配的内存是半字对齐的
  • DCWU分配的内存单元则不需要半字对齐

  汇编控制伪操作

  IF、ELSE及ENDIF

  IF、ELSE及ENDIF伪操作能够根据条件把一段源代码包括在汇编语言程序内或者将其排除在程序之外。"[“是IF伪操作的同义词,”|“是ELSE伪操作的同义词,”]"是ENDIF伪操作的同义词,其中ELSE伪操作可选的,也可以嵌套使用。
  语法格式
  IF logical expression         insttructions or directives ELSE {         insttructions or directives } ENDIF   示例
  IF Version = "1.0"         //指令         //伪指令 ELSE         //指令         //伪指令 ENDIF   WHILE及WEND

  WHILE及WEND伪操作能够根据条件重复汇编相同的或者几乎相同的一段源代码,WHILE及WEND伪操作可以嵌套使用
  语法格式
  WHILE logical expression         insttructions or directives WEND   示例
  count SETA 1 WHILE count <= 4         count SETA count+1 WEND   MACRO及MEND

  MACRO及MEND伪操作标识宏定义的开始,MEND标识宏定义的结束,用MACRO及MEND定义的一段代码,称为宏定义体,可以在程序中通过宏指令多次调用该代码段,宏定义可以嵌套。
  语法格式
  MACRO {$label} macroname {$parameter{,$parameter}...} ... //code ... MEND   

  • $label在宏指令被展开时,label可被替换成相应的符号,通常是一个标号,在一个符号前使用$表示程序被汇编时将使用相应的值替代$后的符号
  • macroname为所定义的宏的名称
  • $parameter为宏指令的参数,当宏指令被展开时,将被替换成相应的值,类似于函数中的形式参数,可以在宏定义时为参数指定相应的默认值
  • 宏定义中的$label是一个可选参数,当宏定义体中用到多个标号时,可以使用类似$label.$internallabel的标号命名规则使程序易读
  • 对于ARM程序中的局部变量,如果该变量在宏定义中被定义,其作用范围为该宏定义体

  示例
  MACRO //宏定义的开始 $label xmac $p1,$p2 //宏的名称为xmac,有两个参数$p1,$p2,宏的标号$label可用于构造宏定义体内的其他标号名称 ... //code ... $label.loop1 //$label.loop1为宏定义体的内部标号 ... //code ... BGE $label.loop1  $lable.loop2 //$label.loop1为宏定义体的内部标号 ... //code ... BL $p1 //参数$p1为一个子程序的名称 BGT $label.loop2  ... //code ... ADR $p2 ... //code ... MEND //宏定义结束  //在程序中调用该宏 abc xmac subr1, de //通过宏的名称xmac调用宏,其中宏的标号为abc,参数为subr1,de   MEXIT

  MEXIT用于从宏中跳转出去
  示例
  MACRO {$label} macroname {$parameter{,$parameter}...} ... //code ... MEXIT ... //code ... MEND   信息报告伪操作

  ASSERT

  ASSERT在汇编编译器对编译程序的第二遍扫描中,如果其中的条件不成立,ASSERT伪操作将报告该错误信息
  语法格式
  ASSERT logical expression   

  • 其中logical expression为一个逻辑表达式
  • 用于保证源程序被汇编时满足相关的条件,如果条件不满足,ASSERT伪操作报告错误类型,并终止汇编

  示例
  ASSERT a>0x10   INFO

  INFO伪操作支持汇编处理过程的第一遍扫描或者第二遍扫描时报告诊断信息
  语法格式
  INFO numeric-expression, string-expression   

  • string-expression为一个串表达式
  • numeric-expression为一个数字表达式
  • 如果numeric-expression的值为0,则在汇编处理中,第二扫描时,伪操作打印string-expression;如果numeric-expression的值不为0,则在汇编处理中,第一遍扫描时,伪操作打印string-expression,并终止汇编
  • INFO伪操作用于用户定义的错误信息

  示例
  INFO 0, "Version 1.0" //在第二遍扫描时,报告版本信息 IF endofdata <= label1         INFO 4, "Data overrun at label1" //如果endofdata<=label1成立,在第一遍扫描时报告错误信息,并终止汇编 ENDIF   OPT

  OPT伪操作,可以在源程序中设置列表选项
  语法格式
  OPT n   

  • 使用编译选项-list将使编译器产生列表文件
  • 默认情况下,-list选项生成常规的列表文件,包括变量声明、宏展开、条件编译伪操作以及MEND伪操作,而且列表文件只是在第二遍扫描时给出,通过OPT伪操作,可以在源程序中改变默认的选项
  • n为所设置的选项的编码

  [tr]选项编码n选项的含义[/tr]
1设置常规列表选项
2关闭常规列表选项
4设置分页符,在新的一页开始显示
8将行号重新设置为0
16设置选项,显示SET、GBL、LCL伪操作
32设置选项,不显示SET、GBL、LCL伪操作
64设置选项,显示宏展开
128设置选项,不显示宏展开
256设置选项,显示宏调用
512设置选项,不显示宏调用
1024设置选项,显示第一遍扫描列表
2048设置选项,不显示第一遍扫描列表
4096设置选项,显示条件汇编伪操作
8192设置选项,不显示条件汇编伪操作
16384设置选项,显示MEND伪操作
21768设置选项,不显示MEND伪操作
示例
在func1前插入"OPT 4"伪操作,func1将在新的一页中显示
  AREA Example, CODE, READONLY start ... //code ... BL func1 ... //code ... OPT 4 func1 ... //code ...   TTL及SUBT

  

  • TTL伪操作在列表文件的每一页的开头插入一个标题,该TTL伪操作将作用在其后的每一页,直到遇到新的TTL伪操作
  • SUBT伪操作在列表文件的每一页的开头插入一个子标题,该SUBT伪操作将作用在其后的每一页,直到遇到新的SUBT伪操作

  语法格式
TTL title
SUBT subtitle
  

  • TTL伪操作在列表文件的页顶部显示一个标题,如果要在列表文件的第一页显示标题,TTL伪操作要放在源程序的第一行
  • 当使用TTL伪操作改变标题时,新的标题将在下一页开始起作用
  • SUBT伪操作在列表文件的页标题的下面显示一个子标题,如果要在列表文件的第一页显示子标题,TTL伪操作要放在源程序的第一行
  • 当使用SUBT伪操作改变页标题时,新的标题将在下一页开始起作用

  示例
  TTL Fisrt Title SUBT Fisrt Subtitle   其他伪操作

  CODE16及CODE32

  CODE16伪操作告诉汇编编译器后面的指令序列为16位的Thumb指令
CODE32伪操作告诉汇编编译器后面的指令序列为32为的ARM指令
  

  • CODE16何CODE32只是告诉编译器后面的指令的类型,伪操作本身不进行程序状态的切换

  示例
程序现在ARM状态下执行,然后通过BX指令切换到Thumb状态,并跳转到相应的Thumb指令处执行,在Thumb程序入口处用CODE16伪操作标识下面的指令为Thumb指令
  AREA ChangeState, CODE, READONLY CODE32 LDR R0, =start + 1 BX R0  CODE16 start MOV R0, #10   EQU

  EQU伪操作为数字常量,基于寄存器的值和程序中的标号(基于PC的值)定义一个字符名称,*是EQU的同义词
  语法格式
name EQU expr{, type}
  

  • expr为基于寄存器的地址值、程序中的标号、32位的地址常量或者32位的常量
  • name为EQU伪操作为expr定义的字符名称
  • type当expr为32位常量时,可以使用type指示expr表示的数据的类型,type有3中取值:CODE16、CODE32、DATA
  • EQU伪操作的作用类似于C语言的#define,用于位一个常量定义字符名称

  AREA

  AREA伪操作用于定义一个代码段或者数据段
  语法格式
AREA sectionName {, attr}{, attr}
  

  • sectionName为所定义的代码段或者数据段的名称,如果该名称以数字开头,则名称必须用"|"括起来,如|1_datasec|,还要一些代码段具有约定的名称,如|.text|
  • attr是该代码段(或程序段)的属性,在AREA伪操作中,各属性间用逗号隔开

  

  • ALIGN=expression:默认情况下,ELF的代码段和数据段是4字节对齐的,Expression可以取0~31的数值,相应的对齐方式为2^expression字节对齐
  • ASSOC=section:指定与本段相关的ELF段,任何时候链接section段页必须包括section段
  • CODE:定义代码段,默认属性为READONLY
  • COMDEF:定义一个通用的段,该段可以包含代码或者数据,在各源文件中,同名的COMDEF段必须相同
  • COMMON:定义一个通用的段,该段不包含任何用于代码和数据,链接器将其初始化为0,各源文件中同名的COMMON段公用同样的内存单元,链接器为其分配合适的尺寸
  • DATA:定义数据段,默认属性为READWRITE
  • NOINIT:指定本数据段仅仅保留内存单元,而没有将各初始值写入内存单元,或者将各内存单元值初始化为0
  • READONLY:指定本地为只读,代码段的默认属性为READONLY
  • READWRITE:指定本地为可读可写,是数据段的默认属性

  

  • 可以用AREA伪操作将程序分成多个ELF格式段,同名的段会被放在同一ELF段中

  ENTRY

  ENTRY伪操作指定程序的入口点
  AREA example CODE, READONLY ENTRY   

  • 一个程序(可以包含多个源文件)中只是要有一个ENTRY(可以有多个ENTRY),但是一个源文件中最多只能有一个ENTRY,也可以没有

  END

  END伪操作告诉编译器已经到源程序结尾
  ALIGN

  ALIGN伪操作通过添加补丁使当前位置满足一定的对齐方式
  语法格式
ALIGN {expr{, offset}}
  

  • expr为数字表达式,用于指定对齐方式,可能的取值为2的n次幂,如果伪操作中没有指定expr,则当前位置对齐到下一个字边界处
  • offset为数字表达式
  • Thumb的宏指令ADR要求地址是字对齐的,而Thumb代码中地址标号可能不是字对齐的,这就需要伪操作ALIGN 4使Thumb代码中的地址标号字对齐
  • 由于有些ARM处理器的Cache采用其他对齐方式,如16字节的对齐方式,此时使用ALIGN伪操作指定合适的对齐方式可以充分使用Cache的性能
  • LDRD及STRD指令要求内存单元使8字节对齐的,这样为LDRD/STRD指令分配的内存单元前,要使用ALIGN 8实现8字节对齐方式
  • 地址标号通常自身没有对齐要求,而在ARM代码中要求地址标号是字对齐的,在Thumb代码中要求字节对齐

  示例
将两个字节数据放在同一个字的第一个字节和第4各字节中
  AREA offsetExample, CODE DCB 1 ALIGN 4, 3 DCB 1   EXPORT及GLOBAL

  EXPORT声明一个符号可以被其他文件引用,相当于声明一个全局变量
FLOBAL是EXPORT的同义词
  语法格式
EXPORT symbol{[WEAK]}
  

  • symbol为声明的符号的名称,区分大小写
  • [WEAK]选项声明其他的同名符号优先于本符号被引用
  • 使用EXPORT伪操作,可以声明一个源文件的符号,使得该符号能够被其他源文件引用

  IMPORT

  IMPORT伪操作告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件可能引用该符号,而且不论本源文件是否实际引用该符号,该符号都被加入本源文件的符号表中
  语法格式
IMPORT symbol{[WEAK]}
  

  • symbol为声明的符号的名称,区分大小写
  • 指定[WEAK],如果symbol在所有的源文件都没有定义,编译器也不会产生任何错误信息,同时编译器也不会到当前被INCLUDE进来的库中去查找该符号
  • 如果IMPORT伪操作声明一个符号是其他源文件定义时,如果链接器链接处理时不能解析该符号,且没有指定[WEAK]选项,则链接器将会报告错误
  • 如果该符号被B或者BL指令引用,则符号被设置成下一条指令的地址

  EXTERN

  EXTERN伪操作告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号,如果本源文件没有实际引用该符号,该符号将不会加入到本源文件的符号表中
  语法格式
EXTERN symbol{[WEAK]}
  

  • 如果该符号被B或者BL指令引用,则符号被设置成下一条指令的地址

  GET及INCLUDE

  GET伪操作将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理,INCLUDE是GET的同义词
  语法格式
GET filename
  

  • filename为被包含的源文件名称,可以使用路径信息
  • 被包含的源文件也可以使用GET伪操作

  INCBIN

  INCBIN伪操作将一个文件包含到当前源文件中,被包含的文件不进行汇编处理
  语法格式
INCBIN filename
  

  • filename为被包含的源文件名称,可以使用路径信息
  • 可以使用INCBIN将一个可执行文件或任意的数据包含到当前文件中,被包含的执行文件或者数据将原封不动放到当前文件中,编译器从INCBIN伪操作后面开始继续处理

  KEEP

  KEEP伪操作告诉编译器将局部符号包含在目标文件的符号表中
  语法格式
KEEP {symbol}
  

  • symbol为被包含在目标文件的符号表中的符号,如果没有指定symbol,则除了基于寄存器外的所有符号将被包含在目标文件的符号表中
  • 默认情况下,编译器仅将如下符号包含到目标文件的符号表中:被输出的符号、将被重定位的符号
  • 使用KEEP伪操作可以将局部符号包含到目标文件的符号表中,从而使的调试工作更加方便

  NOFP

  使用NOFP伪操作可禁止源程序中包含浮点运算指令
  REQUIRE

  REQUIRE伪操作指定段之间的相互依赖关系
  语法格式
REQUIRE label
  

  • label为所需的标号的名称
  • 当进行连接处理时,若遇到包含有REQUIRE label伪操作的源文件,则定义label的源文件也将被包含

  REQUIRE8及PRESERVE8

  REQUIRE8伪操作指示当前代码中要求数据栈8字节对齐
PRESERVE8伪操作指示当前代码中数据栈是8字节对齐的
  

  • LDRD和STRD指令要求内存单元地址是8字节对齐的,当在程序中使用这些指令在数据栈中传送数据时,要求该数据栈是8字节对齐的
  • 链接器要保证要求8字节对齐的数据栈代码只能被数据栈是8字节对齐的代码调用

  RN

  RN伪操作为一个特定的寄存器定义名称
  语法格式
name RN expr
  

  • expr为某个寄存器的编码
  • name为本伪操作给寄存器expr定义的名称

  ROUT

  ROUT伪操作用于定义局部变量的有效范围
  语法格式
{name} ROUT
-name为所定义的作用范围名称
当没有使用ROUT伪操作定义局部变量的作用范围时,局部变量的作用范围为其所在的段
ROUT伪操作作用的范围为本ROUT伪操作和下一个ROUT伪操作(指同一个段的ROUT伪操作)之间
  ARM汇编语言伪指令

  ADR

  ADRL

  LDR

  NOP

  ARM的存储系统

  ATPCS介绍

  异常中断处理

  实际上,当异常中断发生时,程序计数器PC所指的位置对于各种不同的异常中断时不同的,因此,返回地址对于各种不同的异常中断也是不同。
  

  • 异常中断的响应过程

  

  • 保存处理器当前状态、中断屏蔽位以及各条件标志位,通过将当前程序状态寄存器CPSR的内容保存将要执行的异常中断对应的SPSR寄存器中。
  • 设置当前程序状态寄存器CPSR中相应的位,包括设置CPSR中的位,使处理器进入相应的执行模式,设置CPSR中的位。
  • 将寄存器lr_mode设置成返回地址
  • 将程序计数器(PC)设置成改异常中断的中断向量地址,从而跳转到相应的异常中断处理程序执行。

  

  • 异常中断处理程序返回

  

  • 恢复中断的程序的处理器状态,将SPSR_mode寄存器内容复制到CPSR中。
  • 返回发生异常中断的指令的下一条指令处执行,即把lr_mode寄存器的内容复制到程序计数器PC中。

  ARM链接器
举报

更多回帖

发帖
×
20
完善资料,
赚取积分