一、数据传输与运算指令
(一)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 模式设置了栈指针,以便在该模式下进行函数调用、局部变量存储等操作时能正确使用栈。