ARM 汇编指令集

一. 汇编指令集

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. 指令编码格式

ARM 汇编指令集

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种寻址方式

  1. 寄存器寻址:
mov r1, r2
add r1, r1, r2
  1. 立即数寻址:
mov r1, #0FF
add r1, r1, #1
  • 立即数寻址的每个立即数都遵守一个规则:每一个立即数由一个8位的常数循环右移偶数位得到。公式表达为:立即数 = 8 位常数循环右移 (2 * 4位二进制数) 即:

$$
immediate = const_8 ROR (2 * bin_4) 。
$$

  1. 寄存器移位寻址:
mov r1, r2, LSL #2
mov r1, r2, LSL r1 
  1. 寄存器间接寻址:
ldr r1, [r2] => 将 r2 作为地址取其指向的内存数据
add r1, r1, [r2]
  1. 基址变址寻址:
ldr r1, [r2, #4] => 将 r2 内的值自增 4 并将其所指向的内存值加载至 r1 中
ldr r1, [r2, #-4]!
ldr r1, [r2, r3]
ldr r1, [r2], #4 => 将 r2 所指向内存的值存储到 r1 中,并将 r2 自增  4
  1. 多寄存器寻址:
ldmia r1!, {r2 - r7} => 将 r1指向的地址数据存储到 r2 至 r7 的寄存器中,并在每次存储后将 r1 的值自加 4,并且因为!,最后将操作后的 r1 存储回 r1 中.
  1. 堆栈寻址相对寻址:
  • 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} => 出栈
  1. 相对寻址:
beq lable
  ……
lable:
    ……

3. 指令后缀

详见并行指令前后缀表格及条件执行后缀表格

cmp r1, r2
moveq r0, #0FF  => 只有 r1 == r2 才运行 

4. 多级指令流水线

  • ARM11 有 8 级流水线
  • pc 指向正在取指的指令的地址(一般来说就是正在执行指令的后两条)

五. 常用 ARM 汇编指令

1. 数据处理指令

  1. 数据传输指令
  • mov, mvn
  1. 算术指令
  • add, sub , rsb, adc, sbc, rsc
  1. 逻辑指令
  • and, orr, eor, bic
  1. 比较指令
  • cmp, cmn, tst, teq
  1. 乘法指令
  • mul, mla, umull, umlal, smull, smlal
  1. 前导零计数
  • 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

指令语法格式

  1. MOV, MVN (single operand instructions.)
<opcode>{cond}{S} Rd, <Op2>
  1. CMP, CMN, TEQ, TST (instructions which do not produce a result.)
<opcode>{cond} Rn, <Op2>
  1. AND, EOR, SUB, RSB, ADD, ADC, SBC, RSC, ORR, BIC
<opcode>{cond}{S} Rd, Rn, <Op2>

注:

The SUBS PC, LR, #<const> instruction provides an exception return without the use of the stack. It subtracts the immediate constant from LR, branches to the resulting address, and also copies the SPSR to the CPSR. The ARM instruction set contains similar instructions based on other data-processing operations, with a wider range of operands, or both. The use of these other instructions is deprecated, except for MOVS PC, LR.

这段话简单说下,当 mov、sub 等数据操作指令内的操作数含有 PC 和 LR 时,在指令后加 'S' 会使得 SPSR 拷贝至 CPSR,一般用于状态切换,如中断处理函数内的操作;


(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

指令语法格式

  1. LSL, LSR, ASR, ROR
<opcode>{s}<c> <Rd>, <Rn>, <Rm>
<opcode>{s}<c> <Rd>, <Rm>, <#imm5>
  1. RRX
RRX{S}<c> <Rd>, <Rm>			;每次只移一位,将该指令前一条指令的 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_<current_mode>

指令语法格式

MRS{cond} Rd, <psr>
MSR{cond} <psr>, Rm
MSR{cond} <psrf>, Rm
MSR{cond} <psrf>, <#expression>

@其中
MRS		将 PSR 的数据读入通用寄存器
MSR		将通用寄存器或者立即数存入 PSR
<psr> : 可以是 CPSR, CPSR_all, SPSR, SPSR_all
<psrf>: 可以是 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_<mode>[31:0]<- Rm[31:0]
MSR SPSR_flg, Rm 			; SPSR_<mode>[31:28] <- Rm[31:28]
MSR SPSR_flg, #0xC0000000	; SPSR_<mode>[31:28] <- 0xC
							;(set N,Z; clear C,V)
MRS Rd, SPSR 				; Rd[31:0] <- SPSR_<mode>[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;符号扩展则为了可以前后跳转。)。最终可以计算处该指令的寻址范围为:+/- 224+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} <expression>

转移指令在大部分情况下会配合条件指令执行后缀使用,例:

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

指令语法格式

<LDR|STR>{cond}{B} Rd, <Address>

@其中 
LDR		L=1 将内存内的值写入寄存器
STR		L=0 将寄存器内的值写入内存
Address 可以是:
1. <expression> => <label>
	汇编器会将该指令做成 PC 指正加上一个立即数偏移的方式。=> [PC, #imm12]
2. [<Rn>]	=>  访问 Rn 指向的地址				
   [<Rn>, #+/-<imm12>]{!}  =>
   		if have('!')
   			Rn = Rn +/- imm12
            Rd = [Rn]
        else
        	Rd = [Rn +/- imm12]
   [<Rn>], #+/-<imm12>     =>
   		Rd = [Rn]
   		Rn = Rn +/- imm12
3. [<Rn>, +/-<Rm>{, <shift>}]{!}
   [<Rn>], +/-<Rm>{, <shift>}
   同上,两者也是先加和后加,是否保存偏移后的值的关系;

例:

STR R1, [R2, R4]! 		; Store R1 at R2+R4 (both of which are
						; registers) and write back address to
						; R2.
STR R1, [R2], R4 		; Store R1 at R2 and write back
						; R2+R4 to R2.
LDR R1, [R2, #16] 		; Load R1 from contents of R2+16, but
						; don’t write back.
LDR R1, [R2, R3, LSL#2] ; Load R1 from contents of R2+R3*4.
LDREQB  R1, [R6, #5] 	; Conditionally load byte at R6+5 into
						; R1 bits 0 to 7, filling bits 8 to 31
						; with zeros.
STR R1, PLACE 			; Generate PC relative offset to
						; address PLACE.
•
PLACE

(LDRH/STRH/LDRSB/LDRSH)

指令编码格式

31 - - 28 27 - - 25 24 23 22 21 20 19 - - 16 15 - - 12 11 - - 8 7 6 5 4 3 - - 0
cond 0 0 0 P U 0 W L Rn Rd 0 0 0 0 1 S H 1 Rm
1 offset_Hi offset_Lo
  • S [6], H[5] 2-bit
    • 00 = SWP instruction
    • 01 = Unsigned halfwords
    • 10 = Sigend byte
    • 11 = Signed halfword
  • offsetHi [11-8], offsetLo [3-0] 8-bits
    • 立即数偏移,imm8(字节偏移)

指令语法格式

<LDR|STR>{cond}<H|SH|SB> Rd,<address>

@<address> 可以是
1. <expression> => <label>
   汇编器会将该指令做成 PC 指正加上一个立即数偏移的方式。=> [PC, #imm8]
   由于 imm8 偏移地址只有 8 位,所以寻址范围只有 +/-256 bytes,再加上 PC 指针比正在执行的指针多 8 个字节。
2. [<Rn>]
   [<Rn>, #+/-<imm8>]{!}
   [<Rn>], #+/-<imm8>
3. [<Rn>, +/-Rm]{!}
   [<Rn>], +/-Rm

例:

LDRH R1,[R2,-R3]!		; Load R1 from the contents of the
                        ; halfword address contained in
                        ; R2-R3 (both of which are registers)
                        ; and write back address to R2
STRH R3,[R4,#14]		; Store the halfword in R3 at R4+14
						; but don’t write back.
LDRSB R8,[R2],#-223		; Load R8 with the sign extended
                        ; contents of the byte address
                        ; contained in R2 and write back
                        ; R2-223 to R2.
LDRNESH R11,[R0]		; conditionally load R11 with the sign
                        ; extended contents of the halfword
                        ; address contained in R0.
HERE 					; Generate PC relative offset to
                        ; address FRED.
                        ; Store the halfword in R5 at address
                        ; FRED.
STRH  R5, [PC, #(FRED-HERE-8)]
.
FRED

(SWP)

指令编码格式

31 - - 28 27 - - 23 22 21 - - 20 19 - - 16 15 - - 12 11 - - 8 7 - - 4 3 - - 0
cond 0 0 0 1 0 B 0 0 Rn Rd 0 0 0 0 1 0 0 1 Rm

指令语法格式

SWP{B}<c> <Rd>, <Rm>, [<Rn>]
先从 Rn 指向处 load 至 Rd,再将 Rm 存储至 [Rn],这个过程是不可被打断的

例:

SWP R0,R1,[R2] 		; Load R0 with the word addressed by R2, and
					; store R1 at R2.
SWPB R2,R3,[R4] 	; Load R2 with the byte addressed by R4, and
					; store bits 0 to 7 of R3 at R4.
SWPEQ R0,R0,[R1] 	; Conditionally swap the contents of the
					; word addressed by R1 with R0.

(ldm, stm)

指令编码格式

31 - - 28 27 - - 25 24 23 22 21 20 19 - - 16 15 - - 0
cond 1 0 0 P U S W L Rn Register List
  • S [22] 1-bit PSR & force user bit
    • S = 0 => do not load PSR or force user mode
    • S = 1 => load PSR or force user mode
  • Register List [15-0] 15-bits
    • 每一位代表一个寄存器,[15, 14 ~ 0] => [R15, R14 ~ R0]

指令语法格式

<LDM|STM>{cond}<FD|ED|FA|EA|IA|IB|DA|DB> Rn{!}, <Rlist>{^}

@其中
<Rlist>  必须按照寄存器升序排列,例:{R0, R2-R4, R7}
<^>(S=1) 当 Rlist 中包含 PC(R15) 时,会将 SPSR_<current_mode> 拷贝到 CPSR,多用于异常的返回;
    或者在 privileged mode 下强制传送 USER bank(就是用户模式的寄存器)

LDMFD SP!,{R0,R1,R2} 	; Unstack 3 registers.
STMIA R0,{R0-R15} 		; Save all registers.
LDMFD SP!,{R15} 		; R15 <- (SP),CPSR unchanged.

These instructions may be used to save state on subroutine entry, and restore it
efficiently on return to the calling routine:

STMED SP!,{R0-R3,R14} 	; Save R0 to R3 to use as workspace
						; and R14 for returning.
BL somewhere 			; This nested call will overwrite R14
LDMED SP!,{R0-R3,R15} 	; restore workspace and return.

6. 协处理器访问指令

  • LDC, STC
  • MRC, MCR
  • CDP

(LDC, STC)

指令编码格式

31 - - 28 27 - - 25 24 23 22 21 20 19 - - 16 15 - - 12 11 - - 8 7 - - 0
cond 1 1 0 P U N W L Rn CRd CP# offset
  • N [22] 1-bit Transfer length

  • CRd [15-12] 4-bits Coprocessor source/destination register

  • CP# [11-8] 4-bits Coprocessor number

  • offset [7-0] 8-bits 8 位立即数偏移量 (字偏移)

    其中 CRd 和 N 包含了给协处理器的信息,不同的协处理器对该信息有不同的解释,不过约定 CRd 就是需要传输的寄存器或者是批量传输寄存器的起始寄存器,并且约定 N 位就是选择传输长短的位,N=0 时,传输单个寄存器,N=1 时,可以选择传输全部的寄存器以用于上下文的切换。

    需要注意的是 post-index 模式,需要明确的设置 W 位,不像 LDR/STR 一样在 post-index 模式下总是会回写 Rn 寄存器,不过这件事汇编器会做,汇编不需要加 '!'。

    当需要多寄存器传输时,从第二个寄存器传输开始,每次都会自动对目标地址自增 4 bytes 。

指令语法格式

<LDC|STC>{cond}{L} p#,cd,<Address>

@其中
LDC		L=1 将内存内的值写入协处理器的寄存器
STC		L=0 将协处理器寄存器的值写入内存
{L}		如果有 L 表示这是一个多寄存器传输(N=1)
p# 		协处理器标号
cd		协处理器寄存器编号
<Address>
1. <expression> => <label>
   汇编器会将该指令做成 PC 指正加上一个立即数偏移的方式。=> [PC, #imm(8+2)],由于指令编码格式内的 
   offset 是字偏移,而语法格式内写的是字节偏移,所以可寻址范围是 2^10 bytes。
2. [<Rn>]
   [<Rn>, <#expression>]{!}
3. [<Rn>], <#expression> 

例:

LDC p1, c2, table		; Load c2 of coproc 1 from address
						; table, using a PC relative address.
STCEQL p2, c3,[R5,#24]!	; Conditionally store c3 of coproc 2
                        ; into an address 24 bytes up from R5,
                        ; write this address back to R5, and use
                        ; long transfer option (probably to
                        ; store multiple words).

(MRC, MCR)

这组指令一个非常重要的作用就是可以直接联系 coprocessor 和 ARM 的 CPSR 寄存器,例:在 coprocessor 中两个浮点数的比较结果可以直接送入 CPSR 来控制接下来指令的执行。

指令编码格式

31 - - 28 27 - - 24 23 - - 21 20 19 - - 16 15 - - 12 11 - - 8 7 - - 5 4 3 - - 0
cond 1 1 1 0 CP Opc L CRn Rd CP# CP 1 CRm
  • CP Opc [23-21] 3-bits Coprocessor operation mode

  • CRn [19-16] 4-bits Coprocessor source/destination register

  • CP# [11-8] 4-bits Coprocessor number

  • CP [7-5] 3-bits Coprocessor information

  • CRm [3-0] 4-bits Coprocessor operand register

    CP Opc, CRn, CP 和 CRm 字段是给 coprocessor 用的,并且对于这些字段的定义是约定好的。

    在约定的定义中,CP Opc 和 CP 字段定义coprocessor 所执行的操作,CRn 是 coprocessor 需要传送数据的寄存器,CRm 是第二个 coprocessor 寄存器,其可能通过某种方式和详细规定 coprocessor 具体操作相关。

指令语法格式

<MCR|MRC>{cond} p#,<expression1>,Rd,cn,cm{,<expression2>}

@其中
MRC 			(L=1) 将协处理器中的值写入 ARM 寄存器
MCR 			(L=0) 将 ARM 寄存器的值写入协处理器
p#				要操作的协处理器编号
<expression1> 	写入 CP Opc 当中的值(operation),是一个常数
Rd				操作结果返回(ARM)寄存器
cn & cm			分别对应 coprocessor 的合法寄存器
<expression2>	写入 CP 字段当中的值(type),是一个常数

例:

MRC p2,5,R3,c5,c6 		; Request coproc 2 to perform operation 5
                    	; on c5 and c6, and transfer the (single
                    	; 32 bit word) result back to R3.
MCR p6,0,R4,c5,c6 		; Request coproc 6 to perform operation 0
						; on R4 and place the result in c6.
MRCEQ p3,9,R3,c5,c6,2	; Conditionally request coproc 3 to
                   		; perform operation 9 (type 2) on c5 and
                    	; c6, and transfer the result back to R3.

(CDP)

指令编码格式

31 - - 28 27 - - 24 23 - - 20 19 - - 16 15 - - 12 11 - - 8 7 - - 5 4 3 - - 0
cond 1 1 1 0 CP Opc CRn Rd CP# CP 0 CRm
  • 该指令和 MRC/MCR 类似,只是没有操作结果返回到 ARM

指令语法格式

CDP{cond} p#,<expression1>,cd,cn,cm{,<expression2>}

例:

CDP p1,10,c1,c2,c3 		; Request coproc 1 to do operation 10
                    	; on CR2 and CR3, and put the result
                    	; in CR1.
CDPEQ p2,5,c1,c2,c3,2 	; If Z flag is set request coproc 2
                        ; to do operation 5 (type 2) on CR2
                        ; and CR3,and put the result in CR1.

7. 软中断指令

  • SWI

指令编码格式

31 - - 28 27 - - 24 23 - - 0
cond 1 1 1 1 Comment field (ignored by Processor)

指令语法格式

SWI{cond} <expression>

用户可以通过软中断指令进入管理模式,PC 指针被强制置为 0x08 (软中断入口),并且将 CPSR 保存至 SPSR_svc;通过 MOVS PC, R14_svc 可以从中断返回,并将恢复 CPSR。

低 24 位是被处理器忽略的,其一般可以用于用户模式和管理模式的通讯使用。比如,管理模式下可以通过查看该字段并且用它来 index 处理函数列表获取处理函数。

例:

SWI ReadC 			; Get next character from read stream.
SWI WriteI+”k” 		; Output a “k” to the write stream.
SWINE 0 			; Conditionally call supervisor
					; with 0 in comment field.
					
@Supervisor code
@The previous examples assume that suitable supervisor code exists, for instance:

0x08 B Supervisor 	; SWI entry point
EntryTable 			; addresses of supervisor routines
        DCD ZeroRtn
        DCD ReadCRtn
        DCD WriteIRtn
        . . .
Zero	EQU 0
ReadC 	EQU 256
WriteI 	EQU 512

Supervisor:

; SWI has routine required in bits 8-23 and data (if any) in
; bits 0-7.
; Assumes R13_svc points to a suitable stack
                    
STMFD R13,{R0-R2,R14} 	; Save work registers and return
						; address.
LDR R0,[R14,#-4]		; Get SWI instruction.
BIC R0,R0,#0xFF000000 	; Clear top 8 bits.
MOV R1,R0,LSR#8 		; Get routine offset.
ADR R2,EntryTable 		; Get start address of entry table.
LDR R15,[R2,R1,LSL#2] 	; Branch to appropriate routine.
WriteIRtn 				; Enter with character in R0 bits 0-7.
. . . . . .
LDMFD R13,{R0-R2,R15}^ 	; Restore workspace and return,
						; restoring processor mode and flags.

六. 常用伪指令

伪汇编指令分为 ARM Directives 和 GUN 伪指令
关于 ARM Directives 在 "ARM Compiler toolchain version 5.0 Assmbler Reference" 有说明
关于 GUN 伪指令的没有找到官方手册

以下两个博客说的很详细
http://www.cnblogs.com/xiaojiang1025/archive/2016/11/14/6063516.html
https://blog.csdn.net/chun_1959/article/details/44724725

  • LDR
  • .global
  • .data
  • .byte/.short/.long/.word
  • .align
  • .section
  • DCB
    • 用于分配一片连续的字节存储单元并用指定的数据初始化。
  • EQU
    • EQU伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言中的#define。
  • EXTERN
  • IMPORT

1. LDR

ldr r0, =0xadfafadc
ldr r0, =label
  • " ldr r0, label " 和 " ldr r0, =label ":

    两者虽然都会被解释成 " ldr r0, [pc, #imm] " 的形式,但是两者表达的意思是不一样的,前者以 label 为地址,取 label 指向处的数据保存至 0,后者是将 label 所代表的值,也就是 label 所在地址的值存放至代码断中,最终表示成 " ldr r0, [pc, #imm] " 的形式。