本节来学习ARM汇编的常用指令,并结合在uboot中看到的汇编指令进行分析。
目录
一 数据处理指令
(1) MOV 数据传送指令:
mov r0 , #0x04 @r0 = 0x04
mov r0, r1 @r0 = r1
mov r0, r1, lsl#3 @r0 = r1<<3
(2)MVN 数据取反传送指令
mvn r0, #1 @r0 = ~1
mvn r0, r1 @r0 = ~r1
(3)CMP 比较指令
cmp r0, r1 等价于 sub r2, r0, r1 (r2 = r0 - r1), 并根据结果设置CPSR的标志位
常常跟以下指令一起使用:
bne a1 @r0 != r1(即Z位为0时),跳转到a1
beq a2 @r0 == r1(即Z位为1时),跳转到a2
(4)CMN 反值比较指令
cmn r0, r1 等价于 add r0, r1,并根据结果设置CPSR的标志位
(5)TST 位测试指令
用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。当前运算结果为1,则Z=0;当前运算结果为0,则Z=1
bne: 数据跳转指令,标志寄存器中Z标志位不等于零时, 跳转到BNE后标签处
beq: 数据跳转指令,标志寄存器中Z标志位等于零时, 跳转到BEQ后标签处
tst r0, #1 @测试r0中的bit_0位是否为1, 即进行逻辑与运算 r0&1
beq a1 @如果bit_0位为1,则执行a1标签的代码
(6)TEQ 相等测试指令
teq r0, r1
(7)ADD 加法指令
ADD R0, R1, R2 ; R0 = R1 + R2
ADD R0, R1, #256 ; R0 = R1 + 256
ADD R0, R2, R3, LSL#1 ; R0 = R2 + (R3 << 1)
ADDS 是32位数的加法运算, 影响进位标志位C而不影响运算结果的值
(8)ADC 带进位加法指令
用adc指令实现64位的加法运算
基本思路:
r0和r2 分别代表高位
r1和r3分别代表低位
即高位与高位运算r0 = r0 + r2
即低位与低位运算r1 = r1 + r3
实现代码:
mov r0, #1
mov r1, #0xffffffff
mov r2, #1
mov r3, #0x2
adds r1, r1, r3 ;r1+r3溢出 adds进位 c标志位置1
adc r0, r0, r2 ;高位相加,因为adc是带进位的加 所以高位r0 = r0 + r2 运算结果再加 1(进位的1),即r0 = r0 + r2 + 1
(9)SUB 减法指令
SUB R0, R1, R2 ; R0 = R1 - R2
SUB R0, R1, #256 ; R0 = R1 - 256
SUB R0, R2, R3, LSL#1 ; R0 = R2 - (R3 << 1)
SUB R1, R0 ;R1 -= R0
(10)SBC 带借位减法指令
SBC带进位减法指令,即除了加两个数以外,还要把CPSR的C值也要带进来,类似ADC
以下是做64Bit的减法
SUBS R0, R1, R2 ;R0=R1-R2,SUBS中S表示把进位结果写入CPSR
SBC R5, R3, R4 ;R5=R3-R4-C
(11)RSB 逆向减法指令
RSB{条件}{S} 目的寄存器,操作数1,操作数2
RSB指令称为逆向减法指令,用于把操作数2减去操作数1,并将结果存放到目的寄存器中。
rsb r0, r1, r2 ;r0 = r2 - r1
rsb r0, r1, #123 ;r0 = 123 - r1
(12)RSC 带借位的逆向减法指令
和SBC类似
(13) AND 按位与指令
AND{条件}{S} 目的寄存器,操作数1,操作数2
and r0, r0, #3 ;r0 = r0&0x3
(14)ORR 按位或指令
ORR{条件}{S} 目的寄存器,操作数1,操作数2
orr r0, r0, #3 ;r0 = r0|0x3
(15)EOR 按位异或指令
EOR{条件}{S} 目的寄存器,操作数1,操作数2
eor r0, r0, #3 ;r0 = r0^0x3
(16)BIC 位清除指令
BIC 是 逻辑”与非” 指令, 实现的 Bit Clear的功能
BIC R0, R0 , #0xF0000000 @将 R0 高4位清零
(17)移位指令
操作数可以是通用寄 存器,也可以是立即数(0~31)。
— LSL 逻辑左移
通用寄存器,LSL 操作数
LSL可完成对通用寄存器中的内容进行左移的操作,按操作数所指定的数量向左移位,右端用零来填充。
MOV R0, R1, LSL#2 @ R0 = R1<<2
— ASL 算术左移
同LSL
— LSR 逻辑右移
通用寄存器,LSR 操作数
LSR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充。
MOV R0, R1, LSR#2 @ R0 = R1>>2
— ASR 算术右移
通用寄存器,ASR 操作数
ASR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用第31位的值来填充
MOV R0, R1, ASR#2
— ROR 循环右移
通用寄存器,ROR 操作数
ROR可完成对通用寄存器中的内容进行循环右移的操作,按操作数所指定的数量向右循环移位,左端用右端移出的位来填充
MOV R0, R1, ROR#2
— RRX 带扩展的循环右移
通用寄存器,RRX 操作数
RRX可完成对通用寄存器中的内容进行带扩展的循环右移的操作,按操作数所指定的数量向右循环移位,左端用进位标志位 C来填充。
(18)数据交换指令
——SWP指令
SWP{条件} 目的寄存器,源寄存器1,[源寄存器2]
SWP指令用于将源寄存器2所指向的存储器中的字数据传送到目的寄存器中,同时将源寄存器1中的字数据传送到源寄存器2所指向的存储器中。显然,当源寄存 器1和目的寄存器为同一个寄存器时,指令交换该寄存器和存储器的内容。
SWP R0, R1, [R2] @将R2所指向的存储器中的数据传送给R0, 同时将R1寄存器中的字数据传送给R2指向的存储单元
SWP R0, R0, [R2] @R0与R2的数据交换
——SWPB指令
SWP{条件}B 目的寄存器,源寄存器1,[源寄存器2]
SWPB指令用于将源寄存器2所指向的存储器中的字节数据传送到目的寄存器中,目的寄存器的高24清零,
同时将源寄存 器1中的字节数据传送到源寄存器2所指向的存储器中。显然,当源寄存器1和目的寄存器为同一个寄存器时,
指令交换该寄存器和存储器的内容。
(19)乘法与乘加指令
ARM 微处理器支持的乘法指令与乘加指令共有6条,可分为运算结果为32位和运算结果为64位两类。与前面的数据处理指令不同,指令中的所有操作数、目的寄存器 必须为通用寄存器,不能对操作数使用立即数或被移位的寄存器,同时,目的寄存器和操作数1必须是不同的寄存器。
——MUL相乘
MUL{条件}{S} 目的寄存器,操作数1,操作数2
MUL指令完成将操作数1与操作数2的乘法运算,并把结果放置到目的寄存器中,
同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操 作数2均为32位的有符号数或无符号数。
MUL R0, R1, R2 @R0 = R1 * R2
MULS R0, R1, R2 @R0 = R1 * R2 同时设置CPSR中的相关标志位
——MLA带累加的相乘
MLA{条件}{S} 目的寄存器,操作数1,操作数2,操作数3
MLA指令完成将操作数1与操作数2的乘法运算,再将乘积加上操作数3,并把结果放置到目的寄存器中,
同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数或无符号数
MLA R0, R1, R2, R3 @R0 = R1*R2+R3
MLAS R0, R1, R2, R3 @R0=R1*R2+R3 同时设置CPSR中的相关标志位
——SMULL指令
SMULL{条件}{S} 目的寄存器Low,目的寄存器High,操作数1,操作数2
SMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,
结果的高32位放置到目的寄存器High中,同时可以 根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的有符号数
SMULL R0, R1, R2, R3 @R0 等于 R2*R3底32位值, R1等于R2*R3高32位值,
——SMLAL指令
SMLAL{条件}{S} 目的寄存器Low,目的寄存器High,操作数1,操作数2
SMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的有符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位;
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
SMLAL R0, R1, R2, R3 @R0 = R2*R3的低32位数 + R0, R1 = R2*R3的高32位数 + R1
——UMULL指令
UMULL{条件}{S} 目的寄存器Low,目的寄存器High,操作数1,操作数2
UMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,
结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的无符号数
UMULL R0, R1, R2, R3 @R0 等于 R2*R3底32位值, R1等于R2*R3高32位值,
——UMLAL指令
UMLAL{条件}{S} 目的寄存器Low,目的寄存器High,操作数1,操作数2
UMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位;
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
SMLAL R0, R1, R2, R3 @R0 = R2*R3的低32位数 + R0, R1 = R2*R3的高32位数 + R1
二 加载存储指令
— LDR 字数据加载指令
— LDRB 字节数据加载指令
— LDRH 半字数据加载指令
— STR 字数据存储指令
— STRB 字节数据存储指令
— STRH 半字数据存储指令
(1)LDR 字数据加载指令
(注意:加一个[]表示一个内存地址)
ldr r0, =0x12345678 @把0x12345678这个地址写到r0中
ldr r0, [r1] @把r1这个地址数据写到r0中,[ ]代表这是一个内存的地址
ldr r0, [r1, #8] @将地址r1 + 8的字数据读入以r0存储器中
ldr r0, [r1], #8 @将r1的数据读入到r0,并将r1 + 8的值存入r1
(2)STR 字数据存储指令
str r0, [r1, #8] @将r1中的字数据读入以r1 + 8为地址的存储器中
str r0, [r1], #8 @ 将r0的字数据读入r1,并将r1 + 8的值存入r1
(3)批量数据加载/存储指令
1)—LDM
2)—STM
LDM批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,STM批量数据存储指令则完成相反的操作。
LDM(或 STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈(STM)或出栈(LDM)
指令格式为: LDM(或STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}
{类型} 为 以下几种情况:
IA 每次传送后地址加1;
IB 每次传送前地址加1;
DA 每次传送后地址减1;
DB 每次传送前地址减1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
{!}为可选后缀,若选用该后缀,则当数据 传送完毕之后,将最后的地址写入基址寄存器,
否则基址寄存器的内容不改变。基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。
{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,
还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。
uboot中重定位代码的实例:
relocate_to_text:
/*
* relocate u-boot code on memory to text base
* for nexell arm core (add by jhkim)
*/
adr r0, _stext /* r0 <- current position of code */
ldr r1, TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _bss_start_ofs
add r2, r0, r2 /* r2 <- source end address */
copy_loop_text:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop_text //若r0 <= r2 则执行
三 跳转指令
实现指令跳转的方式有两种
使用专门的跳转指令跳转
分支指令 B
带链接的分支指令: BL
带状态切换的分支指令 BX
使用程序计数器PC
直接向程序计数器PC写入跳转地址值,通过向程序计数器PC写入跳转地址值
可以实现在程序地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC等类似指令
可以保存将来的返回地址值,从而实现在连续的线性地址空间的子程序调用
1. B指令
B{条件} 目标地址
B label @跳转到label标签后面的代码执行
CMP R0, R1
BEQ label @当R0=R1时,程序跳转到label处执行
2. BL指令
BL{条件} 目标地址
BL是另一个跳转指令,但跳转之前,会在寄存器R14(LR)中保存PC的当前内容,
因此,可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个 指令处执行。
该指令是实现子程序调用的一个基本但常用的手段
BEQ 相等
BNE 不等
BPL 非负
BMI 负
BCC 无进位
BCS 有进位
BLO 小于(无符号数)
BHS 大于等于(无符号数)
BHI 大于(无符号数)
BLS 小于等于(无符号数)
BVC 无溢出(有符号数)
BVS 有溢出(有符号数)
BGT 大于(有符号数)
BGE 大于等于(有符号数)
BLT 小于(有符号数)
BLE 小于等于(有符号数)
3. BLX指令
BLX 目标地址
BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态有ARM状态切换到Thumb状态,
该指令同时将PC的当前内容保存到寄存器R14中。因此,当子程序使用Thumb指令集,而调用者使用ARM指令集时,
可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成
4. BX指令
BX{条件} 目标地址
BX指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是Thumb指令。
四 程序状态寄存器访问指令
(1)MRS指令
MRS{条件} 通用寄存器 程序状态寄存器(CPSR或SPSR)
MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。该指令一般用在以下两种情况:
Ⅰ.当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。
Ⅱ.当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存
MRS R0, CPSR @将CPSR的内容读到R0
MRS R0, SPSR @将SPSR的内容读到R0
(2)MSR指令
MSR{条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数
MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。
<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:
位[31:24]为条件位域,用f表示;
位[23:16]为状态位域,用s表示;
位[15:8] 为扩展位域,用x表示;
位[7:0] 为控制位域,用c表示;
该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
MSR CPSR, R0 @将R0的内容读到CPSR
MSR SPSR, R0 @将R0的内容读到SPSR
MSR CPSR_C, R0 @讲R0的内容读到CPSR,但仅仅修改CPSR中的控制位域
五 协处理器指令
(1)CDP指令
CDP{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2
CDP指令用于ARM处理器通知ARM协处理器执行特定的操作,若协处理器不能成功完成特定的操作,
则产生未定义指令异常。其中协处理器操作码1和协处理 器操作码2为协处理器将要执行的操作,
目的寄存器和源寄存器均为协处理器的寄存器,指令不涉及ARM处理器的寄存器和存储器。
CDP P3, 2, C12, C10, C3, 4 @该指令完成协处理器P3的初始化
(2)LDC指令
LDC{条件}{L} 协处理器编码,目的寄存器,[源寄存器]
LDC指令用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中,若协处理器不能成功完成传送操作,
则产生未定义指令异常。其中,{L}选项表示指 令为长读取操作,如用于双精度数据的传输
LDC P3, C4, [R0] @将ARM处理器的寄存器R0所指向的存储器中的字数据传送到协处理器P3的寄存器C4中
(3)STC指令
STC{条件}{L} 协处理器编码,源寄存器,[目的寄存器]
STC指令用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。
其中,{L}选项表示指 令为长读取操作,如用于双精度数据的传输
STC P3, C4, [R0] @将协处理器P3的寄存器C4中的字 数据传送到ARM处理器的寄存器R0所指向的存储器中
(4)MCR指令
MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2
MCR指令用于将ARM处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,
则产生未定义指令异常。其中协处理器操作码1和协处理 器操作码2为协处理器将要执行的操作,
源寄存器为ARM处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄 存器
MCR P3, 3, R0, C4, C5, 6 @将ARM处理器寄存器R0中的数据传送到协处理器P3的寄存器C4和C5中
(5)MRC指令
MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2
MRC指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,
则产生未定义指令异常。其中协处理器操作码1和协处理 器操作码2为协处理器将要执行的操作,
目的寄存器为ARM处理器的寄存器,源寄存器1和源寄存器2均为协处理器的寄存器。
MRC P3, 3, R0, C4, C5, 6 @将协处理器P3的寄存器中的数据传送到ARM处理器寄存器中
六 异常产生指令
(1)SWI指令
SWI{条件} 24位的立即数
SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的异常处理程序中提供相应的系统服务,
指令中24位的立即数指定用 户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中24位的立即数被忽略时,
用户程序调用系统例程的类型由通用寄存器R0的内容决定,同 时,参数通过其他通用寄存器传递
SWI 0x02 @调用操作系统编号位02的系统例程
(2)BKPT指令
BKPT 16位的立即数
BKPT指令产生软件断点中断,可用于程序的调试