文章目录
- 指令基础
- ARM汇编语言
- 指令和指令格式
- 指令的可选后缀
- 指令的条件执行
- ARM指令分类
- ARM指令的寻址方式
- 立即寻址
- 寄存器寻址
- 寄存器间接寻址
- 寄存器移位寻址
- 基址变址寻址
- 多寄存器寻址
- 相对寻址
- 堆栈寻址
- 数据处理指令
- 数据传送指令
- 移位操作
- 算术指令
- 逻辑运算指令
- 比较指令
- 乘法指令
- MUL
- MLA
- SMULL
- SMLAL
- UMULL
- UMLAL
- 数据加载与存储指令
- 数据加载与存储指令概述
- 数据加载与存储的方向问题
- 数据加载与存储指令的寻址
- 地址索引
- 单寄存器加载与存储指令
- LDR/STR
- LDRB/STRB
- LDRH/STRH
- LDRSB/LDRSH
- 多寄存器加载与存储指令
- 堆栈操作
- 交换指令
- 分支指令
- 分支指令B
- 带返回的分支指令BL
- 带状态切换的分支指令BX
- 带返回和状态切换的分支指令BLX
- 程序状态寄存器访问指令
- 协处理器指令
- CDP协处理器数据处理指令
- LDC协处理器数据加载指令
- STC协处理器数据存储指令
- MCR ARM处理器寄存器到协处理器存储器的数据传送指令
- MRC 协处理器寄存器到ARN处理器寄存器的数据传送指令
- 软件中断指令
- ARM伪指令
- ADR小范围的地址读取伪指令
- ADRL中等范围的地址读取伪指令
- LDR大范围的地址读取伪指令
- NOP空操作伪指令
- Thumb指令集
- 概述
- Thumb指令寄存器的使用
- ARM-Thumb交互
- 数据处理指令
- 单寄存器加载和存储指令
- 多寄存器加载和存储指令
- 堆栈指令
- 软件中断指令
- Thumb伪指令
- ADR小范围地址读取
- LDR大范围地址读取
- NOP空操作
指令基础
指令周期和时序
时钟信号:是一个周期性的脉冲信号
时钟周期(clock cycle):一个时钟脉冲的时间长度,是主频的倒数
时钟频率:也称为主频,一秒内,时钟信号的个数
指令周期(instruction cycle):执行一条指令需要的时间,不同指令的指令周期不同,和操作数的寻址方式也有关,一般情况下,以访问寄存器所需的最长时间来衡量指令周期
对于ARM7而言,所有存储器的传输周期都可以被归结到以下4个类型:
- 不连续周期:上一次和本次访问的操作数地址没有联系,称为访问非顺序内存位置的周期,简称N周期
- 连续周期:上一次和本次访问的操作数地址相同或连续,称为访问顺序内存位置的周期,简称S周期
- 内部周期:ARM不请求传输,只是执行内部功能,称为内部周期,简称I周期
- 协处理器寄存器传输周期:ARM与协处理器之间的数据总线传输一个字的周期,简称C周期
程序的执行过程
执行一条指令的过程可以分为:取指、译码、执行
- 取指:CPU进入取指阶段,通过控制总线(control bus)发出读命令,并从地址总线(addr bus)所给出的地址,即存储器中取出指令代码,经过数据总线送到CPU的指令寄存器中
- 译码:翻译取出的指令代码
- 执行:执行代码指定的动作
ARM汇编语言
指令和指令格式
指令和指令系统
指令:指示计算机进行某种操作的命令
指令系统:指令的集合称为指令系统
助记符:指令的符号表示
指令的表示方法
指令在内存中,以二进制形式保存
ARM指令代码一般可以分为5个域:
- 【31:28】:条件码域
- 【27:20】:指令码域,除指令编码外,还包含几个很重要的指令特征和可选后缀的编码
- 【15:12】:地址基址Rn,为R0-R15共16个寄存器编码
- 【15:12】:目标或源寄存器Rd,为R0-R15共16个寄存器编码
- 【11:0】:地址偏移或操作寄存器、操作数区
汇编的指令格式
操作码 指令的执行条件 S 目的操作数 源操作数 源操作数2
<opcode> {<cond>} {S} <Rd>, <Rn> {, <OP2>}
- <> :括号里的内容必不可少
- {} : 括号里的内容可省略
- opcode:操作码,如ADD表示加法
- cond:指令执行的条件域,如EQ,NE,省略则为默认AL,无条件执行
- S:决定指令的执行结果是否影响CPSR的值,使用该后缀则影响
- Rd:目的操作数的寄存器
- Rn:第一个操作数的寄存器
- OP2:第二个操作数,可以是立即数、寄存器、偏移地址
指令的可选后缀
ARM指令集中大多数指令,都可以加后缀,这使得指令的使用更加灵活,常见的S和!
S后缀
- 指令使用“S”后缀时,指令执行后程序状态寄存器的条件标志位被刷新
- 不使用“S”后缀时,条件标志位不改变
- “S”后缀通常用于对条件进行测试,例如是否溢出、进位等
!后缀
- 地址表达式中不含“!”,则基址寄存器中的地址值不会发生变化
- 含有“!”,基址寄存器中的地址值发生变化,基址寄存器的值,变为原来的值再加上偏移地址
例如:LDR R3,[R0,#4],关键在于R0是否发生变化
使用“!”需要注意:
- 必须紧跟在地址表达式的后面,而地址表达式要有明确的偏移量
- 不能用于R15(PC)后面
- 当用在单个地址寄存器后面时,必须确信这个寄存器有隐形的偏移量
指令的条件执行
- 程序要执行的指令都保存在存储器中,需要执行时,先产生地址,再根据地址,去存储器取出指令代码,然后译码执行
- 当工作在ARM状态时,几乎所有的指令都根据CPSR中条件码和指令的条件域,有条件的执行,不满足则忽略
- ARM指令包含4位条件码,位于【31:28】,共16种,每种可用两个字母表示,它可以添加在指令助记符后面和指令同时使用
![【ARM汇编】第三章:ARM指令系统_第1张图片](http://img.e-com-net.com/image/info8/3de5222d2ecf4745b589fc384f39e0a7.jpg)
条件后缀和S后缀的关系:
- 都存在时,S写后面,如ADDEQS
- 条件后缀是要测试条件标志位,而S后缀是要刷新条件标志位
- 条件后缀要测试的是执行前的标志位,而S后缀在执行后改变标志位
ARM指令分类
ARM指令集可以分为:数据处理指令、数据加载存储指令、分支指令、程序状态寄存器PSR处理指令、协处理器指令、异常产生指令
![【ARM汇编】第三章:ARM指令系统_第2张图片](http://img.e-com-net.com/image/info8/f2c3d38061d74257bfa815b649f0bc09.jpg)
ARM指令的寻址方式
寻址方式:根据操作数的信息,来寻找操作数对应的实际物理地址
立即寻址
MOV R0,#5
#5就是立即寻址,必须加#前缀
对于十进制,可以加0d或缺省,如:#0d5
对于十六进制,必须加0x或者&,如:#0x5
对于二进制,必须加0b,如:#0b11
寄存器寻址
寄存器寻址,就是利用寄存器中的数值作为操作数
ADD R0, R1, R2
R1+R2 -> R0
寄存器间接寻址
以寄存器的值作为操作数的地址,而操作数本身存放在存储器中
LDR R0, [R4]
以R4的值为地址,去存储器中找到对应物理地址,取出的数才是操作数
寄存器移位寻址
操作数由寄存器的值经过移位后得到,移位的方式在指令中以助记符给出
ADD R0, R1, R2, LSL #1
R1 + (R2 << 1) -> R0
MOV R0, R1, LSL R3
(R1<<3) -> R0
基址变址寻址
将寄存器的内容,与指令给出的偏移地址相加,得到操作数的地址,操作数在存储器中
LDR R0, [R1. #4]
[R1+4] -> R0
LDR R0, [R1. #4]!
[R1+4] -> R0, R1+4 -> R1
LDR R0, [R1], #4
[R1] -> R0, R1+4 -> R1
LDR R0, [R1. R2]
[R1+R2] -> R0
多寄存器寻址
一条指令可以完成多个寄存器值的传送,连续的寄存器用“-”连接,否则用“,”连接
LDMIA R0!, {R1-R4}
[R0] -> R1
[R0+4] -> R2
[R0+8] -> R3
[R0+12] -> R4
该指令的后缀IA表示,每次执行完加载/存储操作后,R0按(32位)增加
相对寻址
相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,相加之后,得到操作数的地址,一般用于子程序的调用,中断异常的处理
堆栈寻址
使用堆栈指针表示当前栈顶位置,R13一般为堆栈指针
ARM中采用LDMFD、STMFD指令来支持POP和PUSH操作
STMFD R13!, {R0-R4}
LDMFD R13!, {R0-R4}
S-STORE,将R0-R4压栈
L-LOAD,将R0-R4出栈
数据处理指令
数据处理指令对存放在寄存器中的数据进行操作,分为
- 数据传送指令
- 算术指令
- 逻辑运算指令
- 比较指令
- 乘法指令
![【ARM汇编】第三章:ARM指令系统_第3张图片](http://img.e-com-net.com/image/info8/2d9aaa7af5674c68804493e83cfad902.jpg)
ARM数据处理指令机器编码格式如下:
![【ARM汇编】第三章:ARM指令系统_第4张图片](http://img.e-com-net.com/image/info8/5bc1f082560f430a915562ce1e290f65.jpg)
- cond:指令执行的条件码
- I:用于区别第二操作数是立即数(I=1)还是寄存器移位(I=0)
- opcode:数据处理指令操作码
- S:用于设置条件码,S=0时条件码不变
- Rn:第一操作数寄存器
- Rd:目标寄存器
- op2:第二操作数,该操作数可以是立即数或寄存器移位数
数据传送指令
将一个寄存器中的数据或立即数,传送到另一个寄存器
MOV
MOV {<cont>} {S} Rd, op2
功能:
- MOV指令将源操作数op2传送到Rd中,通常op2是立即数、寄存器Rm、或寄存器Rm移位
- S决定结果是否影响CPSR中条件标志位
MOV R1, R0
R0 -> R1
MOV PC, R14
R14 -> PC,常用于子程序返回
MOV R1, R0, LEL #3
(R0<<3) -> R1
MOV R0, #5
5 -> R0
MVN
MVN {<cont>} {S} Rd, op2
功能:
- 将一个寄存器、移位寄存器、立即数传送到目的寄存器Rd
- 数据在传送之前按位取反
- S决定结果是否影响CPSR中条件标志位
MVN R0, #1
MVN R1, R2
移位操作
- ARM微处理器一个特点:在操作数进入ALU之前,对操作数进行预处理,如左移和右移
- 这种处理是通过内嵌的桶形移位器(barrel shifter)实现的
- 桶形移位器支持数据的各种移位操作,移位操作在ARM指令集不再作为单独的指令使用,而是作为一个选项
- 包括逻辑左移LSL,逻辑右移LSR,算术右移ASR,循环右移ROR,带扩展的循环右移RRX
LSL
Rm, LSL<opl>
- LSL指令完成对通用寄存器Rm中的内容进行逻辑左移,按opl指定的位数移位,低位用0填充,相当于无符号数乘2^n
- opl可以是立即数,也可以是寄存器方式
LSR
Rm, LSR<opl>
- 寄存器Rm右移opl指定的次数,空位用零填充,opl的要求如同LSL
ASR
Rm, ASR<opl>
- 寄存器Rm按opl指定的次数右移,其中高位用31位原本的值填充(注意有符号数的负数,31位为1)
- opl的要求同上,但是范围限制到1-32
ROR
Rm, ROR<opl>
- 右移指定次数,其中空出来的最高位,用最低位移出去的数来补
- opl要求同上,范围限制1-31
RRX
Rm, RRX
对通用寄存器中的内容进行RRX右移一位时,该寄存器的32位+C标志位,一共33位组成一个循环右移
![【ARM汇编】第三章:ARM指令系统_第5张图片](http://img.e-com-net.com/image/info8/c0a0c46b9dd04ec4ae0de111cfb8b09a.jpg)
算术指令
主要是加减法,实现两个32位数据的加减操作,通常与桶形移位器结合起来,实现许多灵活的功能
主要包括:加法ADD,带进位加法ADC,减法SUB,带借位减法SBC,逆向减法RSB,带借位逆向减法RSC
ADD
ADD {<cont>} {S} Rd, Rn, op2
- 将Rn与op2相加,结果送到Rd,Rn + op2 -> Rd
- Rn要求是寄存器,op2可以是寄存器、移位寄存器、立即数
- S决定指令的操作是否影响CPSR
ADD R0, R1, R2
ADD R0, R1, #5
ADD R0. R1. R2, LSL #2
第二操作数op2的要求:
- 是一个无符号的32位数
- 由一个8位无符号数,用0填充到32位,再循环右移偶数次,得到这个操作数,例如0x8801就不符合
ADC
ADC {<cont>} {S} Rd, Rn, op2
- Rn + op2 + C -> Rd,需要额外加上条件标志位C的值
- Rd,Rn必须是寄存器,op2可以是寄存器、移位寄存器、立即数
- 该指令用于实现超过32位的加法
例:两个64位数相加,第一个64位数,存放在R2、R3中,第二个存放在R4,R5中,64位结果存放在R0,R1中
分析:将两个64位数中,低32位相加,结果影响C标志位,然后将64位中的高32位以及低32位产生的进位相加
ADDS R0, R2, R4
ADC R1, R3, R5
SUB
SUB {<cont>} {S} Rd, Rn, op2
- Rn - op2 -> Rd
- 用于有符号、无符号数的减法运算
SUB R0, R1, R2
SUB R0, R1, #6
SUB R0, R2, R3, LSL #1
SBC
SBC {<cont>} {S} Rd, Rn, op2
- 带借位的减法,Rn - op2 - !C -> Rd
- S标志影响执行后CPSR的NZCV,SUB和SBC生成进位标志的方式不同于常规,如果需要进位,则清除进位标志,在指令执行期间自动反转此位
- 该指令使用进位标志表示借位,这样可以做大于32位的减法
- 注意,存在借位时,C=0
RSB
RSB {<cont>} {S} Rd, Rn, op2
RSC
RSC {<cont>} {S} Rd, Rn, op2
- 带借位的逆向减法,op2 - Rn - !C -> Rd
- 用来做大于32位的减法
逻辑运算指令
逻辑运算是按位进行操作的,位与位之间没有进位或借位,没有数的正负大小之分
主要包括:AND逻辑与,ORR逻辑或,EOR逻辑异或,BIC位清除
AND
AND {<cont>} {S} Rd, Rn, op2
- 将操作数按位进行逻辑与运算,结果放到Rd
- Rn & op2 -> Rd
- 常用于将操作数Rn特定位清零
ORR
ORR {<cont>} {S} Rd, Rn, op2
- 将操作数按位进行逻辑或运算,结果放到Rd
- Rn | op2 -> Rd
- 常用于将操作数Rn特定位置1
EOR
EOR {<cont>} {S} Rd, Rn, op2
- 将操作数按位进行异或运算,结果放到Rd
- Rn ^ op2 -> Rd
- S决定执行后条件标志位的N Z
- 该指令常用于反转操作数Rn中的某些位
BIC
BIC {<cont>} {S} Rd, Rn, op2
- 用于清除操作数Rn的某些位,并把结果放到Rd中
- Rn & (!op2) -> Rd
- 这里的op2可以看做一个32位掩码,如果设置了某一位,则清除Rd中对应位
- S决定执行后条件标志位的N Z
比较指令
- 通常用于把一个寄存器与一个32位的值进行比较或测试
- 比较指令根据结果更新CPSR中的标志位
- 其它指令根据标志位来改变程序执行的顺序
- 包括比较指令CMP,反值比较指令CMN,位测试指令TST,相等测试TEQ
CMP
CMP {<cont>} Rn, op2
比较指令,将Rn与op2进行比较,实质是做减法运算,Rn - op2,并根据结果更新CPSR中条件标志位的值
CMN
CMN {<cont>} Rn, op2
反值比较指令,将Rn与op2取反后进行比较,实质上,是Rn + op2,并根据结果改变标志位
TST
TST {<cont>} Rn, op2
位测试指令
- 将Rn与op2,按位进行与运算,并根据结果改变标志位
- 该指令通常用于检查是否设置了特定的位
- op2可以看做32位的掩码,需要检查某位,则该位设为1
TEQ
TST {<cont>} Rn, op2
相等测试指令
- 将Rn与op2进行异或运算,并根据结果更新标志位
- 通常用于比较操作数Rn和操作数op2是否相等
乘法指令
- 把一对寄存器的内容相乘,然后根据指令类型把结果累加到其他寄存器
- 乘法与乘加指令共6条,分为32位运算和64位运算
- 64位乘法称为长整形乘法指令,结果存放在2个32位寄存器Rdlo(低32)和Rdhi(高32)
- 指令中的所有源、目的操作数必须是通用寄存器,不能是移位和立即数,Rd和Rm必须是不同寄存器
- 分为32位乘法MUL,32位乘加MLA,64位有符号乘SMULL,64位有符号乘加SMLAL,64位无符号乘法UMULL,64位无符号乘加UMLAL
在使用乘法指令时,应该注意以下事项:
- 操作数必须都是寄存器,但是不能是R15,也就是PC寄存器
- Rd和Rm不能是同一个寄存器
- 64位运算中,有符号运算和无符号运算的低32位是没有区别的
- Rdlo,Rdhi,Rm必须使用不同的寄存器
MUL
MUL {<cont>} {S} Rd, Rm, Rs
32位乘法指令
- 将操作数Rm与Rs的乘积,送到Rd
- Rm * Rs -> Rd
- S选项影响条件标志位N和Z
MLA
MLA {<cont>} {S} Rd, Rm, Rs,Rn
32位乘加指令
- 将操作数Rm与Rs的乘积,加上Rn,送到Rd
- Rm * Rs + Rn -> Rd
SMULL
SMULL {<cont>} {S} Rdlo, Rdhi, Rm,Rs
64位有符号乘法指令
- 将操作数Rm与Rs的乘积,低32位送到Rdlo,高32位送到Rdhi
- Rm * Rs -> Rdhi 和 Rdlo
- 其中操作数Rm和Rs都是32位有符号数
SMLAL
SMLAL {<cont>} {S} Rdlo, Rdhi, Rm,Rs
64位有符号乘加指令
- 将操作数Rm与Rs的乘积,低32位与Rdlo累加后送到Rdlo,高32位与Rdhi累加后送到Rdhi
- Rm * Rs + Rdhi/Rdlo -> Rdhi 和 Rdlo
- 其中操作数Rm和Rs都是32位有符号数
UMULL
UMULL {<cont>} {S} Rdlo, Rdhi, Rm,Rs
64位无符号乘法指令
- 将操作数Rm与Rs的乘积,低32位送到Rdlo,高32位送到Rdhi
- Rm * Rs + Rdhi/Rdlo -> Rdhi 和 Rdlo
- 其中操作数Rm和Rs都是32位无符号数
UMLAL
UMLAL {<cont>} {S} Rdlo, Rdhi, Rm,Rs
64位无符号乘加指令
- 将操作数Rm与Rs的乘积,低32位与Rdlo累加后送到Rdlo,高32位与Rdhi累加后送到Rdhi
- Rm * Rs + Rdhi/Rdlo -> Rdhi 和 Rdlo
- 其中操作数Rm和Rs都是32位无符号数
数据加载与存储指令
ARM处理器是加载/存储体系结构的处理器,对于存储器的访问只能通过加载和存储指令实现
![【ARM汇编】第三章:ARM指令系统_第6张图片](http://img.e-com-net.com/image/info8/e4eb6ec8f5ac4ad79dcebc42122b2d85.jpg)
数据加载与存储指令概述
数据加载与存储的方向问题
- 数据加载(load)与存储(store)用于在存储器和处理器之间传递数据
- load用于把内存的数据,装入寄存器
- store用于把寄存器的数据,装入内存
- 有三种类型:单寄存器、多寄存器、交换指令
数据加载与存储指令的寻址
基本格式为:
option {<cont>} Rd,addr
- option为指令代码,如LDR表示将存储器中的数据加载到寄存器
- addr为寄存器地址表达式,可以用基址寄存器+偏移量得到
addr有以下几种形式
- 立即数:一个无符号的数值
- 寄存器:寄存器的值
- 寄存器移位:由通用寄存器和立即数组成,根据立即数移位,生成地址偏移量,再结合基址寄存器,如:
- LDR R3, [R2, R4, LSL #2]
- LDR R3, [R2], -R4, LSR #3
- 标号:这是一种简单的寻址方法,PC是隐含的基址寄存器,偏移量是标号所在的地址与PC的差值
地址索引
- ARM指令中的地址索引也是指令的一个功能,索引作为指令的一部分,它影响指令的执行结果
- 地址索引分为前索引(pre-indexed),自动索引(auto-indexed),后索引(post-indexed)
前索引
前索引也称为前变址,这种索引是在指令执行前,把偏移量和基址相加减,得到的值作为寻址的地址,例如
LDR R5, [R6, #0x04]
STR R0, [R5, -R8]
先把R6加上偏移4,再去寻址
先把R5的值,减去R8,再去寻址
索引并不会改变寄存器的值
自动索引
自动索引也称为自动变址,有时为了修改基址寄存器的内容,使之指向数据传送地址,可以用来自动修改基址寄存器
LDR R5, [R6, #0x04]!
在寻址前,先将R6加上偏移4,再去寻址,通过可选后缀“!”,完成基址寄存器的更新
后索引
后索引也称为后变址,就是用基址寄存器的地址寻址,找到操作数后,完成指令要求的操作,最后把偏移量加到基址寄存器中
- LDR R5, [R6], #0x04
先根据R6寻址,找到操作数后,将地址给R5,再把偏移量加到R6中
- STR R6, [R7], #-0x08
先将R6的值存到R7指向的地址中,再将偏移加到R7中
单寄存器加载与存储指令
这种指令用于把单一的数据传入或传出一个寄存器
LDR/STR
32位数据的加载/存储指令
![【ARM汇编】第三章:ARM指令系统_第7张图片](http://img.e-com-net.com/image/info8/25d4b35b43374159942dd0fc5f82c08b.jpg)
各个位的含义:
- cond:指令执行的条件编码
- I P U W:用于区别不同的地址模式(偏移量),I=0时偏移量位12位立即数,I=1时偏移量为寄存器移位,P表示前后索引,U表示加减,W表示回写
- L:L=1表示加载,L=0表示存储
- B:B=1表示字节访问,B=0表示字访问
- Rd:源/目标寄存器
- Rn:基址寄存器
- addr_mode:表示偏移量,是一个12位的无符号二进制数,与Rn一起构成地址addr
使用格式:
LDR/STR {<cont>} {T} Rd, addr
- LDR用于从存储器中,将一个32位的数据加载到目的寄存器Rd中,当PC作为Rd时,实现程序的跳转
- STR用于从源寄存器中,将一个32位的数据存储到寄存器中
- 后缀T可选,如果有T,即使在特权模式下,存储系统也将访问看成用户模式下的,T在用户模式下无效
addr的表达方式灵活多变,以下举例说明:
LDR R4, START
STR R5, DATA1
LDR R0, [R1]
LDR R0, [R1, R2]
LDR R0, [R1, #8]
LDR R0, [R1, R2, LSL #2]
STR R0, [R1, R2]!
STR R0, [R2, #8]!
STR R0, [R1, R2, LSL #2]!
LDR R0, [R1], #8
LDR R0, [R1], R2
LDR R0, [R1], R2, LSL #2
使用时要注意以下事项:
- 立即数的规定:立即数的绝对值不大于4095,可以使用带符号数,范围-4095~4095
- 标号使用的限制:要注意语句标号不能指向程序存储区,而是指向数据存储区
- 地址对齐:使用传送指令时,偏移量必须保证偏移的结果能够使地址对齐
- 移位的使用:使用寄存器移位的方法计算偏移量时,位数不能超过规定的值
1、ASR 算术左移 1≤n≤32
2、LSL 逻辑左移 0≤n≤31
3、LSR 逻辑右移 1≤n≤32
4、POR 循环右移 1≤n≤31
- 程序计数器PC:R15作为基址寄存器时,不可使用回写,即不能用“!”,另外R15不能作为偏移寄存器使用
LDRB/STRB
字节数据加载/存储指令
使用格式:
LDRB/STRB {<cont>} {T} Rd, addr
- LDRB用于从存储器中,将一个8位的数据加载到目的寄存器Rd中,同时将高24位清零,当PC作为Rd时,实现程序的跳转
- STRB用于从源寄存器中,将一个8位的数据存储到寄存器中,注意是低8位
- 后缀T可选,如果有T,即使在特权模式下,存储系统也将访问看成用户模式下的,T在用户模式下无效
- 即使传送的是8位数据,地址总线仍然以32位的宽度工作,而数据总线也是以32位宽度工作
字节传送和字传送差别很大,下面说明:
- 从寄存器到存储器:寄存器是32位,但是字节传输时,只能将低8位送到存储器,如果想要送其他位,必须通过移位指令
- 从存储器到寄存器:可以选择存储器中任意一个字节,加载到目的寄存器中,传送时,数据总线的高24位填充0
LDRH/STRH
半字数据(16位)加载/存储指令
![【ARM汇编】第三章:ARM指令系统_第8张图片](http://img.e-com-net.com/image/info8/4b68ed22c75742c7bd4f16a27da014e3.jpg)
各个位的含义:
- cond:指令执行的条件编码
- I P U W:用于区别不同的地址模式(偏移量),I=0时偏移量8位立即数,I=1时偏移量为寄存器移位,P表示前后索引,U表示加减,W表示回写
- L:L=1表示加载,L=0表示存储
- S:S=1有符号访问,S=0无符号访问
- H:H=1表示半字访问,H=0表示字节访问
- Rd:源/目标寄存器
- Rn:基址寄存器
- addr_H、addr_L:表示偏移量,I=0时,偏移量为8位立即数,由addr_H和addr_L组成,I=1时,偏移量为寄存器移位addr_H为0,addr_L表示寄存器编号
使用格式:
LDRH/STRH {<cont>} Rd, addr
- LDRH用于从存储器中,将一个16位的数据加载到目的寄存器Rd中,同时将高16位清零,当PC作为Rd时,实现程序的跳转
- STRH用于从源寄存器中,将一个16位的数据存储到寄存器中,注意是低16位
使用半字加载/存储指令需要注意如下事项:
- 必须半字地址对齐
- 对于R15使用需要慎重,R15作为基址寄存器Rn时,不可使用回写功能,不可使用R15作为目的寄存器
- 立即数偏移使用的是8位无符号数
- 不能使用寄存器移位寻址
LDRSB/LDRSH
有符号字节/半字数据加载指令
使用格式:
LDRSB/LDRSH {<cont>} Rd, addr
- LDRSB用于从存储器中,将一个8位的数据加载到目的寄存器Rd中,同时将高24位进行符号位扩展
- LDRSH用于从存储器中,将一个16位的数据加载到目的寄存器Rd中,同时将高16位进行符号位扩展
多寄存器加载与存储指令
- 多寄存器加载/存储指令,也称为批量数据加载/存储指令,可以一次在连续的存储器单元和多个寄存之间传送数据
- 多寄存器加载/存储指令在数据块操作、上下文切换、堆栈操作等方面比单寄存器指令效率更高
LDM/STM
![【ARM汇编】第三章:ARM指令系统_第9张图片](http://img.e-com-net.com/image/info8/3456c334dab448cf8749562943cfe385.jpg)
图中各位含义如下:
- cond:指令执行的条件编码
- P U W:用于区别不同的地址模式,P表示前/后变址,U表示加/减,W表示回写
- S :S=1有符号数访问,S=0无符号数访问
- L:L=1表示加载,L=0表示存储
- Rn:基址寄存器
- regs:表示寄存器列表
使用格式:
LDM/STM {<cont>} {<type>} Rn{!}, <regs>{^}
- LDM指令用于从基址寄存器Rn所指向的连续存储器读取数据,放入寄存器列表regs中,一般用于多个寄存器出栈
- STM指令用于将寄存器列表中多个寄存器的值,存入由基址寄存器所指示的连续存储器中
type表示类型,用于数据存储和读取时有以下几种情况:
- IA:每次传送后地址值加
- IB:每次传送前地址值加
- DA:每次传送后地址值减
- DB:每次传送前地址值减
用于堆栈操作时有如下情况:
- FD:满递减堆栈
- ED:空递减堆栈
- FA:满递增堆栈
- EA:空递增堆栈
{!}为可选后缀,选用后,当数据加载或存储完毕,将最后的地址写入基址寄存器,基址寄存器不允许R15
{^}为可选后缀,当指令为LDM且寄存器列表含有R15时,还会将SPSR复制到CPSR,同时该后缀还表示传输的是用户模式下的寄存器
LDM/STM指令依据其后参数不同,寻址方式差别很大
![【ARM汇编】第三章:ARM指令系统_第10张图片](http://img.e-com-net.com/image/info8/e407fcda31bf47b7a022d46743089530.jpg)
堆栈操作
堆栈
- 堆栈就是在RAM存储器中开辟的,一个特定的存储区域,遵循先进后出的原则
- 堆栈栈底固定,栈顶浮动,堆栈指针SP(R13)指向栈顶
- 当32位数据入栈时,SP的值减4
堆栈操作
建栈
建栈就是规定堆栈底部在RAM存储器中的位置,用户可以通过LDR命令设置SP的值
进栈
- ARM体系结构使用多寄存器指令完成堆栈操作
- 出栈LDM,入栈STM
- LDM和STM指令往往结合下面的一些参数实现堆栈的操作
- FD满递减堆栈
- ED空递减堆栈
- FA满递增堆栈
- EA空递增堆栈
在使用堆栈时,需要确定它是向上生长(递增ascending),还是向下生长(递减descending)
满堆栈(full stack):指堆栈指针SP(R13)指向堆栈的最后一个数据项的地址
空堆栈(empty stack):指SP指向堆栈的第一个没有使用的地址
ARM制定了一个标准:ARM-Thumb过程调用标准(ATPCS),定义了例程如何被调用,寄存器如何分配
其中,堆栈被定义为满递减堆栈,因此ARM汇编中,堆栈的操作是LDMFD和STMFD
出栈
POP操作出栈,ARM中使用LDMFD实现出栈操作
交换指令
数据交换指令能在存储器和寄存器之间交换数据,它是加载与存储指令的特例,交换是原子操作
![【ARM汇编】第三章:ARM指令系统_第11张图片](http://img.e-com-net.com/image/info8/b06e0acf840842dab691a9c8ed6c49a5.jpg)
- cond:指令执行的条件编码
- B:B=0字交换,B=1字节交换
- Rn:基址寄存器
- Rd:目的寄存器
- Rm:源寄存器
SWP
SWP {<cont>} <Rd>, <Rm>, [<Rn>]
- SWP指令用于将寄存器Rn所指向的寄存器中的字数据,加载到目的寄存器Rd中,同时将源寄存器Rm中的字数据,保存到该存储器的位置
- 当Rd与Rm相同时,即完成交换
SWPB
SWPB {<cont>} <Rd>, <Rm>, [<Rn>]
- SWPB指令用于将寄存器Rn所指向的寄存器中的字节数据,加载到目的寄存器Rd中,同时Rd高24位清零,同时将源寄存器Rm中的字节数据,保存到该存储器的位置
- 当Rd与Rm相同时,即完成交换
使用SWP和SWPB时需要注意:
- PC不能作为其中的任何寄存器
- 基址寄存器Rn不应该与源寄存器Rm和目的寄存器Rd相同,但是Rm和Rd可以相同
- 寄存器位置不可以为空,必须满足3个寄存器
分支指令
- 分支指令用于实现程序流程的跳转,这类指令可以用来改变程序的执行流程,或者调用子程序
- 程序流程跳转的两种办法:一是使用分支指令,二是直接往PC寄存器写地址值
![【ARM汇编】第三章:ARM指令系统_第12张图片](http://img.e-com-net.com/image/info8/10ae4dfc4ca149ffb3dc9847a02af6d6.jpg)
分支指令B
机器编码格式如图
![【ARM汇编】第三章:ARM指令系统_第13张图片](http://img.e-com-net.com/image/info8/a75cd19f0a0c4d14b5f4d942d61196ab.jpg)
- cond:表示指令执行的条件
- L:L=1带返回的分支,L=0普通分支
- Label:表示偏移量,是一个24位有符号立即数
B {<cond>} label
B指令是最简单的分支指令,直接跳转到label地址执行,即PC=lablel
label是一个偏移量,不是绝对地址,它的值由汇编器自动计算
带返回的分支指令BL
BL {<cond>} label
- BL指令在跳转前,将当前PC内容保存到R14(LR)中,因此,可以将R14内容恢复到PC中,跳转到之前的位置继续执行
- 该指令用于实现子程序的调用
带状态切换的分支指令BX
![【ARM汇编】第三章:ARM指令系统_第14张图片](http://img.e-com-net.com/image/info8/d249f6ad7ff040e0acc57208c8d281a0.jpg)
- cond:指令执行的条件
- op:op=0是BX指令,op=1是BLX指令
BX {<cond>} Rm
- BX指令跳转到所指定的目标地址,并实现状态的切换,Rm是一个表达目标地址的寄存器
- 当Rm最低位为1时,强制程序从ARM指令状态,转换到Thumb指令状态
- 当Rm最低位为0时,强制程序从Thumb指令状态,转换到ARM指令状态
带返回和状态切换的分支指令BLX
BLX {<cond>} label | Rm
- BLX指令跳转到指定的目标地址,并实现状态切换,同时将PC保存到R14
- 其目标地址可以是一个符号地址,或者是一个寄存器Rm
- 当子程序使用Thumb指令集,调用者使用ARM指令集,可以通过BLX指令实现子程序调用与状态切换
- 子程序的返回可以将R14复制到PC中
程序状态寄存器访问指令
ARM微处理器中的程序状态寄存器不属于通用寄存器,ARM专门用它设立了两条访问指令,用于在程序状态寄存器和通用寄存器之间传输数据
![【ARM汇编】第三章:ARM指令系统_第15张图片](http://img.e-com-net.com/image/info8/3de2e4dff2d847adbf677a60a9a66b44.jpg)
MRS
![【ARM汇编】第三章:ARM指令系统_第16张图片](http://img.e-com-net.com/image/info8/eefe2e9bea5a4046a63bc8889e3fc579.jpg)
- cond:指令执行的条件
- R:用来区别CPSR(R=0)和SPSR(R=1)
- Rd:表示目的寄存器,Rd不允许为R15
MRS {<cond>} <Rd>, <CPSR | SPSR>
- MRS指令用于将程序状态寄存器的内容,传送到通用寄存器中
- 该指令通常在以下情况使用:
- 异常处理、进程切换时,需要保存状态寄存器的值
- 需要改变程序状态寄存器的值时,可以先读出,修改后再写回
MSR
![【ARM汇编】第三章:ARM指令系统_第17张图片](http://img.e-com-net.com/image/info8/a6636987e3df4a7dbf767da6b3d73990.jpg)
- cond:指令执行的条件
- R:用来区别CPSR(R=0)和SPSR(R=1)
- Field_mask:域屏蔽
- immed:8位立即数
- Rm:操作数寄存器
MSR {<cond>} <CPSR | SPSR>_<fields>,<Rm\ #immed >
- MSR指令用于将操作数的内容,传送到程序状态寄存器的特定域中
- 状态寄存器是指CPSR或SPSR,一般是当前工作模式下的状态寄存器
- 操作数可以是通用寄存器Rm或立即数 #immed
- 域用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器分为4个域
![【ARM汇编】第三章:ARM指令系统_第18张图片](http://img.e-com-net.com/image/info8/5f372ede1d884a2695f17eed104c23c7.jpg)
位[31:24]:条件标志位域,用f表示
位[24:16]:状态位域,用s表示
位[16:8]:扩展位域,用x表示
位[8:0]:控制位域,用c表示
位域的表示方法:CPSR或SPSR后,使用下划线,位域必须小写,可随意组合,使用注意:
- 只有特权模式下,才可以改变处理器模式和设置中断
- 程序状态寄存器中的T位用于指示ARM状态和Thumb状态,但程序不能修改T位实现转换,任何时候不可修改T位
- 不可以使用R15作为目标寄存器
协处理器指令
- ARM微处理器可支持16个协处理器,用于各种协处理操作
- 在程序执行的过程中,协处理器只执行针对自身的协处理指令,忽略ARM处理器和其它处理器的指令
![【ARM汇编】第三章:ARM指令系统_第19张图片](http://img.e-com-net.com/image/info8/46bba1d7db604dc7bb13cb0ded20781f.jpg)
CDP协处理器数据处理指令
![【ARM汇编】第三章:ARM指令系统_第20张图片](http://img.e-com-net.com/image/info8/1c5c83e977654fb08d368ef1248fc27e.jpg)
- cond:指令执行的条件
- opcode1/opcode2:协处理器将执行的操作
- CRm/CRn:存放操作数的协处理器寄存器
- CRd:作为目的寄存器的协处理器寄存器
- p:协处理器编号,0≤p≤15
CDP {<cond>} <p>, opcode1, CRd, CRm, CRn {, opcode2}
- ARM处理器通过CDP指令通知ARM协处理器p,要求其在寄存器CRn和CRm上进行操作op1,并把结果放在CRd中,可以使用op2提供有关的补充信息
- 若完成不了,则产生未定义指令异常
- 指令中的所有寄存器均为协处理器寄存器,操作由协处理器完成,指令不涉及ARM处理器的寄存器和存储器
LDC协处理器数据加载指令
![【ARM汇编】第三章:ARM指令系统_第21张图片](http://img.e-com-net.com/image/info8/e8f182ad094a4968baefc736d9740e42.jpg)
- cond:指令执行的条件
- P U W:用于区别不同的地址模式
- N:数据的大小(依赖于协处理器)
- Op:用于区别LDC指令(op=1)还是STC指令(op=0)
- Rn:ARM处理器中作为基地址的寄存器
- CRd:作为目的寄存器的协处理器寄存器
- P:协处理器编号,0≤p≤15
LDC {<cond>} {L} <p>, <CRd>, <addr>
- LDC指令将addr所表示的存储器中的字数据,传送到目的寄存器CRd中
- 若协处理器不能完成传送操作,则产生未定义指令异常
- {L} 选项表示指令为长读取操作,例如用于双精度的传输
- addr为存储器的地址表达式,可表示为[Rn,offset],其中Rn表示基址寄存器,是ARM中的寄存器,offset表示偏移量,是8位无符号数,addr有如下几种表达式:
- 偏移offset为0,如[R6]
- 前索引立即数偏移,[R0, #0x04]
- 后索引立即数偏移,[R4],#0x08
STC协处理器数据存储指令
STC {<cond>} {L} <p>, <CRd>, <addr>
STC指令用于将寄存器CRd的字数据,传送到addr表示的存储器中
MCR ARM处理器寄存器到协处理器存储器的数据传送指令
![【ARM汇编】第三章:ARM指令系统_第22张图片](http://img.e-com-net.com/image/info8/e6d5389094d347279c7dad30991d4362.jpg)
- cond:指令执行的条件
- op1,op2:协处理器将执行的操作
- op:op=0表示MCR指令,op=1表示MRC指令
- CRm、CRn:存放操作数的协处理器寄存器
- Rd:ARM处理器中作为源或目的的寄存器
- P:协处理器编号,0≤p≤15
MCR {<cond>} <p>, op1, Rd, CRm, CRn{, op2}
- 指令用于将ARM寄存器Rd中的数据传送到协处理器CRm、CRn中
- op1和op2是协处理器将要执行的操作
- Rd为源寄存器,是ARM的寄存器,CRm、CRn是目的寄存器,是协处理器的寄存器
MRC 协处理器寄存器到ARN处理器寄存器的数据传送指令
MRC {<cond>} <p>, op1, Rd, CRm, CRn{, op2}
- MRC指令用于将协处理器中的数据传送到ARM处理器寄存器中
- op1和op2是协处理器将要执行的操作
- Rd为源寄存器,是ARM的寄存器,CRm、CRn是目的寄存器,是协处理器的寄存器
软件中断指令
- ARM指令集中的软件中断指令是唯一一条不适用寄存器的ARM指令,也是一条可以条件执行的指令
- ARM指令在用户模式下局限很大,访问一些特殊资源时,使用软件控制的唯一可行办法就是使用SWI(software interrupt)
SWI软件中断指令
![【ARM汇编】第三章:ARM指令系统_第23张图片](http://img.e-com-net.com/image/info8/5c2f19762d8c4397bafe3e942545deca.jpg)
- cond:指令执行的条件
- swi_num:24位立即数,表示调用类型
SWI {<cond>} SWI_number
- SWI指令用于产生软件中断,以便用户程序能够调用操作系统的例程
- 软件中断进入的是管理模式,中断后会改变程序状态寄存器中的相关位
SWI的调用
从用户模式切换到管理模式时,ARM硬件实现以下操作:
- 把中断处的地址值(PC-4)复制到R14,保留中断的地址
- 把CPSR复制到SWI模式的SPSR,保存状态寄存器的值
- 把状态寄存器的模式改为管理模式
- 把中断向量0x00000008赋值给PC
- 禁止IRQ中断,使CPSR[7] = 1
中断时,一般有两种方法进行功能号的传递:
- 把准备传递的参数通过寄存器进行传递:对R0赋值,SWI参数为0
- 用SWI指令传递中断号:SWI带不为0的参数
ARM伪指令
ARM伪指令不是ARM指令集中的指令,只是为了方便编程而定义的,伪指令可以像其他ARM指令一样使用,但是在编译时,将被等效替代
ADR小范围的地址读取伪指令
ADR {cond} Rm, addr
- 将基于PC相对的地址值或基于寄存器相对便宜的地址值读取到寄存中
- 编译时ADR被替换为合适的指令,如果不能替换,则编译失败
- Rm表示要加载的目的寄存器
- addr为地址表达式,当地址值是非字对齐时,取值范围-255 ~ 255字节之间,当地址值字对齐时,取值范围-1020 ~ 1020 字节之间,对于基于PC相对偏移的地址值时,给定范围是相对当前指令地址后两个字处
ADRL中等范围的地址读取伪指令
ADRL {cond} Rm, addr
- ADRL相对于ADR可以读取跟大范围的地址
- 当地址值是非字对齐时,取值范围-64 ~64KB之间,当地址值字对齐时,取值范围-256 ~ 256KB之间
LDR大范围的地址读取伪指令
LDR {cond} Rm, =addr
- 用于加载32位立即数或一个地址值到指定寄存器
- 与ARM指令相比,伪指令LDR的参数有“=”
NOP空操作伪指令
NOP
编译时替换为空操作,比如MOV R0, R0,一般用于延时
Thumb指令集
概述
- 为兼容数据总线宽度为16位的应用系统,ARM体系结构除了支持效率很高的32位ARM指令集外,同时支持16位的Thumb指令集
- 它是ARM指令集的子集,允许编码长度为16位,多用于存储器受限的系统中
![【ARM汇编】第三章:ARM指令系统_第24张图片](http://img.e-com-net.com/image/info8/037c67a02b9547f8b34da5b637c628a8.jpg)
Thumb指令寄存器的使用
- 在Thumb状态下,不能直接访问所有的寄存器,只有寄存器R0R7是可以被任意访问的,寄存器R8R12只能通过MOV ADD CMP等指令访问
- CMP指令和所有操作R0~R7的数据处理指令都会影响CPSR中的条件标志
- 没有与MSR MRS等价的Thumb的指令
- 为了改变CPSR和SPSR,只能切换到ARM状态
![【ARM汇编】第三章:ARM指令系统_第25张图片](http://img.e-com-net.com/image/info8/26a9ae23701f452982b905901b924242.jpg)
ARM-Thumb交互
- ARM-Thumb交互是指对汇编语言和c/c++语言中的ARM和Thumb代码进行连接的办法
- 它进行两种状态(ARM Thumb)的切换
- 在进行切换时,有时要使用额外的代码,这些代码称为胶合(veneer)
数据处理指令
数据处理指令可以操作寄存器中的数据
![【ARM汇编】第三章:ARM指令系统_第26张图片](http://img.e-com-net.com/image/info8/e66de569f4ae4e57ae8ce9f39793003f.jpg)
![【ARM汇编】第三章:ARM指令系统_第27张图片](http://img.e-com-net.com/image/info8/24808a2ae5cc4bb88d6d8002ecee7f97.jpg)
单寄存器加载和存储指令
Thumb指令集支持寄存器的加载和存储
![【ARM汇编】第三章:ARM指令系统_第28张图片](http://img.e-com-net.com/image/info8/6e18105932664967b07836894f92a590.jpg)
![【ARM汇编】第三章:ARM指令系统_第29张图片](http://img.e-com-net.com/image/info8/5005e85c2a694ee9997609ecb9257bc0.jpg)
多寄存器加载和存储指令
Thumb指令集的多寄存器加载和存储指令,是ARM指令集的多寄存器加载和存储指令的简化形式
![【ARM汇编】第三章:ARM指令系统_第30张图片](http://img.e-com-net.com/image/info8/df5fd8d6410145e0a312a03c40189af4.jpg)
堆栈指令
Thumb的堆栈操作与等效的ARM指令不同,因为它们使用更传统的POP和PUSH
![【ARM汇编】第三章:ARM指令系统_第31张图片](http://img.e-com-net.com/image/info8/544d8a83d02d4cab9479775fe6f917d8.jpg)
软件中断指令
与ARM指令集中软件中断类似,Thumb软中断指令SWI也产生中断异常
Thumb伪指令
Thumb伪指令不是Thumb指令集中的指令,类似于ARM伪指令
ADR小范围地址读取
ADR Rm, addr
地址偏移量必须是正数,且小于1kb
LDR大范围地址读取
LDR Rm, =addr
用于加载32位立即数或一个地址值到指定寄存器
NOP空操作
一般用于延时操作![【ARM汇编】第三章:ARM指令系统_第32张图片](http://img.e-com-net.com/image/info8/063d56063c554b319907e9e01434f95f.jpg)
![【ARM汇编】第三章:ARM指令系统_第33张图片](http://img.e-com-net.com/image/info8/c9cf5361c0a242dca23f90d9fa58376a.jpg)
![【ARM汇编】第三章:ARM指令系统_第34张图片](http://img.e-com-net.com/image/info8/5c278bf9d0b745269d570b1e5449fcde.jpg)
![【ARM汇编】第三章:ARM指令系统_第35张图片](http://img.e-com-net.com/image/info8/f3e956c903944903a9d8d23854d79b5e.jpg)
![【ARM汇编】第三章:ARM指令系统_第36张图片](http://img.e-com-net.com/image/info8/51e425d3e2c14707855f7050021c9a9f.jpg)
![【ARM汇编】第三章:ARM指令系统_第37张图片](http://img.e-com-net.com/image/info8/1358f6a21b634b41b4202fd5bfa067d6.jpg)
![【ARM汇编】第三章:ARM指令系统_第38张图片](http://img.e-com-net.com/image/info8/de00f7260cee4615a489107638318301.jpg)