中斷髮生時,作業系統會為當前的任務建立一個快照,陷入核心,把CPU的控制權交給核心。核心趁這個機會做一些工作,比如排程執行其他任務。這只是中斷的作用之一。
使用中斷有一套固定的流程,掌握它即可。流程大概如下:
初始化8259A
初始化工作是對主從8259A
的兩類埠賦值。這兩類埠是:ICW
和OCW
。
ICW
這是初始化命令字埠,一共有四個。
ICW1
,設定是否級聯、是否向ICW4
寫入資料。ICW2
,設定主從8259A
的初始中斷向量號。注意,有一批中斷向量號已經在真實模式下使用,設定初始中斷向量號應該避免覆蓋那批中斷向量號。ICW3
,主片使用點陣圖標識幾號引腳掛載從片。從片使用低3位識別主片傳送的資料的接收方是否是自己。ICW4
,設定是否主動傳送EOI
。不清楚這點。
OCW
這是控制命令字埠。目前只需要操作一個。
設定遮蔽哪些埠、放行哪些埠。1表示遮蔽,0表示放行。
建立IDT
IDT
是中斷向量表。類似GDT
,也是記憶體中的一塊區域。但它內部包含的全是"門描述符"。
IDT
中的每個門描述符的名稱是中斷向量號,選擇子是中斷向量號對應的處理中斷的程式碼。
; Gate : offset,selector,attr,paramCount
%macro Gate 4
dw %1 & 0FFFFh
dw %2 & 0FFFFh
dw ((%3 & 0FFh) << 8) | (%4 & 01Fh)
db %1 >> 16
%endmacro
[SECTION .idt]
LABEL_IDT:
%rep 128
Gate selector32, SpuriousHandler, attr, paramCount
%endrep
.080h: Gate selector32, UserIntHandler, attr, paramCount
IDTLen equ $ - LABEL_IDT
IDTPtr dw IDTLen - 1
dd 0
IDT
中,第一個元素是可以用的,這與GDT
不同。
%rep 128
Gate selector32, offset, attr, paramCount
%endrep
表示IDT
中有128個門描述符都指向相同的目的碼段。從0算起,128個門描述符,緊接著的中斷向量號應該是128,正好是80h
。
建立中斷程式碼段
_SpuriousHandler:
SpuriousHandler equ _SpuriousHandler - $$
mov al, 'A'
mov ah, 0Fh
mov [gs:(80*20+20)*2], ax
_UserIntHandler:
UserIntHandler equ _UserIntHandler - $$
mov al, 'A'
mov ah, 0Fh
mov [gs:(80*20+20)*2], ax
上面的程式碼和IDT
在同一程式碼段code32。SpuriousHandler
、UserIntHandler
是code32內的兩個偏移量。
不能完全理解這種寫法。不過,可暫且當作一種語法規則去記住就行了。以後有類似功能需要實現,就用這種寫法。
當中斷向量號是在0~127
(包括0和127)時,會執行中斷處理程式碼SpuriousHandler
。
當中斷向量號是080h
時,會執行中斷處理程式碼UserIntHandler
。
呼叫中斷
呼叫中斷的語句很簡單,如下:
int 00h
int 01h
int 10h
int 80h
特例--時鐘中斷
增加一箇中斷的流程:
- 在
IDT
中增加一個門描述符,提供目的碼(中斷處理程式)的選擇子、偏移量等。 - 編寫中斷處理程式碼。
- 呼叫中斷。
時鐘中斷很特殊,不需要直接呼叫就能被呼叫。
編寫時鐘中斷的完整程式碼如下:
; Gate : offset,selector,attr,paramCount
%macro Gate 4
dw %1 & 0FFFFh
dw %2 & 0FFFFh
dw ((%3 & 0FFh) << 8) | (%4 & 01Fh)
db %1 >> 16
%endmacro
[SECTION .idt]
LABEL_IDT:
%rep 128
Gate selector32, SpuriousHandler, attr, paramCount
%endrep
.080h: Gate selector32, UserIntHandler, attr, paramCount
.81h: Gate selector32, ClockIntHandler, attr, paramCount
IDTLen equ $ - LABEL_IDT
IDTPtr dw IDTLen - 1
dd 0
_SpuriousHandler:
SpuriousHandler equ _SpuriousHandler - $$
mov al, 'A'
mov ah, 0Fh
mov [gs:(80*20+20)*2], ax
_UserIntHandler:
UserIntHandler equ _UserIntHandler - $$
mov al, 'A'
mov ah, 0Fh
mov [gs:(80*20+20)*2], ax
_ClockIntHandler:
ClockIntHandler equ _ClockIntHandler - $$
inc [gs:(80*20+20)*2]
int 80h
;在死迴圈中,時鐘中斷會以一定的時間間隔發生。
jmp $
[gs:(80*20+20)*2]
這塊記憶體已經有資料了,inc [gs:(80*20+20)*2]
會增加這塊記憶體中的資料的值,效果是在螢幕上出現不斷變換的字母。