本文較長,内容來自王爽老師的《彙編語言》一書,裡面加上了一些個人理解,全部看完可能需要一點時間。
我們通過對 0号中斷,即除法錯誤的中斷處理,來體會一下前面所講的内容。
當CPU執行div等除法指令的時候,如果發生了除法溢出錯誤,将産生中斷類型碼為 0 的中斷信息,CPU将檢測到這個信息,然後引發中斷過程,轉去執行 0 号中斷所對應的中斷處理程序。
mov ax,1000h
mov bh,1
div bh
現在我們考慮改變一下0号中斷處理程序的功能,即重新編寫一個0号中斷處理程序,它的功能是在屏幕中間顯示“overflow!”後,然後返回到操作系統。
分析(1)當發生除法溢出的時候,産生0号中斷信息,從而引發中斷過程。
此時,CPU将進行以下工作:
① 取得中斷類型碼0;
② 标志寄存器入棧,TF、IF設置為0;
③ CS、IP入棧;
④ (IP) = (0*4),(CS) = (0*4 2)
分析(2)可見 ,當中斷 0 發生時,CPU将轉去執行中斷處理程序。
隻要按如下步驟編寫中斷處理程序,當中斷0發生時,即可顯示“overflow!”。
① 相關處理。
② 向顯示緩沖區送字符串“overflow!”。
③ 返回DOS
我們将這段程序稱為do0。
分析(3)現在的問題是:do0 應放在内存中。
因為除法溢出随時可能發生,CPU随時都可能将 CS:IP指向 do0的入口,執行程序。
那麼do0應該放在哪裡呢?
由于我們是在操作系統之上使用計算機,所有的硬件資源都在操作系統的管理之下,所以我們要想得到一塊内存存放do0,應該向操作系統申請。
但我們學習彙編的一個重要目的就是要獲得對計算機底層的編程體驗,所以,在可能的情況下,我們不去理會操作系統,而直接面向硬件資源。
問題變得簡單而直接,我們隻需找到一塊别的程序不會用到的内存區,将do0傳送到其中即可。
我們知道,内存0000:0000~0000:03FF,大小為1KB的空間是系統存放中斷處理程序入口地址的中斷向量表。
中斷向量表在内存中存放,對于8086PC機,中斷向量表指定放在内存地址0處。
從内存0000:0000到0000:03FF的1024個單元中存放着中斷向量表。
8086 支持 256 個中斷,但是,實際上,系統中要處理的中斷事件遠沒有達到256 個 。所以在中斷向量表中,有許多單元是空的。
中斷向量表是PC系統中最重要的内存區,隻用來存放中斷處理程序的入口地址,DOS 系統和其他應用程序都不會随便使用這段空間。
我們可以利用中斷向量表中的空閑單元來存放我們的程序。
一般情況下:
從0000:0200至0000:02FF的256個字節的空間所對應的中斷向量表項都是空的,操作系統和其他應用程序都不占用。
我們估計,do0的長度不可能超過256個字節。
結論:我們可以将do0傳送到内存0000:0200處。
我們将中斷處理程序do0放到 0000:0200 後,若要使得除法溢出發生的時候,CPU轉去執行do0,則必須将do0的入口地址,即0000:0200登記在中斷向量表的對應表項中。
因為除法溢出對應的中斷類型碼為0,它的中斷處理程序的入口地址應該從0×4地址單元開始存放,段地址存放在 0×4 2 字單元中,偏移地址存放在0×4字單元中。
也就是說要将do0的段地址0存放在 0000:0002 字單元中 ,将偏移地址200H存放在0000:0000字單元中。
總結上面的分析,我們要做以下幾件事情:
(1)編寫可以顯示“overflow!”的中斷處理程序:do0;
(2)将do0送入内存0000:0200處;
(3)将do0的入口地址0000:0200存儲在中斷向量表0号表項中。
程序1
assume cs:code
code segment
start: do0安裝程序
設置中斷向量表
mov ax,4c00h
int 21h
do0: 顯示字符串“overflow!”
mov ax,4c00h
int 21h
code ends
end start
我們可以看到,上面的程序分為兩部分:
(1)安裝do0,設置中斷向量
(2)do0
程序1執行時,do0的代碼是不執行的,它隻是作為do0安裝程序所要傳送的數據。
程序1執行時,首先執行do0安裝程序,将 do0 的代碼拷貝到内存 0:200處,然後設置中斷向量表,将do0的入口地址,即偏移地址200H和段地址0,保存在0号表項中。
這兩部分工作完成後,程序就返回了。
程序目的就是在内存0:200處安裝do0 代碼,将0号中斷處理程序的入口地址設置為0:200。
do0的代碼雖然在程序中,卻不在程序執行的時候執行。它是在除法溢出發生的時候才得以執行的中斷處理程序。
do0部分代碼的最後兩條指令是依照我們的編程要求,用來返回DOS的。
現在,我們在反過來從CPU的角度看一下,什麼是中斷處理程序?
我們來看一下do0是如何變成0号中斷的中斷處理程序的:
(1)程序1 在執行時,被加載到内存中,此時do0的代碼在程序1 所在的内存空間中,它隻是存放在程序1的代碼段中的一段要被傳送到其他單元中的數據,我們不能說它是0号中斷的中斷處理程序;
do0是如何變成0号中斷的中斷處理程序的:
(2)程序1中安裝do0 的代碼執行完後,do0的代碼被從程序1的代碼段中拷貝到0:200處。此時,我們也不能說它是0号中斷的中斷處理程序,它隻不過是存放在0:200處的一些數據;
do0是如何變成0号中斷的中斷處理程序的:
(3)程序1中設置中斷向量表的代碼執行完後,在0号表項中填入了do0的入口地址0:200,此時0:200 處的信息,即do0 的代碼,就變成了0号中斷的中斷處理程序。
因為當除法溢出(即0号中斷)發生時,CPU将執行0:200處的代碼。
如何讓一個内存單元中的信息被CPU當作指令來執行?
将它的地址放入CS、IP中;
如何讓一段程序成為N号中斷的中斷處理程序?
将它的入口地址放入中斷向量表的N号表項中。
我們可以使用movsb指令,将do0的代碼送入0:0200處。
我們來看一下,用rep movsb指令的時候需要确定的信息:
(1)傳送的原始位置,段地址:code,偏移地址:offset do0;
(2)傳送的目的位置:0:200;
(3)傳送的長度:do0部分代碼的長度;
(4)傳送的方向:正向。
程序2
assume cs:code
code segmentx
start: 設置es:di指向目的地址
設置ds:si指向源地址
設置cx為傳輸長度
設置傳輸方向為正
rep movsb
設置中斷向量表
mov ax,4c00h
int 21h
do0: 顯示字符串“overflow!”
mov ax,4c00h
int 21h
code ends
end start
“-”是編譯器識别的運算符号,編譯器可以用它來進行兩個常數的減法。
比如:mov ax,8-4,被編譯器處理為指令: mov ax,4。
因此可以用offset do0end-offset do0,得到do0代碼的長度。
下面我們編寫do0程序。do0程序的主要任務是顯示字符串,程序如下:
do0:設置ds:si指向字符串
mov ax,0b800h
mov es,ax
mov di,12*160 36*2;設置es:di指向顯存空間的中間位置
mov cx,9 ;設置cx為字符串長度
s: mov al,[si]
mov es:[di],al
inc si
add di,2
loop s
mov ax,4c00h
int 21h
do0end:nop
注意,“overflow!”在程序2的data段中。程序2執行完成後返回,它所占用的内存空間被系統釋放,而在其中存放的“overflow!”也将很可能被别的信息覆蓋;
而do0程序被放到了0:200處,随時都會因發生了除法溢出而被CPU 執行,很難保證 do0 程序從原來程序2所處的空間中取得的是要顯示的字符串“overflow!”。
因為 do0 程序随時可能被執行,而它要用到字符串“overflow”,所以該字符串也應該存放在一段不會被覆蓋的空間中。
在程序3中,我們将“overflow!”放到do0程序中,程序3執行時,将标号do0到标号
do0end之間的内容送到0000:0200處。
注意,因為在do0程序開始處的“overflow!”不是可以執行的代碼,所以在“overflow!”之前加上一條jmp 指令,轉移到正式的do0 程序。
當除法溢出發生時,CPU 執行0:200 處的jmp 指令,跳過後面的字符串,轉到正式的do0 程序執行。
do0程序執行過程中必須要找到“overflow!”,那麼它在哪裡呢?
首先來看段地址,“overflow!”和do0的代碼處于同一個段中,而除法溢出發生時,CS中必然存放do0的段地址,也就是“overflow!”的段地址;
再來看偏移地址,0:200處的指令為jmp short do0start ,這條指令占兩個字節,所以“overflow!”的偏移地址為202h 。
下面,我們将do0的入口地址0:200,寫入中斷向量表的 0 号表項中,使do0成為0 号中斷的中斷處理程序。
0号表項的地址為0:0,其中0:0字單元存放偏移地址,0:2字單元存放段地址。
程序如下:
-----------------------------------------------------------------------------------------------------------------
程序3
assume cs:code
code segment
start:
;do0安裝程序
mov ax,0
mov es,ax
mov di,200h;設置es:di指向目的地址
mov ax,cs
mov ds,ax
mov si,offset do0;設置ds:si指向源地址
mov cx,offset do0end-offset do0;設置cx為傳輸長度,這裡傳輸了包括‘overflow!’的内容
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:
jmp short do0start
db "overflow!"
do0start:
mov ax,cs
mov ds,ax
mov si,202h
mov ax,0B800h
mov es,ax
mov di,12*160 36*2;設置es:di指向顯存空間的中間位置
mov cx,13;設置cx為字符串長度
s:mov al,[si]
mov es:[di],al
mov byte ptr es:[di 1],2;設置顔色屬性
inc si
add di,2
loop s
mov ax,4c00h
int 21h;中斷處理程序完成後返回到DOS
do0end:nop
code ends
END START
————————————————
主程序
assume cs:code
code segment
main: ;當CPU執行div bh後,會發生除法溢出錯誤,産生0号中斷信息
mov ax,1000H
mov bh,01
div bh
mov ax,4C00H
int 21H
code ends
end main
————————————————
整個過程的思路就是:
(1)編寫可以顯示“overflow!”的中斷處理程序:do0;
(2)将do0這個程序本身送入内存0000:0200處,包括‘overflow!’這個字符串;
(3)将do0的入口地址0000:0200存儲在中斷向量表0号表項中。
(4)這個程序本身并未考慮對原有的0号中斷程序的恢複問題,隻是單純進行了改寫。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!