一. 汇编指令集
1. CISC
complex instruction set computer : 复杂指令集
CISC 体系的设计理念是用最少的指令来完成任务(譬如计算乘法只需要一条 MUL 指令即可),因此 CISC 的CPU 本身设计复杂,工艺复杂,但好处是编译器好设计。CISC 出现较早,至今 Intel 还一直采用 CISC 设计
2. RISC
reduced instruction set computer : 精简指令集
RISC 的设计理念是让软件来完成具体的任务,CPU 本身仅提供基本功能指令集。因此 RISC CPU 的指令集中只有很少的指令,这种设计相对于 CISC,CPU 的设计和工艺简单了,但是编译器的设计变难了。
一般典型 CISC CPU 指令在 300 条左右,ARM CPU 常用指令 30 条左右
二. 统一编址 & 独立编址
1. 统一编址
IO 与内存统一编址,如 ARM (RISC)
CPU 访问外设如同访问内存,将外设寄存器当做一个内存地址来读写,优势是 IO 当做内存来访问,编程简单;缺点是 IO 也需要占用一定的 CPU 地址空间,而 CPU 的地址空间是有限资源
2. 独立编址
IO 与内存分开编址,如 x86 (CISC)
使用专门的 CPU 指令来访问某种特定的外设,优势是不占用 CPU 地址空间;缺点是CPU 设计变复杂了
三. 一般汇编指令格式
需要说明的是,不同指令有不同的指令格式,编码格式以及前后缀等,这里把常用的大概总结下,具体的可以看:
ARM Compiler toolchain version 5.0 Assmbler Reference
ARM Architecture Reference Manual
ARM Instruction Set
1. 指令编码格式
在这里插入图片描述
2. 指令语法前/后缀
条件指令后缀
条件码 条件码助记符 含义 CPSR中条件标志位值
0000 eq 相等 Z=1
0001 ne 不相等 Z=0
0010 cs/hs 无符号数大于/等于 C=1
0011 cc/lo 无符号数小于 C=0
0100 mi 负数 N=1
0101 pl 非负数 N=0
0110 vs 上溢出 V=1
0111 vc 没有上溢出 V=0
1000 hi 无符号数大于 C=1且Z=0
1001 ls 无符号数小于/等于 C=0且Z=1
1010 ge 带符号数大于/等于 N=1且V=1 或 N=0且V=0
1011 lt 带符号数小于 N=1且V=0 或 N=0且V=1
1100 gt 带符号数大于 Z=0且N=V
1101 le 带符号数小于/等于 Z=1或N!=V
1110 al 无条件执行
1111 nv 该指令从不执行
并行指令后缀
并行指令前/后缀 含义
B(BYTE) 操作长度变为8位
H(Half word) 操作长度变为16位
S(Signed) 操作数变为有符号
Q(Signed Saturating) 操作结果做饱和处理
SH
U(Unsigned) 操作数变为无符号
UQ
UH
数据传输指令后缀
批量传输地址模式 含义
数据块传输指令后缀
存储( stm** r0 )
加载( ldm** r0 ) IA Increment After
IB Increment Before
DA Decrement After
DB Decrement Before
堆栈操作指令后缀
压栈( stm** sp )
出栈( ldr** sp ) FD Full Descending
ED Empty Descending
FA Full Ascending
EA Empty Ascending
四. ARM 汇编特点
1. 载入/存储(load/store)架构
ARM属于RISC构架,只能通过载入/存储指令访问寄存器,数据处理指令只对寄存器内的内容进行操作。(ps.相对的,CISC架构允许数据处理指令直接对内存操作)。
自 ARMv6 之后,ARM 处理器在硬件上支持了对非对齐内存进行操作,之前的处理器,如 ARMv4 系列的 ARM7TDMI 访问非对齐的内存需要软件上进行合成处理。
不过对非对齐的内存操作只限于以下指令:
LDRB/LDRSB/STRB
LDRH/LDRSH/STRH
LDR/STR
而不支持:
LDM/STM
LDRD/STRD
对未对齐内存操作的只限于 Normal 类型的内存,并且要通过设置 system control 协处理器的 SCTLR.A 位来使能该功能,如果没有使能就进行该种操作将会产生 alignment fault。
ldr (load register) 将内存内容载入通用寄存器
str (store register) 将寄存器内容写入内存
2. 8种寻址方式
寄存器寻址:
mov r1, r2
add r1, r1, r2
立即数寻址:
mov r1, #0FF
add r1, r1, #1
立即数寻址的每个立即数都遵守一个规则:每一个立即数由一个8位的常数循环右移偶数位得到。公式表达为:立即数 = 8 位常数循环右移 (2 * 4位二进制数) 即:
$$
immediate = const_8 ROR (2 * bin_4) 。
$$
寄存器移位寻址:
mov r1, r2, LSL #2
mov r1, r2, LSL r1
寄存器间接寻址:
ldr r1, [r2] => 将 r2 作为地址取其指向的内存数据
add r1, r1, [r2]
基址变址寻址:
ldr r1, [r2, #4] => 将 r2 内的值自增 4 并将其所指向的内存值加载至 r1 中
ldr r1, [r2, #-4]!
ldr r1, [r2, r3]
ldr r1, [r2], #4 => 将 r2 所指向内存的值存储到 r1 中,并将 r2 自增 4
多寄存器寻址:
ldmia r1!, {r2 - r7} => 将 r1指向的地址数据存储到 r2 至 r7 的寄存器中,并在每次存储后将 r1 的值自加 4,并且因为!,最后将操作后的 r1 存储回 r1 中.
堆栈寻址相对寻址:
ARM设计的内存栈模型有2×2=4种
按照栈在内存增长的方向分为递增栈和递减栈:
递增(Increase)堆栈:向堆栈写入数据时,堆栈由低地址向高地址生长。
递减(Descend)堆栈:向堆栈写入数据时,堆栈由高地址向低地址生长。
根据堆栈指针SP指向的位置,又可以把堆栈分为满堆栈和空堆栈两种。
满堆栈(Full Stack):SP始终指向栈顶元素,压栈的时候先移动SP,再将数据放入SP指向的地址。
空堆栈(Empty Stack):SP始终指向下一个将要放入元素的位置,压栈时先将数据放入SP指向的地址,再移动SP
最后,可以得到4种基本的堆栈类型:
满增栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生长。
满减栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生长。常用这种
空增栈(EA):堆栈指针指向下一个将要压入数据的地址,且由低地址向高地址生长。
空减栈(ED):堆栈指针指向下一个将要压入数据的地址,且由高地址向低地址生长。
stmfd sp!, {r2-r7, lr} => 压栈
ldrfd sp!, {r2-r7, lr} => 出栈
相对寻址:
beq lable
……
lable:
……
3. 指令后缀
详见并行指令前后缀表格及条件执行后缀表格
cmp r1, r2
moveq r0, #0FF => 只有 r1 == r2 才运行
4. 多级指令流水线
ARM11 有 8 级流水线
pc 指向正在取指的指令的地址(一般来说就是正在执行指令的后两条)
五. 常用 ARM 汇编指令
1. 数据处理指令
数据传输指令
mov, mvn
算术指令
add, sub , rsb, adc, sbc, rsc
逻辑指令
and, orr, eor, bic
比较指令
cmp, cmn, tst, teq
乘法指令
mul, mla, umull, umlal, smull, smlal
前导零计数
clz : 统计寄存器中第一个 1 前的 0 的个数
(MOV, MVN,
CMP, CMN, TEQ, TST,
AND, EOR, ORR,
SUB, RSB, ADD, ADC, SBC, RSC,
BIC)
指令编码格式
31 - - 28 27 - - 25 24 - - 21 20 19 - - 16 15 - - 12 11 - - 0
cond 0 0 I opcode S Rn Rd shift_oprand
cond [31-28] 4-bit 指令执行的条件编码
opcode [24-21] 4-bit 指令操作符编码
0000 = AND - Rd:= Op1 AND Op2(与)
0001 = EOR - Rd:= Op1 EOR Op2(异或)
异或: Op1 和 Op2 对应的位不相同,结果为 1,相同结果为 0
0010 = SUB - Rd:= Op1 - Op2
0011 = RSB - Rd:= Op2 - Op1
0100 = ADD - Rd:= Op1 + Op2
0101 = ADC - Rd:= Op1 + Op2 + C
0110 = SBC - Rd:= Op1 - Op2 + C
0111 = RSC - Rd:= Op2 - Op1 + C
1000 = TST - set condition codes on Op1 AND Op2
测试 Op1(Rn) 对应 Op2 为 1 的位是否为 1,都不为 1,则最终结果为 0,则会设置 CPSR.Z 为 1
1001 = TEQ - set condition codes on Op1 EOR Op2
测试 Op1(Rn) 和 Op2 是否相等,通过异或的方式,相等最终结果为 0,则会设置 CPSR.Z 为 1
1010 = CMP - set condition codes on Op1 - Op2
通过向减的方式测试 Op1(Rn) 和 Op2 是否相等,相等最终结果为 0,则会设置 CPSR.Z 为 1
Op1(Rn) < Op2 则会产生借位,则会设置 CPSR.C 为 0
1011 = CMN - set condition codes on Op1 + Op2
通过向加的方式测试 Op1(Rn) 和 Op2 是否为相反数
1100 = ORR - Rd:= Op1 OR Op2
1101 = MOV - Rd:= Op2
1110 = BIC - Rd:= Op1 AND NOT Op2
位清除指令,Op1(Rn) 按位与上 Op2 的非
1111 = MVN - Rd:= NOT Op2
S [20] 1-bit 决定指令的操作是否影响CPSR的值
Rn [19-16] 4-bit 包含第1个操作数的寄存器编码
Rd [15-12] 4-bit 目标寄存器编码
shifter_operand [11-0] 12-bit 表示第2个操作数,由25位 ‘I’ 定义
shift_operand 可以是移位指令( 'I' = 0):如 " MOV R0, R1, LSL#2 " , " MOV R0, R1, LSL R2",表示R1左移两位后赋值给R0。
11 - - 4 3 - - 0
11 - - 7 :
5_bits 偏移量 6 - - 5 : 偏移类型
00 = 逻辑左移(LSL)
01 = 逻辑右移(LSR)
10 = 算术右移(ASR)
11 = 循环右移(ROR) 4 : 0 Rm
11 - - 8 :
Rs 7 : 0 6 - - 5 :
偏移类型 4 : 1
shift
shift_operand 可以表示立即数( ‘I’ = 1 ):immediate = const_8 ROR (2 * bin_4)
11 - - 8 7 - - 0
Rotate:bin_4 Imm:const_8
PS:一般对于立即数的传送会用 MOVT 和 MOVW
使用 Rn(19 - 16), shift_oprand(11 - 0) 用作存放立即数
e30d3fda movw r3, #57306 ; 0xdfda
e34a3dfa movt r3, #44538 ; 0xadfa
指令语法格式
MOV, MVN (single operand instructions.)
{cond}{S} Rd,
CMP, CMN, TEQ, TST (instructions which do not produce a result.)
{cond} Rn,
AND, EOR, SUB, RSB, ADD, ADC, SBC, RSC, ORR, BIC
{cond}{S} Rd, Rn,
(MUL, MLA)
指令编码格式
31 - - 28 27 - - 22 21 20 19 - - 16 15 - - 12 11 - - 8 7 - - 4 3 - - 0
cond 0 A S Rd Rn Rs 1 0 0 1 Rm
cond [31-28] 4-bit 指令执行的条件编码
A [21] 1-bit Accumulate
0 = multiple only
Rd = Rm * Rs
1 = multiple and accumulate
Rd = Rm * Rs + Rn
指令语法格式
MUL{cond}{S} Rd, Rm, Rs
MLA{cond}{S} Rd, Rm, Rs, Rn
@其中
MUL A=0
MLA A=1
(UMULL, UMLAL, SMULL, SMLAL)
指令编码格式
31 - - 28 27 - - 23 22 21 20 19 - - 16 15 - - 12 11 - - 8 7 - - 4 3 - - 0
cond 0 0 0 0 1 U A S RdHi RdLo Rs 1 0 0 1 Rm
U [22] 1-bit Unsigned
0 = unsigned
1 = signed
A [21] 1-bit Accumulate
0 = multiple only
RdHi, RdLo := Rm * Rs.
1 = multiple and accumulate
RdHi, RdLo := Rm * Rs + RdHi, RdLo
指令语法格式
UMULL{cond}{S} RdLo, RdHi, Rm, Rs
UMLAL{cond}{S} RdLo, RdHi, Rm, Rs
SMULL{cond}{S} RdLo, RdHi, Rm, Rs
SMLAL{cond}{S} RdLo, RdHi, Rm, Rs
2. 移位指令
LSL, LSR, ASR, ROR, RRX
指令语法格式
LSL, LSR, ASR, ROR
{s} , ,
{s} , , <#imm5>
RRX
RRX{S} , ;每次只移一位,将该指令前一条指令的 CPSR.C 移至 Rd 的首位
LSL:逻辑左移(Logical Shift Left),寄存器中字的低端空出的位补 0。
LSR:逻辑右移(Logical Shift Right),寄存器中字的高端空出的位补 0。
ASR:算术右移(Arithmetic Shift Right),移位过程中符号位不变,即如果源操作数是正数,则字的高端空出的位补 0,否则补 1。
ROR:循环右移(Rotate Right),由字的低端移出的位填入字的高端空出的位。
RRX:带扩展的循环右移(Rotate Right extended),操作数右移一位,高端空出的位用进位标志 C 的值来填充,低端移出的位填入进位标志位。
3. cpsr 访问指令
msr, mrs
指令编码格式
31 - - 28 27 - - 23 22 21 - - 16 15 - - 12 11 - - 8 8 - - 4 3 - - 0
MRS cond 0 0 0 1 0 Ps 0 0 1 1 1 1 Rd 0
MSR 0 0 0 1 0 Pd 1 0 1 0 0 1 1 1 1 1 0 Rm
0 0 1 1 0 Rotate Imm
Px [22] 1-bit Destination PSR
0 = CPSR
1 = SPSR_
指令语法格式
MRS{cond} Rd,
MSR{cond} , Rm
MSR{cond} , Rm
MSR{cond} , <#expression>
@其中
MRS 将 PSR 的数据读入通用寄存器
MSR 将通用寄存器或者立即数存入 PSR
: 可以是 CPSR, CPSR_all, SPSR, SPSR_all
: 可以是 CPSR_flg, SPSR_flg
而有的时候也会使用 Control, eXtension, Status, Flags,中大写的字母来分别表示不同的位
如:Linux 中断处理中 vector_irq 中的 msr spsr_cxsf, r0 语句,就明确的表示需要更改 SPSR 中的所有位
<#expression> : 遵循立即数移位规则
Note:
@在用户模式下(Usr_Mode)
MSR CPSR_all, Rm ; CPSR[31:28] <- Rm[31:28]
MSR CPSR_flg, Rm ; CPSR[31:28] <- Rm[31:28]
MSR CPSR_flg, #0xA0000000 ; CPSR[31:28] <- 0xA
;(set N,C; clear Z,V)
MRS Rd, CPSR ; Rd[31:0] <- CPSR[31:0]
@在特权模式下(privileged modes)下
MSR CPSR_all, Rm ; CPSR[31:0] <- Rm[31:0]
MSR CPSR_flg, Rm ; CPSR[31:28] <- Rm[31:28]
MSR CPSR_flg, #0x50000000 ; CPSR[31:28] <- 0x5
;(set Z,V; clear N,C)
MRS Rd, CPSR ; Rd[31:0] <- CPSR[31:0]
MSR SPSR_all, Rm ;SPSR_[31:0]<- Rm[31:0]
MSR SPSR_flg, Rm ; SPSR_[31:28] <- Rm[31:28]
MSR SPSR_flg, #0xC0000000 ; SPSR_[31:28] <- 0xC
;(set N,Z; clear C,V)
MRS Rd, SPSR ; Rd[31:0] <- SPSR_[31:0]
4. 跳转(分支)指令
b
bl
bx
(b, bl)
指令编码格式
31 - - 28 27 - - 25 24 23 - - 0
cond 1 0 1 L offset
L [24] 1-bit Link bit
0 = Branch
1 = Branch with Link
Branch with Link (BL) 先将当前 PC 值写入 R14 (LR) 中,用于返回。
返回可以使用 MOV PC, R14
offset [23-0] 24-bits
跳转偏移量:该指令包含 24-bits 的偏移量,它会将偏移量左移 2 位,然后符号扩展至 32 位,最终于 PC 至相加得到最终的跳转位置(左移两位是因为指令是 4 字节对齐的,低两位为 0;符号扩展则为了可以前后跳转。)。最终可以计算处该指令的寻址范围为:+/- 2^24+2-1^ = +/- 32Mbytes
由于 ARM 的指令流水线,PC 指针的值会比正在执行的地址多 2 words (8 bytes),所以偏移量会比当前地址和目标地址的差值小 2 。
Branches beyond +/- 32Mbytes must use an offset or absolute destination which has been previously loaded into a register. In this case the PC should be manually saved in R14 if a Branch with Link type operation is required.
指令语法格式
B{L}{cond}
转移指令在大部分情况下会配合条件指令执行后缀使用,例:
here:
BAL here ; assembles to 0xEAFFFFFE (note effect of
; PC offset).
B there ; Always condition used as default.
CMP R1,#0 ; Compare R1 with zero and branch to fred
; if R1 was zero, otherwise continue
BEQ fred ; continue to next instruction.
BL sub+ROM ; Call subroutine at computed address.
ADDS R1,#1 ; Add 1 to register 1, setting CPSR flags
; on the result then call subroutine if
BLCC sub ; the C flag is clear, which will be the
; case unless R1 held 0xFFFFFFFF.
(bx)
31 - - 28 27 - - 4 3 - - 0
cond 0 0 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 Rn
Rn [3-0] 4-bits Operand Register
Rn[bit_0] = 1, 跳转并切换至 THUMB 指令集
Rn[bit_0] = 0, 跳转并切换至 ARM 指令集
5. 访存指令
我们这里只考虑字节对齐内存的操作,关于非对齐内存的操作感兴趣的可以看下这个链接
ldr, str
ldrh, strh, ldrsb, ldrsh
ldm, stm
swp
(ldr, str)
指令编码格式
31 - - 28 27 - - 26 25 24 23 22 21 20 19 - - 16 15 - - 12 11 - - 0
cond 0 1 I P U B W L Rn Rd offset
P [24] 1-bit Pre/Post indexing bit
0 = post; add offset after transfer
先从 Rn 指向处 load 数据再执行 Rn = Rn + offset,在这种模式下,W 无效且置为 0。
1 = pre; add offset before transfer
先执行 Rn + offset 再从该地址 load 数据,在这种模式下,W 为 0 则保留原 Rn,为 1 Rn = Rn + offset。
U [23] 1-bit Up/Down bit
0 = down; subtract offset from base
1 = up; add offset to base
B [22] 1-bit Byte/Word bit
0 = transfer word quantity
1 = transfer byte quantity
对于内存的访问也分为大小端,这里只讨论小端 (Little endian) 模式
A byte load (LDRB, B = '1') 如果地址在自边界的话,数据输入在 0-7 位,如果地址 +1 则数据将会出现在 8-15 位上,以此类推。最终选中的字节会出现在寄存器的最低 8 位,并且其他为 0 。
A byte store (STRB, B = '1') 会将寄存器的最低 8 位重复的填充在 32 位的数据总线上,外部内存管理单元将所需的字节填充至内存。
W [21] 1-bit Write-back bit
0 = no write-back
1 = write address into base
L [20] 1-bit Load/Store bit
0 = Store to memory
1 = Load from memory
Offset [11-0] 12-bits Immediate offset (字节偏移)
I [25] = 0 => offset is an immediate value, <#imm12>
I [25] = 1 => offset is a register
偏移符合 shift_operand 标准 (在 MOV 指令的编码格式内有描述)
11 - - 4 3 - - 0
Shift Rm
指令语法格式
{cond}{B} Rd,
@其中
LDR L=1 将内存内的值写入寄存器
STR L=0 将寄存器内的值写入内存
Address 可以是:
1. =>