一. 汇编指令集
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. 指令编码格式
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种寻址方式
- 寄存器寻址:
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)
指令编码格式
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
指令语法格式
- MOV, MVN (single operand instructions.)
<opcode>{cond}{S} Rd, <Op2>
- CMP, CMN, TEQ, TST (instructions which do not produce a result.)
<opcode>{cond} Rn, <Op2>
- 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
- 0 = multiple only
指令语法格式
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
- 0 = multiple only
指令语法格式
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
<opcode>{s}<c> <Rd>, <Rn>, <Rm>
<opcode>{s}<c> <Rd>, <Rm>, <#imm5>
- 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。
- 0 = post; add offset after transfer
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] " 的形式。