8086-6-中斷


中斷(8086)

中斷就是打斷處理器當前的執行流程,去執行一些和當前工作不相干的指令,執行完之后,還可以返回到原來的程序流程繼續執行。 就好比你在打游戲突然老板來電話了,你不得不先停止打游戲然后來處理這件更為重要的事件,然后打完電話之后繼續打游戲。

中斷的一些概念:

中斷號:

由於CPU需要通過對不同類型的中斷進行不同處理,所以每種類型的中斷都被統一編號,這稱為中斷類型號、中斷向量或者中斷號。Intel 處理器允許256 個中斷,中斷號的范圍是0~255

中斷源:

中斷信號的來源,或者說產生中斷的設備,被稱為中斷源

中斷嵌套:

當一個中斷事件正在處理時,如果來了一個優先級更高的中斷事件時,允許暫時中止當前的中斷處理,先為優先級較高的中斷事件 服務,這稱為中斷嵌套

實模式下的中斷向量表(Interrupt Vector Table,IVT):

所謂中斷處理,其實就是處理器要執行一段與該中斷有關的程序(指令)你也可以將其當作一個函數。處理器可以識別256 個中斷,那么理論上就需要256 段代碼。這些代碼實際存放的位置並不重要,重要的是,在實模式下,處理器要求將它們的入口點也就是起始地址集中存放到內存中從物理地址0x00000 開始,到0x003ff 結 束,共1KB 的空間內,這就是所謂的中斷向量表。

 

 

每個中斷的入口點地址在中斷向量表中占2 個字,分別是中斷處理代碼的偏移地址和段地址。中斷0的入口點位於物理地址0x00000 處, 也就是邏輯地址0x0000:0x0000;中斷1 的入口點位於物理地址0x00004 處,即邏輯地址0x0000:0x0004;其他中斷入口點地址以此類推。

中斷分類:

中斷大致上可以分為硬件中斷和軟件中斷(簡稱為軟中斷)。

顧名思義,硬件中斷由硬件來提供,比如說:CPU,鼠標鍵盤等。而軟件鍵盤由內部的代碼來定義。

 

 

 

硬件中斷:

硬件中斷還可以分為外部硬件中斷和內部硬件中斷,外部硬件中斷是指除CPU以外的硬件對應的中斷,而內部硬件中斷是內部CPU對應的中斷。

外部硬件中斷:

外部硬件中斷,就是除處理器以外的外部設備中來的中斷信號。當外部設備發生錯誤,或者有數據要傳送(比如,從網絡中接收到一個針對當前主機的數據包),或者處理器交給它的事情處理完了(比如,打印已經完成),它們都會告訴CPU先停下工作,來臨時處理一下。

外部硬件中斷又可以根據中斷是否緊急而細分:因為有一些嚴重的事情,比如說電池沒電了,這個如果不處理馬上就會關機了,而有一些不是很重要,比如說鍵盤輸入,有時候卡死了為了防止外部硬件繼續倒騰導致系統直接崩潰,可以選擇把這種不是很重要的中斷進行屏蔽掉。所以外部硬件中斷又分為可屏蔽中斷和不可屏蔽中斷

外部硬件中斷是通過兩個信號線引入處理器內部的。 從8086 處理器開始,這兩根線的名字就叫NMI 和INTR。

 

 

(這是一個簡化的示意圖, 不是真正的設備連接圖。)

當一個外部硬件中斷發生時,處理器將會從中斷引腳NMI或INTR中得到通知,其中不可屏蔽中斷采用NMI引腳,而可屏蔽中斷采用INTR引腳。

不可屏蔽中斷(Non Maskable Interrupt,NMI):

不可屏蔽中斷是針對一些硬件的很嚴重的事件進行處理。所有的嚴重事件都必須無條件地加以處理,這種類型的中斷是不會被阻斷和屏蔽的,稱為非屏蔽中斷 (Non Maskable Interrupt,NMI)。

在傳統的兼容模式下,NMI 的中斷源通過一個與非門連接到處理器。處理器的NMI 引腳是高電平有效的,而中斷信號是低電平有效。 當不存在中斷的時候,與非門的所有輸入都為高電平也就是所有中斷源的都沒有發出中斷信息,因此與非門的結果就是低電平所以處理器的NMI 引 腳為低電平,這CPU沒有從NMI引腳獲得中斷信息。

當至少有一個不可屏蔽中斷發生時都會導致與非門的輸出結果為高電平,也就會導致NMI引腳為高電平,而CPU就會接受到NMI,然后進行對應的處理。

(與非門:與非門(一種邏輯電路)_百度百科 (baidu.com)

由於不可屏蔽中斷的非常特殊性,幾乎所有觸發NMI 的中斷事件對處理器來說都是致命的,甚至是不可糾正的。所以在這種情況下,努力去搞清楚發生了什么,通常沒有太大的意義,這樣的事最好留到事后, 讓專業維修人員來做。 因此在實模式下,NMI 被賦予了統一的中斷號2,不再進行細分。一旦發生2號中斷,處理器和軟件系統通常會放棄繼續正常工作,也不會糾正已經發生的問題和錯誤,很可能只是由軟件系統給出一個提示信息。

可屏蔽中斷(Interrupt Request)INTR:

這里的INTR就不是可屏蔽中斷的簡稱了,而是一個Interrupt request的簡稱。

這類中斷有兩個特點,第一是數量很多,畢竟有很多外部設備;第二是它們可以被屏蔽,這樣處理器就不對它們進行處理。所以,這類硬件中斷稱為可屏蔽中斷。

可屏蔽中斷是通過INTR 引腳進入處理器內部的,像NMI一樣,不可能為每一個中斷源都提供一個引腳。而且,處理器每次只能處理一個中斷。在這種情況下,需要一個代理,來接受外部設備發出的中斷信號。 還有,多個設備同時發出中斷請求的幾率也是很高的,所以該代理的還包括對它們抉擇到底讓它們中的哪一個優先向處理器提出服務請求。

在個人計算機中,用得最多的中斷代理就是8259芯片,這類硬件就是通常所說的中斷控制器( Interrupt Controller,IC)),從8086 處理器開始,它就一直提供這種服務。即使是現在,在絕大多數單處理器的計算機中,也依然有它的存在。

8559芯片

Intel 處理器允許256 個中斷,中斷號的范圍是0~255,8259 提供其中的15 個,但中斷號並不固定。之所以不固定,是因為設計時,允許軟件根據自己的需要靈活設置中斷號,以防止發生沖突。該中斷控制器芯片有自己的端口號,可以像訪問其他外部設備一樣用in 和 out 指令來改變它的狀態,包括各引腳的中斷號。正是因為這樣,它又叫可編程中斷控制器(Programmable Interrupt Controller,PIC)

 

 

每片8259 只有8 個中斷輸入引腳,在個人計算機上使用它,需要兩塊。如圖所示,第一塊8259 芯片的代理輸出的INT 直接送到處理器的INTR引腳,這是主片(Master);第二塊 8259 芯片的INT輸出送到第一塊的引腳2 上,是從片(Slave),兩塊芯片之間形成級聯(Cascade)關系。

所以兩塊8259 芯片可以向處理器提供15 個中斷信號。根據需要,這些中斷引腳可以被各種設備使用。

8259 的主片引腳0(IR0)接的是系統定時器/計數器芯片;從片的引腳0(IR0)接的是實時時鍾芯片RTC這兩塊芯片的固定連接即使是在硬件更新換代非常頻繁的今天,也沒有改變。

在8259 芯片內部,有中斷屏蔽寄存器(Interrupt Mask Register, IMR),這是個8 位寄存器,對應着該芯片的8 個中斷輸入引腳,對應的位是0 還是1,決定了從該引腳來的中斷信號是否能夠通過8259 送往處 理器(0 表示允許,1 表示阻斷)。當外部設備通 過某個引腳送來一個中斷請求信號時,如果它沒有被IMR 阻斷,那么, 它可以被送往處理器。

(注,8259 芯片是可編程的,主片的端口號是 0x20 和0x21,從片的端口號是0xa0 和0xa1,可以通過這些端口訪問 8259 芯片,設置它的工作方式,包括IMR 的內容。)

中斷能否被處理,除了要看8259 芯片的選擇外,還需要由處理器來決定。在處理器內部,標志寄存器有一個標志位IF,這就是中斷標志(Interrupt Flag)。當IF 為0 時,所有從處理器INTR 引腳來的中斷信號都被忽略掉;當其為1 時,處 理器可以接受和響應中斷。 IF 標志位可以通過兩條指令cli 和sti 來改變。這兩條指令都沒有操作 數,cli(CLear Interrupt flag)用於清除IF 標志位,sti(SeT Interrupt flag)用於置位IF 標志為1。

在計算機內部,中斷發生得非常頻繁,當一個中斷正在處理時,其他中斷也有可能會響應,甚至會有多個中斷同時發生的情況。這就需要中斷控制器了,8259 芯片會記住它們,並按一定的策略決定先為誰服務。總體上來說,中斷的優先級和引腳是相關的,主片的IR0引腳優先級最高,IR7引腳最低,從片也是如此。當然,還要考慮到從片是級聯在主片的IR2引腳上。

小結外部硬件中斷:

外部硬件中斷分為可屏蔽和不可屏蔽,對應NMI和INTR兩個引腳,NMI由於很嚴重只要發生了就直接停下就好了,而不可屏蔽中斷由於功能很多需要選擇所以就需要一個中斷控制器來處理,中斷控制器里可以選擇是否響應中斷,而CPU里也有IF標志位來選擇是否響應中斷。

內部硬件中斷:

內部硬件中斷發生在處理器內部,是由執行的指令引 起的。比如,當處理器檢測到div 或者idiv 指令的除數為零時,或者除法的結果溢出時,將產生中斷0(0 號中斷),這就是除法錯誤中斷。

內部中斷不受標志寄存器IF 位的影響,它們的中斷類型是固定的,可以立即轉入相應的處理過程。

軟中斷

軟中斷與硬件中斷無關,是寫好的存放在計算機里面的中斷,可以類比為一個API函數。

軟中斷是由int 指令引起的中斷處理。中斷號在指令中給出,int 指令的格式如下:

int3    //int3 是斷點中斷指令
int 立即數
into

 

注意,int3 和int 3是不一樣的,int3是一個特殊的斷點指令,而int 3是中斷向量表中的第四個中斷程序。

into 是溢出中斷指令,機器碼為0xCE,也是單字節指令。當處理器 執行這條指令時,如果標志寄存器的OF 位是1,那么,將產生4 號中 斷。否則,這條指令什么也不做。

最有名的軟中斷是BIOS 中斷,之所以稱為BIOS 中斷,是因為這些中斷功能是在計算機加電之后,由BIOS 程序執行期間建立起來的。

CPU調用中斷過程:

拿外部硬件中斷來進行舉例。

當中斷發生時,如果從外部硬件到處理器之間的道路都是暢通的, 那么,處理器在執行完當前的指令后,會立即為硬件服務。它首先會響應中斷,告訴8259 芯片(中斷管理器)准備着手處理該中斷。接着要求 8259 芯片把中斷號送過來。8259 芯片它會把對應的中斷號告訴處理器,處理器接受到中斷號后就開始處理:

① 保護斷點的現場。首先要將標志寄存器FLAGS 壓棧,然后清除它的IF 位和TF 位。接着,再將當前的代 碼段寄存器CS 和指令指針寄存器IP 壓棧。

② 執行中斷處理程序。由於處理器已經拿到了中斷號,它將該號碼乘以4(因為每個中斷地址在中斷向量表中占4 字節),就得到了該中斷入口點在中斷向量表中的偏移地址。接着,從表中依次取出中斷程序的偏移地址和段地址,並分別傳送到IP 和CS,然后,處理器就開始執行中斷處理程序了。

(由於IF 標志被清除,在中斷處理過程中,處理器將不再響應硬件中斷。如果希望更高優先級的中斷嵌套,可以在編寫中斷處理程序 時,適時用sti 指令開放中斷。)

③ 返回到斷點處接着執行。所有中斷處理程序的最后一條指令必須是中斷返回指令iret。這將導致處理器依次從棧中彈出(恢復)IP、CS 和 FLAGS 的原始內容,於是轉到主程序接着執行。

;這里的iret指令用匯編來描述就是
pop ip
pop cs
popf

 

其它的中斷調用過程可以參考外部硬件調用過程,只是少了一個中斷管理器。

 

 

為什么進行中斷處理時,需要先將IF、TF都設置為0?

  將IF設置為0的原因主要是因為8086CPU的設計者認為中斷處理程序一般是不需要對其它中斷做出響應的。

  因此默認的將IF設置為0,禁止CPU處理可屏蔽中斷。當然,如果有的中斷處理程序確實需要處理可屏蔽中斷,也可以在中斷處理程序中開中斷,將IF重新設置為1。指令sti,設置IF=1;指令cti,設置IF=0。

  將TF單步調試功能關閉的原因則是為了避免出現單步中斷的死循環。

  試想如果開啟了單步調試功能,那么在進入中斷處理程序,並執行完第一條指令后,便會引發單步中斷,進而跳轉到單步中斷處理程序中。而在執行了單步中斷處理程序的第一條指令后,又會再度引發單步中斷,這成為了一個死循環。因此,在進入中斷處理程序之前,必須首先把TF置為0,關閉單步調試功能,以避免上述情況產生。

 

 

手動編寫軟中斷:

首先明白如何寫中斷。

1:編寫中斷程序

2:將中斷程序保存在內存中的某一個位置

3:將中斷程序的入口點保存到中斷向量表里

4:調用中斷程序。

1 編寫中斷程序:

這里我采用了很簡單的代碼,修改顯卡的第一個字符的內容和顏色:

show:
    mov ax,0xb800
    mov es,ax
    mov byte es:[0],0x48
    mov byte es:[1],0x04
    iret

 

2 將中斷程序保存在內存中:

8086CPU的內存分布:

 

 

我們可以將我們的中斷程序保存到DRAM內存條里,我們需要找一塊空的內存地址就可以。

這里我查看了0x10000物理地址往上的地址空間:

 

 

 

 

發現是空的,我就采用這塊地址空間了。

然后我們需要把中斷程序的硬編碼保存進去。

復制代碼
    mov ax,0
    mov ds,ax
    mov ax,0x1000
    mov es,ax
    mov si,show
    mov di,0
    mov cx,showend-show
    cld
    rep movsb
復制代碼

 

3 將中斷程序的入口點保存到中斷向量表里

首先用bochs虛擬機查看中斷向量表的空余中斷地址:

 

 

我選擇了這里物理地址為0x180的地方,然后我們將入口點的段地址和偏移地址賦值進去。

    mov ax,0x0
    mov es,ax
    mov es:[0x180],word 0x0
    mov es:[0x182],word 0x1000
    ;//先是偏移地址,再是段地址

 

4 調用中斷程序

調用軟中斷很簡單,只需要int n就行了,n是你的中斷地址相對於起始中斷地址的一個數組偏移值。

這里 n = 行數-1 * 4+對於行的起始地址的偏移量 =24*4+0=96

所以就直接調用指令就可以了:

    int 96

 

驗證中斷調用是否成功:

整個匯編程序的源代碼:

復制代碼
segment myInterrupt vstart=0x7c00
​
​
;將機器碼賦值給0x1000:0的內存地址空間
start:
    mov ax,0
    mov ds,ax
    mov ax,0x1000
    mov es,ax
    mov si,show
    mov di,0
    mov cx,showend-show
    cld
    rep movsb
    
​
;將中斷入口點地址保存到中斷向量表
    mov ax,0x0
    mov es,ax
    mov es:[0x180],word 0x0
    mov es:[0x182],word 0x1000
    
    int 96
    
    jmp start
​
​
show:
    mov ax,0xb800
    mov es,ax
    mov byte es:[0],0x48
    mov byte es:[1],0x04
    iret
​
showend:
    nop
​
times 510-($-$$) db 0
                 db 0x55,0xaa
復制代碼

 

將其編譯后寫入硬盤的主引導扇區就可以加載了。

采用bochs 調試虛擬機來調試,首先將程序運行到主引導扇區的地方:

 

 

然后查看我們的匯編指令,並給int 96指令打一個斷點:

 

 

然后運行到斷點處並查看內存是否寫入:

 

 

可以看到已經成功寫入內存地址為0x10000的地址空間了。

然后查看中斷向量表是否成功寫入:

 

 

可以看到內存地址 0x180的內容已經更改為 0x10000000,因為intel是采用的小端字節序,所以我們肯定更改成功了。

然后我們給int 96中斷處理程序的入口地址打一個斷點,並運行查看是否允運行到了我們的中斷處理程序:

 

 

可以看到已經運行到我們的中斷處理程序處了,而且此時的物理地址也改為了0x10000(可能有人想問前面的if不是置零了嗎,int3斷點中斷也是中斷啊,為啥這里就可以中斷嵌套呢,其實很簡單,那個if是針對外部中斷設備的可屏蔽中斷的,我們這個是軟中斷,和它無關。)

然后我們將這個中斷處理程序運行完,並查看顯示器的內容是否修改:

 

 

可以看到完美實現。所以我們的代碼邏輯肯定沒有問題,以上就是一個簡單的軟中斷實現。

小結:

中斷是CPU的一個重要概念,需要掌握。當然,這次是講述的8086中斷,對於其它CPU雖然不吻合,但是具有很大的參考意義。


免責聲明!

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



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