1. 程式人生 > >第十二章 內中斷

第十二章 內中斷

引言

本書主要講解硬體中斷。

12.1 內中斷的產生

12.2 中斷處理程式

12.3 中斷向量表

  • 中斷向量表在記憶體中存放,對於8086PC機,中斷向量表指定存放在記憶體地址0處。
  • 從記憶體0000:0000到0000:03FF的1024個單元中存放著中斷向量表。為什麼是1024個位元組呢?我們回憶,段地址和偏移地址分別都是16位的,它倆經過組合構成一個20位的實體地址。16位相當於2個位元組,我們要得到實體地址就要分別存放段地址和偏移地址,所以一個實體地址需要4個位元組(也就是4個記憶體單元)來存放,因為中斷向量表中有256箇中斷,一箇中斷對應一個實體地址,所以需要256*4=1024個位元組,即1024個記憶體單元。
  • 我們在第五章有講,記憶體0000:0000~0000:03FF,大小為1KB的空間是系統存放中斷處理程式入口地址的中斷向量表。一般情況下,從0000:0200~0000:02FF(這是256個位元組,即256個記憶體單元)的256個位元組的空間所對應的中斷向量表項都是空的,作業系統的其它應用程式都不佔用。

12.4 中斷過程

8086CPU的中斷過程:

  1. (從中斷資訊中)取得中斷型別碼;
  2. 標誌暫存器的值入棧(保護標誌位);
  3. 設定標誌暫存器的第8位 TF 和第9位 IF 的值為 0;
  4. CS暫存器的內容入棧;
  5. IP暫存器的內容入棧;
  6. 從記憶體地址為中斷型別碼*4中斷型別碼*4+2的兩個單元中讀取中斷處理程式的入口地址設定IP和CS。

更簡潔的描述中斷過程,如下:

  1. 取得中斷型別碼N;
  2. pushf
  3. TF = 0,IF = 0
  4. push CS
  5. push IP
  6. (IP) = (N*4),(CS) = (N*4+2)

在最後一步完成後,CPU開始執行由程式設計師編寫的中斷處理程式。

動畫演示中斷過程(13:45處)

12.5 中斷處理程式和iret指令

中斷處理程式,常規的步驟:

  1. 儲存用到的暫存器;
  2. 處理中斷;
  3. 恢復用到的暫存器;
  4. 用 iret 指令返回。

iret 指令的功能用匯編語法描述為:

pop IP
pop CS
popf

iret 通常和硬體自動完成的中斷過程配合使用。可以看到,在中斷過程中,暫存器入棧的順序是標誌暫存器、CS、IP,而 iret 的出棧順序是IP、CS、標誌暫存器,剛好和入棧對應,實現了用執行中斷處理程式前的CPU現場恢復標誌暫存器和CS、IP的工作。iret 指令執行後,CPU回到執行中斷處理程式前的執行點繼續執行程式。

12.6 除法錯誤中斷的處理

除法溢位對應0號中斷。

我們編寫程式 test.asm 測試除法溢位:

assume cs:codesg

codesg segment
start:
        mov ax,1000h    ;被除數1000
        mov bh,1    ;除數1
        div bh  ;bh決定了該除法為8位除法
        ;商暫存器 al 為 8 位,儲存無符號數除法所得商範圍 0~255
        ;1000/1=1000,很明顯在 al 中無法存放,引發除法溢位

codesg ends
end start

div 指令可以做除法。當進行 8 位除法的時候,用 al 儲存結果的商,ah 儲存結果的餘數;進行 16 位除法的時候,用 ax 儲存結果的商,dx 儲存結果的餘數。

test.asm 編譯,連結和Debug截圖:

螢幕快照 2018-10-15 16.12.28

螢幕快照 2018-10-15 16.13.08

可以看到引發除法溢位 Divide overflow。

8086除法指令DIV,IDIV

12.7 程式設計處理0號中斷

12.8 安裝

更詳細的程式框架:

assume cs:code

code segment
start:
    ;設定ds:si指向源地址
    ;設定es:di指向目的地址
    ;設定cx為傳輸長度
    ;設定傳輸方向為正
    rep movsb

    ;設定中斷向量表

    mov ax,4c00h
    int 21h
    
do0:
    ;顯示字串"Welcome to Fishc.com!"
    
    mov ax,4c00h
    int 21h

code ends
end start

更明確的的程式:

assume cs:code

code segment
start:
    ;設定ds:si指向源地址
    mov ax,cs
    mov ds,ax
    mov si,offset do0

    ;設定es:di指向目的地址
    mov ax,0
    mov es,ax
    mov di,200h

    ;設定cx為傳輸長度
    mov cx,do0部分程式碼的長度

    ;設定傳輸方向為正
    cld

    rep movsb

    ;設定中斷向量表

    mov ax,4c00h
    int 21h

do0:
    ;顯示字串"Welcome to Fishc.com!"
    
    mov ax,4c00h
    int 21h

code ends
end start

接著就是計算do0部分程式碼的長度:

assume cs:code

code segment
start:
    ;設定ds:si指向源地址
    mov ax,cs
    mov ds,ax
    mov si,offset do0

    ;設定es:di指向目的地址
    mov ax,0
    mov es,ax
    mov di,200h

    ;設定cx為傳輸長度
    mov cx,offset do0end - offset do0

    ;設定傳輸方向為正
    cld

    rep movsb

    ;設定中斷向量表

    mov ax,4c00h
    int 21h

do0:
    ;顯示字串"Welcome to Fishc.com!"
    
    mov ax,4c00h
    int 21h
do0end: nop

code ends
end start

接下來是do0程式,do0程式的主要任務是顯示字串,程式如下:

do0:;顯示字串"Welcome to Fishc.com!"
    mov ax,0b800h
    mov es,ax
    mov di,12*160+36*2  ;設定es:di指向視訊記憶體空間的中間位置
    mov cx,21    ;設定cx為字串長度

s:  ;把字串中的字元一個一個的拷貝過去
    mov al,[si]
    mov es:[di],al
    inc si
    add di,2
    loop s
    
    mov ax,4c00h
    int 21h
do0end: nop

現在,我們得到了相對完整的彙編程式版本 program1.asm

assume cs:code

data segment
    db "Welcome to Fishc.com!"
data ends

code segment
start:
    ;設定ds:si指向源地址
    mov ax,cs
    mov ds,ax
    mov si,offset do0

    ;設定es:di指向目的地址
    mov ax,0
    mov es,ax
    mov di,200h

    ;設定cx為傳輸長度
    mov cx,offset do0end - offset do0

    ;設定傳輸方向為正
    cld

    rep movsb

    ;設定中斷向量表

    mov ax,4c00h
    int 21h

do0:;顯示字串"Welcome to Fishc.com!"
    mov ax,0b800h
    mov es,ax
    mov di,12*160+36*2  ;設定es:di指向視訊記憶體空間的中間位置
    mov cx,21    ;設定cx為字串長度

s:  ;把字串中的字元一個一個的拷貝過去
    mov al,[si]
    mov es:[di],al
    inc si
    add di,2
    loop s
    
    mov ax,4c00h
    int 21h
do0end: nop

code ends
end start

程式 program1.asm 看似合理,可實際上卻大錯特錯。

注意,“Welcom to Fishc.com!”在程式 program1 的data段中。程式 program1 執行完成後返回,它所佔用的記憶體空間被系統釋放,而在其中存放的“Welcom to Fishc.com!”也將很可能被別的資訊覆蓋。而do0程式被放到了0000:0200處,隨時都會因發生了除法溢位而被CPU執行,很難保證do0程式從原來程式 program1 所處的空間中取得的是要顯示的字串“Welcom to Fishc.com!”,因為字串“Welcom to Fishc.com!”是存放在資料段的,隨時可能被覆蓋,不是一段安全的記憶體空間。因為 program1 執行完,它的資料段就被釋放了。

故,由於do0程式隨時可能被執行,而它要用到字串“Welcom to Fishc.com!”,所以該字串也應該存放在一段不會被覆蓋的空間中。解決思路是把字串存放到0000:0200處,也就是do0程式在記憶體中的地址0000:0200處。

do0:    ;顯示字串"Welcome to Fishc.com!"
        jmp short do0start  ;執行到此處,CPU直接跳到do0start執行
        db "Welcome to Fishc.com!"  ;在程式碼段裡存放資料。這算是“歪門邪道”
do0start:
        mov ax,0b800h
        mov es,ax
        mov di,12*160+36*2  ;設定es:di指向視訊記憶體空間的中間位置

這樣修改後,我們的字串就能跟隨do0儲存在安全空間中,但是問題又來了。do0程式執行過程中必須找到“Welcom to Fishc.com!”,那麼它在哪裡呢?

首先來看段地址,“Welcom to Fishc.com!”和do0的程式碼處於同一個段中,而除法溢位發生時,CS中必然存放do0的段地址,也就是“Welcom to Fishc.com!”的段地址;再來看偏移地址,0000:0200處的指令為 jmp short do0start ,這條指令佔兩個位元組,所以“Welcom to Fishc.com!”的偏移地址為0202h。

最後,我們將do0的入口地址0000:0200寫入中斷向量表的0號表項中,使do0成為0號中斷的中斷處理程式。0號表項的地址為0000:0000,其中0000:0000字單元存放偏移地址;0000:0002字單元存放段地址。

完整程式實現 program2.asm

assume cs:code

code segment
start:
        ;設定ds:si指向源地址
        mov ax,cs
        mov ds,ax
        mov si,offset do0

        ;設定es:di指向目的地址
        mov ax,0
        mov es,ax
        mov di,200h

        ;設定cx為傳輸長度
        mov cx,offset do0end - offset do0

        ;設定傳輸方向為正
        cld
        rep movsb

        ;設定中斷向量表
        mov ax,0
        mov es,ax
        mov word ptr es:[0*4],200h
        mov word ptr es:[0*4+2],0

        mov ax,4c00h
        int 21h

do0:    ;顯示字串"Welcome to Fishc.com!"
        jmp short do0start  ;執行到此處,CPU直接跳到do0start執行
        db "Welcome to Fishc.com!"  ;在程式碼段裡存放資料。這算是“歪門邪道”
do0start:
        mov ax,cs
        mov ds,ax
        mov si,202h     ;設定ds:si指向字串
        
        mov ax,0b800h
        mov es,ax
        mov di,12*160+36*2  ;設定es:di指向視訊記憶體空間的中間位置

        mov cx,21    ;設定cx為字串長度

s:      ;把字串中的字元一個一個的拷貝過去
        mov al,[si]
        mov es:[di],al
        inc si
        add di,1
        mov al,02h  ;設定顏色
        mov es:[di],al
        add di,1
        loop s
    
        mov ax,4c00h
        int 21h
do0end: nop

code ends
end start

rep movs彙編指令的問題

當我們的程式 program2.asm 執行過後,另外一個程式觸發了0號中斷,就會顯示出字串“Welcom to Fishc.com!”,而不是除法溢位提示。

  • 實際演示截圖

    1. 在沒有執行 program2.exe 前,debug除法溢位程式 test.exe 單步跟蹤提示除法溢位 Divide overflow。編譯、連結 program2.asm

      螢幕快照 2018-10-15 16.47.16

    2. 執行 program2.exe 之後,再 debug test.exe

      螢幕快照 2018-10-15 16.49.26

12.9 do0

12.10 設定中斷向量

12.11 單步中斷

標誌暫存器的 TF 位為 1,則CPU產生單步中斷。

CPU 提供單步中斷功能的原因就是,為單步跟蹤的執行過程,提供了實現機制。

12.12 相應中斷的特殊情況