汇编语言(王爽)第十七章 使用BIOS进行键盘输入和磁盘读写

第十七章

17.1 int 9中断例程对键盘输入的处理

一般的键盘输入,在CPU执行完int 9中断例程后,都放到了键盘缓冲区中。键盘缓冲区共有16个字单元。可存储15个按键的扫描码和对应的ASCII码

那么它们是如何写入的:

初始状态下,没有键盘输入,缓冲区为空(缓冲区是用环形队列结构管理的内存区)

image-20200330165414436

按下A,引发键盘中断,CPU执行int 9中断例程,从60h端口读出A键的通码,然后检测状态字节,看是否有按下Shift、Ctrl等键,若没有,则将A的扫描码1eh和“a”的ASCII码61h写入缓冲区,字单元中,高位字节存储扫描码,低位字节存储ASCII码

image-20200330170237463

按下B、C、D、E键,同理

image-20200330171138833

Shift_A:按下做Shift,引发键盘中断,int 9中断例程接收左Shift键的通码,设置0040:17处的状态字节的第1位为1,表示左Shift键按下

按下A,引发键盘中断,……,发现左Shift键被按下,则将A的扫描码和Shift_A对应的ASCII码,即“A”的ASCII码41h写入缓冲区

image-20200330172143906

松开左Shift,引发键盘中断,int 9中断例程接收左Shift的断码,设置0040:17处的状态字节第1位为0,表示左Shift松开

按下A键……

image-20200330172909569

17.2 使用int 16h中断例程读取键盘缓冲区

BIOS 提供int 16h中断例程供程序员调用,int 16h中断例程中包含的一个重要的功能是从键盘缓冲区中读取一个键盘输入,该功能编号为0

; 从键盘缓冲区中读取一个键盘输入
; 并将其从缓冲区中删除
mov ah,0
int 16h
; 结果 (ah)=扫描码 (al)=ASCII码

那么int 16h是如何读取的,接着上一节

执行

mov ah,0
int 16h

image-20200330213644535

ah中的内容为1Eh,al中的内容为61h

执行

mov ah,0
int 16h

image-20200330214414051

执行多次以后

image-20200330214545339

此时再执行

mov ah,0
int 16h

int 16h中断例程检测键盘缓冲区,发现缓冲区空,则循环等待,直到缓冲区中有数据

按下A键后

image-20200330215156951

循环等待的中断例程检测到缓冲区中有数据,将其读出

image-20200330215323257

可见BIOS的int 9和int 16是一对互相配合的程序,但是写入和读出的时机不同一个是有键按下时执行,一个是应用程序对其进行调用时执行

在编写处理键盘输入的程序的时候,可以调用int 16h从键盘缓冲区中读取键盘的输入

编程,接收用户的键盘输入,输入“r”,将屏幕上的字符设置为红色,“g”绿色,“b”蓝色

assume cs:code

code segment
	start:	mov ah,0
			int 16h
			
			mov ah,1	; 1表示蓝色
			cmp al,'r'
			je red
			cmp al,'g'
			je green
			cmp al,'b'
			je blue
			jmp short sret
			
	  red:	shl ah,1	; 4表示红色
    green:	shl ah,1	; 2表示绿色
     blue:	mov bx,0b800h
     		mov es,bx
     		mov bx,1
     		
     		mov cx,2000
     	s:	and byte ptr es:[bx],11111000b
     		or es:[bx],ah
     		add bx,2
     		loop s
     		
     sret:	mov ax,4c00h
     		int 21h
     		
code ends
end start

在int 16h中断例程中,一定有设置IF=1的指令,因为当键盘缓冲区为空时,如果设置IF=0,int 9中断无法执行,循环等待会死锁

17.3 字符串的输入

编写一个接收字符串输入的子程序,实现3个基本功能:输入的同时显示这个字符串,在输入回车后输入结束,删除已经输入的字符

参数:(dh)、(dl)=字符串在屏幕上显示的行、列位置,ds:si指向字符串的存储空间

因为删除是从尾开始删,即后进先出,所以字符串的存储空间实际是一个字符栈

输入回车后,在字符串中加0,表示结束

每次有输入或删除时,都应该从栈底到栈顶的字符都显示一遍

这些功能可以编写为子程序

(ah)=功能号 0表示入栈,1表示出栈,2表示显示,(al)=入栈/出栈的字符

getstr:		push ax

getstrs:	mov ah,0
			int 16h
			cmp al,20h
			jb nochar		; ASCII码小于20h说明不是字符
			mov ah,0
			call charstack	; 字符入栈
			mov ah,2
			call charstack	; 显示栈中的字符
			jmp getstrs
			
 nochar:	cmp ah,0eh		; 退格键的扫描码
 			je backspace
 			cmp ah,1ch		; Enter键的扫描码
 			je enter
 			jmp getstrs
 			
 backspace:	mov ah,1
 			call charstack	; 字符出栈
 			mov ah,2
 			call charstack	; 显示栈中的字符
 			jmp getstrs
 			
 	enter:	mov al,0
 			mov ah,0		; 把0入栈
 			call charstack
 			mov ah,2
 			call charstack	; 显示栈中的字符
 			pop ax
 			ret
 			
 			
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			; 如果ah>2 无效功能号,返回
			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,0b800h
 			mov es,bx
 			mov al,160
 			mov ah,0
 			mul dh			; (dh)、(dl)分别表示显示的行、列
 			mov di,ax
 			add dl,dl
 			mov dh,0
 			add di,dx		; di表示要显示的偏移地址
 			
 			mov bx,0		; 从栈底开始显示
 			
charshows:	cmp bx,top		; 判断字符串是否已经全部显示
			jne noempty
			mov byte ptr es:[di],' '
			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

image-20200331164451847

17.4 应用int 13h中断例程对磁盘进行读写

以3.5英寸软盘为例,分为上下面,每面80个磁道,每个磁道18个扇区,每个扇区为512字节

磁盘的访问由磁盘控制器进行,以扇区为单位进行磁盘读写,读写扇区时要给出面号、磁道号和扇区号,面号、磁道号从0开始,扇区号从1开始

我们可以通过调用BIOS提供的中断例程(int 13h)来访问磁盘

; 读取0面0道1扇区的内容到0:200
; es:bx指向接收从扇区读入数据的内存区
mov ax,0
mov es,ax
mov bx,200h

; (ah)=int 13h的功能号(3表示写扇区)
; (al)=写入的扇区数
; (ch)=磁道号
; (cl)=扇区号
; (dh)=磁头号(面号)
; (dl)=驱动器号 
; 软驱从0开始	0:软驱A 1:软驱B
; 硬盘从80h开始	80h:硬盘C 81h:硬盘D
mov al,1
mov ch,0
mov cl,1
mod dl,0
mov dh,0
mov ah,2
int 13h
; 返回参数:
; 操作成功:(ah)=0 (al)=写入的扇区数
; 操作失败:(ah)=出错的代码

但是直接向磁盘扇区写数据是很危险的,必须找一张空闲的软盘,同时注意驱动器号是否正确

编程:将当前屏幕的内容保存到磁盘中

assume cs:code
code segment
	start:	mov ax,0b800h
			mov es,ax
			mov bx,0
			
			mov al,8	; 一屏4000字节,需要8个扇区
			mov ch,0
			mov cl,1
			mov dl,0
			mov dh,0
			mov ah,3
			int 13h
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章