ARM指令集与异常处理
一、ARM 微处理器的指令的分类与格式
ARM微处理器的指令集是加载/存储型的,也即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,
而对系统存储器的访问则需要通过专门的加载/存储指令来完成。ARM微处理器的指令集可以分为跳转指令、数据处理指令、
程序状态寄存器(PSR)处理指令、加载/存储指令、协处理器指令和异常产生指令六大类。下面是ARM微处理器的基本指令。
助记符 指令功能描述
ADC 带进位加法指令
ADD 加法指令
AND 逻辑与指令
B 跳转指令
BIC 位清零指令
BL 带返回的跳转指令
BLX 带返回和状态切换的跳转指令
BX 带状态切换的跳转指令
CDP 协处理器数据操作指令
CMN 比较反值指令
CMP 比较指令
EOR 异或指令
LDC 存储器到协处理器的数据传输指令
LDM 加载多个寄存器指令
LDR 存储器到寄存器的数据传输指令
MCR 从ARM 寄存器到协处理器寄存器的数据传输指令
MLA 乘加运算指令
MOV 数据传送指令
MRC 从协处理器寄存器到ARM 寄存器的数据传输指令
MRS 传送CPSR 或SPSR 的内容到通用寄存器指令
MSR 传送通用寄存器到CPSR 或SPSR 的指令
MUL 32 位乘法指令
MLA 32 位乘加指令
MVN 数据取反传送指令
ORR 逻辑或指令
RSB 逆向减法指令
RSC 带借位的逆向减法指令
SBC 带借位减法指令
STC 协处理器寄存器写入存储器指令
STM 批量内存字写入指令
STR 寄存器到存储器的数据传输指令
SUB 减法指令
SWI 软件中断指令
SWP 交换指令
TEQ 相等测试指令
TST 位测试指令
--------------------------------------------------------------------------------------------------------------------------
二、指令的条件域
当处理器工作在ARM状态时,几乎所有的指令均根据CPSR中条件码的状态和指令的条件域有
条件的执行。当指令的执行条件满足时,指令被执行,否则指令被忽略。
每一条ARM指令包含4位的条件码,位于指令的最高4位[31:28]。条件码共有16种,每种条件码
可用两个字符表示,这两个字符可以添加在指令助记符的后面和指令同时使用。16种条件标志码中只有15种可以使
用,标志码1111 保留不用。
条件码 助记符后缀 标 志 含 义
0000 EQ Z 置位 相等
0001 NE Z 清零 不相等
0010 CS C 置位 无符号数大于或等于
0011 CC C 清零 无符号数小于
0100 MI N 置位 负数
0101 PL N 清零 正数或零
0110 VS V 置位 溢出
0111 VC V 清零 未溢出
1000 HI C 置位Z 清零 无符号数大于
1001 LS C 清零Z 置位 无符号数小于或等于
1010 GE N 等于V 带符号数大于或等于
1011 LT N 不等于V 带符号数小于
1100 GT Z 清零且(N 等于V) 带符号数大于
1101 LE Z 置位或(N 不等于V) 带符号数小于或等于
1110 AL 忽略 无条件执行
-------------------------------------------------------------------------------------------------------------------------
三、ARM 指令的寻址方式
1、立即寻址 操作数在指令中直接给出
ADD R0,R0,#1 ;R0←R0+1
ADD R0,R0,#0x3f ;R0←R0+0x3f
2、寄存器寻址 操作数在寄存器
ADD R0,R1,R2 ;R0←R1+R2
3、寄存器间接寻址 操作数的地址在寄存器
ADD R0,R1,[R2] ;R0←R1+[R2]
LDR R0,[R1] ;R0←[R1]
STR R0,[R1] ;[R1]←R0
4、基址变址寻址 操作数地址 = 基址寄存器 + 指令中给出的地址偏移
LDR R0,[R1,#4] ;R0←[R1+4]
LDR R0,[R1,#4]! ;R0←[R1+4]、R1←R1+4
LDR R0,[R1] ,#4 ;R0←[R1]、R1←R1+4
LDR R0,[R1,R2] ;R0←[R1+R2]
5、多寄存器寻址 类似寄存器寻址,此处是多个寄存器而已
LDMIA R0,{R1,R2,R3,R4} ;R1←[R0]
;R2←[R0+4]
;R3←[R0+8]
;R4←[R0+12]
6、相对寻址 目标地址 = pc 当前值 + 指令给出的标号偏移地址
BL NEXT ;跳转到子程序NEXT 处执行
......
NEXT
......
MOV PC,LR ;从子程序返回
7、堆栈寻址
ARM 微处理器支持这四种类型的堆栈工作方式。
- 满递增堆栈:堆栈指针指向最后压入的数据,且由低地址向高地址生成。
- 满递减堆栈:堆栈指针指向最后压入的数据,且由高地址向低地址生成。
- 空递增堆栈:堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。
- 空递减堆栈:堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。
-----------------------------------------------------------------------------------------------------------------------
四、ARM 指令集
1、跳转指令
— 使用专门的跳转指令。 32MB 地址空间
— 直接向程序计数器PC 写入跳转地址值。 4GB 地址空间
— B 跳转指令
— BL 带返回的跳转指令
— BLX 带返回和状态切换的跳转指令
— BX 带状态切换的跳转指令
B{条件} 目标地址 其他的类似
2、数据处理指令
数据处理指令可分为数据传送指令、算术逻辑运算指令和比较指令等。
数据传送指令用于在寄存器和存储器之间进行数据的双向传输。
算术逻辑运算指令完成常用的算术与逻辑的运算,该类指令不但将运算结果保存在目的寄存器
中,同时更新CPSR 中的相应条件标志位。
比较指令不保存运算结果,只更新CPSR 中相应的条件标志位。
数据处理指令包括:
— MOV 数据传送指令 MOV{条件}{S} 目的寄存器,源操作数
MOV R1,R0 ;将寄存器R0 的值传送到寄存器R1
MOV PC,R14 ;将寄存器R14 的值传送到PC,常用于子程序返回
MOV R1,R0,LSL#3 ;将寄存器R0 的值左移3 位后传送到R1
— MVN 数据取反传送指令 MVN{条件}{S} 目的寄存器,源操作数
MVN R0,#0 ;将立即数0 取反传送到寄存器R0 中,完成后R0=-1
— CMP 比较指令 CMP{条件} 操作数1,操作数2
CMP R1,R0 ;将寄存器R1 的值与寄存器R0 的值相减,并根据结果设置CPSR 的标志位
CMP R1,#100 ;将寄存器R1 的值与立即数100 相减,并根据结果设置CPSR 的标志位
— CMN 反值比较指令 CMN{条件} 操作数1,操作数2
CMN R1,R0 ;将寄存器R1 的值与寄存器R0 的值相加,并根据结果设置CPSR 的标志位
CMN R1,#100 ;将寄存器R1 的值与立即数100 相加,并根据结果设置CPSR 的标志位
— TST 位测试指令 TST{条件} 操作数1,操作数2
TST R1,#%1 ;用于测试在寄存器R1 中是否设置了最低位(%表示二进制数)
TST R1,#0xffe ;将寄存器R1 的值与立即数0xffe 按位与,并根据结果设置CPSR 的标志位
— TEQ 相等测试指令 TEQ{条件} 操作数1,操作数2
TEQ R1,R2 ;将寄存器R1 的值与寄存器R2 的值按位异或,并根据结果设置CPSR 的标志位
— ADD 加法指令 ADD{条件}{S} 目的寄存器,操作数1,操作数2
ADD R0,R1,R2 ; R0 = R1 + R2
ADD R0,R1,#256 ; R0 = R1 + 256
ADD R0,R2,R3,LSL#1 ; R0 = R2 + (R3 << 1)
— ADC 带进位加法指令 ADC{条件}{S} 目的寄存器,操作数1,操作数2
ADDS R0,R4,R8 ; 加低端的字
ADCS R1,R5,R9 ; 加第二个字,带进位
ADCS R2,R6,R10 ; 加第三个字,带进位
ADC R3,R7,R11 ; 加第四个字,带进位
— SUB 减法指令 SUB{条件}{S} 目的寄存器,操作数1,操作数2
SUB R0,R1,R2 ; R0 = R1 - R2
SUB R0,R1,#256 ; R0 = R1 - 256
SUB R0,R2,R3,LSL#1 ; R0 = R2 - (R3 << 1)
— SBC 带借位减法指令 SBC{条件}{S} 目的寄存器,操作数1,操作数2
SUBS R0,R1,R2 ; R0 = R1 - R2 - !C,并根据结果设置CPSR 的进位标志位
— RSB 逆向减法指令 RSC{条件}{S} 目的寄存器,操作数1,操作数2
RSB R0,R1,R2 ; R0 = R2 – R1
RSB R0,R1,#256 ; R0 = 256 – R1
RSB R0,R2,R3,LSL#1 ; R0 = (R3 << 1) - R2
— RSC 带借位的逆向减法指令 RSC{条件}{S} 目的寄存器,操作数1,操作数2
RSC R0,R1,R2 ; R0 = R2 – R1 - !C
— AND 逻辑与指令 AND{条件}{S} 目的寄存器,操作数1,操作数2
AND R0,R0,#3 ; 该指令保持R0 的0、1 位,其余位清零。
— ORR 逻辑或指令 ORR{条件}{S} 目的寄存器,操作数1,操作数2
ORR R0,R0,#3 ; 该指令设置R0 的0、1 位,其余位保持不变
— EOR 逻辑异或指令 EOR{条件}{S} 目的寄存器,操作数1,操作数2
EOR R0,R0,#3 ; 该指令反转R0 的0、1 位,其余位保持不变。
— BIC 位清除指令 BIC{条件}{S} 目的寄存器,操作数1,操作数2
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。
3、乘法指令与乘加指令
ARM 微处理器支持的乘法指令与乘加指令共有6 条,可分为运算结果为32 位和运算结果为64
位两类,与前面的数据处理指令不同,指令中的所有操作数、目的寄存器必须为通用寄存器,不能
对操作数使用立即数或被移位的寄存器,同时,目的寄存器和操作数1 必须是不同的寄存器。
乘法指令与乘加指令共有以下6 条:
— MUL 32 位乘法指令 MUL{条件}{S} 目的寄存器,操作数1,操作数2
MUL R0,R1,R2 ;R0 = R1 × R2
MULS R0,R1,R2 ;R0 = R1 × R2,同时设置CPSR 中的相关条件标志位
— MLA 32 位乘加指令 MLA{条件}{S} 目的寄存器,操作数1,操作数2,操作数3
MLA R0,R1,R2,R3 ;R0 = R1 × R2 + R3
MLAS R0,R1,R2,R3 ;R0 = R1 × R2 + R3,同时设置CPSR 中的相关条件标志位
— SMULL 64 位有符号数乘法指令 SMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32 位
;R1 = (R2 × R3)的高32 位
— SMLAL 64 位有符号数乘加指令 SMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32 位 + R0
;R1 = (R2 × R3)的高32 位 + R1
— UMULL 64 位无符号数乘法指令 UMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32 位
;R1 = (R2 × R3)的高32 位
— UMLAL 64 位无符号数乘加指令 UMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32 位 + R0
;R1 = (R2 × R3)的高32 位 + R1
4、程序状态寄存器访问指令
ARM 微处理器支持程序状态寄存器访问指令,用于在程序状态寄存器和通用寄存器之间传送
数据,程序状态寄存器访问指令包括以下两条:
— MRS 程序状态寄存器到通用寄存器的数据传送指令 MRS{条件} 通用寄存器,程序状态寄存器(CPSR 或SPSR)
MRS R0,CPSR ;传送CPSR 的内容到R0
MRS R0,SPSR ;传送SPSR 的内容到R0
— MSR 通用寄存器到程序状态寄存器的数据传送指令 MSR{条件} 程序状态寄存器(CPSR 或SPSR)_<域>,操作数
MSR CPSR,R0 ;传送R0 的内容到CPSR
MSR SPSR,R0 ;传送R0 的内容到SPSR
MSR CPSR_c,R0 ;传送R0 的内容到SPSR,但仅仅修改CPSR 中的控制位域
5、加载/存储指令
ARM 微处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储
器中的数据传送到寄存器,存储指令则完成相反的操作。常用的加载存储指令如下:
— LDR 字数据加载指令 LDR{条件} 目的寄存器,<存储器地址>
LDR R0,[R1] ;将存储器地址为R1 的字数据读入寄存器R0。
LDR R0,[R1,R2] ;将存储器地址为R1+R2 的字数据读入寄存器R0。
LDR R0,[R1,#8] ;将存储器地址为R1+8 的字数据读入寄存器R0。
LDR R0,[R1,R2] ! ;将存储器地址为R1+R2 的字数据读入寄存器R0,并将新地址R1+R2 写入R1。
LDR R0,[R1,#8] ! ;将存储器地址为R1+8 的字数据读入寄存器R0,并将新地址R1+8 写入R1。
LDR R0,[R1],R2 ;将存储器地址为R1 的字数据读入寄存器R0,并将新地址R1+R2 写入R1。
LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4 的字数据读入寄存器R0,并将新地址R1+R2×4 写入R1。
LDR R0,[R1],R2,LSL#2 ;将存储器地址为R1 的字数据读入寄存器R0,并将新地址R1+R2×4 写入R1。
— LDRB 字节数据加载指令 LDR{条件}B 目的寄存器,<存储器地址>
LDRB R0,[R1] ;将存储器地址为R1 的字节数据读入寄存器R0,并将R0 的高24 位清零。
LDRB R0,[R1,#8] ;将存储器地址为R1+8 的字节数据读入寄存器R0,并将R0 的高24 位清零。
— LDRH 半字数据加载指令 LDR{条件}H 目的寄存器,<存储器地址>
LDRH R0,[R1] ;将存储器地址为R1 的半字数据读入寄存器R0,并将R0 的高16 位清零。
LDRH R0,[R1,#8] ;将存储器地址为R1+8 的半字数据读入寄存器R0,并将R0 的高16 位清零。
LDRH R0,[R1,R2] ;将存储器地址为R1+R2 的半字数据读入寄存器R0,并将R0 的高16 位清零。
— STR 字数据存储指令 STR{条件} 源寄存器,<存储器地址>
STR R0,[R1],#8 ;将R0 中的字数据写入以R1 为地址的存储器中,并将新地址R1+8 写入R1。
STR R0,[R1,#8] ;将R0 中的字数据写入以R1+8 为地址的存储器中。
— STRB 字节数据存储指令 STR{条件}B 源寄存器,<存储器地址>
STRB R0,[R1] ;将寄存器R0 中的字节数据写入以R1 为地址的存储器中。
STRB R0,[R1,#8] ;将寄存器R0 中的字节数据写入以R1+8 为地址的存储器中。
— STRH 半字数据存储指令 STR{条件}H 源寄存器,<存储器地址>
STRH R0,[R1] ;将寄存器R0 中的半字数据写入以R1 为地址的存储器中。
STRH R0,[R1,#8] ;将寄存器R0 中的半字数据写入以R1+8 为地址的存储器中。
6、批量数据加载/存储指令
ARM 微处理器所支持批量数据加载/存储指令可以一次在一片连续的存储器单元和多个寄存器
之间传送数据,批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储
指令则完成相反的操作。常用的加载存储指令如下:
— LDM 批量数据加载指令
— STM 批量数据存储指令
LDM(或STM) 指令
LDM(或STM) 指令的格式为:
LDM(或STM) {条件}{类型} 基址寄存器{!},寄存器列表{∧}
LDM(或STM) 指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄
存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。其中,{类型}为以下几种情况:
IA 每次传送后地址加1;
IB 每次传送前地址加1;
DA 每次传送后地址减1;
DB 每次传送前地址减1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否
则基址寄存器的内容不改变。
基址寄存器不允许为R15,寄存器列表可以为R0~R15 的任意组合。
{∧}为可选后缀,当指令为LDM 且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据
传送之外,还将SPSR 复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而
不是当前模式下的寄存器。
STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4 到R12,LR)存入堆栈。
LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4 到R12,LR)。
7、数据交换指令
ARM 微处理器所支持数据交换指令能在存储器和寄存器之间交换数据。数据交换指令有如下
两条:
— SWP 字数据交换指令 SWP{条件} 目的寄存器,源寄存器1,[源寄存器2]
SWP R0,R1,[R2] ;将R2 所指向的存储器中的字数据传送到R0,同时将R1 中的字数据传
送到R2 所指向的存储单元。
SWP R0,R0,[R1] ;该指令完成将R1 所指向的存储器中的字数据与R0 中的字数据交换。
— SWPB 字节数据交换指令 SWP{条件}B 目的寄存器,源寄存器1,[源寄存器2]
SWPB R0,R1,[R2] ;将R2 所指向的存储器中的字节数据传送到R0,R0 的高24 位清零,
同时将R1 中的低8 位数据传送到R2 所指向的存储单元。
SWPB R0,R0,[R1] ;该指令完成将R1 所指向的存储器中的字节数据与R0 中的低8 位数据
交换。
8、移位指令
ARM 微处理器内嵌的桶型移位器(Barrel Shifter),支持数据的各种移位操作,移位操作在
ARM 指令集中不作为单独的指令使用,它只能作为指令格式中是一个字段,在汇编语言中表示为指
令中的选项。例如,数据处理指令的第二个操作数为寄存器时,就可以加入移位操作选项对它进行
各种移位操作。移位操作包括如下6 种类型,ASL 和LSL 是等价的,可以自由互换:
— LSL 逻辑左移 通用寄存器,LSL(或ASL) 操作数
MOV R0, R1, LSL#2 ;将R1 中的内容左移两位后传送到R0 中。
— LSR 逻辑右移 通用寄存器,LSR 操作数
MOV R0, R1, LSR#2 ;将R1 中的内容右移两位后传送到R0 中,左端用零来填充
— ASR 算术右移 通用寄存器,ASR 操作数
MOV R0, R1, ASR#2 ;将R1 中的内容右移两位后传送到R0 中,左端用第31 位的值来填充
— ASL 算术左移 通用寄存器,ASR 操作数
MOV R0, R1, ASL#2 ;将R1 中的内容右移两位后传送到R0 中,右端用第2 位的值来填充
— ROR 循环右移 通用寄存器,ROR 操作数
MOV R0, R1, ROR#2 ;将R1 中的内容循环右移两位后传送到R0 中。
— RRX 带扩展的循环右移 通用寄存器,RRX 操作数
MOV R0, R1, RRX#2 ;将R1 中的内容进行带扩展的循环右移两位后传送到R0 中。
9、协处理器指令
ARM 微处理器可支持多达16 个协处理器,用于各种协处理操作,在程序执行的过程中,每个
协处理器只执行针对自身的协处理指令,忽略ARM 处理器和其他协处理器的指令。
ARM 的协处理器指令主要用于ARM 处理器初始化ARM 协处理器的数据处理操作,以及在
ARM 处理器的寄存器和协处理器的寄存器之间传送数据,和在ARM 协处理器的寄存器和存储器之
间传送数据。
ARM 协处理器指令包括以下5 条:
— CDP 协处理器数操作指令
— LDC 协处理器数据加载指令
— STC 协处理器数据存储指令
— MCR ARM 处理器寄存器到协处理器寄存器的数据传送指令
— MRC 协处理器寄存器到ARM 处理器寄存器的数据传送指令
---CDP 指令
CDP 指令的格式为:
CDP{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
CDP 指令用于ARM 处理器通知ARM 协处理器执行特定的操作,若协处理器不能成功完成特定的操
作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操
作,目的寄存器和源寄存器均为协处理器的寄存器,指令不涉及ARM 处理器的寄存器和存储器。
指令示例:
CDP P3,2,C12,C10,C3,4 ;该指令完成协处理器P3 的初始化
---LDC 指令
LDC 指令的格式为:
LDC{条件}{L} 协处理器编码,目的寄存器,[源寄存器]
LDC 指令用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中,若协处理器不能成功
完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数
据的传输。
指令示例:
LDC P3,C4,[R0] ;将ARM 处理器的寄存器R0 所指向的存储器中的字数据传送到协处理器P3 的寄存器C4 中。
---STC 指令
STC 指令的格式为:
STC{条件}{L} 协处理器编码,源寄存器,[目的寄存器]
STC 指令用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中,若协处理器不能成功
完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数
据的传输。
指令示例:
STC P3,C4,[R0] ;将协处理器P3 的寄存器C4 中的字数据传送到ARM 处理器的寄存器R0 所指向的存储器中。
---MCR 指令
MCR 指令的格式为:
MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成
操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的
操作,源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。
指令示例:
MCR P3,3,R0,C4,C5,6 ;该指令将ARM 处理器寄存器R0 中的数据传送到协处理器P3 的寄存器C4 和C5 中。
---MRC 指令
MRC 指令的格式为:
MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
MRC 指令用于将协处理器寄存器中的数据传送到ARM 处理器寄存器中,若协处理器不能成功完成
操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的
操作,目的寄存器为ARM 处理器的寄存器,源寄存器1 和源寄存器2 均为协处理器的寄存器。
指令示例:
MRC P3,3,R0,C4,C5,6 ;该指令将协处理器P3 的寄存器中的数据传送到ARM 处理器寄存器
arm处理器异常处理
ARM处理器共有7中运行模式:
用户模式(usr) -- 正常程序执行模式
|-- |-- 快速中断模式(fiq) -- 用于高速数据传输和通道处理
特 | 异 | 外部中断模式(irq) -- 用于通常的中断处理
权 --| 常 --| 管理员模式(svc) -- 供操作系统使用的一种保护模式
模 | 模 | 数据访问中止模式(abt) -- 用于虚拟存储及存储保护
式 | 式 |-- 未定义指令中止模式(und) -- 用于支持通过软件仿真硬件的协处理器
|-- 系统模式(sys) -- 用于运行特权级的操作系统任务
特权模式:fiq、irq、svc、abt、und、sys -- 程序可以访问所有的系统资源,也可以任意切换处理器模式
异常模式:fiq、irq、svc、abt、und
ARM异常中断种类及优先级:
优先级 异常中断名称
高 复位(reset)
|| 数据访问中止(data abort)
|| 快速中断请求(FIQ)
|| 外部中断请求(IRQ)
/||/ 指令预取中止(prefetch abort)
// 软中断(SWI)
低 未定义指令(undefined instruction)
异常向量表:
地址 异常中断名称 优先级
. .
. .
. .
0x1c FIQ 3
0x18 IRQ 4
0x14 Reserved X
0x10 Data Abort 2
0x0c Prefetch Abort 4
0x08 SWI 5
0x04 Undefined Instruction 6
0x00 Reset 1
Vector Table可以位于0x0或者0xFFFF0000处(ARM720T、ARM9、ARM10..)
优先级为3的FIQ为什么放在地址0x1c处呢?当初这么设计应该是为了更加快速地响应FIQ中断,也就是说不在0x1c地
址放跳转指令,而是直接存放最关键的FIQ处理代码在0x1c开始一段地址区域内。
异常进入及返回:
当异常产生时:
1. 拷贝当前模式的CPSR值到相应异常模式的SPSR_<mode>,如:CPSR(usr) --> SPSR_svc(svc)。
2. 设置适当的CPSR位:
改变处理器状态进入ARM状态
改变处理器模式进入相应的异常模式
如果需要可以设置中断禁止位禁止相应中断
3. 保存返回地址(pc-4)到LR_<mode>。
4. 设置pc为相应的异常向量。
异常返回时,需要:
1. 从SPSR_<mode>恢复CPSR。
2. 从LR_<mode>恢复pc
(只能在ARM状态下实现该返回操作)
异常返回的指令分析:
* 使用一数据处理指令实现,该指令带“S”后缀,同时pc作为目的寄存器
* 在特权模式下不仅仅要更新pc,而且还要拷贝SPSR到CPSR
1. 从SWI和Undef异常返回:
movs pc , lr
这两种异常都会在导致异常的指令执行周期中就进入异常,没有等到下个时钟周期才进入异常,另外这两种异常都是返回到产生异常指令的下一条指令去继续执行。我从前面可以知道lr中保存的就是pc-4(该pc值是产生异常指令的下下一条指令的地址),所以可以直接将lr的值送入pc。
2. 从FIQ、IRQ和Prefect Abort返回:
subs pc , lr , #4
这三种异常都会等待产生异常的指令执行完才会进入异常,所以此刻的pc已经更新,比如:
...
subs r3, r3, #1 << 26 @ 0x100
bcs 2b @ 0x104
subs r1, r1, #1 << 5 @ 0x108
bcs 1b @ 0x10b
...
如果在执行第1行地址为0x100指令时,发生了上面三种异常,此刻的pc值为0x108,等第1行执行完之后,pc更新为0x10b。同时进入异常处理,在异常进入时将lr值保存为pc-4,即0x108。那么在异常返回后,需要接着执行发生异常指令的下一条指令的话就必须将lr的值减去4才能得到正确的地址,lr-4 = 0x108 - 4 = 0x104。
3. 从数据异常(Data Abort)异常返回:
该异常也是会等待产生异常的那条指令执行完才会进入异常,情况类似于第2类的三种异常,但是有一点不同的是:数据异常返回地址不是产生异常的下一条指令,而是产生异常的那条指令,所以,它的返回指令应该是:
subs pc , lr , #8
按照上面的例子就应该返回到地址0x100处继续执行。为什么会这样,因为数据异常返回后会继续去取数据,想想缺页异常。
4. 如果lr在进入异常后被压栈的话,就需要使用下面的指令来弹出。
LDMFD sp! , {pc}^
(^同时拷贝SPSR到CPSR中,这里的lr在压栈之前已经做了前面3中情况对应的处理了)
SWI异常:
执行SWI软中断指令即可产生软中断异常,进入SWI异常时会做如下动作:
CSPR保存到SPSR_svc。
改变处理器状态进入ARM状态
改变处理器模式进入相应的管理员模式(svc)
看需要禁止中断
保存返回地址(pc-4)到LR_svc
设置pc为0x08或者0xFFFF0008
** 需要注意一点的是:如果在执行SWI指令时系统正处于svc模式时,那么将会覆盖掉原来LR_svc的值。所以
在SWI指令之前应该对LR_svc压栈保存。
SWI异常返回时,做如下动作:
从SPSR_svc恢复CPSR
从LR_svc恢复pc,不需要修正
在c语言中使用关键字“__swi”来定义一个软中断函数:
__swi(0x30) void my_swi(void);
void fun(void)
{
my_swi();
}
-----> 转换成汇编
fun
STMFD sp! , {lr}
swi 0x24
LDMFD sp! , {pc}
swi调用带参和swi处理函数带参:
swi调用带参:
使用swi指令时,通常有两种方法来传递参数:
1. 使用swi号
swi指令的低24bits(ARM指令集)组成或者低8bits(Thumb指令集)来指定软中断号,
其余参数通过寄存器来传递。
2. r0决定软中断号,其余参数使用同样寄存器传递。
** c语言中,关键字“__swi”定义的软中断函数允许最多4个参数,使用r0~r4来传递。
swi处理函数带参:
1. 汇编中,存取调用者设置的寄存器即可
2. 传参给c,通常才用压栈的方法:
将参数压栈,给调用的函数传递一个指向这些参数的指针。
获取SWI号:
ARM core不提供直接传递软中断号到处理程序的机制,SWI处理程序必须定位SWI指令并提取SWI指令中的常数域
1. 检查SPSR_svc的Tbit,可以确定陷入swi异常之前的指令时ARM指令集还是Thumb指令集。
2. 然后通过LR_svc的值确定SWI指令的地址。ARM状态下是LR-4,而Thumb状态下是LR-2的位置。
3. SWI指令格式:
ARM态:
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
| Cond |1 1 1 1 | SWI number |
Thumb态
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
| 1 1 0 1 1 1 1 1| SWI number |