一、数据传输与运算指令
(一)MOV 指令
用于加载 12 位立即数到寄存器,或转移一个寄存器的值到另一个寄存器。
- 格式 1:
MOV{S}<c> <Rd>, #<const>
示例:mov r0, #2,将立即数 2 加载到寄存器r0。- 格式 2:
MOV{S}<c> <Rd>, <Rm>
示例:mov r1, r0,把r0寄存器的值加载到r1。
大多数指令格式为opcode rd, rn, rm,其中rd是目标寄存器,rn是第一操作数寄存器。
(二)MVN 指令
对立即数按位取反后放入目标寄存器。
示例:mvn r3, #1,将 1 按位取反后存入 r3,结果为 0xFFFFFFFE。若 MOV 指令无法操作寄存器,会替换为 MVN 指令。
(三)ADD 指令
实现加法运算,常用两种格式:

ADD{S}<c> <Rd>, <Rn>, #<const>ADD{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
(四)SUB 指令
进行减法运算,格式如下:
SUB{S}<c> <Rd>, <Rn>, #<const>SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
(五)立即数
立即数相当于汇编中的常量,前加 #。判断一个数是否为立即数有三个条件(以 12 位立即数为例):
1、如果某个数的数值范围是0~0xFF之间,那么这个数一定是立即数;
2、把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位;
3、这个数的二进制序列凑够8位之后的的右边必须为偶数个连续的 0
(六)LDR 寄存器加载指令
用于从内存读取数据到寄存器,有多种格式:
LDR{<c>}{<q>} <Rt>, <label>
示例:ldr r0, =0x2FAB4,多用于从 RAM 将 32 位字数据传送到目的寄存器。LDR<c> <Rt>, [<Rn>{, #+/-<imm12>}]
示例:LDR R0,[R1,#4],将内存地址为R1 + 4的字数据读入R0,#4作为 12 位立即数可省略。LDR<c> <Rt>, [<Rn>], #+/-<imm12>
示例:ldr r0, [r1], #8,将内存地址R1的字数据读入r0,之后r1 + 8。LDR<c> <Rt>, [<Rn>, #+/-<imm12>]!
示例:LDR R0,[R1,#8] !,将存储器地址为R1 + 8的字数据读入R0,并将新地址R1 + 8写入R1。
(七)STR 指令
用于将寄存器数据存入内存地址。示例:str r1, [r0, #4],把 r1 的内容写入 r0 + 4 的地址。
(八)BIC 指令
实现指定位清零,格式:BIC{S}<c> <Rd>, <Rn>, #<const>,将 rn 中 const 为 1 的比特清零,结果放入 rd。示例:
eg:mov r0, 0xFFFFFFFF
bic r0, r0, #3;立即数哪些位为1就清掉
r0数据:0xFFFFFFFC
bic r0, r0, #(0x1F << 8);连续5位清0
操作后 r0 数据会相应清零指定比特位。
(九)ORR 指令
实现指定位置 1,格式:ORR{S}<c> <Rd>, <Rn>, #<const>。示例:
eg:mov r0, #0
orr r0, r0, #5;立即数带1的比特位 置1
orr r0, r0, #(1<< 9);第9位置1
可将 r0 中立即数带 1 的比特位置 1,如第 9 位。
(十){s} 后缀
加 s 后缀的指令会影响 CPSR 寄存器(当前程序状态寄存器)。CPSR 的 N、Z、C、V 位含义如下:
-
N:结果为有符号二进制补码时,若为负数则N=1,否则N=0。 -
Z:结果为 0 则Z=1,否则Z=0。 -
C:无符号数最高有效位向更高位进位时C=1;减法中最高有效位从更高位借位时C=0。 -
V:有符号数操作时,若两个最高有效位均为 0 的数相加结果最高有效位为 1,或两个最高有效位均为 1 的数相加结果最高有效位为 0,则V=1,否则V=0。
示例:mov r0, #0xFFFFFFFF
adds r2, r0, #0
adds 影响 CPSR,N 置 1(结果为负)。
(十一)<c> 执行条件
可自选增加执行条件,示例:movcs r0, #100,表示只有 C 位置位时,才把 100 加载入 r0,方便实现指令的有条件执行。
(十二)CMP 指令
用于比较两个寄存器的值或一个寄存器与立即数的值,原理是对两个数求差,看结果是否为 0,会无条件修改 N、V、C、Z 位。
eg:从 r0、r1 代表的两个有符号数中找较大值放入 r2 寄存器(ge表示将大于等于的放到r0<great>;lt表示小的<last>)

2、找出三个数的最大值
;三位数找最大
;cmp r0, r1
;blt less
;great
;mov r2, r0
;b cmp1
;less
;mov r3, r1
;cmp1
;cmp r3, r2
;bge finished
;mov r3, r2
二、跳转与循环指令
(一)跳转指令 b
类似 C 语言的 goto 语句,能实现无条件跳转,跳转时需指定 lable。本质是往 PC(程序计数器)里填跳转地址,b loop 相当于 ldr pc, =loop。
练习:实现从 0 加到 100 的和,可通过 while 循环或 do while 循环

(二)bl 指令
用于函数调用,与 b 指令的区别是 bl 会在 lr(链接寄存器)中保存返回地址,以便函数调用完毕后回到调用处的下一行指令执行。
(三)bx 指令
示例:bx lr,将 lr 中的地址装入 pc,实现函数返回。

三、栈操作
(一)引入
函数调用时,r0、r1 等寄存器值可能被修改,函数嵌套调用时 lr 值也会被修改,为解决此问题,需在函数调用前保护现场,调用完毕后恢复现场,这就需要栈结构。
(二)栈的操作方法
ARM 体系采用满减栈(先让栈指针自减,再写入数据)。操作前需指定栈底位置,如设置栈底指针寄存器:ldr sp, =0x40001000,使用 2440 内部从 0x40000000 开始的 4k RAM(地址范围 [0x40000000~0x40000FFF])。
(三)栈操作指令
- 入栈保护指令
stmfd(STMDB) :格式STMFD<c> <Rn>{!}, <registers>,Rn为栈底指针寄存器,<registers>为需入栈保护的寄存器,!表示入栈后sp自动自减。
示例:
stmfd sp!, {r0, r1, r2, r3-r12, lr}。
- 出栈恢复指令
ldmfd(LDM/LDMIA) :格式LDMFD<c> <Rn>{!}, <registers>,Rn为栈底指针寄存器,<registers>为需恢复的寄存器,!表示出栈后sp自动自增。
示例:
ldmfd sp!, {r0, r1, r2, r3-r12, lr}。
练习:

四、汇编与 C 相互调用函数
(一)汇编调用 C 中函数
- 用
import声明函数,并用bl指令跳转到 C 函数。 - 调用者(主调方)负责保护和恢复现场。
- 传参规则:
- 参数个数≤4 个,用
r0~r3寄存器传参。 - 参数个数>4 个,第 5 个及以后参数通过栈传参。
- C 函数返回值通过
r0寄存器返回
- 参数个数≤4 个,用
eg: c函数

arm函数

(二)C 调用汇编中的函数
-
extern 声明 arm函数 eg: int _asm_min(int a,intb)
-
主函数调用 arm _asm_min(10,20) 函数
-
Arm 函数 _asm_min 在函数之前需要声明 调出函数 export _asm_min
-
参数返回也是在R0寄存器中
arm函数

c函数

五、模式切换
(一)、CPS 指令
CPS 指令可以直接修改处理器模式,格式为 CPS #<mode>,比如想要切换到系统模式,可以写 CPS #0x1F(系统模式对应的编码)。不过需要注意,Keil 编译器不支持该指令,在实际使用 Keil 进行开发时,不能采用这种方式。
(二)、MRS / MSR 指令
通过 MRS(Move to Register from Special register)读取特殊寄存器的值到通用寄存器,再通过 MSR(Move to Special register from Register)将修改后的值写回特殊寄存器,从而实现模式切换。
示例代码(切换到 User 模式并设置栈指针):
; 读取 CPSR 到 R0
MRS R0, CPSR
; 清除 CPSR 的低 5 位(模式位)
BIC R0, R0, #(0x1F)
; 设置为 User 模式(User 模式对应的编码是 0x10,即二进制 10000)
ORR R0, R0, #0x10
; 将修改后的值写回 CPSR 的控制域(c 表示控制域)
MSR CPSR_c, R0
; 设置栈指针,这里假设栈底为 0x40001000
LDR SP, =0x40001000

这样就完成了从当前模式切换到 User 模式,并且为 User 模式设置了栈指针,以便在该模式下进行函数调用、局部变量存储等操作时能正确使用栈。