一. 汇编指令集

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 设计变复杂了

三. 一般汇编指令格式

需要说明的是,不同指令有不同的指令格式,编码格式以及前后缀等,这里把常用的大概总结下,具体的可以看:

1. 指令编码格式

ARM 汇编指令集-LMLPHP

2. 指令语法前/后缀

条件指令后缀

并行指令后缀

数据传输指令后缀

四. 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)

指令编码格式

  • 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。

    • shift_operand 可以表示立即数( ‘I’ = 1 ):immediate = const_8 ROR (2 * bin_4)

  • 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>

(MUL, MLA)

指令编码格式

  • 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)

指令编码格式

  • 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

指令编码格式

  • Px [22] 1-bit Destination PSR
    • 0 = CPSR
    • 1 = SPSR_

指令语法格式

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)

指令编码格式

  • 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} <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)

  • 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)

指令编码格式

  • 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 指令的编码格式内有描述)

指令语法格式

<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)

指令编码格式

  • S [6], H[5] 2-bit
    • 00 = SWP instruction
    • 01 = Unsigned halfwords
    • 10 = Sigend byte
    • 11 = Signed halfword
  • offset~Hi~ [11-8], offset~Lo~ [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)

指令编码格式

指令语法格式

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)

指令编码格式

  • 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 多用于异常的返回

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)

指令编码格式

  • 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 来控制接下来指令的执行。

指令编码格式

  • 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)

指令编码格式

  • 该指令和 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

指令编码格式

指令语法格式

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.

六. 常用伪指令

  • 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] " 的形式。

12-18 01:24