彙編語言(王爽)第十五章 外中斷

第十五章

在計算機系統中,CPU除了能執行指令,進行運算外,還能對外部設備進行控制,接收它們的輸入,向它們進行輸出,即I/O能力,比如在文本編輯器中,我們按鍵盤中的一個鍵,可以看到屏幕上出現該字母,是CPU將從鍵盤上輸入的鍵對應的字符送到顯示器上

及時處理外設的輸入,有兩個問題,即外設的輸入隨時可能發生,CPU如何得知,以及CPU從何處得到外設的輸入

以鍵盤爲例進行討論

15.1 接口芯片和端口

外設的輸入不直接送入內存和CPU,而是送入相關的接口芯片的端口中,CPU向外設的輸出也是先送入端口中,再由相關的芯片送到外設,CPU還可以向外設輸出控制命令,控制命令也是先送到相關芯片的端口中

CPU通過端口和外部設備進行練習冊

15.2 外中斷信息

由於外設隨時都可能發生需要CPU及時處理的事件,因此CPU提供中斷機制,比如外設的輸入到達,相關芯片將向CPU發出相應的中斷信息嗎,CPU在執行完當前指令後,檢測到中斷信息,引發中斷過程,處理外設的輸入

PC系統中,外中斷源有兩類

1、可屏蔽中斷

CPU可以不響應,是否響應取決於標誌寄存器IF位的設置,當CPU檢測到可屏蔽中斷信息時,如果IF=1,則CPU在執行完當前指令後響應中斷,IF=0則可以屏蔽

可屏蔽中斷信息來自於CPU外部,中斷類型碼通過數據總線送入CPU,而內中斷的中斷類型碼是在CPU內部產生的,可屏蔽中斷引發的中斷過程除了中斷類型碼來源不同,其他步驟基本和內中斷相同

因此中斷過程中將IF設置爲0是爲了在進入中斷處理程序後,禁止其他的可屏蔽中斷

如果中斷處理程序中需要處理可屏蔽程序,將IF置1

sti	; IF=1
cli	; IF=0

2、不可屏蔽中斷

是CPU必須響應的中斷,CPU檢測到不可屏蔽中斷信息時,執行完當前指令後立即響應

8086CPU不可屏蔽中斷的中斷類型碼固定爲2,所以中斷過程不需要中斷類型碼

並且最後設置(IP)=(8) (CS)=(0AH)

幾乎所有外設引發的外中斷都是可屏蔽的(如鍵盤輸入)

不可屏蔽中斷是在系統中有必須處理的緊急情況時通知CPU,我們主要討論可屏蔽中斷

15.3 PC機鍵盤的處理過程

1、鍵盤輸入

鍵盤上每個鍵相當於一個開關,鍵盤中有一個芯片對每個鍵的開關狀態進行掃描

按下一個鍵時,開關接通,芯片產生一個掃描碼,掃描碼說明了按下的鍵在鍵盤上的位置,掃描碼被送入主板上相關接口芯片的寄存器中,該寄存器的端口地址爲60h

鬆開按下的鍵,也產生一個掃描碼,說明鬆開鍵的位置,該掃描碼也被送到60h端口中

按下鍵時產生的掃描碼成爲通碼,按下時產生的稱爲斷碼,掃描碼長度爲一個字節

斷碼=通碼+80h(通碼第7位爲0,斷碼第7位爲1)

如:g的通碼爲22h,斷碼爲a2h

2、引發9號中斷

鍵盤的輸入到達60h端口時,相關芯片就會向CPU發出中斷類型碼爲9的可屏蔽中斷信息,CPU根據IF的值決定是否響應中斷

3、執行int 9中斷例程

BIOS提供int 9中斷例程,進行基本的鍵盤輸入處理

執行過程:首先讀出60h端口中的掃描碼,如果時字符鍵的掃描碼,將該掃描碼和它對應的ASCII碼送入內存中的BIOS鍵盤緩衝區;如果是控制鍵(如Ctrl)和切換鍵(CapsLock)的掃描碼,將其轉變爲狀態字節(用二進制位記錄控制鍵和切換鍵狀態的字節)寫入內存中存儲狀態字節的單元,最後對鍵盤系統進行相關的控制

BIOS鍵盤緩衝區是系統啓動後BIOS用於存放int 9中斷例程所接收的鍵盤輸入的內存區

該內存區可以存儲15個鍵盤輸入,一個鍵盤輸入佔一個字單元,高位存放掃描碼,低位存放字符碼

0040:17這個內存單元存儲鍵盤狀態字節,該字節記錄了控制鍵和切換鍵的狀態,具體各個位記錄的信息如下

image-20200327194735576

15.4 編寫int9中斷例程

編程:在屏幕中間依次顯示“a”~“z”,並可以讓人看得清在顯示過程中,按下Esc鍵後,改變顯示的顏色

如果用普通的循環向一個內存單元依次傳送一個字母,由於CPU執行速度很快,所以字母切換太快,無法看清

所以每顯示一個字母,讓CPU執行一段時間的空循環,用兩個16位寄存器存放32位的循環次數

現在需要改變顯示的顏色,我們編寫的int 9中斷例程功能:

首先從60端口讀鍵盤的輸出

調用BIOS的int 9中斷例程處理其他硬件細節

判斷是否爲Esc的掃描碼

有一個問題在於新的int 9中斷例程會改變中斷向量表中相應的入口地址,那麼我們想調用原來的int 9中斷例程就會有麻煩,因此在更改中斷向量表中的入口地址前,先保存原有的地址,然後對int指令進行模擬,從而實現對中斷例程的調用

int n指令執行時,中斷類型碼n的作用就是從中斷向量表中獲取中斷例程的入口地址,而這裏我們已經提前保存了入口地址,因此只需要標誌寄存器入棧,設置IF=0、TF=0,隨後CS、IP入棧,最後修改CS、IP的值

我們發現call dword ptr ds:[0]即可完成後兩步

最後一個問題是,在程序返回前,需要將中斷向量表中的int 9中斷例程的入口地址恢復爲原來的地址,否則程序返回後,別的程序將無法使用鍵盤

assume cs:code

stack segment
	db 128 dup (0)
stack ends

data segment
	dw 0,0		; 保存原有中斷例程的地址
data ends

code segment
	start:	mov ax,stack
			mov ss,ax
			mov sp,128
			
			mov ax,data
			mov ds,ax
			
			mov ax,0
			mov es,ax
			
			push es:[9*4]		; 將原來的int 9中斷例程的入口地址
			pop ds:[0]			; 保存到ds:0 ds:2單元中
			push es:[9*4+2]
			pop ds:[2]			
			
			mov word ptr es:[9*4],offset int9
			mov es:[9*4+2],cs	; 設置新的int 9中斷例程的入口地址
			
			mov ax,0b800h
			mov es,ax
			mov ah,'a'
		s:	mov es:[160*12+40*2],ah
			call delay			; 空循環,保證每個字母都可以看得清
			inc ah				; 下一個字母
			cmp ah,'z'
			jna s
			
			mov ax,0
			mov es,ax
			
			push ds:[0]			; 該程序不安裝
			push ds:[2]			; 只是臨時更改int 9中斷例程的入口
			pop es:[9*4]		; 在顯示字母的過程中只要按下Esc就可以變色
			pop es:[9*4+2]		; 將int 9中斷例程的入口恢復爲原來的地址
			
			mov ax,4c00h
			int 21h
			
	delay:	push ax
			push dx
			
			mov dx,1000h	; 循環10000000h次
			mov ax,0
	   s1:	sub ax,1
	   		sbb dx,0
	   		cmp ax,0
	   		jne s1
	   		cmp dx,0		; 只有ax dx都不等於0 
	   		jne s1			; 循環才結束
	   		
	   		pop dx
            pop ax
            ret
            
      ; -------------以下爲新的int 9中斷例程------------
     int9:	push ax
    		push bx
    		push es
    		
    		in al,60h		; 從端口取出掃描碼
    		
    		; 調用系統提供的int 9中斷例程
    		pushf			; 標誌寄存器入棧
    		
    		pushf			
    		pop bx
    		and bh,11111100b	; 設置IF=0 TF=0
    		push bx
    		popf
    		
    		call dword ptr ds:[0] ;相當於CS IP入棧並修改CS IP的值
    		
    		cmp al,1		; 判斷是否爲Esc鍵
    		jne int9ret
    		
    		mov ax,0b800h
    		mov es,ax
    		inc byte ptr es:[160*12+40*2+1]	; 屬性值加一,改變顏色
    		
   int9ret:	pop es
   			pop bx
   			pop ax
   			iret
	   		
code ends
end start

在模擬int9中斷例程時,我門可以精簡程序,因爲在進入我們寫的中斷例程中時,IF呵TF都已經置爲0,因此在進入BIOS的int 9中斷例程時,沒必要再設置一次IF和TF

pushf				; 標誌寄存器入棧
    		
pushf			
pop bx
and bh,11111100b	; 設置IF=0 TF=0
push bx
popf

call dword ptr ds:[0] ;相當於CS IP入棧並修改CS IP的值

; 精簡爲
pushf				; 標誌寄存器入棧

call dword ptr ds:[0] ;相當於CS IP入棧並修改CS IP的值

還有一個問題,如果在主程序重新設置的int 9中斷例程的段地址和偏移地址的指令之間發生了鍵盤中斷,則CPU將轉去一個錯誤的地址執行(CS寄存器中的內容可能會改變)

cli		; 設置IF爲0.不響應可屏蔽中斷
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs	; 設置新的int 9中斷例程的入口地址
sti

15.5 安裝新的int 9中斷例程

安裝一個新的int 9中斷例程,在DOS下,按F1鍵後改變當前屏幕的顏色

assume cs:code

stack segment
	db 128 dup(0)
stack ends

code segment
	start:	mov ax,stack
			mov ss,ax
			mov sp,128
			
			push cs
			pop ds
			
			mov ax,0
			mov es,ax
			
			; 安裝
			mov si,offset int9
			mov di,204h
			mov cx,offset int9end-offset int9
			cls
			rep movsb
			
			push es:[9*4]
			pop es:[200h]
			push es:[9*4+2]
			pop es:[20h]
			
			cli
			mov word ptr es:[9*4],204h
			moc word ptr es:[9*4+2],0
			
			mov ax,4c00h
			int 21h
			
	int9:	push ax
			push bx
			push cx
			push es
			
			in al,60h
			
			pushf
			call dowrd ptr cs:[200h]
			
			cmp al,3bh			; F1掃描碼爲3bh
			
			mov ax,0b800h
			mov es,ax
			mov bx,1
			mov cx,2000
	    s:	inc byte ptr es:[bx]
	    	add bx,2
	    	loop s
	    	
  int9ret:	pop es
  			pop cx
  			pop ax
  			pop ax
  			iret
  			
  int9end:	nop
  
code ends
end start

端口和中斷機制是CPU進行I/O的基礎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章