指令格式说明
汇编指令的总体构成如下:
opcode{cond}{s} Rd, Rn, oprand2
每个替代符号的含义:
opcode
指令名称, 必须有, 否则无法构成一条指令
cond
条件码, 可选, 使用时跟指令名称和在一起写, 如指令mov
和条件码eq
, 应当写成 moveq
只要指令格式中有{cond}
就代表这条可以使用条件码
s
状态码, 可选, 跟条件码一样, 要跟指令合并在一起写
Rd
destination register, 目标寄存器, 指令的最终运算结果要保存到的寄存器
Rn
第N号寄存器, 第一个操作寄存器, 就是说在这个位置必须是某个寄存器
oprand2
操作数, 第二个操作数, 就是说, 在这个位置, 可以是一个寄存器, 也可以是一个立即数, 也可以是一个表达式
赋值指令
搬移指令mov
:
语法格式:mov{cond}{s} Rd, oprand2
功能: 将操作数 oprand2 保存到 Rd 寄存器中
例:
mov R0 #0xFF
mov R0 R1
取反搬移指令mvn
:
语法格式:mvn{cond}{s} Rd, oprand2
功能: 将操作数 oprand2 取反后保存到 Rd 寄存器中
例:
mvn R0 #0xFF
mvn R0 R1
伪指令 ldr
指令ldr
:
伪指令有很多, ldr只是其中一例, 这里列出ldr就是为了呈现伪指令的样子。
语法格式: ldr Rd, =num
功能:将操作数保存到Rd中, num可以是任意数, 不用关心是否为立即数
例:
ldr r0, =0x12345678
算数运算指令
加法指令add
(忽略状态位):
语法格式: add{cond}{s} Rd, Rn, oprand2
功能: 将两个操作数相加, 结果保存到Rd中, 忽略状态位
例:
add r0, r1, #18 @ 不影响状态位
adds r0, r1, #18 @ 影响状态位
addlt r0, r1, #18 @ 满足条件时才相加
状态加法指令adc
(操作状态位):
语法格式: adc{cond}{s} Rd, Rn, oprand2
功能: 将两个操作数相加, 结果保存到Rd中, 并加上状态C位
例:
adc r0, r1, #18 @ 不影响状态位
adcs r0, r1, #18 @ 影响状态位
adclt r0, r1, #18 @ 满足条件时才相加
减法指令sub
(忽略状态位):
语法格式: sub{cond}{s} Rd, Rn, opand2
功能:使用Rn减去opand2, 结果保存到Rd中, 忽略状态位
例:
sub r0, r1, #18 @ 不影响状态位
subs r0, r1, #18 @ 影响状态位
sublt r0, r1, #18
状态减法指令sbc
(操作状态位):
语法格式: sbc{cond}{s} Rd, Rn, opand2
功能: 使用Rn减去opand2, 结果保存到Rd中, 并减去!C
例:
sbc r0, r1, #18 @ 不影响状态位
sbcs r0, r1, #18 @ 影响状态位
sbclt r0, r1, #18
乘法指令mul
:
语法格式: mul{cond}{s} Rd, Rn, oprand2
功能: 两数相乘, 结果保存到Rd中
例:
mul r0, r1, #18
mul r0, r1, r2
比较运算指令
比较指令cmp
:
语法格式: cmp Rn, oprand2
功能: 影响NZCV位, 产生比较结果
注意: cmp指令没有目标寄存器, 它只影响状态寄存器
cmp本质上是 Rn - operand2
例:
cmp r0, #5
cmp r0, r1
load/store单寄存器内存读写指令
读内存指令ldr
ldrh
ldrb
:
助记: ld ===> load 读取
h ===> half 半字
b ===> byte 字节
语法格式:
普通读写:
ldr{cond} Rn, [Rm] 从地址Rm对应内存读取1个字的内容
ldrh{cond} Rn, [Rm] 从地址Rm对应内存读取半字内容
ldrb{cond} Rn, [Rm] 从地址Rm对应内存读取1个字节的内容
内存地址索引方式读写:
1. 前置索引
ldr{cond} Rn, [Rm, #4] 从地址Rm+4对应内存读取1个字的内容
ldrh{cond} Rn, [Rm, #4] 从地址Rm+4对应内存读取半字内容到
ldrb{cond} Rn, [Rm, #4] 从地址Rm+4对应内存读取1个字节的内容
2. 后置索引
ldr{cond} Rn, [Rm], #4 从地址Rm对应内存读取1个字的内容
并将Rm的地址修改为Rm + 4
ldrh{cond} Rn, [Rm], #4 从地址Rm+4对应内存读取半字内容到
并将Rm的地址修改为Rm + 4
ldrb{cond} Rn, [Rm], #4 从地址Rm+4对应内存读取1个字节的内容
并将Rm的地址修改为Rm + 4
3. 自索引
ldr{cond} Rn, [Rm, #4]! 从地址Rm+4对应内存读取1个字的内容
同时将Rm的地址修改为Rm + 4
ldrh{cond} Rn, [Rm, #4]! 从地址Rm+4对应内存读取半字内容到
同时将Rm的地址修改为Rm + 4
ldrb{cond} Rn, [Rm, #4]! 从地址Rm+4对应内存读取1个字节的内容
同时将Rm的地址修改为Rm + 4
功能: 将Rm的值读取到Rn寄存器中
注意: Rm 相当于指针, [Rm] 相当于取出指针所指向地址的值
例:
ldr r0, =0xFFFFFFF5 @将地址保存到r0寄存器中
ldr r0, [r0] @取出r0所指向的值, 保存到r0中
ldr r0, =0x12345678 @读取结果:0x12345678
ldrh r0, =0x12345678 @读取结果:0x5678
ldrb r0, =0x12345678 @读取结果:0x78
@ 前置索引
ldr r0, [r0, #2] @从r0+2对应的内存地址中读取内容
@ 后置索引
ldr r0, [r0], #2 @从r0中读取内容, r0修改为r0+2
@ 自索引
ldr r0, [r0, #2]! @从r0+2中读取内容, r0修改为r0+2
写内存指令str
strh
strb
:
助记: st ===> store 保存
h ===> half 半字
b ===> byte 字节
语法格式:
普通读写
str{cond} Rn, [Rm] 保存1个字的内容到Rm对应的内存中
strh{cond} Rn, [Rm] 保存半字内容到Rm对应的内存中
strb{cond} Rn, [Rm] 保存1个字节的内容到Rm对应的内存中
内存地址索引方式读写
1. 前置索引
str{cond} Rn, [Rm, #4] 保存1个字的内容到Rm+4对应的内存中
strh{cond} Rn, [Rm, #4] 保存半字内容到Rm+4对应的内存中
strb{cond} Rn, [Rm, #4] 保存1个字节的内容到Rm+4对应的内存中容
2. 后置索引
str{cond} Rn, [Rm], #4 保存1个字的内容到Rm对应的内存中
并将Rm的地址修改为Rm + 4
strh{cond} Rn, [Rm], #4 保存半字内容到Rm对应的内存中
并将Rm的地址修改为Rm + 4
strb{cond} Rn, [Rm], #4 保存1个字节的内容到Rm对应的内存中容
并将Rm的地址修改为Rm + 4
3. 自索引
str{cond} Rn, [Rm, #4]! 保存1个字的内容到Rm+4对应的内存中
同时将Rm的地址修改为Rm + 4
strh{cond} Rn, [Rm, #4]! 保存半字内容到Rm+4对应的内存中
同时将Rm的地址修改为Rm + 4
strb{cond} Rn, [Rm, #4]! 保存1个字节的内容到Rm+4对应的内存中容
同时将Rm的地址修改为Rm + 4
功能: 将Rm的值保存到Rn寄存器中
注意: Rm 相当于指针, [Rm] 相当于取出指针所指向地址的值
例:
ldr r0, =0x40000300 @将地址保存到r0寄存器中
ldr r1, #0x12345678
str r1, [r0] @将寄存器r1的值保存到r0对应的内存中
strb r1, [r0] @保存结果 78
strh r1, [r0] @保存结果 5678
@ 前置索引
str r1, [r0, #4] @ 保存r1到r0+4对应的内存中
@ 后置索引
str r1, [r0], #4 @ 保存r1到r0中, 修改r0为r0+4
@ 自索引
str r1, [r0, #4]! @ 保存r1到r0+4中,修改r0为r0+4
load/store多寄存器内存读写指令
多寄存器写到连续内存指令stm
语法格式: stm{cond} Rn, {寄存器列表}
助记: m->multiple
功能: 将寄存器列表中的内容保存到连续的内存空间中
例:
stm r0, {r1-r3}
stm r0, {r1, r5-r7, r9}
stm r0, {r1, r3, r5}
连续内存读到多寄存器指令ldm
语法格式: ldm{cond} Rn, {寄存器列表}
助记: m->multiple
功能: 将Rn中地址所对应的连续空间的内容保存到寄存器列表中
例:
ldm r0, {r1-r5}
ldm r0, {r1, r5-r7, r9}
移位操作指令
逻辑左移指令(最右补0)lsl
语法格式: lsl{cond}{s} Rd, Rn, oprand2
功能: 逻辑左移, 无符号左移, 高位移出, 低位补零
例:
lsl r0, r1, #3
逻辑右移指令(最左补0)lsr
语法格式: lsr{cond}{s} Rd, Rn, oprand2
功能: 逻辑右移, 无符号数的右移, 低位移出, 高位补零
例:
lsr r0, r1, #3
算数右移指令(最左补符号位)asr
语法格式: asr{cond}{s} Rd, Rn, oprand2
功能: 算数右移, 有符号数的右移, 低位移出, 高位补符号位
例:
asr r0, r1, #3
循环右移指令ror
语法格式: ror{cond}{s} Rd, Rn, oprand2
功能: 循环右移, 低位移出, 补到高位
例:
ror r0, r1, #3
位运算指令
按位与指令and
语法格式: and{cond}{s} Rd, Rn, oprand2
功能: 按位与, 与0清0, 与1不变
例:
and r0, r1, #FFFFFFEF
按位或指令orr
语法格式: orr{cond}{s} Rd, Rn, oprand2
功能: 按位或, 或1置1, 或0不变
例:
orr r0, r1, #(1 << 3)
按位抑或指令eor
语法格式: eor{cond}{s} Rd, Rn, oprand2
功能: 按位异或, 异或1取反, 异或0不变
例:
orr r0, r1, #AAAAAAAA
按位取反指令~
语法格式: ~oprand2
功能: 按位取反
例:
mov r0, #~0x30
按位清零指令bic
语法格式: bic{cond}{s} Rd, Rn, oprand2
功能: 按位清零, Rn遇到对应oprand2中含有1的位都会被清零
例:
bic r0, r1, #3
跳转指令
无返回跳转指令b
语法格式: b{cond} 标签
功能: 直接跳转, 不保存返回地址到lr寄存器
例:
b stop
有返回跳转指令bl
语法格式: bl{cond} 标签
功能: 跳转, 并保存返回地址到lr寄存器中
例:
bl func_add
func_add:
@ do add
mov pc lr
特殊寄存器操作指令
读取状态寄存器指令mrs
语法格式: mrs cpsr oprand2
功能: 读取 cpsr寄存器中的值到寄存器oprand2中
例:
mrs cpsr r0
写入状态寄存器指令msr
语法格式: msr cpsr oprand2
功能: 将oprand2的值写入到cpsr寄存器中
例:
msr cpsr r0
栈操作指令
栈的分类
- 空栈:当前栈指针指向的空间, 认为没有有效数据, 压栈时覆盖掉此空间
- 满栈:当前栈指针指向的空间, 认为存在有效数据, 压栈时先移动栈指针,再压栈
- 增栈:压栈时,栈顶指针往高地址移动
- 减栈:压栈时,栈顶指针向低地址移动
栈空间的4中操作方式
- 满增栈:
stmfa
ldmfa
full ascending - 满减栈:
stmfd
ldmfd
full descending - 空增栈:
stmea
ldmea
empty ascending - 空减栈:
stmed
ldmed
empty descending
压栈指令stmfd
stmfa
stmed
stmea
语法格式: stmfd sp!, {寄存器列表} 满增式压栈
语法格式: stmfa sp!, {寄存器列表} 满减式压栈
语法格式: stmed sp!, {寄存器列表} 空增式压栈
语法格式: stmea sp!, {寄存器列表} 空减式压栈
功能:将寄存器列表中的数据压入栈中
例:
stmfd sp!, {r0-r5} @认为栈顶满,且为低地址, 跳过栈顶, 向高地址依次写入
stmfa sp!, {r0-r5} @认为栈顶满, 且为高地址, 跳过栈顶, 向低地址依次写入
stmed sp!, {r0-r5} @认为栈顶空, 且为低地址, 写入栈顶, 并依次向高地址写入
stmea sp!, {r0-r5} @认为栈顶空, 且为高地址, 写入栈顶, 并依次向低地址写入
出栈指令ldmfd
ldmfa
ldmed
ldmea
语法格式: ldmfd sp!, {寄存器列表} 满增式出栈
语法格式: ldmfa sp!, {寄存器列表} 满减式出栈
语法格式: ldmed sp!, {寄存器列表} 空增式出栈
语法格式: ldmea sp!, {寄存器列表} 空减式出栈
功能: 出栈数据存入到寄存器列表中
例:
ldmfd sp!, {r1, r5} @认为栈顶满,由低向高压栈而来, 所以返回栈顶,向低地址依次出栈
ldmfa sp!, {r1, r5} @认为栈顶满, 有高向低压栈而来, 所以返回栈顶,向高地址依次出栈
ldmed sp!, {r1, r5} @认为栈顶空, 由低向高压栈而来, 跳过栈顶, 向低地址依次出栈
ldmea sp!, {r1, r5} @认为栈顶空, 有高向低压栈而来, 跳过栈顶, 向高地址依次出栈