使用BIOS進(jìn)行鍵盤輸入和磁盤讀寫
大多數(shù)有用的程序都需要處理用戶的輸入,鍵盤輸入是最基本的輸入。程序和數(shù)據(jù)通常需要長(zhǎng)期存儲(chǔ),磁盤是最常用的存儲(chǔ)設(shè)備。
BIOS為這兩種外設(shè)的I/O提供了最基本的中斷例程。下面將對(duì)它們的應(yīng)用和相關(guān)問題進(jìn)行學(xué)習(xí)。
int 9中斷例程對(duì)鍵盤輸入的處理
鍵盤輸入將引發(fā)9號(hào)中斷,BIOS提供了int 9中斷例程。
CPU在9號(hào)中斷發(fā)生后,執(zhí)行int 9中斷例程,從60h端口讀出掃描碼,并將其轉(zhuǎn)化為相應(yīng)的ASCII碼或狀態(tài)信息,存儲(chǔ)在內(nèi)存的指定空間(鍵盤緩沖區(qū)或狀態(tài)字節(jié))中。
一般的鍵盤輸入,在CPU執(zhí)行完int 9中斷例程后,都放到了鍵盤緩沖區(qū)中。鍵盤緩沖區(qū)有16個(gè)字單元,可以存儲(chǔ)15個(gè)按鍵的掃描碼和對(duì)應(yīng)的ASCII碼。
下面我們按照鍵盤緩沖區(qū)的邏輯結(jié)構(gòu),來看一下鍵盤輸入的掃描碼和對(duì)應(yīng)的ASCII碼是如何寫入鍵盤緩沖區(qū)的。
注意:在我們的課程中,僅在邏輯結(jié)構(gòu)的基礎(chǔ)上,討論BIOS鍵盤緩沖區(qū)的讀寫問題。其實(shí)鍵盤緩沖區(qū)是用環(huán)形隊(duì)列結(jié)構(gòu)管理的內(nèi)存區(qū),但我們不對(duì)隊(duì)列和環(huán)形隊(duì)列的實(shí)現(xiàn)進(jìn)行討論,因?yàn)槟鞘橇硪婚T專業(yè)課《數(shù)據(jù)結(jié)構(gòu)》的內(nèi)容。
下面,我們通過下面幾個(gè)鍵:
A、B、C、D、E、shift_A、A
的輸入過程,簡(jiǎn)要地看一下int 9中斷例程對(duì)鍵盤輸入的處理方法:
(1)初始狀態(tài)下,沒有鍵盤輸入,鍵盤緩沖區(qū)空,此時(shí)沒有任何元素:
(2)按下A鍵,引發(fā)鍵盤中斷:CPU執(zhí)行int 9中斷例程,從60h端口讀出A鍵的通碼;然后檢測(cè)狀態(tài)字節(jié),看看是否有shift、Ctrl等切換鍵按下;發(fā)現(xiàn)沒有切換鍵按下,則將A鍵的掃描碼1eh和對(duì)應(yīng)的ASCII碼,即字母“a”的ASCII碼61h,寫入鍵盤緩沖區(qū)。緩沖區(qū)的字單元中,高位字節(jié)存儲(chǔ)掃描碼,低位字節(jié)存儲(chǔ)ASCII碼。此時(shí)緩沖區(qū)中的內(nèi)容如下:
(3)按下B鍵,引發(fā)鍵盤中斷:CPU執(zhí)行int 9中斷例程,從60h端口讀出B鍵的通碼;然后檢測(cè)狀態(tài)字節(jié),看看是否有切換鍵按下;發(fā)現(xiàn)沒有切換鍵按下,將B鍵的掃描碼30h和對(duì)應(yīng)的ASCII碼,即字母“b”的ASCII碼62h,寫入鍵盤緩沖區(qū)。此時(shí)緩沖區(qū)中的內(nèi)容如下:
(4)按下C、D、E鍵后,緩沖區(qū)中的內(nèi)容如下:
(5)按下左shift鍵,引發(fā)鍵盤中斷:int 9中斷例程接收左shift鍵的通碼,設(shè)置0040:17處的狀態(tài)字節(jié)的第1位為1,表示左shift鍵按下。
(6)按下A鍵,引發(fā)鍵盤中斷:CPU執(zhí)行int 9中斷例程;從60h端口讀出A鍵的通碼;檢測(cè)狀態(tài)字節(jié),看看是否有切換鍵按下,發(fā)現(xiàn)左shift鍵被按下,則將A鍵的掃描碼1Eh和shift_A對(duì)應(yīng)的ASCCII碼,即字母“A”的ASCII碼41,寫入鍵盤緩沖區(qū),此時(shí)緩沖區(qū)中的內(nèi)容如下:
1E61
|
2062
|
2E63
|
2064
|
1265
|
1E41
|
|
|
|
|
|
|
|
|
|
|
(7)松開左shift鍵,引發(fā)鍵盤中斷:int 9中斷例程接收左shift鍵的斷碼,設(shè)置0040:17處的狀態(tài)字節(jié)的第1位為0,表示左shift鍵松開。
(8)按下A鍵,引發(fā)鍵盤中斷:CPU執(zhí)行int 9中斷例程,從60h端口讀出A鍵的通碼,然后檢測(cè)狀態(tài)字節(jié),看看是否有切換鍵按下,發(fā)現(xiàn)沒有切換鍵按下,則將A鍵的掃描碼1Eh和A對(duì)應(yīng)的ASCCII碼,即字母“a”的ASCII碼61h,寫入鍵盤緩沖區(qū),此時(shí)緩沖區(qū)中的內(nèi)容如下:
1E61
|
2062
|
2E63
|
2064
|
1265
|
1E41
|
1E61
|
|
|
|
|
|
|
|
|
|
使用int 16h中斷例程讀取鍵盤緩沖區(qū)
BIOS提供了int 16h中斷例程供程序員調(diào)用。int 16h中斷例程中包含的一個(gè)最重要的功能是從鍵盤緩沖區(qū)中讀取一個(gè)鍵盤輸入,該功能的編號(hào)為0。
下面的指令從鍵盤緩沖區(qū)中讀取一個(gè)鍵盤輸入,并將其從緩沖區(qū)中刪除:
mov ah,0
int 16h
結(jié)果:(ah)=掃描碼,(al)=ASCII碼。
int 16h中斷例程的0號(hào)功能,進(jìn)行如下的工作:
1)檢測(cè)鍵盤緩沖區(qū)中是否有數(shù)據(jù);
2)沒有則繼續(xù)做第1步;
3)讀取緩沖區(qū)第一個(gè)字單元中的鍵盤輸入;
4)將讀取的掃描碼送入ah,ASCII碼送入al;
5)將已讀取的鍵盤輸入從緩沖區(qū)中刪除。
可見,BIOS的int 9中斷例程和int 16h中斷例程是一對(duì)相互配合的程序,int 9中斷例程向鍵盤緩沖區(qū)中寫入,int 16h中斷例程從緩沖區(qū)中讀出。
它們寫入和讀出的時(shí)機(jī)不同,int 9中斷例程是在有按鍵按下的時(shí)候向鍵盤緩沖區(qū)中寫入數(shù)據(jù);而int 16h中斷例程是在應(yīng)用程序?qū)ζ溥M(jìn)行調(diào)用的時(shí)候,將數(shù)據(jù)從鍵盤緩沖區(qū)中讀出。
我們?cè)诰帉懸话愕奶幚礞I盤輸入的程序的時(shí)候,可以調(diào)用int 16h從鍵盤緩沖區(qū)中讀取鍵盤的輸入。
編程,接收用戶的鍵盤輸入,輸入“r”,將屏幕上的字符設(shè)置為紅色;輸入“g”,將屏幕上的字符設(shè)置為綠色;輸入“b”,將屏幕上的字符設(shè)置為藍(lán)色。
程序如下:
assume cs:code
code segment
start:mov ah,0
int 16h
mov ah,1
cmp al,'r'
je red
cmp al,'g'
je green
cmp al,'b'
je blue
jmp short sret
red:shl ah,1
green:shl ah,1
blue:mov bx,0b800h
mov es,bx
mov cx,2000
s:add byte ptr es:[bx],11111000b
or es:[bx],ah
add bx,2
loop s
sret:mov ax,4c00h
int 21h
code ends
end start
字符串的輸入
用戶通過鍵盤輸入的通常不僅僅是單個(gè)字符而是字符串。
最基本的字符串輸入程序,需要具備下面的功能:
1)在輸入的同時(shí)需要顯示這個(gè)字符串;
2)一般在輸入回車符后,字符串輸入結(jié)束;
3)能夠刪除已經(jīng)輸入的字符。
編寫一個(gè)接收字符串的輸入子程序,實(shí)現(xiàn)上面三個(gè)基本功能。
子程序的參數(shù)如下:
(dh)、(dl)=字符串在屏幕上顯示的行、列位置;
ds:si指向字符串的存儲(chǔ)空間,字符串以0為結(jié)尾符。
分析:
(1)字符的輸入和刪除。
每個(gè)新輸入的字符都存儲(chǔ)在前一個(gè)輸入的字符之后,而刪除是從最后面的字符進(jìn)行的。
看下面的過程:
空字符串:
輸入“a”:a
輸入“b”:ab
輸入“c”:abc
輸入“d”:abcd
刪除一個(gè)字符:abc
刪除一個(gè)字符:ab
刪除一個(gè)字符:a
刪除一個(gè)字符:
可以看出在字符串輸入的過程中,字符的輸入和輸出是按照棧的訪問規(guī)則進(jìn)行了的,即后進(jìn)先出。這樣,我們就可以用棧的方式來管理字符串的存儲(chǔ)空間,也就是說,字符串的存儲(chǔ)空間實(shí)際上是一個(gè)字符棧。字符棧中的所有字符,從棧底到棧頂,組成一個(gè)字符串。
(2)在輸入回車符后,字符串輸入結(jié)束。
輸入回車符后,可以在字符串中加入0,表示字符串結(jié)束。
(3)在輸入的同時(shí)需要顯示這個(gè)字符串。
每次有新的字符輸入和刪除一個(gè)字符的時(shí)候,都應(yīng)該重新顯示字符串,即從字符棧的棧底到棧頂,顯示所有的字符。
(4)程序的處理過程。
1)調(diào)用int 16h讀取鍵盤輸入;
2)如果是字符,進(jìn)入字符棧,顯示字符棧中的所有字符;繼續(xù)執(zhí)行1
3)如果是退格鍵,從字符棧中彈出一個(gè)字符,顯示字符棧中的所有字符;繼續(xù)執(zhí)行1;
4)如果是Enter鍵,向字符棧中壓入0,返回。
從程序的處理過程中可以看出,字符棧的入棧、出棧和顯示棧中的內(nèi)容,是需要在多處使用的功能,應(yīng)該將它們寫為子程序。
子程序:字符棧的入棧、出棧和顯示。
參數(shù)說明:(ah)=功能號(hào),0表示入棧,1表示出棧,2表示顯示;
ds:si指向字符棧空間;
對(duì)于0號(hào)功能:(al)=入棧字符;
對(duì)于1號(hào)功能:(al)=返回字符;
對(duì)于2號(hào)功能:(dh)、(dl)=字符串在屏幕上顯示的行、列位置。
charstack: jmp short charstart
table dw charpush,charpop,charshow
top dw 0 ;棧頂
charstart: push bx
push dx
push di
push es
cmp ah,2
ja sret
mov bl,ah
mov bh,0
add bx,bx
jmp word ptr table[bx]
charpush: mov bx,top
mov [si][bx],al
inc top
jmp sret
charpop: cmp top,0
je sret
dec top
mov bx,top
mov al,[si][bx]
jmp sret
charshow: mov bx,0b800b
mov es,bx
mov al,160
mov ah,0
mul dh
mov di,ax
add dl,dl
mov dh,0
add di,dx
mov bx,0
charshows: cmp bx,top
jne noempty
mov byte ptr es:[dl],’ ‘
jmp sret
noempty: mov al,[si][bx]
mov es:[di],al
mov byte ptr es:[di+2], ‘ ‘
inc bx
add di,2
jmp charshows
sret: pop es
pop di
pop dx
pop bx
ret
顯示棧中字符的時(shí)候,要注意清除屏幕上上一次顯示的內(nèi)容。
完整的接收字符串輸入的子程序:
getstr: push ax
getstrs: mov ah,0
int 16h
cmp al,20h
jb nochar ;ASCII碼小于0,說明不是字符
mov ah,0
call charstack ;字符入棧
mov ah,2
call charstack ;顯示棧中的字符
jmp getstrs
nochar:cmp ah,0eh ;退格鍵的掃描碼
je backspace
cmp ah,1ch ;回車鍵的掃描碼
je enter
jmp getstrs
backspace:mov ah,1
call charstack ;字符出棧
mov ah,2
call charstack ;顯示棧中的字符
jmp getstrs
enter: mov al,0
mov ah,0
call charstack ;0入棧
mov ah,2
call charstack ;顯示棧中的字符
pop ax
ret
應(yīng)用int 13h中斷例程對(duì)磁盤進(jìn)行讀寫
BIOS提供的訪問磁盤的中斷例程為int 13h。
入口參數(shù):
(ah)=int 13h的功能號(hào)(2表示讀扇區(qū)、3表示寫扇區(qū))
(al)=讀取的扇區(qū)數(shù)/寫入的扇區(qū)數(shù)
(ch)=磁道號(hào)
(cl)=扇區(qū)號(hào)
(dh)=磁頭號(hào)(對(duì)于軟盤即面號(hào),因?yàn)橐粋€(gè)面用一個(gè)磁頭來讀寫。)
(dl)=驅(qū)動(dòng)器號(hào) 軟驅(qū)從0開始,0:軟驅(qū)A,1:軟驅(qū)B;
硬盤從80h開始,80h:硬盤C,81h:硬盤D。
es:bx指向接收從扇區(qū)讀入數(shù)據(jù)的內(nèi)存區(qū)/將寫入磁盤的數(shù)據(jù)
返回參數(shù):
操作成功:(ah)=0,(al)=讀入的扇區(qū)數(shù)/寫入的扇區(qū)數(shù)
操作失敗:(ah)=出錯(cuò)代碼
以下指令,讀取0面0道1扇區(qū)的內(nèi)容到內(nèi)存單元0:200:
mov ax,0
mov es,ax
mov bx,200h
mov al,1
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov ah,2
int 13h
以下指令,將0:200中的內(nèi)容寫入0面0道1扇區(qū):
mov ax,0
mov es,ax
mov bx,200h
mov al,1
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov ah,3
int 13h
PC機(jī)是如何進(jìn)入操作系統(tǒng)的?
開機(jī)后,CPU自動(dòng)進(jìn)入到FFFF:0單元處執(zhí)行,此處有一條轉(zhuǎn)跳指令。CPU執(zhí)行該指令后,轉(zhuǎn)去執(zhí)行BIOS中的硬件系統(tǒng)檢測(cè)和初始化程序。
初始化程序?qū)⒔?/span>BIOS所支持的中斷向量,即將BIOS提供的中斷例程的入口地址登記在中斷向量表中。
硬件系統(tǒng)檢測(cè)和初始化完成后,調(diào)用int 19h進(jìn)行操作系統(tǒng)的引導(dǎo)。
如果設(shè)為從軟盤啟動(dòng)操作系統(tǒng),則int 19h將主要完成以下工作:
1)控制0號(hào)軟驅(qū),讀取軟盤0道0面1扇區(qū)的內(nèi)容到0:7c00。
2)將CS:IP指向0:7c00。
軟盤的0道0面1扇區(qū)中裝有操作系統(tǒng)引導(dǎo)程序。int 19h將其裝到0:7c00處后,設(shè)置CPU從0:7c00開始執(zhí)行此處的引導(dǎo)程序,操作系統(tǒng)被激活,控制計(jì)算機(jī)。