ARM处理器工作模式
工作模式 | 解释 |
---|---|
User(USR) | 非特权模式,大部分任务执行在这种模式 |
System(特权) | 和User共用寄存器的特权模式 |
FIQ(特权) | 快速中断模式 |
IRQ(特权) | 低速中断模式 |
Supervisor(特权)(SVC) | 复位或者软中断的工作模式 |
Abort(特权)(ABT) | 预取异常的工作模式 |
Undef(特权)(UND) | 未定义指令的工作模式 |
Moniter(Cortex-A)(MON) | 执行安全监控代码的模式 |
Hyp(HYP) | Implemented with Vituralization Extensions |
ARM内核寄存器组
寄存器 | ||||||||
---|---|---|---|---|---|---|---|---|
R0 | √ | |||||||
R1 | √ | |||||||
R2 | √ | |||||||
R3 | √ | |||||||
R4 | √ | |||||||
R5 | √ | |||||||
R6 | √ | |||||||
R7 | √ | |||||||
R8 | √ | √ | ||||||
R9 | √ | √ | ||||||
R10 | √ | √ | ||||||
R11 | √ | √ | ||||||
R12 | √ | √ | ||||||
SP(R13) | √ | √ | √ | √ | √ | √ | √ | √ |
LR(R14) | √ | √ | √ | √ | √ | √ | √ | |
PC(R15) | √ | |||||||
CPSR | √ | |||||||
SPSR | √ | √ | √ | √ | √ | √ | √ | |
ELR_hyp | √ | |||||||
模式 | User/System | Hyp | Supervisor | Abort | Undef | Moniter | IRQ | FIQ |
-
User与System模式共用所有的寄存器,其他模式共用R0~R12寄存器。
-
CPSR为当前程序状态的寄存器。
-
SPSR为保存程序状态的寄存器。
-
SP为栈指针寄存器,满栈式压栈指向最后压栈时的地址,此时指向的地址内部有数据,空栈式压栈指向最后压栈时的地址-4(即栈顶上面),此时指向的地址内部无数据。
-
LR为链接寄存器,指向的时函数执行完毕后的返回地址,也就是所谓的程序跳转断点。
-
PC为程序计数器,指向当前的机器码。
CPSR寄存器
以下寄存器位由高至低,[31:0]。
寄存器位 | 置1时的含义 |
---|---|
N | ALU的结果位负数 |
Z | ALU的结果为0 |
C | ALU操作进位/借位 |
V | ALU操作溢出 |
Q | 累计饱和(也称为粘性),饱和:不循环 |
IT[1:0] | |
J | 内核是否处于Jazelle状态 |
空位[3:0] | |
GE[3:0] | 使用一些SIMD指令 |
IT[7:2] | if then条件执行Thumb-2指令组 |
E | 控制加载/存储字节序 |
A | 禁用abort模式 |
I | 禁用IRQ模式 |
F | 禁用FIQ模式 |
T | 显示核心是否处于Thumb状态 |
M[4:0] | 处理器模式:0x10~0x1F |
*加粗的为KEIL关注的寄存器位。
CPSR寄存器M位与处理器模式:
M值 | 模式 |
---|---|
0x10 | User |
0x11 | FIQ |
0x13 | SVC |
0x17 | Abort |
0x1B | Undef |
0x1F | System |
0x16 | Monitor |
0x12 | IRQ |
异常向量表
异常向量表在程序起始位置的开始,以下所有地址皆为程序start为基点的偏移量。
地址 | Hypa | Monitor | Secure | Non-secure |
---|---|---|---|---|
0x00 | NO USE | NO USE | Reset | NO USE |
0x04 | Udefined | NO USE | Undefined Instruction | Undefined Instruction |
0x08 | Hypervisor Call | Secure Monitor Call | Superviesor Call | Supervisor Call |
0x0C | Perfetch Abort | Perfetch Abort | Perfetch Abort | Perfetch Abort |
0x10 | Data Abort | Data Abort | Data Abort | Data Abort |
0x14 | Hyp trap | NO USE | NO USE | NO USE |
0x18 | IRQ | IRQ | IRQ | IRQ |
0x1C | FIQ | FIQ | FIQ | FIQ |
汇编示例:
.text
.global _start
_start:
@0
b reset
@4
b undef_handler
@8
b swi_handler
b perfetch_handler
b databort_handler
nop
b irq_handler
b fiq_handler
对于FIQ
-
FIQ有更多的独立寄存器(R8~R12),在存储现场压栈时压入内存的寄存器更少以此达到更快的中断。
-
FIQ在中断向量表的最末尾,FIQ操作的代码段则紧接这中断向量表,从而少跳转一次达到更快的中断效果。
-
同时进入FIQ模式会禁止IRQ,拥有比IRQ更高的中断优先级,在FIQ结束前,IRQ不会被相应。
ARM指令
指令流水线
CPU执行一条指令分为三步,即:
-
Fetch:读指令:从存储器读取指令
-
Decode:译码:解码指令中用到的寄存器
-
Exexute:执行:寄存器读(从寄存器Bank),移位及ALU操作,寄存器写(到寄存器Bank)
ARM | Thumb | Step |
---|---|---|
PC | PC | Fetch |
PC-4 | PC-2 | Decode |
PC-8 | PC-4 | Execute |
为了提高CPU执行指令的效率,所以CPU在执行指令时是以流水线的方式进行的:
单元 | step1 | step2 | step3 | step4 | step5 |
---|---|---|---|---|---|
Fetch单元 | 指令1:Fetch | 指令2:Fetch | 指令3:Fetch | ||
Decode单元 | 指令1:Decode | 指令2:Decode | 指令3:Decode | ||
Execute单元 | 指令1:Execute | 指令2:Execute | 指令3:Execute |
指令格式
<Mnemonic>{<cond>}{s} {Rd},{Rn},{shifter_operand}
原文 | 解释 |
---|---|
Mnemonic | 助记符 |
cond | 条件码 |
s | 当前指令执行会引起CPSR寄存器的NZCV位变化 |
Rd | 目标寄存器 |
Rn | 第一操作寄存器 |
shifter_operand | 第二操作数 |
注:比较指令没有目标寄存器,不加s修饰也会引起CPSR寄存器NZCV位变化。
ARM指令语句助记符
种类 | 指令 | 含义 |
---|---|---|
数据传输指令 | MOV | MOV Rd,Rn;将第一操作寄存器的数据移到目标寄存器Rd |
MVN | MVN Rd,Rn;将第一操作寄存器的数据取反后移到目标指令集Rd | |
比较指令 | CMP | CMP Rd,Rn;减法比较Rd-Rn,更新CPSR |
CMN | CMN Rd,Rn;加法比较Rd+Rn,更新CPSR | |
TST | TST Rd,Rn;位测试Rd&Rn,更新CPSR | |
TEQ | TEQ Rd,Rn;相等测试Rd^Rn,更新CPSR | |
加法指令 | ADD | ADD Rd,Rn,shifted_opt;Rd=Rn+shifted_opt |
ADC | ADC Rd,Rn,shifted_opt;Rd=Rn+Rn+C(CPSR);加法进位指令 | |
减法指令 | SUB | SUB Rd,Rn,shifted_opt;Rd=Rn-shifted_opt |
SBC | SUB Rd,Rn,shifted_opt;Rd=Rn-shifted_opt-C(CPSR);减法借位指令 | |
RSB | RSB Rd,Rn,shifted_opt;Rd=shifted_opt-Rn;反减 | |
RSC | RSB,Rd,Rn,shifted_opt;Rd=shifted_opt-Rn-C(CPSR);反减借位 | |
位运算指令 | AND | AND Rd,Rn,shifted_opt;Rd=Rn&shifted_opt |
ORR | ORR Rd,Rn,shifted_opt;Rd=Rn|shifted_opt | |
EOR | EOR Rd,Rn,shifted_opt;Rd=Rn^shifted_opt;相同为零不同为一 | |
BIC | EOR Rd,Rn,shifted_opt;取二进制shifter_opt为1的位将Rn对应的位写零,结果导入到Rd | |
无符号乘法指令 | UMULL | UMULL Rd_low,Rd_high,Rn,Rm;Rd_high:Rd_low=Rn*Rm |
UMLAL | UMLAL Rd_low,Rd_high,Rn,Rm;Rd_high:Rd_low=Rn*Rm+Rd_high:Rd_low |
条件码(conditional execution)
条件码 | 助记符 | 寄存器标识位条件 | 含义 |
---|---|---|---|
0000 | EQ | Z=1 | 相等 |
0001 | NE | Z=0 | 不相等 |
0010 | CS | C=1 | 无符号数大于或等于 |
0011 | CC | C=0 | 无符号数小于 |
0100 | MI | N=1 | 负数 |
0101 | PL | N=0 | 正数或零 |
0110 | VS | V=1 | 溢出 |
0111 | VC | V=0 | 未溢出 |
1000 | HI | C=1&&Z=1 | 无符号数大于 |
1001 | LS | C=0||Z=1 | 无符号数小于或等于 |
1010 | GE | N=V | 带符号数大于或等于 |
1011 | LT | N!=V | 带符号数小于 |
1100 | GT | Z=0&&N=V | 带符号数大于 |
1101 | LE | Z=1||N!=V | 带符号数小于或等于 |
1110 | AL | Any | Always |
跳转指令B配合条件码,可实现条件跳转的逻辑结构。
指令语句示例代码
MOV/MVN赋值指令
@transmit commmand
MOV R0,#1
MVN R0,#1
MOV将值赋给目标寄存器,MVN取反输入(因为是无符号数,所以0x-1回环为0xFFFFFFFF)。
CMP/CMN/TST/TEQ比较指令
MOV R0,#5
MOV R1,#3
CMP R0,R1 @ CPSR
SUBHI R0,R0,R1 @ R0<-(R0-R1)
SUBLS R1,R1,R0
CMP比较后CPSR寄存器的C位由0置为1。
@compare command
MOV R0,#2
CMP R0,#2
CMP R0,#3
MOV R0,#2
CMN R0,#-2
MOV R0,#16
TST R0,#(1<<4)
MOV R0,#234
TEQ R0,#234
ADD加法运算/ADC进位加法运算
MOV R0,#0x1
MOV R1,#0xFFFFFFFF
MOV R2,#0x1
MOV R3,#0x1
ADDS R5,R3,R1
ADC R4,R0,R2 @进位
NOP
NOP
寄存器 | 运行前 | 第一步后 | 第二步后 | 第三步后 | 第四步后 | 第五步后 | 第六步后 |
---|---|---|---|---|---|---|---|
R0 | 0x00000000 | 0x00000001 | |||||
R1 | 0x00000000 | 0xFFFFFFFF | |||||
R2 | 0x00000000 | 0x00000001 | |||||
R3 | 0x00000000 | 0x00000001 | |||||
R4 | 0x00000000 | 0x00000003 | |||||
R5 | 0x00000000 | 0x00000000 |
指令 | CPSR |
---|---|
CMP减法比较 | 小于(N置1),大于等于(C,Z置1) |
CMN加法比较(与负数) | |
TST位比较(与位移量) | C置1 |
TEQ相等测试 |
SUB减法运算/SBC借位减法运算
MOV R0,#0x2
MOV R1,#0x1
MOV R2,#0x1
MOV R3,#0xFFFFFFFF
SUB R5,R1,R3
SBC R4,R0,R2 @借位
NOP
NOP
R0&R1组成数1的高八位和低八位,R2&R3组成数二的高八位低八位。
R1-R3时借位,此时R0被借位后为0x00000001。
寄存器 | 运行前 | 第一步后 | 第二步后 | 第三步后 | 第四步后 | 第五步后 | 第六步后 |
---|---|---|---|---|---|---|---|
R0 | 0x00000000 | 0x00000002 | |||||
R1 | 0x00000000 | 0x00000001 | |||||
R2 | 0x00000000 | 0x00000001 | |||||
R3 | 0x00000000 | 0xFFFFFFFF | |||||
R4 | 0x00000000 | 0x00000000 | |||||
R5 | 0x00000000 | 0x00000002 |
RSB反减运算
因为受汇编格式第一第二操作数皆为寄存器而不能是常数的限制,所以出现常数为被减数,寄存器值为减数的情况时需要使用RSB指令。
MOV R1,#0x01
RSB R0,R1,#0xFF @反减
寄存器变化:
寄存器 | 运行前 | 运行后 |
---|---|---|
R0 | 0x00000000 | 0x000000FE |
R1 | 0x00000000 | 0x00000001 |
BIC按位清零/MSR寄存器给特殊寄存器赋值
MRS R0,CPSR
BIC R0,#0x3 @按位清零
MSR CPSR,R0
NOP
NOP
寄存器变化:
寄存器 | 运行前 | 第一步后 | 第二步后 | 第三步 |
---|---|---|---|---|
R0 | 0x00000000 | 0x000000D3 | 0x000000D0 | 0x000000D0 |
CPSR | 0x000000D3 | 0x000000D3 | 0x000000D3 | 0x000000D0 |
模式状态 | SVC | SVC | SVC | USER |
UMULL乘法运算/UMLAL乘法累加运算
MOV r2,#2
MOV R3,#0XFFFFFFFF
UMULL R0,R1,R2,R3
UMLAL R0,R1,R2,R3
NOP
NOP
寄存器变化
寄存器 | 运行前 | 第一步后 | 第二步后 | 第三步后 | 第四步后 |
---|---|---|---|---|---|
R2 | 0x00000000 | 0x00000002 | |||
R3 | 0x00000000 | 0xFFFFFFFF | |||
R1 | 0x00000000 | 0x00000001 | 0x00000003 | ||
R0 | 0x00000000 | 0xFFFFFFFE | 0xFFFFFFFC |
R1为高32位,R0位低32位。
UMULL:0xFFFFFFFF*2=0x1FFFFFFFE ;
UMLAL:Rd_high:Rd_low=Rn*Rm+Rd_high:Rd_low ;2*0xFFFFFFFF+0x1FFFFFFFE=0x3FFFFFFFC
B 跳转指令
MOV R0,#0X1
MOV R1,#0X2
B MDADD
LOOP:
B .
MDADD:
ADD R2,R0,R1;
MOV PC,LR
B MDADD可以使程序跳过LOOP部分,直接执行MDADD部分。MOV PC,LR使程序PC指针又指向程序起始位置。
ARM伪指令
指令语句有对应的机器码,伪指令没有对应的机器码。伪指令的格式与指令的格式相同。
伪指令因为可以操作内u才能空间,所以引入了两种特殊符号:
- =:=DATA,可以获取数据段的内存地址,类似C语言的&。
- []:[Register],可以获取寄存器存储的内存地址里所存储的数据,类似C语言的*。
同时引入.text代码段与.data数据段,数据段的数据存储在内存,数据格式更加灵活,且可以被更改。
伪指令助记符
种类 | 指令 | 含义 |
---|---|---|
加载指令,可操作内存空间,使寄存器与内存数据交换 | LDR | LDR Rd,Addr;将立即数/地址赋值给目标寄存器 |
LDRB | 以字节为单位加载数据 | |
LDRH | 以半字为单位加载数据 | |
存储指令,可操作内存空间,使寄存器与内存数据交换 | STR | STR Rd,=Addr 将Rd的数据存储到地址 |
STRB | 以字节为单位存储数据 | |
STRH | 以半字为单位存储数据 | |
加载指令,load from memory to register | LDM | LDM Rd!,{Rm-Rn};意为将Rd所指地址的数据自增形式存储到Rm到Rn的寄存器里,Rd自增。 |
存储指令,store to memory from register | STM | STM Rd!,{Rm-Rn};意为将Rm到Rn寄存器里的值由寄存器的低地址到高地址,以自增形式赋给Rd所指向的首地址空间,Rd自增。 |
*单位:字为4byte,即32bit,八位十六进制数;半字即为2byte,16bit;字节即1byte,8bit
伪指令于语句示例
LDR/STR
LDR R9,=VAR1
LDR R0,[R9]
ADD R0,R0,#0X1
STR R0,[R9]
LDR R1,[R9]
NOP
NOP
.data @声明此处是数据段
VAR1: .WORD 0X12345678 @声明整个VAR1存储在一个字里
- 将VAR1的地址存储在R9
- 取R9所存储的地址里数据赋给R0,R0变化为0x12345678
- R0加0x1变为0x12345679
- 将R0存储的数据0x12345679存储到R9存储的地址,也就是VAR1此时变为0x12345679,可在keil的Memory窗口查看内存变化
- 将R9所存储的地址里的数据赋给R1,R1变化为0x12345679
LDRB/STRB
LDR R0,=ARR1
LDR R2,=ARR2
COPY:
LDRB R1,[R0],#1
STRB R1,[R2],#1
CMP R1,#0
BNE COPY
NOP
NOP
.data
ARR1: .STRING "1234567\0" @字符串"1234567\0"
ARR2: .SPACE 8 @在内存申请一片8字节的空间
-
将ARR1的首地址存储在R0
-
将ARR2的首地址存储在R2
-
将R0存储的首地址里的数据赋给R1,存储单位为一个字节,之后R1值加1,即R1充当指针,指针向后一个字节
-
将R1的数据赋值给R2存储的首地址内存里,之后R2加1,即指针向后一个字节
-
比较R1与0,在此程序中,0意味着中止符“\0”
-
比较结果若不同,则重复COPY段的指令。
这样就可以将ARR1字符串赋给ARR2字符串。
LDMIA/STMIA/LDMIB/STMIB/LDMDA/STMDA/LDMDB/STMDB
- IA:increment after;先操作,后增加;num++
- IB:increment before;先增加,后操作;++num
- DA:decrement after;先操作,后减少;num–
- DB:decrement before;先减少;后操作;–num
LDR R0,=ARR1
LDR R1,=ARR2
LDMIA R0!,{R3-R4}
STMIA R1!,{R3-R4}
NOP
NOP
.data
ARR1:.BYTE '1','2','3','4','5','6','7','8' @元素为一字节的ARR1
ARR2: .SPACE 8
-
将ARR1的首地址存储在R0
-
将ARR2的首地址存储在R1
-
将R0首地址的数据赋给R3到R4,之后R0增加,即指针向后移动一个单位,以此类推直至R3-R4将ARR1全部数据存储。
-
R3-R4的数据赋给R1存储的地址,即ARR2的首地址,之后R1增加,即指针向后移动一个单位,以此类推直至将R3-R4的全部数据存储到ARR2
这种操作可以在压栈出栈中使用,后文压栈出栈和软中断会进行详细分析。