|
在特权模式运行的程序可通过改变CPSR寄存器轻松的实现CPU的工作模式切换,那么用户模式如果有改变CPU工作模式的需求怎么办呢?
在用户模式下只有中断才能将CPU的工作模式转换到特权模式,而软件中断同样有此特性,它似乎就是为此目的而设计。
软件中断在汇编中只需要一条指令,在C语言中需要关键字__swi(x),在C语言中调用软件中断需要声明一个软件中断调用函数,如:
void __swi(0)
EnterSystem(void);
表示此函数的调用将触发软件中断0.而这个函数只需要声明其原型,而不需要实现代码,中断的执行
代码由启动程序中的软件中断向量分发处理。为此如果软件中断的例程仍是由C编写的那么必须在软件中断向量中调用此C程序。另外,软件中断
函数的原型格式是任意的,可以有返回值及一个或更多的参数。如:
int __swi(1) Add(int x,int y);
下面让我们看一下RealView
MDK-ARM中软件中断示例:
首先是启动代码(sam7.s)中的相关部分:
Vectors LDR PC,Reset_Addr
LDR PC,Undef_Addr
LDR PC,SWI_Addr
LDR PC,PAbt_Addr
LDR PC,DAbt_Addr
NOP ; Reserved
Vector
; LDR PC,IRQ_Addr
LDR PC,[PC,#-0xF20]
; Vector From AIC_IVR
; LDR PC,FIQ_Addr
LDR PC,[PC,#-0xF20]
; Vector From AIC_FVR
IMPORT
SWI_Handler
Reset_Addr DCD
Reset_Handler
Undef_Addr DCD
Undef_Handler
SWI_Addr DCD
SWI_Handler
PAbt_Addr DCD
PAbt_Handler
DAbt_Addr DCD
DAbt_Handler
DCD 0 ; Reserved
Address
IRQ_Addr DCD
IRQ_Handler
FIQ_Addr DCD
FIQ_Handler
软件中断向量转到了SWI_Handler,这个SWI_Handler在另一个汇编程序文件swi.s中实现:
T_Bit EQU 0x20
PRESERVE8 ; 8-Byte aligned Stack
AREA SWI_Area, CODE, READONLY
ARM
EXPORT
SWI_Handler
SWI_Handler
STMFD
SP!, {R12, LR} ; Store R12, LR
MRS
R12, SPSR ; Get SPSR
STMFD
SP!, {R8, R12} ; Store R8, SPSR
TST
R12, #T_Bit ; Check Thumb Bit
LDRNEH
R12, [LR,#-2] ; Thumb: Load Halfword
BICNE
R12, R12, #0xFF00 ; Extract SWI Number
LDREQ
R12, [LR,#-4] ; ARM: Load Word
BICEQ
R12, R12, #0xFF000000 ; Extract SWI Number
LDR
R8, SWI_Count
CMP
R12, R8
BHS
SWI_Dead ; Overflow
ADR
R8, SWI_Table
LDR
R12, [R8,R12,LSL #2] ; Load SWI Function Address
MOV LR, PC ; Return Address
BX R12 ; Call
SWI Function
LDMFD
SP!, {R8, R12} ; Load R8, SPSR
MSR
SPSR_cxsf, R12 ; Set SPSR
LDMFD
SP!, {R12, PC}^ ; Restore R12 and Return
SWI_Dead
B SWI_Dead ; None Existing SWI
SWI_Cnt
EQU (SWI_End-SWI_Table)/4
SWI_Count
DCD SWI_Cnt
IMPORT __SWI_0
IMPORT __SWI_1
IMPORT __SWI_2
IMPORT
__SWI_3
SWI_Table
DCD
__SWI_0 ; SWI 0 Function Entry
DCD
__SWI_1 ; SWI 1 Function Entry
DCD
__SWI_2 ; SWI 2 Function Entry
DCD __SWI_3 ; SWI 3 Function Entry
; ...
SWI_End
END
在这段代码中调用了C程序中实现的四个处理函数__SWI_0 ~
__SWI_3,分别用于处理0,1,2,3号软件中断,C程序实现如下:
int __swi(0)
add (int i1, int i2);//声明原型
int __SWI_0
(int i1, int i2) {//中断中回调的实现函数
return (i1 +
i2);
}
int __swi(1)
mul4(int i);//声明原型
int __SWI_1
(int i) {//中断中回调的实现函数
return (i <<
2);
}
int __swi(2)
div (int i1, int i2); //声明原型
int __SWI_2
(int i1, int i2) { //中断中回调的实现函数
return (i1 /
i2);
}
int __swi(3)
mod (int i1, int i2);//声明原型
int __SWI_3
(int i1, int i2) {//中断中回调的实现函数
return (i1 %
i2);
}
RealView这个示例除了示意没有任何意义,因为简单的加减乘除操作不需要在管理模式下运算,下面我们来实现一个非常实际的功能,就是
通过软件中断改变全局中断标记,此功能在实际中经常用到尤其在基于操作系统的应用当中。
在RealView内部函数中有__enable_irq,__disable_irq等相应的函数,但是在用户模式下运行的C程序调用它是没什么意义的,因为在
用户模式下不能改变CPSR寄存器,所以这些函数也不可能让我们达到目的,唯一的办法就得用软件中断,在中断处理程序中改变CPSR寄存器使
全局中断功能禁止或允许。以下是上面示例基础上实现的此功能:
T_Bit EQU 0x20
PRESERVE8 ; 8-Byte aligned Stack
AREA SWI_Area, CODE, READONLY
ARM
EXPORT SWI_Handler
SWI_Handler
STMFD SP!,
{R12, LR} ; Store R12, LR
MRS R12,
SPSR ; Get SPSR
STMFD SP!,
{R8, R12} ; Store R8, SPSR
TST R12, #T_Bit
; Check Thumb Bit
LDRNEH R12,
[LR,#-2] ; Thumb: Load Halfword
BICNE R12,
R12, #0xFF00 ; Extract SWI Number
LDREQ R12, [LR,#-4]
; ARM: Load Word
BICEQ R12,
R12, #0xFF000000 ; Extract SWI Number
; add code
to enable/disable the global IRQ flag
CMP R12,#0xFE ; disable IRQ implemented as __SWI
0xFE
BEQ disable_IRQ
CMP R12,#0xFF ; enable IRQ implemented as __SWI
0xFF
BEQ enable_IRQ
LDR R8,
SWI_Count
CMP R12, R8
BHS SWI_Dead
; Overflow
ADR R8,
SWI_Table
LDR R12,
[R8,R12,LSL #2] ; Load SWI Function Address
MOV LR, PC ; Return Address
BX R12 ; Call SWI
Function
LDMFD SP!,
{R8, R12} ; Load R8, SPSR
MSR
SPSR_cxsf, R12 ; Set SPSR
LDMFD SP!,
{R12, PC}^ ; Restore R12 and Return
SWI_Dead
B SWI_Dead ; None Existing SWI
SWI_Cnt
EQU (SWI_End-SWI_Table)/4
SWI_Count
DCD SWI_Cnt
IMPORT __SWI_0
IMPORT __SWI_1
IMPORT __SWI_2
IMPORT
__SWI_3
SWI_Table
DCD __SWI_0
; SWI 0 Function Entry
DCD __SWI_1
; SWI 1 Function Entry
DCD __SWI_2
; SWI 2 Function Entry
DCD __SWI_3 ; SWI 3 Function Entry
; ...
SWI_End
disable_IRQ
LDMFD SP!, {R8, R12} ; Load R8, SPSR
ORR R12, R12, #0x80 ; Set IRQ flag to disable it
MSR SPSR_cxsf, R12 ; Set SPSR
LDMFD SP!, {R12, PC}^ ; Restore R12 and Return
enable_IRQ
LDMFD SP!, {R8, R12} ; Load R8, SPSR
BIC R12, R12, #0x80 ; Set IRQ flag to disable it
MSR SPSR_cxsf, R12 ; Set SPSR
LDMFD SP!, {R12, PC}^ ; Restore R12 and Return
END
棕色字体为增加部分,在C程序中软件中断格式如下:
void __swi(0xFE) disable_irq
(void);
void __swi(0xFF) enable_irq
(void);
如果我们的系统没有用到其它SWI中断功能,完全可以不这样啰嗦,直接将简单的处理程序添加到启动代码即可:
在KEIL提供的标准启动代码中添加:
SWI_Handler
CMP
R0, #4
LDRLO
PC, [PC, R0, LSL #2]
MOVS
PC, LR
SwiFunction
DCD
IRQDisable ;0
DCD
IRQEnable ;1
DCD
FIQDisable ;2
DCD
FIQEnable ;3
IRQDisable
;关IRQ中断
MRS
R0, SPSR
ORR
R0, R0, #I_Bit
MSR
SPSR_c, R0
MOVS
PC, LR
IRQEnable
;开IRQ中断
MRS
R0, SPSR
BIC
R0, R0, #I_Bit
MSR
SPSR_c, R0
MOVS
PC, LR
FIQDisable
;关FIQ中断
MRS
R0, SPSR
ORR
R0, R0, #F_Bit
MSR
SPSR_c, R0
MOVS
PC, LR
FIQEnable
;开FIQ中断
MRS
R0, SPSR
BIC R0, R0, #F_Bit
MSR SPSR_c, R0
MOVS PC, LR
C程序中的声明及调用方法如下:
__swi(0) void SwiHandle1(int Handle);
#define IRQDisable() SwiHandle1(0)
#define IRQEnable() SwiHandle1(1)
#define FIQDisable() SwiHandle1(2)
#define FIQEnable() SwiHandle1(3)
//注:此方法参考了周立功公司启动代码
以下是我在示例2的基础上做的一次实现,因为示例2是靠定时器中断实现的LED闪烁,现在我要得用软件中断将中断屏蔽,如果LED停止闪烁
说明软件中断功能正确,请点击这里下载示我的测试代码。
芯艺设计室(http://www.chipart.cn) 2010.08.24 转载请注明出处!
|