?
3.4? 異常中斷處理
異?;蛑袛嗍怯脩舫绦蛑凶罨镜囊环N執(zhí)行流程和形態(tài)。這部分主要對(duì)ARM架構(gòu)下的異常中斷做詳細(xì)說(shuō)明。
ARM有7種類型的異常,按優(yōu)先級(jí)從高到低的排列如下:復(fù)位異常(Reset)、數(shù)據(jù)異常(Data Abort)、快速中斷異常(FIQ)、外部中斷異常(IRQ)、預(yù)取異常(Prefetch Abort)、軟件中斷(SWI)和未定義指令異常(Undefined instruction)。
注意 |
在ARM文檔中,使用術(shù)語(yǔ)Exception來(lái)描述異常。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)行軟件仿真,或通過(guò)軟件仿真實(shí)現(xiàn)指令集擴(kuò)展。例如,在一個(gè)不包含浮點(diǎn)運(yùn)算的系統(tǒng)中,CPU遇到浮點(diǎn)運(yùn)算指令時(shí),將發(fā)生未定義指令異常中斷,在該未定義指令異常中斷的處理程序中可以通過(guò)其他指令序列仿真浮點(diǎn)運(yùn)算指令。
仿真功能可以通過(guò)下面步驟實(shí)現(xiàn)。
① 將仿真程序入口地址鏈接到向量表中未定義指令異常中斷入口處(0x00000004或0xffff0004),并保存原來(lái)的中斷處理程序。
② 讀取該未定義指令的bits[27∶24],判斷其是否是一條協(xié)處理器指令。如果bits[27∶24]值為0b1110或0b110x,該指令是一條協(xié)處理器指令;否則,由軟件仿真實(shí)現(xiàn)協(xié)處理器功能,可以同過(guò)bits[11∶8]來(lái)判斷要仿真的協(xié)處理器功能(類似于SWI異常實(shí)現(xiàn)機(jī)制)。
③ 如果不仿真該未定義指令,程序跳轉(zhuǎn)到原來(lái)的未定義指令異常中斷的中斷處理程序執(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ù)取無(wú)效的指令時(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值無(wú)關(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è)備通常通過(guò)該異常中斷請(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)的模式。此外,也可以通過(guò)編程改變CPSR,進(jìn)入任何一種ARM處理器模式。
注意 |
用戶和系統(tǒng)模式是僅有的不可通過(guò)異常進(jìn)入的兩種模式,也就是說(shuō),要進(jìn)入這兩種模式,必須通過(guò)編程改變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)。這可以通過(guò)檢測(cè)SPSR的T位來(lái)判斷。
通常情況下,只有在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ù)中通過(guò)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來(lái)實(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ù)可以通過(guò)一條指令來(lái)實(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è)非常重要的問題就是返回地址的確定。前面提到過(guò),處理器進(jìn)入異常時(shí)會(huì)有一個(gè)保存LR的動(dòng)作,但是該保持值并不一定是正確中斷的返回地址。以一個(gè)簡(jiǎn)單的指令執(zhí)行流水狀態(tài)圖來(lái)對(duì)此加以說(shuō)明,如圖3.7所示。
圖3.7 ?3級(jí)流水線示例
在ARM架構(gòu)里,PC值指向當(dāng)前執(zhí)行指令地址加8。也就是說(shuō),當(dāng)執(zhí)行指令A(yù)(地址0x8000)時(shí),PC等于0x8000+8=0x8008,即等于指令C的地址。假設(shè)指令A(yù)是BL指令,則當(dāng)執(zhí)行時(shí),會(huì)把PC值(0x8008)保存到LR寄存器。但是,接下來(lái)處理器會(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)過(guò)跳轉(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í)行,也就是說(shuō),返回地址應(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è)過(guò)程。
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(無(wú)條件跳轉(zhuǎn)指令編碼)做邏輯與操作,從而得到要寫到向量表中的跳轉(zhuǎn)指令的正確編碼。
下面的例子顯示了這樣一個(gè)標(biāo)準(zhǔn)過(guò)程。
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ǔ)言過(guò)程。
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)來(lái)說(shuō),中斷源可能多達(dá)幾十個(gè)。為此,在系統(tǒng)集成的時(shí)候,一般都會(huì)有一個(gè)異常控制器來(lái)處理異常信號(hào),如圖3.8所示。
圖3.8? 中斷系統(tǒng)
這時(shí)候用戶程序可能存在多個(gè)IRQ/FIQ的中斷處理函數(shù)。為了使從向量表開始的跳轉(zhuǎn)始終能找到正確的處理函數(shù)入口,需要設(shè)置一套處理機(jī)制和方法。
多數(shù)情況下是由軟件來(lái)處理異常分支的,因?yàn)檐浖梢酝ㄟ^(guò)讀取中斷控制器來(lái)獲得中斷源的信息,如圖3.9所示。
有些芯片可能支持特殊的硬件分支功能,這需要查看具體的芯片說(shuō)明。
因?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)行過(guò)程中,能夠很方便地動(dòng)態(tài)改變異常服務(wù)內(nèi)容。
圖3.9? 軟件控制中斷分支
圖3.10? 靈活的軟件分支設(shè)計(jì)
?
進(jìn)入異常處理程序后,用戶可以完全按照自己的意愿來(lái)進(jìn)行程序設(shè)計(jì),包括調(diào)用Thumb狀態(tài)的函數(shù)等。但對(duì)于絕大多數(shù)的系統(tǒng)來(lái)說(shuō),有兩個(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,這樣編譯出來(lái)的函數(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)閉中斷使能。如果在異常處理過(guò)程中,簡(jiǎn)單地打開中斷使能而發(fā)生中斷嵌套時(shí),顯然新的異常處理將破壞原來(lái)的中斷現(xiàn)場(chǎng)而導(dǎo)致出錯(cuò)。但有時(shí)需要中斷必須是可重入的,因此要通過(guò)程序設(shè)計(jì)來(lái)解決這個(gè)問題。其中有兩個(gè)關(guān)鍵問題。
① 新中斷使能之前,必須要保護(hù)好前一個(gè)中斷的現(xiàn)場(chǎng)信息。比如LR_irq和SPSR_irq等,這一點(diǎn)比較容易做的。
② 中斷處理過(guò)程中對(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)求,接下來(lái)要進(jìn)行新中斷的響應(yīng),處理器在新中斷響應(yīng)過(guò)程中也要進(jìn)行LR_irq保存。這次對(duì)LR_irq的操作發(fā)生了沖突,當(dāng)新中斷返回后,往下執(zhí)行STMFD指令,這時(shí)候壓棧的LR已不是原來(lái)的ADD指令地址,從而使子程序Foo()無(wú)法正確返回。
這個(gè)問題無(wú)法通過(guò)增加額外的現(xiàn)場(chǎng)保護(hù)指令來(lái)解決。一個(gè)辦法就是在重新使能中斷之前改變處理器模式,也就是使上面程序的“BL? Foo”指令不要運(yùn)行在IRQ模式下。這樣當(dāng)新的中斷發(fā)生時(shí),就不會(huì)造成LR寄存器的沖突。考慮ARM的所有運(yùn)行模式,采用SYSTEM模式是比較合適的,因?yàn)樗翘貦?quán)模式,不是IRQ模式,與中斷響應(yīng)無(wú)關(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ù)通過(guò)LR寄存器內(nèi)容得到SWI指令地址,并從存儲(chǔ)器中得到SWI指令編碼。通常這些工作通過(guò)匯編語(yǔ)言、內(nèi)嵌匯編來(lái)完成。
下面的例子顯示了提取中斷向量號(hào)的標(biāo)準(zhǔn)過(guò)程。
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指令的地址,再通過(guò)“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)的提取)必須用匯編語(yǔ)言完成,但第二級(jí)中斷處理函數(shù)(根據(jù)提取的中斷向量號(hào),跳轉(zhuǎn)到具體處理函數(shù))就可以使用C語(yǔ)言來(lái)完成。
因?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ù)也可以通過(guò)堆棧返回操作的結(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ù)返回值只能通過(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)的。
?
下面的例子顯示了如何使用_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來(lái)傳遞所需運(yùn)算的值。根據(jù)AAPCS標(biāo)準(zhǔn),r12為IP寄存器,并且專用于函數(shù)調(diào)用。其他時(shí)間內(nèi)可將其用作暫存寄存器。如前面所述,通用SWI參數(shù)和返回值通過(guò)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