匯編語言:實驗十二 編寫0號中斷的處理程序


實驗內容

編寫0號中斷處理程序,使得在除法溢出發生時,在屏幕中間顯示字符串"divide error!",然后返回到DOS。

解題

這一章都在介紹中斷,包括中斷的產生、中斷處理程序、中斷向量表、中斷過程、相關指令。
解決本次實驗的前提是將本章的內容理解好,那么在完成這部分(原書第12章-內中斷)之后,開始實驗吧~

分析整個中斷過程

(1)當發生除法溢出的時候,產生0號中斷信息,從而引發中斷。
CPU會完成以下工作:

  1. 取得中斷類型碼 0
  2. 標志寄存器入棧,TF、IF 設置為 0
  3. CS、IP入棧
  4. (IP) = (0 * 4), (CS) = (0 * 4 + 2)

對於第二步,為社么要將TF、IF設置為0?
在十一小節中王爽老師給出了解答:CPU在執行完一條指令之后,如果檢測到標志寄存器TF位為1,則產生單步中斷,引發中斷過程。單步中斷的中斷類型碼為1,它引發的中斷過程如下:
在這里插入圖片描述
這也是本問題的關鍵了,反過來想,如果TF不設置為0,很顯然CPU在即將執行中斷處理程序的第一條指令時檢測到了TF為1,就會在執行完該條指令之后繼續進入到單步中斷的過程當中… 就這樣可能無限循環下去了,所以在中斷處理之前必須將TF置為0。
對於IF書中沒有多余解釋,對此筆者搜集到了以下資料:
在這里插入圖片描述
↑Linux系統中的中斷分類

只看表格中的第二行第一條的處理方式一欄:清除標志寄存器eflags的IF標志可屏蔽中斷,筆者推測IF標志位的功能就是顯示中斷的開(On)關(Off),可以譯為:Interrupt Flag,
(查閱了其他博客也確實是這樣)

IF:中斷允許標志位。它用來控制8086是否允許接收外部中斷請求。若IF=1,8086能響應外部中斷,反之則屏蔽外部中斷;

這么想就能夠串通了,只是用來屏蔽掉其他中斷(處理中斷的過程中關閉中斷功能,確保能夠順利執行完本次中斷,處理完成后再打開中斷)

好了,現在明白CPU在發生中斷之后做的事情了,那么我們需要完成什么呢?
1.相關處理
2.向顯示緩沖區寫入想要顯示的字符串 “divide error!”
3.返回 DOS

按照王爽老師的講解,將這段程序命名為 do0。

但是一個從未碰到過的問題來了:do0程序應該放到哪?
do0應該放到內存中,因為除法溢出隨時可能發生,CPU隨時都可能將CS:IP指向do0的入口,然后執行它。

按理來說我們需要向操作系統申請一塊空間去放置do0程序,但是過多的討論申請內存將偏離問題的主線,所以這里簡單做:直接使用一塊別的程序不會用到的內存區,將do0拷貝到其中就可以了。

12.3 中斷向量表 章節中作者講解了中斷向量表的存儲位置(針對8086CPU):
0000:0000 到 0000:03FF 共計 400H (1024字節)個單元中存放了256個中斷,但是實際上系統中要處理的中斷事件沒有達到256個,所以在表中有許多單元是空着的。

根據書中的指示:0000:0200 到 0000:02FF 的256個字節的空間所對應的單元都是空的
因此,可以將do0拷貝到內存0000:0200處

do0程序的放置解決了,接下來就是當發生除法溢出的時候,CPU會取得中斷碼0,然后到(4 * 0) = 0H的地址(0000:0000H)去找中斷處理程序的偏移地址,到(4 * 0 + 2) = 2H的地址找中斷處理程序的段地址。
如下圖:
在這里插入圖片描述

總結一下將要做的事情:

  1. 編寫可以顯示“divide error!”的中斷處理程序 do0;
  2. 將do0拷貝到內存0000:0200處;
  3. 將do0的入口地址0000:0200 存儲在中斷向量表0號單元中。

程序框架:
在這里插入圖片描述

安裝do0程序與設置中斷向量表

需要明晰的是:我們編寫的程序在運行時do0處的代碼是不執行的!
我們要做的是將do0這部分代碼放入到之前選擇好的那塊沒有程序會使用的內存空間0000:0200H上,並且設置好中斷向量表中0號單元的內容,這樣CPU就可以在發生除法溢出的時候乖乖地去0號向量表單元取出對應的偏移地址、段地址,然后順利完成中斷處理程序do0的執行。

安裝do0程序

將編寫好的do0程序代碼送入到0000:0200H內存段中,需要使用到 movsb指令,如下(由於csdn沒有比較好展示匯編代碼的方式,這里筆者先放上Notepad++中的截圖方便查看):

在這里插入圖片描述
上面就是do0程序的安裝過程,其中需要注意的是第18行:
mov cx, offset do0end - offset do0
為了計算do0段的大小,需要額外設置標號 do0end 放在do0程序段結束的位置。
注:如果只是單純的計算出所編寫的do0程序塊大小,然后賦值給cx,這種方式的編程很明顯是不可取的,因為do0稍微改一下可能影響程序段的大小,又得重新計算,所以使用標號法交給編譯器去做這些麻煩的事情吧~

設置中斷向量表

對於0號中斷,我們需要在0000:0000處填上偏移地址,在0000:0004處填上中斷處理程序段地址。
在這里插入圖片描述
注:對於N號中斷,偏移地址應該填在0000:(N * 4) 字單元中,段地址應該填在0000:(N * 4 + 2) 字單元中。

編寫do0程序

do0程序需要做的:
1.相關處理
2.向顯示緩沖區寫入想要顯示的字符串 “divide error!”
3.返回 DOS

這個相關處理指的是什么?
先別急,看第二條:向顯示緩沖區寫入想要顯示的字符串 “divide error!”
這個我們熟悉吧?不熟悉請點這里-> 在顯示緩沖區內編程完成字符串顯示
這個不難,但是既然是字符串的顯示,字符串的存儲位置也是需要考慮的問題。

在這里插入圖片描述
想一想:像上圖這樣放在代碼的起始位置行不行?
答案是不行,因為那樣字符串的位置是不夠“安全”的,因為在整個程序(完成拷貝的程序,不是do0程序)結束之后,原來的空間會被釋放(別的程序可能會用到它),那么難免這塊空間會被修改,我們需要一塊在do0程序被執行時裝有字符串的空間,說起來比較抽象,看代碼:
在這里插入圖片描述

完整代碼

assume cs:code

;編寫0號中斷程序,使得在除法溢出發生時,
;在屏幕中顯示字符串"divide error!"
;然后返回dos

code segment
start:
	;首先將中斷處理程序送入到中斷向量地址處
	mov ax, cs
	mov ds, ax
	mov si, offset do0	;源地址 cs:offset do0
	
	mov ax, 0
	mov es, ax
	mov di, 200H	;目標地址 0:200H
	
	mov cx, offset do0end - offset do0	;用標號計算出do0段的大小
	cld	;設置si、di遞增
	rep movsb
	
	;設置中斷向量表
	mov ax, 0
	mov es, ax
	mov word ptr es:[0 * 4], 200h	;04H = 200H
	mov word ptr es:[0 * 4 + 2], 0	;00 = 0H
	
	mov ax, 4c00h
	int 21h
	
	
do0:
	jmp short do0start
	db "divide error!"
	
do0start:;中斷程序執行開始處:打印字符串
	mov ax, cs
	mov ds, ax
	mov si, 202H
	
	mov ax, 0b800H
	mov es, ax	;找到顯卡位置
	mov di, 160 * 12 + 36 * 2	;顯示在顯卡中間
	
	mov cx, 13	;一共13個字符,挨個拷貝
	mov dh, 11000010B	;顯示 紅底閃爍綠字
	
s:
	mov dl, [si]	;將需要顯示的字符串放到dl
	mov es:[di], dl	;輸送到顯卡處
	mov es:[di + 1], dh;設置字的屬性
	inc si	;往后偏移一個字節
	add di, 2;往后偏移一個字
	loop s
	
	mov ax, 4c00h
	int 21H
do0end:
	nop
code ends
end start

效果展示

在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM