目录
- 一、简介
- 二、ARM 汇编指令说明
- 三、实例讲解
- 3.1 MRS
- 3.2 MSR
- 3.3 PRIMASK
- 3.4 FAULTMASK
- 3.5 BX指令
- 3.6 零寄存器 wzr、xzr
- 3.7 立即寻址指令MOV
- 3.8 寄存器间接寻址指令LDR
- 3.9 寄存器移位寻址指令LSL
- 3.10 基址寻址指令 STR
- 3.11 多寄存器寻址指令
- 3.12 无条件转移B,BAL
- 3.13 条件转移B.cont
- 3.14 WFE 和 WFI 对比
- 3.15 MRC:协处理器寄存器到ARM寄存器的数据传输
- 3.16 MCR:寄存器到协处理器寄存器的数据传输
- 3.17 STM:将指令中寄存器列表中的各寄存器数值写入到连续的内存单元中
- 3.18 LDM:将数据从连续内存单元中读取到指令的寄存器列表中的各寄存器中
- 3.19 LDR:从内存中将一个32位的字读取到目标寄存器
- 3.20 STR:将32位字数据写入到指定的内存单元
- 3.21 SWI:软中断指令
- 3.22 BIC清除位
- 3.23 EOR逻辑异或指令
- 3.24 CMN与负数对比
- 3.25 MVN取反
- 3.26 LSL(Logical Shift Left)左移运算
- 3.27 STP
- 实例解析
一、简介
本文主要整理了arm常用的汇编指令,同时通过实例进一步讲述语句的用法。
二、ARM 汇编指令说明
寄存器分类介绍
1. 通用寄存器
通用寄存器是一组用于存储数据和地址的寄存器。在 ARM 架构的不同版本中,这些寄存器的数量和命名有所不同。
R0-R15 (R0-R14 + PC):
在 ARMv7 和之前的版本中,有 16 个通用寄存器,编号从 R0 到 R15。
R0 到 R14 用于存储数据和地址。
R15 通常被称为程序计数器(PC),用于存储下一条指令的地址。
X0-X30 (X0-X28+ FR + LR):
在 ARMv8 和之后的版本中,有 31 个通用寄存器,编号从 X0 到 X30。
X0 到 X28 用于存储数据和地址。
X29: Frame Pointer (FP) 寄存器,它的主要作用是指向当前函数的栈帧(Stack Frame)
X30: 链接寄存器(LR),用于保存返回地址。
在ARMv7架构中使用程序状态寄存器(Current Program Status Register,CPSR)来表示当前的处理器状态(processor stste),而在ARMv8里使用PSTATE寄存器来表示。
ARMv8及以后版本:
寄存器 | 位数 | 描述 |
---|---|---|
X0-X30 | 64bit | 通用寄存器,如果有需要可以当作32bit使用:W0-W30 |
FP(X29) | 64bit | 保存栈帧地址(栈底指针) |
LR(X30) | 64bit | 程序链接寄存器,保存子程序结束后需要执行的下一条指令 |
SP | 64bit | 保存栈顶指针,使用SP/WSP来进行对SP寄存器的访问。 |
PC | 64bit | 程序计数器,俗称PC指针,总是指向即将要执行的下一条指令,在arm64中,软件不能修改PC寄存器 |
PSTATE | 64bit | 状态寄存器,用于保存处理器的当前状态信息。 |
ARM 64包含31个64bit寄存器,记为X0~X30。
每一个通用寄存器,它的低32bit都可以被访问,记为W0~W30。
LDR和STR分别从地址中读入内容到寄存器和向地址中写入寄存器的内容,后缀B表示字节,H表示半字,W表示单字
向量和浮点寄存器。32个寄存器,向量和浮点共用,每个128位,用V0到V31来表示。不同的记号可以表示不同的长度,B表示字节,H表示半字,S表示单字,D表示双字,Q表示四字
X0 - X7: 这8个寄存器通常用作函数参数寄存器,在函数调用时用来传递前8个参数,若参数个数大于8,就采用栈来传递。64位的函数返回值通常存放在X0寄存器中,128位的返回结果存在X0和X1两个寄存器中。
X8: 间接结果位置寄存器,用于保存子函数的返回地址。在一些情况下,X8可以用于普通的临时寄存器,用于存储中间计算结果。
X9 - X15: 通常被用作临时变量和中间计算结果的存储。调用者有责任在函数调用前保存它们的值,以免被覆盖。函数返回后,调用者也需要恢复这些寄存器的值。
X16、X17: X16 (IP0, Intra-Procedure-call scratch register 0)这个寄存器通常被用作临时寄存器,用于存储函数内部的中间计算结果。它在函数调用过程中可能会被修改,所以调用者需要自行保存和恢复。X17与X16类似。
X18: X18 (Platform register)这个寄存器通常被用作平台相关的寄存器,其用途取决于具体的硬件平台和软件环境。在某些系统中,X18 可能被用作过程链接表(PLT)指针,用于动态链接。在其他系统中,X18 可能被用作线程局部存储(Thread Local Storage)的指针。
X19 - X28: 通常用于存储函数的局部变量和中间计算结果。被调用的函数有责任在返回前保存和恢复这些寄存器的值,确保调用者可以继续使用。
X29: Frame Pointer (FP) 寄存器,它的主要作用是指向当前函数的栈帧(Stack Frame),方便访问函数内部的局部变量和参数。当一个函数被调用时,x29 寄存器会被设置为指向该函数的栈帧起始地址。这样可以通过 x29 寄存器轻松访问函数内部的局部变量和参数,而不需要依赖 x30 寄存器(Link Register)中存储的返回地址。
X30: Link Register (LR),它的主要作用是在函数调用时存储函数的返回地址,以便函数执行完毕后能够正确地返回到调用点。当一个函数被调用时,CPU 会将当前执行点的地址保存到 x30 寄存器中。这样在函数执行完毕后,只需要从 x30 寄存器中恢复返回地址,就可以正确地返回到调用点。注意:当一个函数被调用时,CPU 会自动将当前执行点的地址(也就是函数调用语句的下一条指令地址)保存到 x30 寄存器中。
X9 - X15和X19 - X28这两组寄存器的区别:
x9 到 x15 则主要用作临时变量和中间计算结果的存储。x19 到 x28 通常用作函数的局部变量和中间计算结果的存储。对于 x9 到 x15 这些"caller-saved"寄存器,调用者(caller)有责任在函数调用前保存它们的值,并在调用后恢复。对于 x19 到 x28 这些"callee-saved"寄存器,被调用的函数(callee)有责任在返回前保存和恢复它们的值。使用"callee-saved"寄存器通常可以减少对栈的访问,提高性能。但同时也增加了函数调用时保存和恢复寄存器的开销。
ARMv8前版本:
R0-R15寄存器 根据“ARM-thumb 过程调用标准”:
R0-R3 其中,R0通常用于存储函数的返回值,R1-R3则常用于传递函数参数。在子程序调用之前,可以将R0-R3用于任何用途。被调用函数在返回之前不必恢复R0-R3。如果调用函数需要再次使用 r0-r3的内容,则它必须保留这些内容。
R4-R11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
R12是内部调用暂时寄存器IP。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。
在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复R12。
R13是栈指针SP:
在ARM指令集中,R13常被用作堆栈指针,用于存储程序中的局部变量和函数调用时的返回地址。它不能用于任何其它用途。SP中存放的值在退出被调用函数时必须与进入时的值相同。用户也可以使用其他寄存器作为堆栈指针,但在Thumb指令集中,某些指令强制要求使用R13作为堆栈指针。
R14是链接寄存器LR:
用于存储函数调用之前的返回地址,如果您保存了返回地址,则可以在调用之前将R14用于其它用途,程序返回时要恢复。当执行子程序调用指令(如BL或BLX)时,R14会被设置成该子程序的返回地址。在子程序返回时,将R14的值复制回程序计数器PC即可完成子程序的调用返回。
R15是程序计数器PC:
用于存储当前正在执行的指令的地址。程序计数器是处理器控制指令执行的关键寄存器之一。它不能用于任何其它用途。由于ARM采用了流水线机制,当正确读取了PC的值后,该值为当前指令地址加8个字节,即PC指向当前指令的下两条指令地址。
注意:在中断程序中,所有的寄存器都必须保护,编译器会自动保护R4~R11。
2. 专用寄存器
ARM处理器中的专用寄存器主要包括程序状态寄存器(CPSR)和备份的程序状态寄存器(SPSRs)。
程序状态寄存器(CPSR)
CPSR是一个32位的特殊寄存器,用于存储当前程序的状态信息。它包含以下内容:
ALU状态标志 :如条件码(如零标志Z、负标志N、进位标志C等),用于反映ALU的运算结果。
中断使能位 :用于控制中断的使能状态。
执行模式位 :用于标识当前处理器的执行模式(如用户模式、系统模式、中断模式等)。
CPSR和SPSR都是程序状态寄存器,其中SPSR是用来保存中断前的CPSR中的值,以便在中断返回之后恢复处理器程序状态。
CPSR在任何处理器模式下都可被访问和修改(但某些位可能需要特权级代码才能修改)。通过读取和修改CPSR寄存器的各个标志位和控制位,可以控制程序的执行流程和处理器的行为。
备份的程序状态寄存器(SPSRs)
ARM处理器还包含5个备份的程序状态寄存器(SPSR_fiq、SPSR_irq、SPSR_svc、SPSR_abt、SPSR_und),用于在异常处理期间保存CPSR的值。当处理器进入异常模式时,会将CPSR的内容复制到对应的SPSR中;当从异常模式返回时,则可以将SPSR的内容复制回CPSR以恢复处理器的状态。
3. 控制寄存器
虽然控制寄存器不直接归类为通用或专用寄存器,但它们在ARM处理器的控制中发挥着重要作用。这些寄存器通常包含处理器的控制位和配置位,用于控制处理器的行为和工作模式。由于控制寄存器的访问和修改通常需要特权级代码,因此它们在普通的应用程序中很少被直接访问。如控制CPU的行为,如 CTRL 和 ACTLR。
2.1 32位数据操作指令
名字 | 功能 |
---|---|
ADC | 带进位加法 |
ADD | 加法 |
ADDW | 宽加法(可以加 12 位立即数) |
AND | 按位与 |
ASR | 算术右移 |
BIC | 位清零(把一个数按位取反后,与另一个数逻辑与) |
BFC | 位段清零 |
BFI | 位段插入 |
CMN | 负向比较(把一个数和另一个数的二进制补码比较,并更新标志位) |
CMP | 比较两个数并更新标志位 |
CLZ | 计算前导零的数目 |
EOR | 按位异或 |
LSL | 逻辑左移 |
LSR | 逻辑右移 |
MLA | 乘加 |
MLS | 乘减 |
MOVW | 把 16 位立即数放到寄存器的底16位,高16位清0 |
MOV | 加载16位立即数到寄存器(其实汇编器会产生MOVW——译注) |
MOVT | 把 16 位立即数放到寄存器的高16位,低 16位不影响 |
MVN | 移动一个数的补码 |
MUL | 乘法 |
ORR | 按位或 |
ORN | 把源操作数按位取反后,再执行按位或( |
RBIT | 位反转(把一个 32 位整数先用2 进制表达,再旋转180度——译注) |
REV | 对一个32 位整数做按字节反转 |
REVH/REV16 | 对一个32 位整数的高低半字都执行字节反转 |
REVSH | 对一个32 位整数的低半字执行字节反转,再带符号扩展成32位数 |
ROR | 圆圈右移 |
RRX | 带进位的逻辑右移一格(最高位用C 填充,且不影响C的值——译注) |
SFBX | 从一个32 位整数中提取任意的位段,并且带符号扩展成 32 位整数 |
SDIV | 带符号除法 |
SMLAL | 带符号长乘加(两个带符号的 32 位整数相乘得到 64 位的带符号积,再把积加到另一个带符号 64位整数中) |
SMULL | 带符号长乘法(两个带符号的 32 位整数相乘得到 64位的带符号积) |
SSAT | 带符号的饱和运算 |
SBC | 带借位的减法 |
SUB | 减法 |
SUBW | 宽减法,可以减 12 位立即数 |
SXTB | 字节带符号扩展到32位数 |
TEQ | 测试是否相等(对两个数执行异或,更新标志但不存储结果) |
TST | 测试(对两个数执行按位与,更新标志但不存储结果) |
UBFX | 无符号位段提取 |
UDIV | 无符号除法 |
UMLAL | 无符号长乘加(两个无符号的 32 位整数相乘得到 64 位的无符号积,再把积加到另一个无符号 64位整数中) |
UMULL | 无符号长乘法(两个无符号的 32 位整数相乘得到 64位的无符号积) |
USAT | 无符号饱和操作(但是源操作数是带符号的——译注) |
UXTB | 字节被无符号扩展到32 位(高24位清0——译注) |
UXTH | 半字被无符号扩展到32 位(高16位清0——译注) |
2.2 32位存储器数据传送指令
名字 | 功能 |
---|---|
LDR | 加载字到寄存器 |
LDRB | 加载字节到寄存器 |
LDRH | 加载半字到寄存器 |
LDRSH | 加载半字到寄存器,再带符号扩展到 32位 |
LDM | 从一片连续的地址空间中加载多个字到若干寄存器 |
LDRD | 从连续的地址空间加载双字(64 位整数)到2 个寄存器 |
STR | 存储寄存器中的字 |
STRB | 存储寄存器中的低字节 |
STRH | 存储寄存器中的低半字 |
STM | 存储若干寄存器中的字到一片连续的地址空间中 |
STRD | 存储2 个寄存器组成的双字到连续的地址空间中 |
PUSH | 把若干寄存器的值压入堆栈中 |
POP | 从堆栈中弹出若干的寄存器的值 |
2.3 32位转移指令
名字 | 功能 |
---|---|
B | 无条件转移 |
BL | 转移并连接(呼叫子程序) |
TBB | 以字节为单位的查表转移。从一个字节数组中选一个8位前向跳转地址并转移 |
TBH | 以半字为单位的查表转移。从一个半字数组中选一个16 位前向跳转的地址并转移 |
2.4 其它32位指令
名字 | 功能 |
---|---|
LDREX | 加载字到寄存器,并且在内核中标明一段地址进入了互斥访问状态 |
LDREXH | 加载半字到寄存器,并且在内核中标明一段地址进入了互斥访问状态 |
LDREXB | 加载字节到寄存器,并且在内核中标明一段地址进入了互斥访问状态 |
STREX | 检查将要写入的地址是否已进入了互斥访问状态,如果是则存储寄存器的字 |
STREXH | 检查将要写入的地址是否已进入了互斥访问状态,如果是则存储寄存器的半字 |
STREXB | 检查将要写入的地址是否已进入了互斥访问状态,如果是则存储寄存器的字节 |
CLREX | 在本地的处理上清除互斥访问状态的标记(先前由 LDREX/LDREXH/LDREXB做的标记) |
MRS | 加载特殊功能寄存器的值到通用寄存器 |
MSR | 存储通用寄存器的值到特殊功能寄存器 |
NOP | 无操作 |
SEV | 发送事件 |
WFE | 休眠并且在发生事件时被唤醒 |
WFI | 休眠并且在发生中断时被唤醒 |
ISB | 指令同步隔离(与流水线和 MPU等有关——译注) |
DSB | 数据同步隔离(与流水线、MPU 和cache等有关——译注) |
DMB | 数据存储隔离(与流水线、MPU 和cache等有关——译注) |
DMB | 数据存储器隔离。DMB 指令保证: 仅当所有在它前面的存储器访问操作都执行完毕后,才提交(commit)在它后面的存储器访问操作。 |
DSB | 数据同步隔离。比 DMB 严格: 仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访 问操作——译者注) |
ISB | 指令同步隔离。最严格:它会清洗流水线,以保证所有它前面的指令都执行完毕之后,才执行它后面的指令。 |
2.5 立即数
1、立即数:一个立即数是一块数据存储作为指令本身,而不是在一个中的一部分内容存储器位置或寄存器。立即值通常用于加载值或对常量执行算术或逻辑运算的指令。
2、比如一个数 10,把他存入内存中,高级语言表示法是 int i=10,这个数放入内存之前叫立即数,放入之后就不是了,再比如一个数 10,把他存入寄存器中,这个数放入寄存器之前叫立即数,放入之后就不是了。
2.6 逻辑数
逻辑数是用来表示二值逻辑中的"是"与"否"、或称"真"与"假"两个状态的数据。在计算机中,可以用一位基2码表示逻辑数据,即8个逻辑数据可以存放在1个字节中,可用其中的每个bit(位)表示一个逻辑数据。逻辑数可以用计算机中的基2码的两个状态"1"和"0"来表示,其中"1"表示真,"0"表示假。
2.7 逻辑运算和算术运算
逻辑运算是一种只存在于二进制中的运算。在计组中逻辑运算经常出现的是 或、与、非和异或,这几种运算方式。
算数运算我们平常十进制的 加减乘除,但因为在计算机中是二进制所以就只能是加法运算。在计算机中也可以算数运算也可以区分成进位的算数运算和不进位的算数运算。带进位的算数运算
三、实例讲解
3.1 MRS
将状态寄存器CPSR或SPSR的内容移动到一个通用寄存器
MRS R0,CPSR //传送CPSR的内容到R0
MRS R0,SPSR //传送 SPSR的内容到R0
3.2 MSR
将立即数或通用寄存器的内容加载到CPSR或SPSR的指定字段中
MSR CPSR,R0 //传送R0的内容到CPSR
MSR SPSR,R0 //传送R0的内容到SPSR
MSR CPSR_c,R0 //传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域
3.3 PRIMASK
用于disable NMI和硬 fault之外的所有异常,它有效地把当前优先级改为 0(可编程 优先级中的最高优先级)。
CPS指令会更改CPSR中的一个或多个模式以及A、I和F位,但不更改其他CPSR位。CPSID就是中断禁止,CPSIE中断允许,
A:表示启用或禁止不精确的中止;I:表示启用或禁止IRQ中断;F:表示启用或禁止FIQ中断
3.4 FAULTMASK
CPSIE f; / CPSID f;
MSR FAULTMASK,R0
FAULTMASK更绝,它把当前优先级改为-1。这么一来,连硬fault都被掩蔽了。使用方案与
PRIMASK的相似。但要注意的是,FAULTMASK会在异常退出时自动清零。
3.5 BX指令
BX{条件} 目标地址
BX 指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM 指令,也可以是Thumb指令。
3.6 零寄存器 wzr、xzr
因为我们在使用 str 的是没法使用立即数 0 给寄存器赋值,所以 wzr xzr就是干这个事情的。是一个比较特殊又常常见到的寄存器。
3.7 立即寻址指令MOV
SUBS R0,R0,#1 //R0 减 1 ,结果放入 R0 ,并且影响标志位
MOV R0,#0xFF000 //将立即数 0xFF000 装入 R0 寄存器 寄存器寻址指令举例如下:
MOV R1,R2 //将 R2 的值存入 R1
SUB R0,R1,R2 //将 R1 的值减去 R2 的值,结果保存到 R0
3.8 寄存器间接寻址指令LDR
LDR R1,[R2] //将 R2 指向的存储单元的数据读出保存在 R1 中
SWP R1,R1,[R2] //将寄存器 R1 的值和 R2 指定的存储单元的内容交换,将R2的数值作为一个地址,将此地址处的数值与R1中的内容交换
按照从简单到复杂的分类方法,可以通过以下方式来指定访存指令的地址:从寄存器中获取地址;通过寄存器内容再加上偏移来获取地址;对偏移进行扩展、移位等运算之后,再与寄存器内容相加,获得地址。
LDR X0, [X1] ; 直接从寄存器X1的内容中获取地址。
LDR X0, [X1, #-8] ; X1的内容加上-8的偏移,得到地址。
LDR X0, [X1, X2] ; X!的内容和X2的内容相加得到地址。
LDR X0, [X1, W2, SXTW] ; 对W2的内容做符号扩展,再与X1的内容相加,作为地址。
LDR X0, [X1, X2, LSL #2] ; 把X2的内容左移2位,再与X1的内容相加,作为地址
3.9 寄存器移位寻址指令LSL
MOV R0,R2,LSL #3 //R2 的值左移 3 位,结果放入R0 ,即是R0=R2×8
ANDS R1,R1,R2,LSL R3 //R2 的值左移 R3 位,然后和R1相“与”操作,结果放入R1
寻址模式
简单模式:X1的内容不会被改变,例如。
LDR X0, [X1]
LDR X0, [X1, #4]
前变址模式,X1的内容在load之前变化,例如。
LDR X0, [X1, #4]!
等价于
ADD X1, X1, #4
LDR X0, [X1]
后变址模式,X1的内容在load之后变化,例如。
LDR X0, [X1], #4
等价于
LDR X0, [X1]
ADD X1, X1, #4
支持对整型、浮点标量和向量,要求源寄存器和目的寄存器必须具有相同的宽度,例如。
LDP W3, W7, [X0] ; [X0] => W3, [X0 + 4 bytes] =>W7
STP Q0, Q1, [X4] ; Q0 => [X4], Q1=>[X4 + 16 bytes]
3.10 基址寻址指令 STR
LDR R2,[R3,#0x0C] //读取 R3+0x0C 地址上的存储单元的内容,放入 R2
STR R1,[R0,#-4]! //先 R0=R0-4 ,然后把 R1 的值寄存到 R0 指定的存储单元
3.11 多寄存器寻址指令
LDMIA R1!,{R2-R7,R12} //将 R1 指向的单元中的数据读出到R2 ~R7、R12 中 (R1自动加1)
STMIA R0!,{R2-R7,R12} //将寄存器 R2 ~ R7 、 R12 的值保存到 R0 指向的存储单元中(R0自动加1)
3.12 无条件转移B,BAL
举例: B LABEL ; LABEL为某个位置
CMP x3,x4
B.CS {pc}+0x10 ; 0xc000800094
BCC是指CPSR寄存器条件标志位为0时的跳转。结合CMP R3, R1,意思是比较R3 R1寄存器,当相等时跳转到环测试。因为CMP指令减去两个值并在CPSR中设置条件标志位。
3.13 条件转移B.cont
BEQ 相等
BNE 不等
BPL 非负
BMI 负
BCC 无进位
BCS 有进位
BLO 小于(无符号数)
BHS 大于等于(无符号数)
BHI 大于(无符号数)
BLS 小于等于(无符号数)
BVC 无溢出(有符号数)
BVS 有溢出(有符号数)
BGT 大于(有符号数)
BGE 大于等于(有符号数)
BLT 小于(有符号数)
BLE 小于等于(有符号数)
blr Xm:跳转到由Xm目标寄存器指定的地址处,同时将下一条指令存放到X30寄存器中。例如:blr x20.
br Xm:跳转到由Xm目标寄存器指定的地址处。不是子程序返回
ret {Xm}:跳转到由Xm目标寄存器指定的地址处。是子程序返回。Xm可以不写,默认是X30.
3.14 WFE 和 WFI 对比
wfi 和 wfe 指令都是让ARM核进入standby睡眠模式。wfi是直到有wfi唤醒事件发生才会唤醒CPU,wfe是直到wfe唤醒事件发生,这两类事件大部分相同。唯一不同之处在于wfe可以被其他CPU上的sev指令唤醒,sec指令用于修改event寄存器的指令。
WFE
Wait For Event,是否实现此指令是可选的。如果此指令未实现,它将作为NOP指令来执行。如果指令作为NOP在目标处理器上执行,汇编程序将生成诊断消息。
SEV
Set Event,其是否实现是可选的。如果未实现,它将作为NOP执行。如果指令作为NOP在目标上执行,汇编程序将生成诊断消息。
SEV在ARMv6T2中作为NOP指令执行。
3.15 MRC:协处理器寄存器到ARM寄存器的数据传输
MRC指令将协处理器的寄存器中数值传送到ARM处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。
MRC p2,5,r3,c5,c6协处理器p2把c5和c6经过5操作的结果赋给r3
MRC p3,9,r3,c5,c6,2协处理器p3把c5和c6经过9操作(类型2)的结果赋给r3
3.16 MCR:寄存器到协处理器寄存器的数据传输
MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。
MCR p6,0,r4,c5,c6协处理器p6把r4执行0操作后将结果存放进c5
3.17 STM:将指令中寄存器列表中的各寄存器数值写入到连续的内存单元中
STM指令是Store Multiple的缩写,它的作用是将多个寄存器的值保存到栈中。在ARM汇编中,栈是一种后进先出 (LIFO)的数据结构,用来存储临时数据和函数调用过程中的返回地址
STM指令的语法如下:
STM{条件码}{模式} SP!,{寄存器列表}
其中,条件码是可选项,用来指定条件执行STM指令的条件;模式用来指定存储模式,
1、寻址模式(mode)
mode决定了基址寄存器是在执行指令前地址增减还是指令执行后增减.
I为Increment(递增)
D为Decrement (递减)
B为Before
A为After
常用的模式有IA (递增后存储) 、IB (递增前存储) 、DA (减后存储)和DB(递减前存储);SP是栈指针寄存器,用来指定栈的起始地址;寄存器列表指定要保存的寄存器。
另外四种也是寻址模式
FD 慢递减堆栈
FA 满递增堆栈
ED 空递减堆栈
EA 空递增堆栈
STMFD SP![RO,R1,R2)
在上述代码中,STMFD指令存储了RO、R1和R2的值到栈中。SP!表示栈指针寄存器递增,即存储完后栈指针自动增加,以便下一次保存操作。
2、“!”
在传输数据完成后,更新基址寄存器中的值
3、“^”
在数据传输完成后,将SPSR的值复制到CPSR中,常用于异常模式下的返回.
3.18 LDM:将数据从连续内存单元中读取到指令的寄存器列表中的各寄存器中
LDMIA
LDMIA R0! ,{R3-R9} ; //将R0指向的地址上连续空间的数据,保存到R3-R9当中,!表示R0值更新,IA后缀表示按WORD递增
3.19 LDR:从内存中将一个32位的字读取到目标寄存器
ldr 加载指令: LDR{条件} 目的寄存器,<存储器地址>
LDR指令用亍从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
3.20 STR:将32位字数据写入到指定的内存单元
STR指令的格式为:
STR{条件} 源寄存器,<存储器地址>
STR指令用亍从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用,寻址方式灵活多样,使用方式可参考指令LDR。
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
str r1, [r0] ;将r1寄存器的值,传送到地址值为r0的(存储器)内存中
3.21 SWI:软中断指令
SWI指令格式如下:
SWI{cond} immed_24
MOV R0,#34 ;设置功能号为34
SWI 12 ;产生软中断,中断号为12
3.22 BIC清除位
BIC指令的格式为: BIC{条件}{S} 目的寄存器,操作数1,操作数2
BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器, 操作数2可以是一个寄存器、被移位的寄存器、或一个立即数。操作数2为32位的掩码,如果在 掩码中置了某一位1,则清除这一位。未设置的掩码位保持不变。
BIC R0,R0,#0X1F
0X1F=11111B
//其含义:清除R0的bit[4:0]位。
BIC R4, R4, #0xFF000000 指令将E4高8位清除为0
3.23 EOR逻辑异或指令
EOR{<cond>}{S} <Rd>,<Rn>,<shifter_operand>
逻辑异或EOR(Exclusive OR)指令将寄存器中的值和<shifter_operand>的值执行按位“异或”操作,并将执行结果存储到目的寄存器中,同时根据指令的执行结果更新CPSR中相应的条件标志位。
3.24 CMN与负数对比
CMN 同于 CMP,但它允许你与负值进行比较,比如难于用其他方法实现的用于结束列表的 -1。这样与 -1 比较将使用:
CMN R0, #1 ; 把 R0 与 -1 进行比较
3.25 MVN取反
将每一位操作数都取反,若为有符号的数据则进行补码保存
MVN R0 0x4
其中上图中的0x4用二进制数(00000100)表示, 然后对其取反得到(11111011),可见取反后为负数,因此针对负数求其补码则为储存在R0中的值,先将负数最高位转换为正数(01111011)取反,得到(10000100),加1得到其补码,最后结果为(10000101),即结果为-5;
3.26 LSL(Logical Shift Left)左移运算
用于将寄存器的值向左移位,末尾填充0。在ARM处理器中,每个寄存器都有32位,当LSL被使用时,指令将寄存器中的二进制数值向左移动指定的位数,并用0填充未使用的右侧位数。
3.27 STP
STP是一条用于将General-Purpose Registers(通用寄存器)的值存储到内存地址的指令。STP是Store Pair的缩写,用于同时将两个寄存器的值存储到连续的内存地址中。
STP <Rt1>, <Rt2>, [<Xn|SP>, #<imm>]
Rt1和Rt2是要存储的寄存器,可以是X0-X30中的任何一个。
Xn是基础地址寄存器,可以是X0-X30中的任何一个,但不能是Rt1或Rt2。
#是一个常数值,用于指定一个偏移量,范围是-2048到2047。
例如,以下是使用STP指令将X2和X3寄存器的值存储到基础地址寄存器X18指定的内存地址偏移24字节处的代码:
STP X2, X3, [X18, #24]!
注意:结尾的!表示同时更新基础寄存器的值,即存储操作后,X18将指向下一个地址。如果不需要更新基础寄存器,可以省略!
实例解析
IMPORT |Image$RW_IRAM1$Base| //从别处导入data段的链接地址
IMPORT |Image$RW_IRAM1$Length| //从别处导入data段的长度
IMPORT |Load$RW_IRAM1$Base| //从别处导入data段的加载地址
IMPORT |Image$RW_IRAM1$ZI$Base| //从别处导入ZI段的链接地址
IMPORT |Image$RW_IRAM1$ZI$Length| //从别处导入ZI段的长度
Load$$region_name$$Base //Load address of the region.
Load$$region_name$$Length //Region length in bytes.
Load$$region_name$$Limit //Address of the byte beyond the end of the execution region.
//复制数据段
LDR R0, = |Load$RW_IRAM1$Base| //将data段的加载地址存入R0寄存器
LDR R1, = |Image$RW_IRAM1$Base| //将data段的链接地址存入R1寄存器
LDR R2, = |Image$RW_IRAM1$Length| //将data段的长度存入R2寄存器
CopyData
SUB R2, R2, #4 //每次复制4个字节的data段数据
LDR R3, [R0, R2] //把加载地址处的值取出到R3寄存器
STR R3, [R1, R2] //把取出的值从R3寄存器存入到链接地址
CMP R2, #0 //将计数和0相比较
BNE CopyData //如果不相等,跳转到CopyData标签处,相等则往下执行
//清除BSS段
LDR R0, = |Image$RW_IRAM1$ZI$Base| //将bss段的链接地址存入R1寄存器
LDR R1, = |Image$RW_IRAM1$ZI$Length| //将bss段的长度存入R2寄存器
CleanBss
SUB R1, R1, #4 //每次清除4个字节的bss段数据
MOV R3, #0 //将0存入r3寄存器
STR R3, [R0, R1] //把R3寄存器存入到链接地址
CMP R1, #0 //将计数和0相比较
BNE CleanBss //如果不相等,跳转到CleanBss标签处,相等则往下执行
IMPORT mymain //通知编译器要使用的标号在其他文件
BL mymain //跳转去执行main函数
B . //原地跳转,即处于循环状态
ENDP
ALIGN //填充字节使地址对齐
END //整个汇编文件结束