?
3.4? 異常中斷處理
異?;蛑袛嗍怯脩舫绦蛑凶罨镜囊环N執(zhí)行流程和形態(tài)。這部分主要對(duì)ARM架構(gòu)下的異常中斷做詳細(xì)說明。
ARM有7種類型的異常,按優(yōu)先級(jí)從高到低的排列如下:復(fù)位異常(Reset)、數(shù)據(jù)異常(Data Abort)、快速中斷異常(FIQ)、外部中斷異常(IRQ)、預(yù)取異常(Prefetch Abort)、軟件中斷(SWI)和未定義指令異常(Undefined instruction)。
注意 |
在ARM文檔中,使用術(shù)語(yǔ)Exception來描述異常。Exception主要是從處理器被動(dòng)接受異常的角度出發(fā),而Interrupt帶有向處理器主動(dòng)申請(qǐng)的色彩。在本書中,對(duì)“異?!焙汀爸袛唷辈蛔鰢?yán)格區(qū)分,兩者都是指請(qǐng)求處理器打斷正常的程序執(zhí)行流程,進(jìn)入特定程序循環(huán)的一種機(jī)制。 |
3.4.1? 異常種類
ARM體系結(jié)構(gòu)中,存在7種異常處理。當(dāng)異常發(fā)生時(shí),處理器會(huì)把PC設(shè)置為一個(gè)特定的存儲(chǔ)器地址。這一地址放在被稱為向量表(vector table)的特定地址范圍內(nèi)。向量表的入口是一些跳轉(zhuǎn)指令,跳轉(zhuǎn)到專門處理某個(gè)異?;蛑袛嗟?a class="article-link" target="_blank" href="/baike/499965.html">子程序。
存儲(chǔ)器映射地址0x00000000是為向量表(一組32位字)保留的。在有些處理器中,向量表可以選擇定位在存儲(chǔ)空間的高地址(從偏移量0xffff0000開始)。一些嵌入式操作系統(tǒng),如Linux和Windows CE就要利用這一特性。
表3.4列出了ARM的7種異常。
表3.4? ARM的7種異常
異 常 類 型 |
處理器模式 |
執(zhí)行低地址 |
執(zhí)行高地址 |
復(fù)位異常(Reset) |
特權(quán)模式 |
0x00000000 |
0xFFFF0000 |
未定義指令異常(Undefined interrupt) |
未定義指令中止模式 |
0x00000004 |
0xFFFF0004 |
軟中斷異常(Software Abort) |
特權(quán)模式 |
0x00000008 |
0xFFFF0008 |
預(yù)取異常(Prefetch Abort) |
數(shù)據(jù)訪問中止模式 |
0x0000000C |
0xFFFF000C |
數(shù)據(jù)異常(Data Abort) |
數(shù)據(jù)訪問中止模式 |
0x00000010 |
0xFFFF0010 |
外部中斷請(qǐng)求IRQ |
外部中斷請(qǐng)求模式 |
0x00000018 |
0xFFFF0018 |
快速中斷請(qǐng)求FIQ |
快速中斷請(qǐng)求模式 |
0x0000001C |
0xFFFF001C |
異常處理向量表如圖3.5所示。
當(dāng)異常發(fā)生時(shí),分組寄存器r14和SPSR用于保存處理器狀態(tài),操作偽指令如下。
R14_<exception_mode> = return link
SPSR_<exception_mode> = CPSR
CPSR[4∶0] = exception mode number
CPSR[5] = 0?? /*進(jìn)入ARM狀態(tài)*/
If <exception_mode> = = reset or FIQ then
??? CPSR[6] = 1????????????????? /*屏蔽快速中斷FIQ*/
??? CPSR[7] = 1????????????????? /*屏蔽外部中斷IRQ*/
??? PC = exception vector address
圖3.5? 異常處理向量表
異常返回時(shí),SPSR內(nèi)容恢復(fù)到CPSR,連接寄存器r14的內(nèi)容恢復(fù)到程序計(jì)數(shù)器PC。
1.復(fù)位異常
當(dāng)處理器的復(fù)位引腳有效時(shí),系統(tǒng)產(chǎn)生復(fù)位異常中斷,程序跳轉(zhuǎn)到復(fù)位異常中斷處理程序處執(zhí)行。復(fù)位異常中斷通常用在下面兩種情況下。
·? 系統(tǒng)上電。
·? 系統(tǒng)復(fù)位。
當(dāng)復(fù)位異常時(shí),系統(tǒng)執(zhí)行下列偽操作。
R14_svc = UNPREDICTABLE value
SPSR_svc = UNPREDICTABLE value
CPSR[4∶0] = 0b10011????????? /*進(jìn)入特權(quán)模式*/
CPSR[5] = 0????????????????? /*處理器進(jìn)入ARM狀態(tài)*/
CPSR[6] = 1????????????????? /*禁止快速中斷*/
CPSR[7] = 1????????????????? /*禁止外設(shè)中斷*/
If high vectors configured then
??? PC = 0xffff0000
Else
??? PC = 0x00000000
?
復(fù)位異常中斷處理程序?qū)⑦M(jìn)行一些初始化工作,內(nèi)容與具體系統(tǒng)相關(guān)。下面是復(fù)位異常中斷處理程序的主要功能。
·? 設(shè)置異常中斷向量表。
·? 初始化數(shù)據(jù)棧和寄存器。
·? 初始化存儲(chǔ)系統(tǒng),如系統(tǒng)中的MMU等。
·? 初始化關(guān)鍵的I/O設(shè)備。
·? 使能中斷。
·? 處理器切換到合適的模式。
·? 初始化C變量,跳轉(zhuǎn)到應(yīng)用程序執(zhí)行。
2.未定義指令異常
當(dāng)ARM處理器執(zhí)行協(xié)處理器指令時(shí),它必須等待一個(gè)外部協(xié)處理器應(yīng)答后,才能真正執(zhí)行這條指令。若協(xié)處理器沒有相應(yīng),則發(fā)生未定義指令異常。
未定義指令異??捎糜谠跊]有物理協(xié)處理器的系統(tǒng)上,對(duì)協(xié)處理器進(jìn)行軟件仿真,或通過軟件仿真實(shí)現(xiàn)指令集擴(kuò)展。例如,在一個(gè)不包含浮點(diǎn)運(yùn)算的系統(tǒng)中,CPU遇到浮點(diǎn)運(yùn)算指令時(shí),將發(fā)生未定義指令異常中斷,在該未定義指令異常中斷的處理程序中可以通過其他指令序列仿真浮點(diǎn)運(yùn)算指令。
仿真功能可以通過下面步驟實(shí)現(xiàn)。
① 將仿真程序入口地址鏈接到向量表中未定義指令異常中斷入口處(0x00000004或0xffff0004),并保存原來的中斷處理程序。
② 讀取該未定義指令的bits[27∶24],判斷其是否是一條協(xié)處理器指令。如果bits[27∶24]值為0b1110或0b110x,該指令是一條協(xié)處理器指令;否則,由軟件仿真實(shí)現(xiàn)協(xié)處理器功能,可以同過bits[11∶8]來判斷要仿真的協(xié)處理器功能(類似于SWI異常實(shí)現(xiàn)機(jī)制)。
③ 如果不仿真該未定義指令,程序跳轉(zhuǎn)到原來的未定義指令異常中斷的中斷處理程序執(zhí)行。
當(dāng)未定義異常發(fā)生時(shí),系統(tǒng)執(zhí)行下列的偽操作。
r14_und = address of next instruction after the undefined instruction
SPSR_und = CPSR
CPSR[4∶0] = 0b11011?????????????? /*進(jìn)入未定義指令模式*/
CPSR[5] = 0????????????????????? /*處理器進(jìn)入ARM狀態(tài)*/
/*CPSR[6]保持不變*/
CPSR[7] = 1????????????????????? /*禁止外設(shè)中斷*/
If? high vectors configured then
????? PC = 0xffff0004
Else
????? PC = 0x00000004
3.軟中斷SWI
軟中斷異常發(fā)生時(shí),處理器進(jìn)入特權(quán)模式,執(zhí)行一些特權(quán)模式下的操作系統(tǒng)功能。軟中斷異常發(fā)生時(shí),處理器執(zhí)行下列偽操作。
r14_svc = address of next instruction after the SWI instruction
SPSR_und = CPSR
CPSR[4∶0] = 0b10011????????????? /*進(jìn)入特權(quán)模式*/
CPSR[5] = 0???????????????????? /*處理器進(jìn)入ARM狀態(tài)*/
/*CPSR[6]保持不變*/
CPSR[7] = 1???????????????????? /*禁止外設(shè)中斷*/
If? high vectors configured then
????? PC = 0xffff0008
Else
????? PC = 0x00000008
4.預(yù)取指令異常
預(yù)取指令異常使由系統(tǒng)存儲(chǔ)器報(bào)告的。當(dāng)處理器試圖去取一條被標(biāo)記為預(yù)取無效的指令時(shí),發(fā)生預(yù)取異常。
如果系統(tǒng)中不包含MMU時(shí),指令預(yù)取異常中斷處理程序只是簡(jiǎn)單地報(bào)告錯(cuò)誤并退出。若包含MMU,引起異常的指令的物理地址被存儲(chǔ)到內(nèi)存中。
預(yù)取異常發(fā)生時(shí),處理器執(zhí)行下列偽操作。
r14_svc = address of the aborted instruction + 4
SPSR_und = CPSR
CPSR[4∶0] = 0b10111?????????????? /*進(jìn)入特權(quán)模式*/
CPSR[5] = 0????????????????????? /*處理器進(jìn)入ARM狀態(tài)*/
/*CPSR[6]保持不變*/
CPSR[7] = 1????????????????????? /*禁止外設(shè)中斷*/
If? high vectors configured then
???? PC = 0xffff000C
Else
???? PC = 0x0000000C
5.?dāng)?shù)據(jù)訪問中止異常
數(shù)據(jù)訪問中止異常是由存儲(chǔ)器發(fā)出數(shù)據(jù)中止信號(hào),它由存儲(chǔ)器訪問指令Load/Store產(chǎn)生。當(dāng)數(shù)據(jù)訪問指令的目標(biāo)地址不存在或者該地址不允許當(dāng)前指令訪問時(shí),處理器產(chǎn)生數(shù)據(jù)訪問中止異常。
當(dāng)數(shù)據(jù)訪問中止異常發(fā)生時(shí),處理器執(zhí)行下列偽操作。
r14_abt = address of the aborted instruction + 8
SPSR_abt = CPSR
CPSR[4∶0] = 0b10111
CPSR[5] = 0
/*CPSR[6]保持不變*/
CPSR[7] = 1????????????? /*禁止外設(shè)中斷*/
If? high vectors configured then
???? PC = 0xffff000C10
Else
???? PC = 0x00000010
?
當(dāng)數(shù)據(jù)訪問中止異常發(fā)生時(shí),寄存器的值將根據(jù)以下規(guī)則進(jìn)行修改。
① 返回地址寄存器r14的值只與發(fā)生數(shù)據(jù)異常的指令地址有關(guān),與PC值無關(guān)。
② 如果指令中沒有指定基址寄存器回寫,則基址寄存器的值不變。
③ 如果指令中指定了基址寄存器回寫,則寄存器的值和具體芯片的Abort Models有關(guān),由芯片的生產(chǎn)商指定。
④ 如果指令只加載一個(gè)通用寄存器的值,則通用寄存器的值不變。
⑤ 如果是批量加載指令,則寄存器中的值是不可預(yù)知的值。
⑥ 如果指令加載協(xié)處理器寄存器的值,則被加載寄存器的值不可預(yù)知。
6.外部中斷IRQ
當(dāng)處理器的外部中斷請(qǐng)求引腳有效,而且CPSR寄存器的I控制位被清除時(shí),處理器產(chǎn)生外部中斷IRQ異常。系統(tǒng)中各外部設(shè)備通常通過該異常中斷請(qǐng)求處理器服務(wù)。
當(dāng)外部中斷IRQ發(fā)生時(shí),處理器執(zhí)行下列偽操作。
r14_irq = address of next instruction to be executed + 4
SPSR_irq = CPSR
CPSR[4∶0] = 0b10010????????????? /*進(jìn)入特權(quán)模式*/
CPSR[5] = 0???????????????????? /*處理器進(jìn)入ARM狀態(tài)*/
/*CPSR[6]保持不變*/
CPSR[7] = 1???????????????????? /*禁止外設(shè)中斷*/
If? high vectors configured then
???? PC = 0xffff0018
Else
???? PC = 0x00000018
7.快速中斷FIQ
當(dāng)處理器的快速中斷請(qǐng)求引腳有效且CPSR寄存器的F控制位被清除時(shí),處理器產(chǎn)生快速中斷請(qǐng)求FIQ異常。
當(dāng)快速中斷異常發(fā)生時(shí),處理器執(zhí)行下列偽操作。
r14_fiq = address of next instruction to be executed + 4
SPSR_fiq = CPSR
CPSR[4∶0] = 0b10001????????? /*進(jìn)入FIQ模式*/
CPSR[5] = 0
CPSR[6] = 1
CPSR[7] = 1
If? high vectors configured then
???? PC= 0xffff001c
Else
???? PC = 0x0000001c
3.4.2? 異常優(yōu)先級(jí)
每一種異常按表3.5中設(shè)置的優(yōu)先級(jí)得到處理。
表3.5???? 異常優(yōu)先級(jí)
優(yōu)? 先? 級(jí) |
異??? 常 |
最高????????? 1 |
復(fù)位異常 |
????????????? 2 |
數(shù)據(jù)中止 |
????????????? 3 |
快速中斷請(qǐng)求 |
????????????? 4 |
中斷請(qǐng)求 |
????????????? 5 |
預(yù)取指令異常 |
????????????? 6 |
軟件中斷 |
最低????????? 7 |
未定義指令 |
異常可以同時(shí)發(fā)生,處理器按表3.5的優(yōu)先級(jí)順序處理異常。例如,復(fù)位異常的優(yōu)先級(jí)最高,處理器上電時(shí)發(fā)生復(fù)位異常。所以當(dāng)產(chǎn)生復(fù)位時(shí),它將優(yōu)先于其他異常得到處理。同樣,當(dāng)一個(gè)數(shù)據(jù)訪問中止異常發(fā)生時(shí),它將優(yōu)先于除復(fù)位異常外的其他所有異常。
優(yōu)先級(jí)最低的2種異常是軟件中斷和未定義指令異常。因?yàn)檎趫?zhí)行的指令不可能既是一條SWI指令,又是一條未定義指令,所以軟件中斷異常SWI和未定義指令異享有相同的優(yōu)先級(jí)。
3.4.3? 處理器模式和異常
每一種異常都會(huì)導(dǎo)致內(nèi)核進(jìn)入一種特定的模式。表3.6顯示了ARM處理器異常及其對(duì)應(yīng)的模式。此外,也可以通過編程改變CPSR,進(jìn)入任何一種ARM處理器模式。
注意 |
用戶和系統(tǒng)模式是僅有的不可通過異常進(jìn)入的兩種模式,也就是說,要進(jìn)入這兩種模式,必須通過編程改變CPSR。 |
表3.6? ARM處理器異常及其對(duì)應(yīng)模式
異??? 常 |
模??? 式 |
用??? 途 |
快速中斷請(qǐng)求 |
FIQ |
進(jìn)行快速中斷請(qǐng)求處理 |
外部中斷請(qǐng)求 |
IRQ |
進(jìn)行外部中斷請(qǐng)求處理 |
SWI |
SVC |
進(jìn)行操作系統(tǒng)的高級(jí)處理 |
復(fù)位 |
SVC |
進(jìn)行操作系統(tǒng)的高級(jí)處理 |
預(yù)取指令中止異常 |
ABORT |
虛存和存儲(chǔ)器保護(hù) |
數(shù)據(jù)中止異常 |
ABORT |
虛存和存儲(chǔ)器保護(hù) |
未定義指令 |
Undefined |
軟件模擬硬件協(xié)處理器 |
?
3.4.4? 異常響應(yīng)流程
1.判斷處理器狀態(tài)
當(dāng)異常發(fā)生時(shí),處理器自動(dòng)切換到ARM狀態(tài),所以在異常處理函數(shù)中要判斷在異常發(fā)生前處理器是ARM狀態(tài)還是Thumb狀態(tài)。這可以通過檢測(cè)SPSR的T位來判斷。
通常情況下,只有在SWI處理函數(shù)中才需要知道異常發(fā)生前處理器的狀態(tài)。所以在Thumb狀態(tài)下,調(diào)用SWI軟中斷異常必須注意以下兩點(diǎn)。
① 發(fā)生異常的指令地址為(lr-2)而不是(lr-4)。
② Thumb狀態(tài)下的指令是16位的,在判斷中斷向量號(hào)時(shí)使用半字加載指令LDRH。
下面的例子顯示了一個(gè)標(biāo)準(zhǔn)的SWI處理函數(shù),在函數(shù)中通過SPSR的T位判斷異常發(fā)生前的處理器狀態(tài)。
T_bit EQU 0x20??????????????? ; bit 5. SPSR中的ARM/Thumb狀態(tài)位,
:
:
SWIHandler
STMFD sp!, {r0-r3,r12,lr}????? ; 寄存器壓棧,保護(hù)程序現(xiàn)場(chǎng)
MRS r0, spsr???????????????? ; 讀SPSR寄存器,判斷異常發(fā)生前的處理器狀態(tài)
TST r0, #T_bit??????????????? ; 檢測(cè)SPSR的T位,判斷異常發(fā)生前是否為Thumb狀態(tài)
LDRNEH r0,[lr,#-2]??????????? ; 如果是Thumb狀態(tài),使用半字加載指令讀取發(fā)生異常的指令地址
BICNE r0,r0,#0xFF00?????????? ; .提取中斷向量號(hào).
LDREQ r0,[lr,#-4]???????????? ; 如果是ARM狀態(tài),使用字加載指令,讀取發(fā)生異常的指令地址
BICEQ r0,r0,#0xFF000000??????? ; 提取中斷向量號(hào)并將中斷向量號(hào)存入r0
; r0 存儲(chǔ)中斷向量號(hào)
CMP r0, #MaxSWI?????????????? ; 判斷中斷是否超出范圍
LDRLS pc, [pc, r0, LSL#2]????? ; 如果未超出范圍,跳轉(zhuǎn)到軟中斷向量表Switable
B SWIOutOfRange?????????????? ; 如果超出范圍,跳轉(zhuǎn)到軟中斷越界處理程序
switable
DCD do_swi_1
DCD do_swi_2
:
:
do_swi_1
; 1號(hào)軟中斷處理函數(shù)
LDMFD sp!, {r0-r3,r12,pc}^ ; Restore the registers and return.
????????????????????????? ; 恢復(fù)寄存器并返回
do_swi_2 ??????????????? ?? ; 2號(hào)軟中斷處理函數(shù)
:
2.向量表
如前面介紹向量表時(shí)提到的,每一個(gè)異常發(fā)生時(shí)總是從異常向量表開始跳轉(zhuǎn)。最簡(jiǎn)單的一種情況是向量表里面的每一條指令直接跳向?qū)?yīng)的異常處理函數(shù)。其中快速中斷處理函數(shù)FIQ_handler()可以直接從地址0x1C處開始,省下一條跳轉(zhuǎn)指令,如圖3.6所示。
圖3.6? 異常處理向量表
但跳轉(zhuǎn)指令B的跳轉(zhuǎn)范圍為±32MB,但很多情況下不能保證所有的異常處理函數(shù)都定位在向量的32MB范圍內(nèi),需要更大范圍的跳轉(zhuǎn),而且由于向量表空間的限制,只能由一條指令完成。具體實(shí)現(xiàn)方法有下面兩種。
(1)MOV? PC,#imme_value
這種辦法將目標(biāo)地址直接賦值給PC。但這種方法受格式限制不能處理任意立即數(shù)。這個(gè)立即數(shù)由一個(gè)8位數(shù)值循環(huán)右移偶數(shù)位得到。
(2)LDR? PC,[PC+offset]
把目標(biāo)地址先存儲(chǔ)在某一個(gè)合適的地址空間,然后把這個(gè)存儲(chǔ)器單元的32位數(shù)據(jù)傳送給PC來實(shí)現(xiàn)跳轉(zhuǎn)。
這種方法對(duì)目標(biāo)地址值沒有要求。但是存儲(chǔ)目標(biāo)地址的存儲(chǔ)器單元必須在當(dāng)前指令的±4KB空間范圍內(nèi)。
注意 |
在計(jì)算指令中引用offset數(shù)值的時(shí)候,要考慮處理器流水線中指令預(yù)取對(duì)PC值的影響。 |
?
3.4.5? 從異常處理程序中返回
當(dāng)一個(gè)異常處理返回時(shí),一共有3件事情需要處理:通用寄存器的恢復(fù)、狀態(tài)寄存器的恢復(fù)以及PC指針的恢復(fù)。通用寄存器的恢復(fù)采用一般的堆棧操作指令即可,下面重點(diǎn)介紹狀態(tài)寄存器的恢復(fù)以及PC指針的恢復(fù)。
1.恢復(fù)被中斷程序的處理器狀態(tài)
PC和CPSR的恢復(fù)可以通過一條指令來實(shí)現(xiàn),下面是3個(gè)例子。
·? MOVS? PC,LR
·? SUBS? PC,LR,#4
·? LDMFD? SP!,{PC}^
這幾條指令是普通的數(shù)據(jù)處理指令,特殊之處在于它們把程序計(jì)數(shù)器寄存器PC作為目標(biāo)寄存器,并且?guī)Я颂厥獾暮缶Y“S”或“^”。其中“S”或“^”的作用就是使指令在執(zhí)行時(shí),同時(shí)完成從SPSR到CPSR的拷貝,達(dá)到恢復(fù)狀態(tài)寄存器的目的。
2.異常的返回地址
異常返回時(shí),另一個(gè)非常重要的問題就是返回地址的確定。前面提到過,處理器進(jìn)入異常時(shí)會(huì)有一個(gè)保存LR的動(dòng)作,但是該保持值并不一定是正確中斷的返回地址。以一個(gè)簡(jiǎn)單的指令執(zhí)行流水狀態(tài)圖來對(duì)此加以說明,如圖3.7所示。
圖3.7 ?3級(jí)流水線示例
在ARM架構(gòu)里,PC值指向當(dāng)前執(zhí)行指令地址加8。也就是說,當(dāng)執(zhí)行指令A(yù)(地址0x8000)時(shí),PC等于0x8000+8=0x8008,即等于指令C的地址。假設(shè)指令A(yù)是BL指令,則當(dāng)執(zhí)行時(shí),會(huì)把PC值(0x8008)保存到LR寄存器。但是,接下來處理器會(huì)對(duì)LR進(jìn)行一次自動(dòng)調(diào)整,使LR=LR-0x4。所以,最終保存在LR里面的是圖3.5中所示的B指令地址。所以當(dāng)從BL返回時(shí),LR里面正好是正確的返回地址。
同樣的跳轉(zhuǎn)機(jī)制在所有的LR自動(dòng)保存操作中都存在。當(dāng)進(jìn)入中斷響應(yīng)時(shí),處理器對(duì)保存的LR也進(jìn)行一次自動(dòng)調(diào)整,并且跳轉(zhuǎn)動(dòng)作也是LR=LR-0x04。由此,就可以對(duì)不同異常類型的返回地址依次比較。
假設(shè)在指令B處(地址0x8004)發(fā)生了異常,進(jìn)入異常相應(yīng)后,LR經(jīng)過跳轉(zhuǎn)保存的地址值應(yīng)該是C的地址0x8008。
(1)軟中斷異常
如果發(fā)生軟中斷異常,即指令B為SWI指令,從SWI中斷返回后下一條執(zhí)行指令就是C,正好是LR寄存器保存的地址,所以只有直接把LR恢復(fù)給PC即可。
(2)IRQ或FIQ異常
如果發(fā)生的是IRQ或FIQ異常,因?yàn)橥獠恐袛嗾?qǐng)求中斷了正在執(zhí)行的指令B,當(dāng)中斷返回后,需要重新回到B指令執(zhí)行,也就是說,返回地址應(yīng)該是B(0x8004),需要把LR減4送PC。
(3)Data Abort數(shù)據(jù)中止異常
在指令B處進(jìn)入數(shù)據(jù)異常的相應(yīng),但導(dǎo)致數(shù)據(jù)異常的原因卻應(yīng)該是上一條指令A(yù)。當(dāng)中斷處理程序恢復(fù)數(shù)據(jù)異常后,要回到A重新執(zhí)行導(dǎo)致數(shù)據(jù)異常的指令,因此返回地址應(yīng)該是LR加8。
為方便起見,表3.7總結(jié)了各異常和返回地址的關(guān)系
表3.7????? 異常和返回地址
異??? 常 |
地??? 址 |
用??? 途 |
復(fù)位 |
- |
復(fù)位沒有定義LR |
數(shù)據(jù)中止 |
LR-8 |
指向?qū)е聰?shù)據(jù)中止異常的指令 |
FIQ |
LR-4 |
指向發(fā)生異常時(shí)正在執(zhí)行的指令 |
IRQ |
LR-4 |
指向發(fā)生異常時(shí)正在執(zhí)行的指令 |
預(yù)取指令中止 |
LR-4 |
指向?qū)е骂A(yù)取指令異常的那條指令 |
SWI |
LR |
執(zhí)行SWI指令的下一條指令 |
未定義指令 |
LR |
指向未定義指令的下一條指令 |
?
3.4.6? 在應(yīng)用程序中安裝異常處理程序
1.使用匯編語(yǔ)言安裝異常處理程序
如果系統(tǒng)啟動(dòng)不依賴于Debug或Debug monitor軟件,可以使用匯編語(yǔ)言在系統(tǒng)啟動(dòng)時(shí)直接安裝異常處理程序。
下面的例子顯示了系統(tǒng)從0x0地址啟動(dòng),直接安裝異常處理程序的方法。
Vector_Init_Block
???? LDR PC, Reset_Addr
???? LDR PC, Undefined_Addr
???? LDR PC, SWI_Addr
???? LDR PC, Prefetch_Addr
???? LDR PC, Abort_Addr
???? NOP???????????????????????? ;保留向量
???? LDR PC, IRQ_Addr
???? LDR PC, FIQ_Addr
Reset_Addr DCD Start_Boot
Undefined_Addr DCD Undefined_Handler
SWI_Addr DCD SWI_Handler
Prefetch_Addr DCD Prefetch_Handler
Abort_Addr DCD Abort_Handler
???? DCD 0?????????????????????? ;保留向量
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
?
有些情況下,系統(tǒng)0x0地址不一定是ROM。如果0x0地址為RAM,那么就系統(tǒng)將中斷向量表從ROM復(fù)制RAM,下面的例子顯示了這樣一個(gè)過程。
MOV R8, #0
ADR R9, Vector_Init_Block
LDMIA R9!,{r0-r7}??????????????? ;復(fù)制中斷向量表 (8 words)
STMIA R8!,{r0-r7}
LDMIA R9!,{r0-r7}??????????????? ;復(fù)制由偽操作 DCD定義的地址
STMIA R8!,{r0-r7}
注意 |
可以使用Scatter文件定義加載向量表的地址,這樣上述代碼的拷貝工作由C庫(kù)函數(shù)完成。 |
2.使用C語(yǔ)言安裝異常處理程序
程序中有時(shí)需要在main()函數(shù)中使用C語(yǔ)言安裝中斷向量表。這就要求指令經(jīng)編譯后的解碼能安裝在內(nèi)存的正確位置。
(1)向量表中使用跳轉(zhuǎn)指令的情況
如果在向量表中使用跳轉(zhuǎn)指令,使用下面的步驟完成向量表的安裝。
① 讀取異常處理程序的地址。
② 從異常處理程序地址中減去向量表中的偏移。
③ 為適應(yīng)指令流水線,將上一步得到的地址減8。
④ 將得到的結(jié)果右移2位,得到以字為單位的地址偏移量。
⑤ 將結(jié)果的高8位清零,得到跳轉(zhuǎn)指令的24位偏移量。
⑥ 將上一步得到的結(jié)果和0xea000000(無條件跳轉(zhuǎn)指令編碼)做邏輯與操作,從而得到要寫到向量表中的跳轉(zhuǎn)指令的正確編碼。
下面的例子顯示了這樣一個(gè)標(biāo)準(zhǔn)過程。
unsigned Install_Handler (unsigned routine, unsigned *vector)
{ unsigned vec, oldvec;
vec = ((routine - (unsigned)vector - 0x8)>>2);
if ((vec & 0xFF000000))
{
/* diagnose the fault */
prinf ("Installation of Handler failed");
exit (1);
}
vec = 0xEA000000 | vec;
oldvec = *vector;
*vector = vec;
return (oldvec);
}
(2)在向量表中使用加載PC指令
在向量表中使用加載PC指令,按照下面的步驟完成。
① 讀取異常處理程序地址。
② 從異常處理程序地址中減去向量表中的偏移。
③ 為適應(yīng)指令流水線,將上一步得到的地址減8。
④ 保留結(jié)果的后12位。
⑤ 將結(jié)果與0xe59ff000(LDR PC, [PC,#offset])做邏輯或操作,從而得到要寫到向量表中的跳轉(zhuǎn)指令的正確編碼。
⑥ 將異常處理程序的地址放到相應(yīng)的存儲(chǔ)單元。
下面的例子顯示了一個(gè)標(biāo)準(zhǔn)的C語(yǔ)言過程。
unsigned Install_Handler (unsigned location, unsigned *vector)
{ unsigned vec, oldvec;
vec = ((unsigned)location - (unsigned)vector - 0x8) | 0xe59ff000;
oldvec = *vector;
*vector = vec;
return (oldvec);
}
3.4.7? FIQ和IRQ中斷處理函數(shù)的設(shè)計(jì)
1.中斷分支
ARM內(nèi)核只有兩個(gè)外部中斷輸入信號(hào)nFIQ和nIRQ。但對(duì)于一個(gè)系統(tǒng)來說,中斷源可能多達(dá)幾十個(gè)。為此,在系統(tǒng)集成的時(shí)候,一般都會(huì)有一個(gè)異常控制器來處理異常信號(hào),如圖3.8所示。
圖3.8? 中斷系統(tǒng)
這時(shí)候用戶程序可能存在多個(gè)IRQ/FIQ的中斷處理函數(shù)。為了使從向量表開始的跳轉(zhuǎn)始終能找到正確的處理函數(shù)入口,需要設(shè)置一套處理機(jī)制和方法。
多數(shù)情況下是由軟件來處理異常分支的,因?yàn)檐浖梢酝ㄟ^讀取中斷控制器來獲得中斷源的信息,如圖3.9所示。
有些芯片可能支持特殊的硬件分支功能,這需要查看具體的芯片說明。
因?yàn)檐浖撵`活性,可以設(shè)計(jì)出比圖3.9更好的流程控制方法,如圖3.10所示。
Int_vector_table是用戶自己開辟的一塊存儲(chǔ)器空間,里面按次序存放異常處理函數(shù)的地址。IRQ_Handler()從中斷控制器獲取中斷源信息,然后再?gòu)腎nt_vector_table中的對(duì)應(yīng)地址單元得到異常處理函數(shù)的入口地址,完成一次異常響應(yīng)的跳轉(zhuǎn)。這種方法的好處是用戶程序在運(yùn)行過程中,能夠很方便地動(dòng)態(tài)改變異常服務(wù)內(nèi)容。
圖3.9? 軟件控制中斷分支
圖3.10? 靈活的軟件分支設(shè)計(jì)
?
進(jìn)入異常處理程序后,用戶可以完全按照自己的意愿來進(jìn)行程序設(shè)計(jì),包括調(diào)用Thumb狀態(tài)的函數(shù)等。但對(duì)于絕大多數(shù)的系統(tǒng)來說,有兩個(gè)步驟必須處理,一是現(xiàn)場(chǎng)保護(hù),二是要把中斷控制器中對(duì)應(yīng)的中斷狀態(tài)標(biāo)識(shí)清除,表明該中斷請(qǐng)求已經(jīng)得到響應(yīng),否則,中斷函數(shù)退出以后,又會(huì)被再一次觸發(fā),從而進(jìn)入周而復(fù)始的死循環(huán)。
?
2.ARM編譯器對(duì)中斷處理函數(shù)編寫的擴(kuò)展
考慮到中斷處理函數(shù)在現(xiàn)場(chǎng)保護(hù)和返回地址的處理上與普通函數(shù)的不同之處,不能直接把普通函數(shù)體連接到異常向量表上,需要在上面加上一層封裝,下面是一個(gè)例子。
IRQ_Handler?????????????????????? ;中斷相應(yīng)函數(shù)
??? STMFD??? SP!,{r0-r12,lr}????????? ;保護(hù)現(xiàn)場(chǎng),一般只需要保護(hù){r0-r3,LR}
??? BL?? IrqHandler???????????????? ;進(jìn)入普通處理函數(shù),C或匯編均可
……
??? LDMFD??? sp!,{r0-r12,LR}????????? ;恢復(fù)現(xiàn)場(chǎng)
??? SUBS??? pc,lr,#4???????????????? ;中斷返回,注意返回地址
為了方便使用高級(jí)語(yǔ)言直接編寫異常處理函數(shù),ARM編譯器對(duì)此做了特定的擴(kuò)展,可以使用函數(shù)聲明關(guān)鍵字_irq,這樣編譯出來的函數(shù)就可以滿足異常響應(yīng)對(duì)現(xiàn)場(chǎng)保護(hù)和恢復(fù)的需要,并且自動(dòng)加入LR減4的處理,符合IQR和FIQ中斷處理的要求。
下面的例子顯示了使用_irq對(duì)中斷處理函數(shù)產(chǎn)生的影響。
C語(yǔ)言源程序如下。
__irq void IRQHandler (void)
{
volatile unsigned int *base = (unsigned int *) 0x80000000;
if (*base == 1)
{
/*調(diào)用C語(yǔ)言中斷處理函數(shù)*/
C_int_handler();
}
/*清楚中斷標(biāo)志*/
*(base+1) = 0;
}
使用armcc編譯出的匯編代碼如下。
IRQHandler PROC
??? STMFD sp!,{r0-r4,r12,lr}
??? MOV r4,#0x80000000
??? LDR r0,[r4,#0]
??? SUB sp,sp,#4
??? CMP r0,#1
??? BLEQ C_int_handler
??? MOV r0,#0
??? STR r0,[r4,#4]
??? ADD sp,sp,#4
??? LDMFD sp!,{r0-r4,r12,lr}
??? SUBS pc,lr,#4
??? ENDP
如果不使用_irq子程序聲明關(guān)鍵字,編譯出的匯編代碼如下。
IRQHandler PROC
??? STMFD sp!,{r4,lr}
??? MOV r4,#0x80000000
??? LDR r0,[r4,#0]
??? CMP r0,#1
??? BLEQ C_int_handler
??? MOV r0,#0
??? STR r0,[r4,#4]
??? LDMFD sp!,{r4,pc}
??? ENDP
3.可重入中斷設(shè)計(jì)
在缺省情況下,ARM中斷是不可重入的。因?yàn)橐坏┻M(jìn)入異常響應(yīng)狀態(tài),ARM自動(dòng)關(guān)閉中斷使能。如果在異常處理過程中,簡(jiǎn)單地打開中斷使能而發(fā)生中斷嵌套時(shí),顯然新的異常處理將破壞原來的中斷現(xiàn)場(chǎng)而導(dǎo)致出錯(cuò)。但有時(shí)需要中斷必須是可重入的,因此要通過程序設(shè)計(jì)來解決這個(gè)問題。其中有兩個(gè)關(guān)鍵問題。
① 新中斷使能之前,必須要保護(hù)好前一個(gè)中斷的現(xiàn)場(chǎng)信息。比如LR_irq和SPSR_irq等,這一點(diǎn)比較容易做的。
② 中斷處理過程中對(duì)BL進(jìn)行保護(hù)。
在中斷處理函數(shù)中發(fā)生函數(shù)調(diào)用BL是很常見的,假設(shè)有下面一種情況。
IRQ_Handler:
???? ……
???? BL??? Foo
???? ADD
其中,
Foo:
??? STMFD? SP!,{r0-r3,LR}
??? ……
??? LDMFD? SP!{r0-r3,PC}
上述程序,在IRQ處理函數(shù)IRQ_Handler()中調(diào)用了函數(shù)Foo()。若是在IRQ_Handler()里面中斷可重入的話,可能發(fā)生問題,考察下面的情況:當(dāng)新的中斷請(qǐng)求恰好在“BL? Foo”指令執(zhí)行完成后發(fā)生。這時(shí)候LR_irq寄存器(因在IRQ模式下,所以是LR_irq)的值將調(diào)整為BL指令的下一條指令(ADD)地址,使其能從Foo()正確返回;但是因?yàn)檫@時(shí)候發(fā)生了中斷請(qǐng)求,接下來要進(jìn)行新中斷的響應(yīng),處理器在新中斷響應(yīng)過程中也要進(jìn)行LR_irq保存。這次對(duì)LR_irq的操作發(fā)生了沖突,當(dāng)新中斷返回后,往下執(zhí)行STMFD指令,這時(shí)候壓棧的LR已不是原來的ADD指令地址,從而使子程序Foo()無法正確返回。
這個(gè)問題無法通過增加額外的現(xiàn)場(chǎng)保護(hù)指令來解決。一個(gè)辦法就是在重新使能中斷之前改變處理器模式,也就是使上面程序的“BL? Foo”指令不要運(yùn)行在IRQ模式下。這樣當(dāng)新的中斷發(fā)生時(shí),就不會(huì)造成LR寄存器的沖突??紤]ARM的所有運(yùn)行模式,采用SYSTEM模式是比較合適的,因?yàn)樗翘貦?quán)模式,不是IRQ模式,與中斷響應(yīng)無關(guān)。
下面的例子顯示了標(biāo)準(zhǔn)的IRQ/FIQ異常中斷處理程序。
??? PRESERVE8
??? AREA INTERRUPT, CODE, READONLY
??? IMPORT C_irq_handler
IRQ
??? SUB lr, lr, #4??????????????? ;跳轉(zhuǎn)返回地址
STMFD sp!, {lr}????????????????? ;保存返回地址
MRS r14, SPSR??????????????????? ;讀取SPSR
STMFD sp!, {r12, r14}???????????? ;保存寄存器
; 清除中斷源
MSR CPSR_c, #0x1F??????????????? ;切換到SYSTEM模式,
STMFD sp!, {r0-r3, lr}??????????? ;保存lr_USR 和其他使用到的寄存器
BL C_irq_handler???????????????? ;跳轉(zhuǎn)到C中斷處理函數(shù)
LDMFD sp!, {r0-r3, lr}??????????? ;恢復(fù)用戶模式寄存器
MSR CPSR_c, #0x92??????????????? ;切換回irq模式
LDMFD sp!, {r12, r14}
MSR SPSR_cf, r14
LDMFD sp!, {pc}^
END
?
3.4.8? SWI異常處理函數(shù)的設(shè)計(jì)
本小節(jié)主要介紹編寫SWI處理程序時(shí)需要注意的幾個(gè)問題,包括下面內(nèi)容。
·? 判斷SWI中斷號(hào)。
·? 使用匯編語(yǔ)言編寫SWI異常處理函數(shù)。
·? 使用C語(yǔ)言編寫SWI異常處理函數(shù)。
·? 在特權(quán)模式下使用SWI異常中斷處理。
·? 從應(yīng)用程序中調(diào)用SWI。
·? 從應(yīng)用程序中動(dòng)態(tài)調(diào)用SWI。
1.判斷SWI中斷號(hào)
當(dāng)發(fā)生SWI異常,進(jìn)入異常處理程序時(shí),異常處理程序必須提取SWI中斷號(hào),從而得到用戶請(qǐng)求的特定SWI功能。
在SWI指令的編碼格式中,后24位稱為指令的“comment field”。該域保存的24位數(shù),即為SWI指令的中斷號(hào),如圖3.11所示。
圖3.11? SWI指令編碼格式
第一級(jí)的SWI處理函數(shù)通過LR寄存器內(nèi)容得到SWI指令地址,并從存儲(chǔ)器中得到SWI指令編碼。通常這些工作通過匯編語(yǔ)言、內(nèi)嵌匯編來完成。
下面的例子顯示了提取中斷向量號(hào)的標(biāo)準(zhǔn)過程。
PRESERVE8
AREA TopLevelSwi, CODE, READONLY???????? ;第一級(jí)SWI處理函數(shù).
EXPORT SWI_Handler
SWI_Handler
STMFD sp!,{r0-r12,lr}????????????????? ;保存寄存器
LDR r0,[lr,#-4]??????????????????????? ;計(jì)算SWI指令地址.
BIC r0,r0,#0xff000000????????????????? ;提取指令編碼的后24位
;
; 提取出的中斷號(hào)放r0寄存器,函數(shù)返回
;
LDMFD sp!, {r0-r12,pc}^???????????????? ;恢復(fù)寄存器
END
例子中,使用LR-4得到SWI指令的地址,再通過“BIC r0, r0, #0xFF000000”指令提取SWI指令中斷號(hào)。
2.匯編語(yǔ)言編寫SWI異常處理函數(shù)
最簡(jiǎn)單的方法是利用得到的中斷向量號(hào),使用跳轉(zhuǎn)表直接跳轉(zhuǎn)到實(shí)現(xiàn)相應(yīng)SWI功能的處理程序。
下面的例子,使用匯編語(yǔ)言實(shí)現(xiàn)了這種跳轉(zhuǎn)。
??? CMP r0,#MaxSWI??????????????? ;中斷向量范圍檢測(cè)
??? LDRLS pc, [pc,r0,LSL #2]
??? B SWIOutOfRange
SWIJumpTable
??? DCD SWInum0
??? DCD SWInum1
; 使用DCD 定義各功能函數(shù)入口地址
SWInum0???????????????????????? ;0號(hào)中斷
??? B EndofSWI
SWInum1???????????????????????? ;1號(hào)中斷
??? B EndofSWI
;
EndofSWI
3.使用C語(yǔ)言編寫SWI異常處理函數(shù)
雖然第一級(jí)SWI處理函數(shù)(完成中斷向量號(hào)的提?。┍仨氂脜R編語(yǔ)言完成,但第二級(jí)中斷處理函數(shù)(根據(jù)提取的中斷向量號(hào),跳轉(zhuǎn)到具體處理函數(shù))就可以使用C語(yǔ)言來完成。
因?yàn)榈谝患?jí)的中斷處理函數(shù)已經(jīng)將中斷號(hào)提取到寄存器r0中,所以根據(jù)AAPCS函數(shù)調(diào)用規(guī)則,可以直接使用BL指令跳轉(zhuǎn)到C語(yǔ)言函數(shù),而且中斷向量號(hào)作為第一個(gè)參數(shù)被傳遞到C函數(shù)。
例如匯編中使用了“BL ?C_SWI_Handler”跳轉(zhuǎn)到C語(yǔ)言的第二級(jí)處理函數(shù),則第二級(jí)的C語(yǔ)言函數(shù)示例如下所示。
void C_SWI_handler (unsigned number)
{
??? switch (number)
???? {
????? case 0 : /* SWI number 0 code */
????? break;
????? case 1 : /* SWI number 1 code */
????? break;
????? ...
????? default : /* Unknown SWI - report error */
???? }
}
另外,如果需要傳遞的參數(shù)多于1個(gè),那么可以使用堆棧,將堆棧指針作為函數(shù)的參數(shù)傳遞給C類型的二級(jí)中斷處理程序,就可以實(shí)現(xiàn)在兩級(jí)中斷之間傳遞多個(gè)參數(shù)。
例如:
MOV r1, sp???????????????? ;將傳遞的第二個(gè)參數(shù)(堆棧指針)放到r1中
BL C_SWI_Handler??????????? ;調(diào)用C函數(shù)
相應(yīng)的C函數(shù)的入口變?yōu)椋?/p>
void C_SWI_handler(unsigned number, unsigned *reg)
同時(shí),C函數(shù)也可以通過堆棧返回操作的結(jié)果。
?
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????????????????????????? ;堆棧指針放r1作為參數(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)行編譯,而沒有額外的開銷。但有如下的AAPCS限制。
·? 函數(shù)調(diào)用參數(shù)只能使用r0~r3傳遞。
·? 函數(shù)返回值只能通過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ù)并通過結(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處理程序完成需要的操作。
通過匯編語(yǔ)言可以實(shí)現(xiàn)第二種解決辦法:通過寄存器(通常為r0或r12)傳遞所需要的操作數(shù),這樣可以重新編寫SWI處理程序,對(duì)相應(yīng)寄存器中的值進(jìn)行處理。
但有些情況下,為了節(jié)省程序開銷,需要直接使用SWI中斷號(hào)對(duì)程序調(diào)用。例如,操作系統(tǒng)可能會(huì)使用單一的一條SWI指令并用寄存器來傳遞所需運(yùn)算的編號(hào)。這使得其他SWI空間可用于特定應(yīng)用程序的SWI。在一個(gè)特定的應(yīng)用程序中,如果從指令中提取SWI編號(hào)的開銷太大,就可使用這個(gè)方法。ARM(0x123456)和Thumb(0xAB)半主機(jī)方式的SWI就是這樣實(shí)現(xiàn)的。
?
下面的例子顯示了如何使用_swi將C函數(shù)調(diào)用映射到半主機(jī)方式的SWI。
#ifdef __thumb
/* Thumb 狀態(tài)的Semihosting軟中斷處理*/
#define SemiSWI 0xAB
#else
/* ARM狀態(tài)下的Semihosting的軟中斷處理*/
#define SemiSWI 0x123456
#endif
/* 使用Semihosting軟中斷輸出一個(gè)字符*/
__swi(SemiSWI) void Semihosting(unsigned op, char *c);
#define WriteC(c) Semihosting (0x3,c)
void write_a_character(int ch)
{
char tempch = ch;
WriteC( &tempch );
}
編譯程序含有一個(gè)機(jī)制,用以支持使用r12來傳遞所需運(yùn)算的值。根據(jù)AAPCS標(biāo)準(zhǔn),r12為IP寄存器,并且專用于函數(shù)調(diào)用。其他時(shí)間內(nèi)可將其用作暫存寄存器。如前面所述,通用SWI參數(shù)和返回值通過r0~r3寄存器傳遞。而r12可用于傳遞通用SWI調(diào)用的中斷功能編號(hào)。
下面的例子顯示了通用SWI的C語(yǔ)言程序框架。
__swi_indirect(0x80)
unsigned SWI_ManipulateObject(unsigned operationNumber,
unsigned object,unsigned parameter);
unsigned DoSelectedManipulation(unsigned object,
unsigned parameter, unsigned operation)
{
return SWI_ManipulateObject(operation, object, parameter);
}
生成的匯編代碼如下。
DoSelectedManipulation PROC
STMFD sp!,{r3,lr}
MOV r12,r2
SWI 0x80
LDMFD sp!,{r3,pc}
ENDP