[匯編]《匯編語言》第12章 內中斷


王爽《匯編語言》第四版 超級筆記

第12章 內中斷

任何一個通用的CPU,比如8086,都具備一種能力,可以在執行完當前正在執行的指令之后,檢測到從CPU外部發送過來的或內部產生的一種特殊信息,並且可以立即對所接收到的信息進行處理。

這種特殊的信息,我們可以稱其為:中斷信息。中斷的意思是指,CPU不再接着(剛執行完的指令)向下執行,而是轉去處理這個特殊信息。

“中斷信息”是要求CPU馬上進行某種處理,並向所要進行的該種處理提供了必備的參數的通知信息。因為本書的內容不是微機原理與接口或組成原理,我們只能用一些便於理解的說法來描述一些比較復雜的機器工作原理,從而使學習者忽略一些和我們的學習重心無關的內容。

但筆者又需要對這些問題有一個嚴謹的交代,所以有了這些補充說明的文字。如果你不理解這些文字所講的東西,就不必去理解了。

中斷信息可以來自CPU的內部和外部,這一章中,我們主要討論來自於CPU內部的中斷信息。

12.1 內中斷的產生、中斷處理程序

當CPU的內部有什么事情發生的時候,將產生需要馬上處理的中斷信息呢?

對於8086CPU,當CPU內部有下面的情況發生的時候,將產生相應的中斷信息。

(1)除法錯誤,比如,執行div指令產生的除法溢出;
(2)單步執行;
(3)執行into指令;
(4)執行int指令。

我們現在不要去管這4種情況的具體含義,只要知道CPU內部有4種情況可以產生需要及時處理的中斷信息即可。雖然我們現在並不很清楚,這4種情況到底是什么,但是有一點是很清楚的,即它們是不同的信息。既然是不同的信息,就需要進行不同的處理。

要進行不同的處理,CPU首先要知道,所接收到的中斷信息的來源。所以中斷信息中必須包含識別來源的編碼。8086CPU用稱為中斷類型碼的數據來標識中斷信息的來源。

中斷類型碼為一個字節型數據,可以表示256種中斷信息的來源。以后,我們將產生中斷信息的事件,即中斷信息的來源,簡稱為中斷源,上述的4種中斷源,在8086CPU中的中斷類型碼如下。

(1)除法錯誤:0
(2)單步執行:1
(3)執行into指令:4
(4)執行int指令,該指令的格式為int n,指令中的n為字節型立即數,是提供給CPU的中斷類型碼。


CPU收到中斷信息后,需要對中斷信息進行處理。而如何對中斷信息進行處理,可以由我們編程決定。我們編寫的,用來處理中斷信息的程序被稱為中斷處理程序。一般來說,需要對不同的中斷信息編寫不同的處理程序。

CPU在收到中斷信息后,應該轉去執行該中斷信息的處理程序。我們知道,若要8086CPU執行某處的程序,就要將CS:IP指向它的入口(即程序第一條指令的地址)。可見首要的問題是,CPU在收到中斷信息后,如何根據中斷信息確定其處理程序的入口。

CPU的設計者必須在中斷信息和其處理程序的入口地址之間建立某種聯系,使得CPU根據中斷信息可以找到要執行的處理程序。

我們知道,中斷信息中包含有標識中斷源的類型碼。根據CPU的設計,中斷類型碼的作用就是用來定位中斷處理程序。

比如CPU根據中斷類型碼4,就可以找到4號中斷的處理程序。可隨之而來的問題是,若要定位中斷處理程序,需要知道它的段地址和偏移地址,而如何根據8位的中斷類型碼得到中斷處理程序的段地址和偏移地址呢?

12.2 中斷向量表、中斷過程、中斷處理程序和iret指令

CPU用8位的中斷類型碼通過中斷向量表找到相應的中斷處理程序的入口地址。那么什么是中斷向量表呢?

中斷向量表就是中斷向量的列表。那么什么又是中斷向量呢?

所謂中斷向量,就是中斷處理程序的入口地址。展開來講,中斷向量表,就是中斷處理程序入口地址的列表。

中斷向量表在內存中保存,其中存放着256個中斷源所對應的中斷處理程序的入口,如圖12.1所示。

image

可以看到,CPU只要知道了中斷類型碼,就可以將中斷類型碼作為中斷向量表的表項號,定位相應的表項,從而得到中斷處理程序的入口地址。

可見,CPU用中斷類型碼,通過查找中斷向量表,就可以得到中斷處理程序的入口地址。在這個方案中,一個首要的問題是,CPU如何找到中斷向量表?

現在,找到中斷向量表成了通過中斷類型碼找到中斷處理程序入口地址的先決條件。

中斷向量表在內存中存放,對於8086PC機,中斷向量表指定放在內存地址0處。從內存0000:0000到0000:03FF的1024個單元中存放着中斷向量表。能不能放在別處呢?

不能,如果使用8086CPU,中斷向量表就必須放在0000:0000~0000:03FF單元中,這是規定,因為8086CPU就從這個地方讀取中斷向量表。

那么在中斷向量表中,一個表項占多大的空間呢?

一個表項存放一個中斷向量,也就是一個中斷處理程序的入口地址,對於8086CPU,這個入口地址包括段地址和偏移地址,所以一個表項占兩個字,高地址字存放段地址,低地址字存放偏移地址。


從上面的講解中,我們知道,可以用中斷類型碼,在中斷向量表中找到中斷處理程序的入口。找到這個入口地址的最終目的是用它設置CS和IP,使CPU執行中斷處理程序。

用中斷類型碼找到中斷向量,並用它設置CS和IP,這個工作是由CPU的硬件自動完成的。CPU硬件完成這個工作的過程被稱為中斷過程。

CPU收到中斷信息后,要對中斷信息進行處理,首先將引發中斷過程。硬件在完成中斷過程后,CS:IP將指向中斷處理程序的入口,CPU開始執行中斷處理程序。

有一個問題需要考慮,CPU在執行完中斷處理程序后,應該返回原來的執行點繼續執行下面的指令。所以在中斷過程中,在設置CS:IP之前,還要將原來的CS和IP的值保存起來。

在使用call指令調用子程序時有同樣的問題,子程序執行后還要返回到原來的執行點繼續執行,所以call指令先保存當前CS和IP的值,然后再設置CS和IP。

下面是8086CPU在收到中斷信息后,所引發的中斷過程。

(1)(從中斷信息中)取得中斷類型碼;

(2)標志寄存器的值入棧(因為在中斷過程中要改變標志寄存器的值,所以先將其保存在棧中);

(3)設置標志寄存器的第8位TF和第9位IF的值為0(這一步的目的后面將介紹);

(4)CS的內容入棧;

(5)IP的內容入棧;

(6)從內存地址為中斷類型碼x4和中斷類型碼x4+2的兩個字單元中讀取中斷處理程序的入口地址設置IP和CS。

CPU在收到中斷信息之后,如果處理該中斷信息,就完成一個由硬件自動執行的中斷過程(程序員無法改變這個過程中所要做的工作)。

中斷過程的主要任務就是用中斷類型碼在中斷向量表中找到中斷處理程序的入口地址,設置CS和IP。因為中斷處理程序執行完成后,CPU還要回過頭來繼續執行被中斷的程序,所以要在設置CS、IP之前,先將它們的值保存起來。可以看到CPU將它們保存在棧中。

我們注意到,在中斷過程中還要做的一個工作就是設置標志寄存器的TF、IF位,對於這樣做的目的,我們將在后面的內容和下一章中進行討論。因為在執行完中斷處理程序后,需要恢復在進入中斷處理程序之前的CPU現場(某一時刻,CPU中各個寄存器的值)。所以應該在修改標記寄存器之前,將它的值入棧保存。

我們更簡潔地描述中斷過程,如下:

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

在最后一步完成后,CPU開始執行由程序員編寫的中斷處理程序。


由於CPU隨時都可能檢測到中斷信息,也就是說CPU隨時都可能執行中斷處理程序,所以中斷處理程序必須一直存儲在內存某段空間之中。

而中斷處理程序的入口地址,即中斷向量,必須存儲在對應的中斷向量表表項中。

中斷處理程序的編寫方法和子程序的比較相似,下面是常規的步驟:

(1)保存用到的寄存器;
(2)處理中斷;
(3)恢復用到的寄存器;
(4)用iret指令返回。

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

pop IP
pop CS
popf

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

12.3 除法錯誤中斷的處理、編程處理0號中斷

我們通過對0號中斷,即除法錯誤中斷的處理,來體會一下前面所講的內容。

當CPU執行div等除法指令的時候,如果發生了除法溢出錯誤,將產生中斷類型碼為0的中斷信息,CPU將檢測到這個信息,然后引發中斷過程,轉去執行0號中斷所對應的中斷處理程序。

我們看一下下面程序的執行結果,如圖12.2所示(不同的操作系統下顯示可能不同)。

mov ax,1000h
mov bh,1
div bh

image

可以看到,當CPU執行div bh時,發生了除法溢岀錯誤,產生0號中斷信息,從而引發中斷過程,CPU執行0號中斷處理程序。

我們從圖中可以看出系統中的0號中斷處理程序的功能:顯示提示信息“Divide overflow"后,返回到操作系統中。


現在我們考慮改變一下0號中斷處理程序的功能,即重新編寫一個0號中斷處理程序,它的功能是在屏幕中間顯示“overflow!",然后返回到操作系統,如圖12.3所示。

image

當CPU執行div bh后,發生了除法溢出錯誤,產生0號中斷信息,引發中斷過程,CPU執行我們編寫的0號中斷處理程序。

在屏幕中間顯示提示信息“overflow!"后,返回到操作系統中。

編程:當發生除法溢出時,在屏幕中間顯示“overflow!",返回DOS。

我們首先進行分析:

(1) 當發生除法溢出的時候,產生0號中斷信息,從而引發中斷過程。

此時,CPU將進行以下工作。

① 取得中斷類型碼0;
② 標志寄存器入棧,TF、圧設置為0;
③ CS、IP入棧;
④ (IP)=(0x4),(CS)=(0x4+2)。

(2) 當中斷0發生時,CPU將轉去執行中斷處理程序。

只要按如下步驟編寫中斷處理程序,當中斷0發生時,即可顯示“overflow!"。

① 相關處理;
② 向顯示緩沖區送字符串“overflow!”;
③ 返回DOS。

我們將這段程序稱為:do0。

(3) 現在的問題是:do0應存放在內存中。因為除法溢出隨時可能發生,CPU隨時都可能將CS:IP指向do0的入口,執行程序。

那么do0應該放在哪里呢?

由於我們是在操作系統之上使用計算機,所有的硬件資源都在操作系統的管理之下, 所以我們要想得到一塊內存區存放do0,應該向操作系統申請。

但在這里出於兩個原因我們不想這樣做:

① 過多地討論申請內存將偏離問題的主線;
② 我們學習匯編的一個重要目的就是要獲得對計算機底層的編程體驗。所以,在可能的情況下,我們不去理會操作系統,而直接面向硬件資源。

問題變得簡單而直接,我們只需找到一塊別的程序不會用到的內存區,將do0傳送到其中即可。

內存0000:0000~0000:03FF,大小為1KB的空間是系統存放中斷處理程序入口地址的中斷向量表。8086支持256個中斷,實際上系統中要處理的中斷事件遠沒有達到256個。所以在中斷向量表中,有許多單元是空的。

中斷向量表是PC系統中最重要的內存區,只用來存放中斷處理程序的入口地址,DOS系統和其他應用程序都不會隨便使用這段空間。

可以利用中斷向量表中的空閑單元來存放我們的程序。一般情況下,從0000:0200至0000:02FF的256個字節的空間所對應的中斷向量表項都是空的,操作系統和其他應用程序都不占用。我們在前面的課程中使用過這段空間。

根據以前的編程經驗,我們可以估計出,do0的長度不可能超過256個字節。

結論:我們可以將do0傳送到內存0000:0200處。

(4) 將中斷處理程序do0放到0000:0200后,若要使得除法溢岀發生的時候,CPU轉去執行do0,則必須將do0的入口地址,即0000:0200登記在中斷向量表的對應表項中。

因為除法溢出對應的中斷類型碼為0,它的中斷處理程序的入口地址應該從0x4地址單元開始存放,段地址存放在0x4+2字單元中,偏移地址存放在0x4字單元中。也就是說要將do0的段地址0存放在0000:0002字單元中,將偏移地址200H存放在0000:0000字單元中。

總結上面的分析,我們要做以下幾件事情。

(1)編寫可以顯示“overflow!”的中斷處理程序:do0;
(2)將do0送入內存0000:0200處;
(3)將do0的入口地址0000:0200存儲在中斷向量表0號表項中。

程序的框架如下。

程序12.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。

程序12.1執行時,do0的代碼是不執行的,它只是作為do0安裝程序所要傳送的數據。程序12.1執行時,首先執行do0安裝程序,將do0的代碼復制到內存0:200處,然后設置中斷向量表,將do0的入口地址,即偏移地址200H和段地址0,保存在0號表項中。

這兩部分工作完成后,程序就返回了。程序的目的就是在內存0:200處安裝do0的代碼,將0號中斷處理程序的入口地址設置為0:200。

do0的代碼雖然在程序中,卻不在程序執行的時候執行。它是在除法溢出發生的時候才得以執行的中斷處理程序。

do0部分代碼的最后兩條指令是依照我們的編程要求,用來返回DOS的。

現在,我們再反過來從CPU的角度看一下,什么是中斷處理程序?

我們來看一下do0是如何變成0號中斷的中斷處理程序的。

(1)程序12.1在執行時,被加載到內存中,此時do0的代碼在程序12.1所在的內存空間中,它只是存放在程序12.1的代碼段中的一段要被傳送到其他單元中的數據,我們不能說它是0號中斷的中斷處理程序;

(2)程序12.1中安裝do0的代碼執行完后,do0的代碼被從程序12.1的代碼段中復制到0:200處。此時,我們也不能說它是0號中斷的中斷處理程序,它只不過是存放在0:200處的一些數據;

(3)程序12.1中設置中斷向量表的代碼執行完后,在0號表項中填入了do0的入口地址0:200,此時0:200處的信息即do0的代碼,就變成了0號中斷的中斷處理程序。因為當除法溢出(即0號中斷)發生時,CPU將執行0:200處的代碼。

12.4 安裝、do0程序

可以使用movsb指令,將do0的代碼送入0:200處。程序如下。

assume cs:code

code segment

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

        設置中斷向量表

        mov ax,4c00h
        int 21h

    do0:顯示字符串"overflow!"
        mov ax,4c00h
        int 21h

code ends

end start

我們來看一下,用rep movsb指令的時候要確定的信息。

(1)傳送的原始位置,段地址:code,偏移地址:offset do0;
(2)傳送的目的位置:0:200;
(3)傳送的長度:do0部分代碼的長度;
(4)傳送的方向:正向。

更明確的程序如下。

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,do0部分代碼的長度       ;設置cx為傳輸長度
        cld     ;設置傳輸方向為正
        rep movsb

        設置中斷向量表

        mov ax,4c00h
        int 21h

   do0 :顯示字符串"overflow!"
        mov ax,4c00h
        int 21h

code ends

end start

問題是,我們如何知道do0代碼的長度?

最簡單的方法是,計算一下do0中所有指令碼的字節數。但是這樣做太麻煩了,因為只要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     ;設置cx為傳輸長度

        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)x5/10,被編譯器處理為指令: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

程序寫好了,可要顯示的字符串放在哪里呢?我們看下面的程序。

程序12.2

assume cs:code

data segment
    db "overflow!"
data ends

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     ;設置cx為傳輸長度
        cld     ;設置傳輸方向為正
        rep movsb

        設置中斷向量表
        mov ax,4c00h
        int 21h

do0 :   mov ax,data
        mov ds,ax
        mov si,0        ;設置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

code ends

end start

上面的程序,看似合理,可實際上卻大錯特錯。注意,“overflow!”在程序12.2的data段中。程序12.2執行完成后返回,它所占用的內存空間被系統釋放,而在其中存放的“overflow!"也將很可能被別的信息覆蓋。

而do0程序被放到了0:200處,隨時都會因發生了除法溢岀而被CPU執行,很難保證do0程序從原來程序12.2所處的空間中取得的是要顯示的字符串"overflow!”。

因為do0程序隨時可能被執行,而它要用到字符串"overflow!”,所以該字符串也應該存放在一段不會被覆蓋的空間中。正確的程序如下。

程序12.3

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     ;設置cx為傳輸長度 
        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        ;設置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

在程序12.3中,將“overflow!”放到do0程序中,程序12.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。

12.5 設置中斷向量、單步中斷、響應中斷的特殊情況

下面,將do0的入口地址0:200,寫入中斷向量表的0號表項中,使do0成為0號中斷的中斷處理程序。

0號表項的地址為0:0,其中0:0字單元存放偏移地址,0:2字單元存放段地址。

程序如下:

mov ax,0
mov es,ax
mov word ptr es:[0x4],200h
mov word ptr es:[0x4+2],0


基本上,CPU在執行完一條指令之后,如果檢測到標志寄存器的TF位為1,則產生單步中斷,引發中斷過程。

單步中斷的中斷類型碼為1,則它所引發的中斷過程如下。

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

如上所述,如果TF=1,則執行一條指令后,CPU就要轉去執行1號中斷處理程序。 CPU為什么要提供這樣的功能呢?

我們在使用Debug的t命令的時候,有沒有想過這樣的問題,Debug如何能讓CPU在執行一條指令后,就顯示各個寄存器的狀態?

我們知道,CPU在執行程序的時候是從CS:IP指向的某個地址開始,自動向下讀取指令執行。也就是說,如果CPU不提供其他功能的話,就按這種方式工作,只要CPU一加電,它就從預設的地址開始一直執行下去,不可能有任何程序能控制它在執行完一條指令后停止,去做別的事情。

可是,我們在Debug中看到的情況卻是,Debug可以控制CPU執行被加載程序中的一條指令,然后讓它停下來,顯示寄存器的狀態。

Debug有特殊的能力嗎?

我們只能說Debug利用了CPU提供的一種功能。只有CPU提供了在執行一條指令后就轉去做其他事情的功能,Debug或是其他的程序才能利用CPU提供的這種功能做出我們使用T命令時的效果。

好了,我們來簡要地考慮一下Debug是如何利用CPU所提供的單步中斷的功能的。

首先,Debug提供了單步中斷的中斷處理程序,功能為顯示所有寄存器中的內容后等待輸入命令。

然后,在使用t命令執行指令時,Debug將TF設置為1,使得CPU工作於單步中斷方式下,則在CPU執行完這條指令后就引發單步中斷,執行單步中斷的中斷處理程序,所有寄存器中的內容被顯示在屏幕上,並且等待輸入命令。

接下來的問題是,當TF=1時,CPU在執行完一條指令后將引發單步中斷,轉去執行中斷處理程序。注意,中斷處理程序也是由一條條指令組成的,如果在執行中斷處理程序之前,TF=1,則CPU在執行完中斷處理程序的第一條指令后,又要產生單步中斷,則又要轉去執行單步中斷的中斷處理程序,在執行完中斷處理程序的第一條指令后,又要產生單步中斷,則又要轉去執行單步中斷的中斷處理程序……

看來,上面的過程將陷入一個永遠不能結束的循環,CPU永遠執行單步中斷處理程序的第一條指令。

CPU當然不能讓這種情況發生,解決的辦法就是,在進入中斷處理程序之前,設置TF=0。

從而避免CPU在執行中斷處理程序的時候發生單步中斷。這就是為什么在中斷過程中有TF=0這個步驟,我們再來看一下中斷過程。

(1) 取得中斷類型碼N;
(2) 標志寄存器入棧,TF=0、IF=0;
(3) CS、IP入棧;
(4) (IP)=(Nx4), (CS)=(Nx4+2)。

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


一般情況下,CPU在執行完當前指令后,如果檢測到中斷信息,就響應中斷引發中斷過程。

在有些情況下,CPU在執行完當前指令后,即便是發生中斷,也不會響應。對於這些情況,我們不一一列舉,只是用一種情況來進行說明。

在執行完向ss寄存器傳送數據的指令后,即便是發生中斷,CPU也不會響應。這樣做的主要原因是,ss:sp聯合指向棧頂,而對它們的設置應該連續完成。

如果在執行完設置ss的指令后,CPU響應中斷,引發中斷過程,要在棧中壓入標志寄存器、CS和IP的值。而ss改變,sp並未改變,ss:sp指向的不是正確的棧頂,將引起錯誤。

所以CPU在執行完設置ss的指令后,不響應中斷。這給連續設置ss和sp指向正確的棧頂提供了一個時機。即我們應該利用這個特性,將設置ss和sp的指令連續存放,使得設置sp的指令緊接着設置ss的指令執行,而在此之間,CPU不會引發中斷過程。

比如,我們要將棧頂設為1000:0,應該:

mov ax,1000h
mov ss,ax
mov sp,0

而不應該:

mov ax,1000h
mov ss,ax
mov ax,0
mov sp,0

好了,現在我們回過來看一下,實驗2中的“(3)下一條指令執行了嗎?”現在你知道原因了嗎?

Debug利用單步中斷來實現T命令的功能,也就是說,用T命令執行一條指令后,CPU響應單步中斷,執行Debug設置好的處理程序,才能在屏幕上顯示寄存器的狀態,並等待命令的輸入。

而在mov ss,ax指令執行后,CPU根本就不響應任何中斷,其中也包括單步中斷,所以Debug設置好的用來顯示寄存器狀態和等待輸入命令的中斷處理程序根本沒有得到執行,所以我們看不到預期的結果。CPU接着向下執行后面的指令mov sp,10h,然后響應單步中斷,我們才看到正常的結果。


免責聲明!

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



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