官术网_书友最值得收藏!

6 中斷處理程序的制作(harib03e)重印時的補充說明:本文中只講到了IRQ1和IRQ12的中斷處理程序。事實上附屬光盤中還有IRQ7的中斷處理程序。要它干什么呢?因為對于一部分機種而言,隨著PIC的初始化,會產生一次IRQ7中斷,如果不對該中斷處理程序執(zhí)行STI(設置中斷標志位,見第4章),操作系統(tǒng)的啟動會失敗。關于inthandler27的處理內容,大家讀一讀7.1節(jié)會更容易理解。

今天的內容所剩不多了,大家再加一把勁。鼠標是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語言寫對于我們今天這個程序來說,在中斷處理程序中無限循環(huán),IRETD指令得不到執(zhí)行,所以怎么都行。之所以說“不能用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ù)吧。

主站蜘蛛池模板: 岱山县| 昌都县| 璧山县| 罗甸县| 莱芜市| 碌曲县| 临洮县| 桃园县| 奈曼旗| 玉环县| 邵武市| 林周县| 海门市| 昌宁县| 馆陶县| 离岛区| 高雄市| 栾城县| 平陆县| 彰化县| 东丽区| 镇安县| 城口县| 南丰县| 南康市| 青田县| 宁乡县| 睢宁县| SHOW| 罗江县| 金华市| 清水县| 玉环县| 石首市| 盐山县| 广东省| 阿图什市| 海南省| 克什克腾旗| 大安市| 桑植县|