- 30天自制操作系統(tǒng)
- (日)川合秀實
- 2399字
- 2020-03-11 14:01:51
6 中斷處理程序的制作(harib03e)
今天的內容所剩不多了,大家再加一把勁。鼠標是IRQ12,鍵盤是IRQ1,所以我們編寫了用于INT 0x2c和INT 0x21的中斷處理程序(handler),即中斷發(fā)生時所要調用的程序。
int.c的節(jié)選
void inthandler21(int *esp) /* 來自PS/2鍵盤的中斷 */ { struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8-1, 15); putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard"); for (; ; ) { io_hlt(); } }
正如大家所見,這個函數(shù)只是顯示一條信息,然后保持在待機狀態(tài)。鼠標的程序也幾乎完全一樣,只是顯示的信息不同而已。“只寫鼠標程序不就行了嗎,怎么鍵盤也寫了呢?”,因為鍵盤與鼠標的處理方法很相像,所以順便寫了一下。inthandler21接收了esp指針的值,但函數(shù)中并沒有用。在這里暫時不用esp,不必在意。
■■■■■
如果這樣就能運行,那就太好了,可惜還不行。中斷處理完成之后,不能執(zhí)行“return; ”(=RET指令),而是必須執(zhí)行IRETD指令,真不好辦。而且,這個指令還不能用C語言寫。所以,還得借助匯編語言的力量修改naskfunc.nas。
本次的naskfunc.nas節(jié)選
EXTERN _inthandler21, _inthandler2c _asm_inthandler21: PUSH ES PUSH DS PUSHAD MOV EAX, ESP PUSH EAX MOV AX, SS MOV DS, AX MOV ES, AX CALL _inthandler21 POP EAX POPAD POP DS POP ES IRETD
我們只解釋鍵盤程序,因為鼠標程序和它是一樣的。最后的IRETD剛才已經講過了。最開頭的EXTERN指令,在調用(CALL)的地方再進行說明。這樣一來,問題就只剩下PUSH和POP了。
■■■■■
繼續(xù)往下說明之前,我們要先好好解釋一下棧(stack)的概念。
寫程序的時候,經常會有這種需求——雖然不用永久記憶,但需要暫時記住某些東西以備后用。這種目的的記憶被稱為緩沖區(qū)(buffer)。突然一下子接收到大量信息時,先把它們都保存在緩沖區(qū)里,然后再慢慢處理,緩沖區(qū)一詞正是來源于這層意思。根據(jù)整理記憶內容的方式,緩沖區(qū)分為很多種類。
最簡單明了的方式,就是將信息從上面逐漸加入進來,需要時再從下面一個個取出。

緩沖的種類(1)
最先加入的信息也最先取出,所以這種緩沖區(qū)是“先進先出”(first in, first out),簡稱FIFO。這應該是最普通的方式了。有的書中也會稱之為“后進后出”(last in, last out),即LILO。叫法雖然不同,但實質上是同樣的東西。
下面要介紹的一種方式,有點類似于往桌上放書,也就是信息逐漸從上面加入進來,而取出時也從最上面開始。

緩沖的種類(2)
最先加入的信息最后取出,所以這種緩沖區(qū)是“先進后出”(first in, last out),簡稱FILO。有的書上也稱之為“后進先出”(last in, first out),即LIFO。
■■■■■
這里要說明的棧,正是FILO型的緩沖區(qū)。PUSH將數(shù)據(jù)壓入棧頂,POP將數(shù)據(jù)從棧頂取出。PUSH EAX這個指令,相當于:
ADD ESP, -4 MOV [SS:ESP], EAX
也就是說,ESP的值減去4,以所得結果作為地址值,將寄存器中的值保存到該地址所對應內存里。反過來,POP EAX指令相當于:
MOV EAX, [SS:ESP] ADD ESP,4
CPU并不懂棧的機制,它只是執(zhí)行了實現(xiàn)棧功能的指令而已。所以,即使是PUSH太多,或者POP太多這種沒有意義的操作,基本上CPU也都會遵照執(zhí)行。
所以,如果寫了以下程序,
PUSH EAX PUSH ECX PUSH EDX 各種處理 POP EDX POP ECX POP EAX
在“各種處理”那里,即使把EAX, ECX, EDX改了,最后也還會恢復回原來的值……其實ES、DS這些寄存器,也就是靠PUSH和POP等操作而變回原來的值的。
■■■■■
還有一個不怎么常見的指令PUSHAD,它相當于:
PUSH EAX PUSH ECX PUSH EDX PUSH EBX PUSH ESP PUSH EBP PUSH ESI PUSH EDI
反過來,POPAD指令相當于按以上相反的順序,把它們全都POP出來。
■■■■■
結果,這個函數(shù)只是將寄存器的值保存到棧里,然后將DS和ES調整到與SS相等,再調用_inthandler21,返回以后,將所有寄存器的值再返回到原來的值,然后執(zhí)行IRETD。內容就這些。如此小心翼翼地保存寄存器的值,其原因在于,中斷處理發(fā)生在函數(shù)處理的途中,通過IRETD從中斷處理返回以后,如果寄存器的值亂了,函數(shù)就無法正常處理下去了,所以一定要想盡辦法讓寄存器的值返回到中斷處理前的狀態(tài)。
關于在DS和ES中放入SS值的部分,因為C語言自以為是地認為“DS也好,ES也好,SS也好,它們都是指同一個段”,所以如果不按照它的想法設定的話,函數(shù)inthandler21就不能順利執(zhí)行。所以,雖然麻煩了一點,但還是要這樣做。
這么說來,CALL也是一個新出現(xiàn)的指令,它是調用函數(shù)的指令。這次要調用一個沒有定義在naskfunc.nas中的函數(shù),所以我們最初用一個EXTERN指令來通知nask:“馬上要使用這個名字的標號了,它在別的源文件里,可不要搞錯了”。
■■■■■
好了,這樣_asm_inthandler21的講解就沒有問題了吧。下面要說明的,就是要將這個函數(shù)注冊到IDT中去這一點。我們在dsctbl.c的init_gdtidt里加入以下語句。
/* IDT的設定 */ set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32); set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);
asm_inthandler21注冊在idt的第0x21號。這樣,如果發(fā)生中斷了,CPU就會自動調用asm_inthandler21。這里的2 * 8表示的是asm_inthandler21屬于哪一個段,即段號是2,乘以8是因為低3位有著別的意思,這里低3位必須是0。
所以,“2 * 8”也可以寫成 “2<<3”,當然,寫成16也可以。
不過,號碼為2的段,究竟是什么樣的段呢?
set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);
程序中有以上語句,說明這個段正好涵蓋了整個bootpack.hrb。
最后的AR_INTGATE32將IDT的屬性,設定為0x008e。它表示這是用于中斷處理的有效設定。
■■■■■
還有就是對bootpack.c的HariMain的補充。“io_sti(); ”僅僅是執(zhí)行STI指令,它是CLI的逆指令。就是說,執(zhí)行STI指令后,IF(interrupt flag,中斷許可標志位)變?yōu)?, CPU接受來自外部設備的中斷(參考4.6節(jié))。CPU的中斷信號只有一根,所以IF也只有一個,不像PIC那樣有8位。
在HariMain的最后,修改了PIC的IMR,以便接受來自鍵盤和鼠標的中斷。這樣程序就完成了。只要按下鍵盤上某個鍵,或動一動鼠標,中斷信號就會傳到CPU,然后CPU執(zhí)行中斷處理程序,輸出信息。
■■■■■
那好,我們運行一下試試看。“make run”……然后按下鍵盤上的“A”……哦!顯示了一行信息。

讓我們先退出程序,再運行一次“make run”吧。這次我們隨便轉轉鼠標。但怎么讓鼠標轉起來呢?首先我們在QEMU畫面的某個地方單擊一下,這樣就把鼠標與QEMU綁定在一起了,鼠標事件都會由QEMU接受并處理。然后我們上下左右移動鼠標,就會產生中斷。哎?怎么沒反應呢?

在這個狀態(tài)下,我們不能對Windows進行操作,所以只好按下Ctr鍵再按Alt鍵,先把鼠標從QEMU中解放出來。然后點擊“×”,關閉QEMU窗口。
雖然今天的結果還不能讓人滿意,但天色已經很晚了,就先到此為止吧。原因嘛,讓我們來思考一夜。但不論怎么說,鍵盤的中斷設定已經成功了,至于鼠標的問題,肯定也能很快找到原因的。我們明天再繼續(xù)吧。
- PLC控制程序精編108例
- 鴻蒙生態(tài):開啟萬物互聯(lián)的智慧新時代
- UNIX操作系統(tǒng)設計
- 嵌入式Linux驅動程序和系統(tǒng)開發(fā)實例精講
- 混沌工程實戰(zhàn):手把手教你實現(xiàn)系統(tǒng)穩(wěn)定性
- Learning Magento 2 Administration
- 計算機系統(tǒng):基于x86+Linux平臺
- Windows 8實戰(zhàn)從入門到精通(超值版)
- 大規(guī)模分布式系統(tǒng)架構與設計實戰(zhàn)
- Linux集群之美
- Learn Quantum Computing with Python and IBM Quantum Experience
- Linux內核修煉之道
- Microsoft Hyper-V Cluster Design
- Getting Started with UDK
- 鴻蒙HarmonyOS應用開發(fā)從入門到精通