第十二章
CPU可以在執行完當前正在執行的指令之後,檢測到從CPU外部或內部產生的一種特殊信息,並且可以立即對所接收到的信息進行處理,這種特殊的信息稱爲中斷信息,其向所要進行的該種處理提供了必備的參數的通知信息
中斷:CPU不在接着剛執行完的指令向下執行,而是轉去執行這個特殊信息
12.1 內中斷的產生
對於8086CPU,當CPU內部有下面的情況發生的時候,將產生中斷信息
1、除法錯誤,比如,執行div指令產生的除法溢出
2、單步執行
3、執行into指令
4、執行int指令
CPU首先要知道,所接受到的中斷信息的來源,所以中斷信息中必須包含識別來源的編碼,稱爲中斷類型碼,爲一個字節型數據,可以表示256種中斷信息的來源,我們將產生中斷信息的事件,即中斷信息的來源,簡稱爲中斷源
上述4種中斷源的中斷類型碼如下
1、除法錯誤:0
2、單步執行:1
3、執行into指令:4
4、執行int指令,該指令的格式爲int n,指令中的n爲字節型立即數,是提供給CPU的中斷類型碼
12.2 中斷處理程序
如何對中斷信息進行處理,可以由我們編程決定,用來處理中斷信息的程序被稱爲中斷處理程序,不同的中斷信息編寫不同的處理程序
CPU收到中斷信息後,應該轉去執行相應的中斷處理程序,即設置CS:IP指向該程序的入口,所以需要在中斷信息和其處理程序的入口地址之間建立某種聯繫
中斷信息中包含有標識中斷源的類型碼,中斷類型碼的作用就是定位中斷處理程序,那麼如何根據8位中斷類型碼得到中斷處理程序的段地址和偏移地址?
12.3 中斷向量表
CPU用8位的中斷類型碼,通過查找中斷向量表找到相應中斷處理程序的入口地址
中斷向量就是中斷處理程序的入口地址,中斷向量表,就是中斷處理程序入口地址的列表
中斷向量表在內存中保存,其中存放着256箇中斷源所對應的中斷處理程序的入口,在8086PC機中,中斷向量表指定放在內存地址0處,從0000:0000到0000:03FF共1024個單元
中斷向量表中一個表項存放一個入口地址,佔兩個字,高地址字存放段地址,低地址字存放偏移地址
中斷向量表(內存中) |
---|
0號中斷源對應的中斷處理程序的入口地址 |
1號中斷源對應的中斷處理程序的入口地址 |
2號中斷源對應的中斷處理程序的入口地址 |
3號中斷源對應的中斷處理程序的入口地址 |
…… |
用Debug查看內存,情況如下:
0000:0000 68 10 A7 00 8B 01 70 00-16 00 9D 03 8B 01 70 00
則3號中斷源對應的中斷處理程序的入口地址爲:0070:018B
12.4 中斷過程
用中斷類型碼找到中斷向量,並用它設置CS和IP,這個工作由CPU的硬件自動完成。這個過程稱爲中斷過程,硬件完成中斷過程後,CPU開始執行CS:IP指向的中斷處理程序
CPU在指向玩中斷處理程序後,還要返回原來的執行點繼續執行下面的指令,所以在中斷過程中,設置CS:IP之前,要將原來的CS和IP保存起來(CPU在取完一條指令後,執行這條指令前,IP指向下一條指令)
具體過程:
1、(從中斷信息中)取得中斷類型碼
2、標誌寄存器的值入棧(中斷過程要改變標誌寄存器的值) pushf
3、設置標誌寄存器的第8位TF和第9位IF的值爲0
4、CS的內容入棧 push CS
5、IP的內容入棧 push IP
6、從內存中讀取中斷處理程序的入口地址
最後一步完成後,CPU開始執行由程序員編寫的中斷處理程序
12.5 中斷處理程序和iret指令
CPU隨時都可能檢測到中斷信息,也就是說。CPU隨時都可能執行中斷處理程序,所以中斷處理程序必須一直存儲在內存某段空間之中(CPU可以直接訪問內存)
中斷處理程序的編寫方法和子程序的比較相似
1、保存用到的寄存器
2、處理中斷
3、恢復用到的寄存器
4、用iret指令返回 pop IP pop CS popf
iret通常和中斷過程配合使用
12.6 除法錯誤中斷的處理
當CPU執行div等除法指令時,如果發生了除法溢出錯誤,將產生中斷類型碼爲0的中斷信息,引發中斷過程,執行相應中斷處理程序
mov ax,1000h
mov bh,1
div bh
該中斷處理程序會顯示提示信息"Divide overflow",返回到操作系統中
12.7 編程處理0號中斷
重新編寫一個0號中斷處理程序,當除法溢出時,在屏幕中間顯示"overflow",返回DOS
這裏我們跳過操作系統,直接面向硬件
在內存儲存中斷向量表的空間中,有很多單元(0000:0200至0000:02FF)是空的,可以存放我們的程序
同時,在中斷向量表對應的表項中設置新的地址
程序框架爲
assume cs:code
code segment
start: do0安裝程序
設置中斷向量表
mov ax,4c00h
int 21h
do0: 顯示字符串"overflow!"
mov ax,4c00h
int 21h
code ends
end start
該程序執行時do0部分是不執行的,而是作爲中斷處理程序被複制到內存0:0200處,最後兩行代碼是根據要求返回DOS
do0部分的代碼只有在被複制到0:200處,且設置中斷向量表完畢後,才成爲0號中斷的中斷處理程序
即:讓一段程序成爲N號中斷的中斷處理程序,需要將它的入口地址放入中斷向量表的N號表項中
12.8 安裝
可以使用movsb指令,將do0的代碼送入0:200處,因此我們需要知道原始位置和目的位置,以及do0代碼的長度,我們利用編譯器來計算do0的長度,具體做法見代碼
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0 ; 設置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ; 設置es:di指向目的地址
mov cx,offset do0end-offset do0
cld ; 設置正向傳輸
rep movsb
設置中斷向量表
mov ax,4c00h
int 21h
do0: 顯示字符串"overflow!"
mov ax,4c00h
int 21h
do0end: nop
code ends
end start
"-"是編譯器識別的運算符號,可以用來進行兩個常數的減法
mov ax,8-4 ; 編譯器處理爲mov ax,4
mov ax,(5+3)*5/10 ; 編譯器處理爲mov ax,4
12.9 do0
字符串"overflow"的位置值得思考,如果跟之前一樣,在主程序中開闢一個data段,那麼在程序執行結束後,其佔用的內存空間被系統釋放,data段的位置可能會被別的信息覆蓋,到時候可能顯示的就不是正確的內容
所以應該把字符串放在一段不會被覆蓋的空間中
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0 ; 設置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ; 設置es:di指向目的地址
mov cx,offset do0end-offset do0
cld ; 設置正向傳輸
rep movsb
設置中斷向量表
mov ax,4c00h
int 21h
do0: jmp short do0start
db "overflow!" ; 使用一部分內存存儲字符串
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,9
s: mov al,[si]
mov es:[di],al
inc si
add si,2
loop s
mov ax,4c00h
int 21h
do0end: nop
code ends
end start
當除法溢出發生時,CPU執行0:200處的jmp指令跳過字符串,轉到正式的程序執行
該字符串的段地址與溢出發生時執行的中斷處理程序段地址相同,由於jmp short do0start佔兩個字節,所以偏移地址爲202h
12.10 設置中斷向量
將do0的入口地址寫入0號表項中
mov ax,0
mov es,ax
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0
12.11 單步中斷
CPU在執行完一條指令後,若檢測到標誌寄存器的TF位爲1,則產生單步中斷,中斷類型碼爲1
中斷過程如下:
1、取得中斷類型碼1
2、標誌寄存器入棧,TF、IF設置爲0
3、CS、IP入棧
4、設置CS、IP
如Debug的T命令,可以單步執行被加載程序的指令
Debug提供了單步中斷的中斷處理程序,功能爲顯示所有寄存器中的內容後等待輸入,然後在使用t命令時,Debug將TF設置爲1,使得CPU工作於單步中斷方式下,則在CPU執行完這條指令後就引發單步中斷,執行單步中斷的中斷處理程序,在屏幕上顯示寄存器的內容,並等待輸入命令
中斷處理程序也是由一條條指令組成的,如果執行中斷處理程序之前TF仍爲1,則在執行完中斷處理程序的第一條指令以後,又會產生單步中斷……不斷套娃,所以我們注意到在中斷過程中,TF被設置爲0
所以完整過程爲:T命令(設置TF=1)->執行當前指令->中斷過程(設置TF=0)->中斷處理程序執行(顯示寄存器內容&等待輸入)->T命令(設置TF=1)->返回程序執行下一條指令……
CPU提供單步中斷功能的原因是,爲單步跟蹤程序的執行過程提供了實現機制
12.12 響應中斷的特殊情況
在有些情況下,CPU在執行完一條指令後,即便發生中斷,也不會立即響應
舉例:在執行完mov ss,ax之後,即便發生中斷,也不會響應,因爲這樣可能會導致ss:sp聯合指向棧頂時,指向不正確的棧頂,導致錯誤,所以CPU在執行完設置ss的指令後,不響應中斷,這給連續設置ss和sp提供了機會,所以我們應該利用這一特性,將兩條指令連續存放,如果將棧頂設置爲1000:0,不應該
mov ax,1000h
mov ss,ax
mov ax,0
mov sp,0
因此,在Debug利用單步中斷實現T命令時,在mov ss,ax指令執行之後,CPU不響應中斷(不管下一條是什麼指令,都不響應),而是等到下一條指令mov sp,10h執行後,才響應中斷