ARM微處理器的編程模型之:異常中斷處理
4.在特權(quán)模式下使用SWI異常處理
在特權(quán)模式下使用SWI異常處理,和IRQ/FIQ中斷嵌套基本類似。當(dāng)執(zhí)行SWI指令后,處理器執(zhí)行下面操作。
① 處理器進(jìn)入特權(quán)模式。
② 將程序狀態(tài)字內(nèi)容CPSR保存到SPSR_svc。
③ 返回地址放入LR_svc。
如果處理器已經(jīng)處于特權(quán)模式,再發(fā)生SWI異常,則LR_svc和SPSR_svc寄存器的值將丟失。
所以在特權(quán)模式下,調(diào)用SWI軟中斷異常,必須先將LR_svc和SPSR_svc寄存器的值壓棧保護(hù)。下面的例子顯示了一個(gè)可以在特權(quán)模式下調(diào)用的SWI處理函數(shù)。
AREA SWI_Area, CODE, READONLY
PRESERVE8
EXPORT SWI_Handler
IMPORT C_SWI_Handler
T_bit EQU 0x20
SWI_Handler
STMFD sp!,{r0-r3,r12,lr} ;寄存器壓棧保護(hù)
MOV r1, sp ;堆棧指針?lè)舝1作為參數(shù)傳遞.
MRS r0, spsr ;讀取spsr.
STMFD sp!, {r0, r3} ;將spsr壓棧保護(hù)
;
;
LDR r0,[lr,#-4] ;計(jì)算SWI指令地址.
BIC r0,r0,#0xFF000000 ;讀取SWI中斷向量號(hào).
; r0存放中斷向量號(hào)
; r1 堆棧指針
BL C_SWI_Handler ;調(diào)用C程序的SWI處理函數(shù).
LDMFD sp!, {r0, r3} ;從堆棧中讀取spsr.
MSR spsr_cf, r0 ;恢復(fù)spcr
LDMFD sp!, {r0-r3,r12,pc}^ ;恢復(fù)其他寄存器并返回.
END
5.從應(yīng)用程序中調(diào)用SWI
可從匯編語(yǔ)言或 C/C++ 中調(diào)用 SWI。
(1)從匯編應(yīng)用程序中調(diào)用SWI
從匯編語(yǔ)言程序中調(diào)用SWI,只要遵循AAPCS標(biāo)準(zhǔn)即可。調(diào)用前,設(shè)定所有必須的值并發(fā)出相關(guān)的 SWI。例如:
MOV r0, #65 ; 將軟中斷的子功能號(hào)放到r0中
SWI 0x0
注意 | SWI指令和其他所以ARM指令一樣,可以被條件執(zhí)行。 |
(2)從C應(yīng)用程序中調(diào)用SWI
在C或C++應(yīng)用程序中調(diào)用SWI,要將C語(yǔ)言的子程序用編譯器擴(kuò)展_swi聲明,例如:
__swi(0) void my_swi(int);
……
……
……
my_swi(65);
編譯器擴(kuò)展_swi確保了SWI以內(nèi)聯(lián)方式進(jìn)行編譯,而沒(méi)有額外的開銷。但有如下的AAPCS限制。
· 函數(shù)調(diào)用參數(shù)只能使用r0~r3傳遞。
· 函數(shù)返回值只能通過(guò)r0~r3傳遞。
向內(nèi)聯(lián)的SWI函數(shù)傳遞參數(shù)和向?qū)嶋H的子函數(shù)傳遞參數(shù)基本類似。但返回值的情況比較復(fù)雜。如果有兩到四個(gè)返回值,則必須告訴編譯程序返回值是以結(jié)構(gòu)形式返回的,并使用__value_in_regs 偽操作聲明。這是因?yàn)榛诮Y(jié)構(gòu)值的函數(shù)通常被處理為一個(gè)void(空)型函數(shù),且第一個(gè)自變量必須為存放結(jié)果結(jié)構(gòu)的地址。
下面的例子顯示了對(duì)編號(hào)為0x0、0x1、0x2和0x3的SWI軟中斷的調(diào)用。其中,SWI0x0和SWI0x1傳遞兩個(gè)整型參數(shù)并返回一個(gè)單一結(jié)果;SWI0x2傳遞4個(gè)參數(shù)并返回一個(gè)單一結(jié)果;而SWI0x3傳遞4個(gè)參數(shù)并通過(guò)結(jié)構(gòu)體返回4個(gè)結(jié)果。
#include stdio.h>
#include swi.h
unsigned *swi_vec = (unsigned *)0x08;
extern void SWI_Handler(void);
int main( void )
{
int result1, result2;
struct four_results res_3;
Install_Handler( (unsigned) SWI_Handler, swi_vec );
printf(result1 = multiply_two(2,4) = %dn, result1 = multiply_two(2,4));
printf(result2 = multiply_two(3,6) = %dn, result2 = multiply_two(3,6));
printf(add_two( result1, result2 ) = %dn, add_two( result1, result2 ));
printf(add_multiply_two(2,4,3,6) = %dn, add_multiply_two(2,4,3,6));
res_3 = many_operations( 12, 4, 3, 1 );
printf(res_3.a = %dn, res_3.a );
printf(res_3.b = %dn, res_3.b );
printf(res_3.c = %dn, res_3.c );
printf(res_3.d = %dn, res_3.d );
return 0;
}
__swi(0) int multiply_two(int, int);
__swi(1) int add_two(int, int);
__swi(2) int add_multiply_two(int, int, int, int);
struct four_results
{
int a;
int b;
int c;
int d;
};
__swi(3) __value_in_regs struct four_results many_operations(int, int, int, int);
(3)應(yīng)用程序中動(dòng)態(tài)調(diào)用SWI
在某些情形下,需要調(diào)用直到運(yùn)行時(shí)才會(huì)知道其編號(hào)的 SWI。例如,當(dāng)有很多相關(guān)操作可在同一目標(biāo)上執(zhí)行,并且每一個(gè)操作都有其自己的 SWI 時(shí),就會(huì)發(fā)生這種情況。在此情況下,上一小節(jié)的方法不適用。
解決的方法有兩種。
· 在運(yùn)行時(shí)得到SWI功能號(hào),然后構(gòu)造出相應(yīng)的SWI指令的編碼,將該編碼保存在某個(gè)存儲(chǔ)單元中,將PC指針指向該單元,執(zhí)行指令。
· 使用一個(gè)通用的SWI異常中斷處理程序,將運(yùn)行時(shí)需要調(diào)用的SWI功能號(hào)作為參數(shù)傳遞給該通用的SWI異常處理程序,通用的SWI異常中斷處理程序根據(jù)參數(shù)值調(diào)用相應(yīng)的SWI處理程序完成需要的操作。
通過(guò)匯編語(yǔ)言可以實(shí)現(xiàn)第二種解決辦法:通過(guò)寄存器(通常為r0或r12)傳遞所需要的操作數(shù),這樣可以重新編寫SWI處理程序,對(duì)相應(yīng)寄存器中的值進(jìn)行處理。
但有些情況下,為了節(jié)省程序開銷,需要直接使用SWI中斷號(hào)對(duì)程序調(diào)用。例如,操作系統(tǒng)可能會(huì)使用單一的一條SWI指令并用寄存器來(lái)傳遞所需運(yùn)算的編號(hào)。這使得其他SWI空間可用于特定應(yīng)用程序的SWI。在一個(gè)特定的應(yīng)用程序中,如果從指令中提取SWI編號(hào)的開銷太大,就可使用這個(gè)方法。ARM(0x123456)和Thumb(0xAB)半主機(jī)方式的SWI就是這樣實(shí)現(xiàn)的。
評(píng)論