2007年11月4日 星期日

OrzMicrokernel irq_handlers.asm IRQ處理和操作系統計時器 IRQ0 的函式註解

Standard
irq_handlers.asm 是 Orz Microkernel 用來處理 IRQ 的函式檔案,裡面有著處理所有 IRQ 的程式碼。之前工作排程器的部份有提到,定時切換工作是靠 IRQ0 的系統計時器達成的,其原理是當工作被賦予執行權時,同時也被授權了能佔用 CPU 的時間長度,接著,再靠著系統計時器的功能倒數計時。最後,該工作能佔用 CPU 的時間被用完,系統計時器就會如同鬧鐘響起一般,去呼叫 tasks.asm 的 Schedule 準備切換到下一個工作行程。

程式碼和詳細註解:
;------------------------------------------------------------------------
; IRQ 是處理硬體中斷請求的通道,硬體可透過 IRQ 要求 CPU 產生中斷,以處理硬體的
; 相關操作。
;
; IRQ 在中斷(INT)的編號定義:
; 08 Hardware IRQ0 System Timer
; 09 Hardware IRQ1 Keyboard
; 0A Hardware IRQ2 Redirected
; 0B Hardware IRQ3 Serial Comms. COM2/COM4
; 0C Hardware IRQ4 Serial Comms. COM1/COM3
; 0D Hardware IRQ5 Reserved/Sound Card
; 0E Hardware IRQ6 Floppy Disk Controller
; 0F Hardware IRQ7 Parallel Comms.
; 70 Hardware IRQ8 Real Time Clock
; 71 Hardware IRQ9 Redirected IRQ2
; 72 Hardware IRQ10 Reserved
; 73 Hardware IRQ11 Reserved
; 74 Hardware IRQ12 PS/2 Mouse
; 75 Hardware IRQ13 Math's Co-Processor
; 76 Hardware IRQ14 Hard Disk Drive
; 77 Hardware IRQ15 Reserved
;
; 控制 IRQ 開啟或關閉的控制開關(Operation Control Word) 位址定義:
; IRQ0 ~ IRQ7 的控制字元位址是 0x21(bit0 ~ bit7)
; IRQ8 ~ IRQ15 的控制字元位址是 0xA1(bit0 ~ bit7)
;
; 中斷結束前要傳送『中斷結束』給『可程式化中斷控制器』:
; 中斷結束訊號 End Of Interrupt(EOI) :0x20
; 可程式化中斷控制器 Programmable Interrupt Controller(PIC) 定義:
; IRQ0 ~ IRQ7 的 MASTER_PIC 的 I/O 位址:0x20
; IRQ8 ~ IRQ15 的 SLAVE_PIC 的 I/O 位址:0xA0

IRQ_Dummy_M:
push ax ; 將原有 AX 暫存至堆疊
mov al, END_OF_INTERR ; 設 AL 為中斷結束訊號
out MASTER_PIC, al ; 設定主要可程式化中斷控制器
pop ax ; 從堆疊取回原有的 AX
sti ; 啟用中斷
iret

IRQ_Dummy_S:
push ax ; 將原有 AX 暫存至堆疊
mov al, END_OF_INTERR ; 設 AL 為中斷結束訊號
out MASTER_PIC, al ; 設定主要可程式化中斷控制器
out SLAVE_PIC, al ; 設定次要可程式化中斷控制器
pop ax ; 從堆疊取回原有的 AX
sti ; 啟用中斷
iret

;------------------------------------------------------------------------
;;;; 計時器

IRQ_0:
; 讓目前正在執行的工作先交還執行權給核心
cli ; 禁用中斷以免干擾工作執行權切換
push ax ; 將 AX 存入堆疊
push ds ; 將 DS 存入堆疊
mov ax, kernel_data - _GDT ; 計算 kernel_data 的資料區段位址
mov ds, ax ; 設資料區段暫存器 DS = AX

inc DWORD [globalTime]

; 倒數目前正在執行的工作擁有的時間
mov eax, [taskRunningTime] ; 設 EAX 目前工作可佔用 CPU 的時間長度
dec eax ; EAX 減一
mov [taskRunningTime], eax ; 回存至 taskRunningTime
jz IRQ_0_Switch ; 直到 EAX 為零,此時工作不可再佔用 CPU
; 呼叫 IRQ_0_Switch 準備切換到下一個工作

; 核心將執行權交還給目前正在執行的工作
pop ds ; 恢復目前工作的資料區段暫存器 DS
pop ax ; 恢復目前工作的計數暫存器 AX
jmp IRQ_Dummy_M ; 跳到 IRQ_Dummy_M 設定中斷並返回結束

IRQ_0_Switch:
mov al, END_OF_INTERR
out MASTER_PIC, al
pop ds ; 恢復目前工作的資料區段暫存器 DS
pop ax ; 恢復目前工作的計數暫存器 AX

GDT_Adjustment
; 儲存目前工作的所有暫存器狀態
mov [task_ebp], ebp ; 暫時將目前工作的基底暫存器 EBP 狀態移開至
; task_ebp

mov ebp, [currentTaskP] ; 將 EBP 指向目前工作的狀態記錄記憶體位址
mov [ds:ebp + s_task.r_eax], eax ; 儲存目前工作的累積暫存器 EAX
mov [ds:ebp + s_task.r_ebx], ebx ; 儲存目前工作的基底暫存器 EBX
mov [ds:ebp + s_task.r_ecx], ecx ; 儲存目前工作的計數暫存器 ECX
mov [ds:ebp + s_task.r_edx], edx ; 儲存目前工作的資料暫存器 EDX
mov [ds:ebp + s_task.r_esi], esi ; 儲存目前工作的來源索引暫存器 ESI
mov [ds:ebp + s_task.r_edi], edi ; 儲存目前工作的目的索引暫存器 EDI
mov eax, [task_ebp] ; 從 task_ebp 取回 EBP 狀態
mov [ds:ebp + s_task.r_ebp], eax ; 儲存目前工作的基底暫存器 EBP
; 目前堆疊內:[EFLAGS][CS][EIP]
pop eax ; 取得指標暫存器 EIP 狀態
mov [ds:ebp + s_task.r_eip], eax ; 儲存目前工作的指標暫存器 EIP
pop eax ; 將目前工作的暫存器 CS 移出堆疊
pop eax ; 取得目前工作的旗標暫存器 EFLAGS
mov [ds:ebp + s_task.r_eflags], eax ; 儲存目前工作的旗標暫存器 EFLAGS
mov [ds:ebp + s_task.r_esp], esp ; 儲存目前工作的堆疊暫存器 ESP

; 將核心堆疊暫存器狀態回復到 ESP
mov ax,kernel_data - _GDT ; 計算 kernel_data 的資料區段位址
mov ss,ax ; 設定 SS 堆疊區段為 AX
mov esp, [kernel_esp] ; 將之前核心的堆疊暫存器 ESP 狀態回復到 ESP

; 切換到下一個工作行程
jmp Schedule ; 呼叫 tasks.asm 的 Schedule 以切換到下一個
; 工作行程



後記

系統計時器這部份就是工作排程器的心臟,因為它不斷的定時中斷,我們可以不停的檢查工作是否該切換了,讓核心生生不息的跳動。