• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            luqingfei@C++

            為中華之崛起而崛起!
            兼聽則明,偏聽則暗。

            匯編語言--內中斷

             

            內中斷

            任何一個通用的CPU,比如8086,都具備一種能力,可以在執(zhí)行完當前正在執(zhí)行的指令之后,檢測到從CPU外部發(fā)送過來的或內部產生的一種特殊信息,并且可以立即對所接收到信息進行處理。這種特殊的信息,我們可以稱其為:中斷信息。

            中斷的意思是指,CPU不再接著(剛執(zhí)行完的指令)向下執(zhí)行,而是轉去處理這個特殊信息。

             

            注意,這里所說的中斷信息,是為了便于理解而采用的一種邏輯上的說法。它是對幾個具有先后順序的硬件操作所產生的事件的統(tǒng)一描述。

            “中斷信息”是要求CPU馬上進行某種處理,并向所要進行的該種處理提供了必備的參數(shù)的通知信息。

             

            中斷信息可以來自CPU的內部和外部。

             

             

             

            內中斷的產生

            對于8086CPU,當CPU內部有下面的情況發(fā)生的時候,將產生相應的中斷信息:

            1) 除法錯誤,比如:執(zhí)行div指令產生的除法溢出;

            2) 單步執(zhí)行;

            3) 執(zhí)行int0指令;

            4) 執(zhí)行int指令。

             

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

            1) 除法錯誤:0

            2) 單步執(zhí)行:1

            3) 執(zhí)行int0指令:4

            4) 執(zhí)行int指令,該指令的格式為int n,指令中的n為字節(jié)型立即數(shù),是提供給CPU的中斷類型碼。

             

             

             

            中斷處理程序

            CPU收到中斷信息后,需要對中斷信息進行處理。

            而如何對中斷信息進行處理,可以由我們編程決定。

            我們編寫的,用來處理中斷信息的程序被稱為中斷處理程序。

            一般來說,需要對不同的中斷信息編寫不同的處理程序。

             

            CPU在收到中斷信息后,應該轉去執(zhí)行該中斷信息的處理程序。

             

            若要8086CPU執(zhí)行某處的程序,就要將CS:IP指向它的入口(即程序第一條指令的地址)。

             

            可見首要的問題是,CPU在收到中斷信息后,如何根據(jù)中斷信息確定其處理程序的入口。

             

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

             

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

            若要定位中斷處理程序,需要知道它的段地址和偏移地址,而如何根據(jù)8位的中斷類型碼得到中斷處理程序的段地址和偏移地址呢?

             

             

             

            中斷向量表

            CPU8位的中斷類型碼通過中斷向量表找到相應的中斷處理程序的入口地址。

            中斷向量表就是中斷向量的列表,中斷向量,就是中斷處理程序的入口地址。

            中斷向量表,就是中斷處理程序入口地址的列表。

             

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

             

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

             

            中斷向量表在內存中存放,對于8086PC機,中斷向量表指定放在內存地址0處。從內存0000:00000000:03e81000個單元中存放著中斷向量表。這是規(guī)定。

             

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

             

             

            存儲N號中斷源對應的中斷處理程序入口的偏移地址的內存單元的地址為:N*4;

            存儲N號中斷源對應的中斷處理程序入口的段地址的內存單元的地址為:N*4+2。

            N0開始。)

             

             

             

            中斷過程

            可以用中斷類型碼,在中斷向量表中找到中斷處理程序的入口。

            找到這個入口地址的最終目的是用它設置CSIP,使CPU執(zhí)行中斷處理程序。

             

            用中斷類型碼找到中斷向量,并用它設置CSIP,這個工作是由CPU的硬件自動完成的。

            CPU硬件完成這個工作的過得被稱為中斷過程。

             

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

             

            8086CPU在收到中斷信息后,所引發(fā)的中斷過程:

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

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

            3)設置標志寄存器的第8TF和第9IF的值為0;

            4CS的內容入棧;

            5IP的內容入棧;

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

             

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

             

            中斷過程的主要任務就是用中斷類型碼在中斷向量表中找到中斷處理程序的入口地址,設置CSIP。

             

            因為中斷處理程序執(zhí)行完成后,CPU還要回過頭來繼續(xù)執(zhí)行被中斷的程序,所以要在設置CS、IP之前,先將它們的值保存起來。

             

            因為在執(zhí)行中斷處理程序后,需要恢復在進入中斷處理程序之前的CPU現(xiàn)場(某一時刻,CPU中各個寄存器的值)。所以應該在修改標記寄存器之前,將它的值入棧保存。

             

             

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

            1) 取得中斷類型碼N;

            2) pushf

            3) TF=0,IF=0

            4) push CS

            5) push IP

            6) (IP)=(N*4), (CS)=(N*4+2)

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

             

             

             

             

            中斷處理程序

            由于CPU隨時隨地可能檢測到中斷信息,也就是說,CPU隨時隨地可能執(zhí)行中斷處理程序,所以中斷處理程序必須一直存儲在內存某段究竟之中。而中斷處理程序的入口地址,即中斷向量,處理存儲在對應的中斷向量表表項中。

             

             

            中斷處理程序的編寫方法和子程序的比較相似,常規(guī)步驟如下:

            1) 保存用到的寄存器。

            2) 處理中斷。

            3) 恢復用到的寄存器。

            4) iret指令返回。

             

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

            pop IP

            pop CS

            popf

            iret通常和硬件自動完成的中斷過程配合使用。

            iret指令執(zhí)行后,CPU回到執(zhí)行中斷處理程序前的執(zhí)行點繼續(xù)執(zhí)行程序。

             

             

            除法錯誤中斷的處理

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

            mov ax,1000h

            mov bh,1

            div bh

             

             

            編程處理0號中斷

            編程:當發(fā)生除法溢出時,在屏幕中間顯示“overflow!”,返回DOS

            分析:

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

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

                   1)取得中斷類型碼0;

                   2)標志寄存器入棧,TF、IF設置為0;

                   3CSIP入棧;

                   4(IP)=(0*4), (CS)=(0*4+2)。

             

            2)可見,當中斷0發(fā)生時,CPU將轉去執(zhí)行中斷處理程序。

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

                   1)相關處理。

                   2)向顯示緩沖區(qū)送字符串“overflow!”。

                   3)返回DOS

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

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

                   那么do0應該放在哪里呢?

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

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

            n         過多地討論申請內存將偏離問題的主線;

                   我們學習匯編的一個重要目的就是要獲得對計算機底層的編程體驗。所以,在可能的情況下,我們不去理會操作系統(tǒng),而直接面向硬件資源。

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

             

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

             

                   中斷向量表是PC系統(tǒng)中最重要的內存區(qū),只用來存放中斷處理程序的入口地址,DOS系統(tǒng)和其他應用程序都不會隨便使用這段空間。我們可以利用中斷向量表中的空閑單元來存放我們的程序。一般情況下,從0000:02000000:0300256個字節(jié)。

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

             

            4)我們將中斷處理程序do0放到0000:0200后,若要使得除法溢出發(fā)生的時候,CPU轉去執(zhí)行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號表項中。

             

             

            程序的框架如下:

            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,設置中斷向量的程序;

            2do0。

             

            do0的代碼是不執(zhí)行的,它只是作為do0安裝程序所要傳送的數(shù)據(jù)。

            首先執(zhí)行do0安裝程序,將do0的代碼拷貝到內存0:200處,然后設置中斷向量表,將do0的入口地址,即偏移地址200H和段地址0,保存在0號項中。這兩部分工作完成后,程序就返回了。

            程序的目的就是在內存0:200處安裝do0的代碼,將0號中斷處理程序的入口地址設置為0:200。do0的代碼雖然在程序中,卻不在程序執(zhí)行的時候執(zhí)行。它是在除法溢出發(fā)生的時候才得以執(zhí)行的中斷處理程序。

             

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

             

             

                   現(xiàn)在,我們再反過來從CPU的角度看一下,什么是中斷處理程序?我們來看一下do0是如何變成0號中斷的中斷處理程序的:

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

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

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

             

            回憶以下:

            如何讓一個內存單元成為棧頂?將它的地址放入SS、SP中;

            如何讓一個內存單元中的信息被CPU當作指令來執(zhí)行?將它的地址放入CS、IP中;

            如何讓一段程序成為N號中斷的中斷處理程序?將它的入口地址放入中斷向量表的N號表項中。

             

             

             

            安裝

            使用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的長度,如下所示:

            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

            do0end:

            code ends

            end start

                         

            -”是編譯器識別的運算符號,編譯器可以用它來進行了兩個常數(shù)的減法。比如:

            指令:mov ax,8-4,被編譯器處理為指令:mov ax,4

             

            匯編編譯器可以處理表達式,比如:

            指令:mov ax,(5+3)*5/10,被編譯器處理為指令:mov ax,4

             

             

             

            do0

            do0程序的主要任務是顯示字符串,程序如下:

            do0: 設置ds:si指向字符串

                   mov ax,0b800h

                   mov es,ax

                   mov di,12*150+36*2            ;設置es:di指向顯存空間的中間位置

                   mov cx,9                             ;設置cx為字符串長度

             s:   mov al,[si]

                   mov es:[di],al

                   inc si

                   add di,2

                   loop s

            do0end:nop

             

                   mov ax,4c00h

                   int 21h

             

            程序寫好了,可要顯示的字符串放在哪里呢?

            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 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]

                       move s:[di],al

                       inc   si

                       add di,2

                       loop s

             

                       mov ax,4c00h

                       int 21h

             do0end:nop

            code ends

            end start

             

            上述程序有一處大錯,注意字符串“overflow!”,是在data段中。程序執(zhí)行完成后返回,它所占用的內存空間被系統(tǒng)釋放,而在其中存放的“overflow!”也將很可能被別的信息覆蓋。

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

             

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

            assume cs:code

            code segment

             start:    mov ax,cs

                          mov ds,ax

                          mov si,offset do0          ;設置ds:si指向源地址

             

                          mov ax,0

                          move s,ax

                          mov di,200h                 ;設置es:di指向目的地址

             

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

                          cld                               ;設置傳輸方向為正

             

                          rep movsb

                          設置中斷向量表

             

                          mov 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

             

            上面的程序,將字符串“overflow!”放到了do0程序中,在程序執(zhí)行時,將標號do0到標號do0end之間的內容送到0000:0200處。

             

            注意,因為在do0程序開始處的“overflow!”不是可以執(zhí)行的代碼,所以在“overflow!”之前加上一條jmp指令,轉移到正式的do0程序,當除法溢出發(fā)生時,CPU執(zhí)行0:200處的jmp指令,路過后面的字符串,轉到正式的do0程序執(zhí)行。

             

                   do0程序執(zhí)行過程中必須要找到“overflow!”,那么它在哪里呢?首先來看段地址,”overflow!”do0的代碼處于同一個段中,而除法溢出發(fā)生時,CS中必須存放do0的段地址,也就是“overflow!”的段地址;再來偏移地址,0:200處的指令為jmp short do0start,這條指令占兩個字節(jié),所以“overflow!”的偏移地址為202h。

             

             

            設置中斷向量

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

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

            mov ax,0

            mov es,ax

            mov word ptr es:[0*4],200h

            mov word ptr es:[0*4+2],0

             

             

             

            單步中斷

            CPU在執(zhí)行完一條指令之后,如果檢測到標志寄存器的TF位為1,則產生單步中斷,引發(fā)中斷過程。單步中斷的中斷類型碼為1,則它所引發(fā)的中斷過程如下:

            1)取得中斷類型碼1;

            2)標志寄存器入棧,TFIF設置為0;

            3CS、IP入棧;

            4)(IP=1*4),(CS=1*4+2)。

             

            如果TF=1,則執(zhí)行一條指令后,CPU就要轉去執(zhí)行1號中斷處理程序。

             

            在進入單步中斷處理程序之前,設置TF=0,從而避免CPU在執(zhí)行中斷處理程序的時候發(fā)生單步中斷。這就是為什么在中斷過程中有TF=0這個步驟,再來看一下中斷過程:

             

            CPU為什么要提供這樣的功能呢?

            CPU提供單步中斷功能的原因就是,為單步跟蹤程序的執(zhí)行過程,提供了實現(xiàn)機制。

             

             

             

            響應中斷的特殊情況

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

            但是,在有些情況下,CPU在執(zhí)行完當前執(zhí)行后,即便是發(fā)生中斷,也不會響應。比如:

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

             

             

             

             

             

             

             

             

             

            posted on 2010-08-04 10:35 luqingfei 閱讀(1667) 評論(0)  編輯 收藏 引用 所屬分類: 匯編語言基礎學習

            導航

            <2010年8月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            2930311234

            統(tǒng)計

            留言簿(6)

            隨筆分類(109)

            隨筆檔案(105)

            Blogers

            Game

            Life

            NodeJs

            Python

            Useful Webs

            大牛

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            評論排行榜

            久久精品亚洲精品国产欧美| 97超级碰碰碰碰久久久久| 国产激情久久久久影院老熟女免费| 久久九九精品99国产精品| 久久这里只有精品久久| 无码人妻久久一区二区三区蜜桃| 国产精品99久久久精品无码| 狠狠色丁香久久综合五月| 欧洲性大片xxxxx久久久| 久久夜色精品国产噜噜噜亚洲AV | 色婷婷久久久SWAG精品| 日韩久久久久久中文人妻| 久久亚洲精品无码播放| 丁香狠狠色婷婷久久综合| 久久久精品国产| 青青草国产精品久久| 日本强好片久久久久久AAA| 久久婷婷五月综合97色直播| 狠狠色丁香久久综合婷婷| 久久精品国产精品亚洲毛片| 国内精品久久国产| 一本综合久久国产二区| 国产精品美女久久久免费| 久久精品www| 9久久9久久精品| 国内精品久久久久久久97牛牛| 久久综合久久鬼色| 久久影视综合亚洲| 久久久久99精品成人片三人毛片 | 精品国产婷婷久久久| 久久99精品久久久久久| 国产91色综合久久免费| 久久精品国产亚洲av麻豆小说| 亚洲国产另类久久久精品| 午夜精品久久久久久久久| 国内精品久久久久影院薰衣草| 97久久婷婷五月综合色d啪蜜芽 | 99久久综合国产精品二区| 91精品免费久久久久久久久| 免费观看久久精彩视频| 中文精品久久久久国产网址|