之前剛開始的時候是直接看移植代碼,後來看到後面實在看不下去了,轉過頭回去看了一個星期的核心結構,以前也看過一遍核心結構,但是有點暈暈的,現在重新看了一次清楚多了,相信回過頭來看移植部分也應該更清楚了。 現在先來掌握一下關于軟體中斷swi的内容,這是我比較發暈的源泉 軟中斷: 中斷不傳回形式:void _swi(swi_num) swi_name(arguments) 傳回一個結果到R0中 int _swi(swi_num) swi_name(arguments);最多可以傳回四個結果R0-R3到一個結構struct type{ int a,b,c,d}中 type(傳回類型) _value_in_regs(傳回多個結果的修飾符) _swi(swi_num) swi_name(arguments); 在ARM中實作軟中斷的方法我在blog裡面搜了很多文章也沒有看到講的通俗一點的,還是自己看ARM的移植代碼吧 首先定義了一堆軟中斷的中斷号,其中0和1的中斷服務子程式是用彙編編寫的,其他的都是在c語言編寫的中斷服務子程式SWI_Exception中。 __swi(0x00) void OS_TASK_SW(void);
__swi(0x01) void _OSStartHighRdy(void);
__swi(0x02) void OS_ENTER_CRITICAL(void);
__swi(0x03) void OS_EXIT_CRITICAL(void); __swi(0x40) void *GetOSFunctionAddr(int Index);
__swi(0x41) void *GetUsrFunctionAddr(int Index);
__swi(0x42) void OSISRBegin(void);
__swi(0x43) int OSISRNeedSwap(void); __swi(0x80) void ChangeToSYSMode(void);
__swi(0x81) void ChangeToUSRMode(void);
__swi(0x82) void TaskIsARM(INT8U prio);
__swi(0x83) void TaskIsTHUMB(INT8U prio);
比如在程式運作到調用OS_TASK_SW(void)函數時,就産生軟體中斷,然後就進入中斷服務子程式,按照什麼指令走呢?恩,就按照下面這個代碼,這個代碼是将軟體中斷異常處理程式挂接到核心的作用的,是在啟動代碼中實作的: LDR PC,SWI_Addr SWI_Addr DCD SoftwareInterrupt 是以當産生軟中斷之後PC就跳到了SoftwareInterrupt,這時就算真正進入了軟體異常中斷處理部分了,然後就是執行下面的彙編代碼 SoftwareInterrupt
LDR SP, StackSvc ; 重新設定堆棧指針
STMFD SP!, {R0-R3, R12, LR}
MOV R1, SP ; R1指向參數存儲位置 MRS R3, SPSR
TST R3, #T_bit ; 中斷前是否是Thumb狀态,判斷SPSR的T位是不是為零 LDRNEH R0, [LR,#-2] ; 不為零即THUMB指令集: 取得Thumb狀态SWI号
BICNE R0, R0, #0xff00 ;在THUMB指令集中軟中斷功能号為8位,是以取低八位即為功能号
LDREQ R0, [LR,#-4] ; 為零即ARM指令集: 取得arm狀态SWI号
BICEQ R0, R0, #0xFF000000 ;在ARM指令集中軟中斷功能号為24位,是以取低6位即為功能号
; r0 = SWI号,R1指向參數存儲位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw ;功能号為0到OSIntCtxSw執行中斷任務切換函數
LDREQ PC, =__OSStartHighRdy ; SWI 0x01為第一次任務切換 BL SWI_Exception ;否則進入c編寫的中斷服務函數
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4) 怎麼進入c編寫的中斷服務子程式SWI_Exception呢?通過下面的申明 IMPORT SWI_Exception ;軟中斷異常處理程式,表示将c程式中的該函數挂接到此段彙編代碼中 同樣的道理 EXPORT __OSStartHighRdy
EXPORT OSIntCtxSw ;中斷退出時的入口,參見startup.s中的IRQ_Handler EXPORT SoftwareInterrupt ;軟中斷入口 上面的申明是将該段彙編代碼挂接到外面,是以在外部可以直接調用函數名 繼續看OS_CPU_A.S的其他部分代碼,就是兩個軟體異常中斷處理函數OSIntCtxSw和OSStarHighRdy OSIntCtxSw代碼是中斷服務子程式使得更高優先級的任務進入就緒狀态後,中斷傳回後需要切換到該任務時調用的,這是被切換的任務的CPU寄存器的值已經在響應中斷後存入了堆棧中,是以,這裡不需要重複儲存了直接切換任務即可,具體過程看代碼 OSIntCtxSw
;下面為儲存任務環境 ;當響應軟體異常中斷後進入了系統模式,在上面的代碼中我們可以看到,進入系統模式時儲存的堆棧結構從頂到底依次是:R0,R1,R2,R3,R12,LR,而在使用者模式中任務的堆棧結構應該是:OsEnterSum,CPSR,RO-12,LR,PC,是以在進行軟體中斷任務切換之前先要儲存原來任務的堆棧結構。
LDR R2, [SP, #20] ;擷取PC
LDR R12, [SP, #16] ;擷取R12
MRS R0, CPSR MSR CPSR_c, #(NoInt | SYS32Mode)
MOV R1, LR
STMFD SP!, {R1-R2} ;儲存LR,PC
STMFD SP!, {R4-R12} ;儲存R4-R12 MSR CPSR_c, R0
LDMFD SP!, {R4-R7} ;擷取R0-R3
ADD SP, SP, #8 ;出棧R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;儲存R0-R3
LDR R1, =OsEnterSum ;擷取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;儲存CPSR,OsEnterSum ;儲存目前任務堆棧指針到目前任務的TCB
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1] BL OSTaskSwHook ;調用鈎子函數
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4]
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
OSIntCtxSw_1
;擷取新任務堆棧指針
LDR R4, [R6]
ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #(NoInt | SVC32Mode) ;進入管理模式
MOV SP, R4 ;設定堆棧指針 LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum
;恢複新任務的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢複CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;運作新任務