汇编17:使用BIOS进行键盘输入和磁盘读写
使用BIOS进行键盘输入和磁盘读写
我们通过讨论键盘输入和磁盘读写来理解CPU对I/O的控制。
BIOS键盘缓冲区
键盘输入会引发9号中断,CPU会执行中断例程,从60h端口读出扫描码,然后将其转换为对应的ascii码信息,将扫描码和对应的ascii码存入内存中的指定空间中,这个空间一般是键盘缓冲区,键盘缓冲区是一个环形队列。
如果我们按下A键就会引发键盘中断,CPU执行int9中断例程,从60h端口读出A键的通码,然后检测状态字节,将A的扫描码1eh和对应的ascii码61h写入键盘缓冲区中:
![](https://img2020.cnblogs.com/blog/1742600/202004/1742600-20200430224805093-1115505920.png)
接下来按下B、C、D、E后,缓冲区的内容如下,每个字单元的高位字节存储扫描码,低位字节存储ascii码:
![](https://img2020.cnblogs.com/blog/1742600/202004/1742600-20200430224821658-1374912635.png)
然后我们按下左shift键,此时int9接受到左shift的通码,设置0040:17处的状态字节的第一位为1,缓冲区不变。
此时按下A键,因为检测到状态字节的改变,所以能确认左shift被按下,所以此时对应的ascii码为大写字母的ascii码41h,缓冲区内容如下:
![](https://img2020.cnblogs.com/blog/1742600/202004/1742600-20200430224835010-551195285.png)
松开左shift键也会引发中断例程,然后将对应状态字节恢复为0.
读取键盘缓冲区
BIOS提供了int16h中断例程供程序员使用,它有一个功能是从键盘缓冲区中读取一个键盘输入,该功能的编号为0,下列指令可以读取一个键盘输入,同时将其从缓冲区中删除:
mov ah,0
int 16h
执行完毕后,ah中是扫描码,al中是ascii码。
这段代码执行后会检查键盘缓冲区中是否有数据,如果没有就一直检测,如果有的话再执行读取和从缓冲区中删除。可见,int9中断例程和int16h中断例程是一对相互配合的程序。
字符串的输入
最基本的字符串输入程序,需要具备下面的功能:
1、在输入的同时需要显示这个字符串
2、一般在输入回车符后,字符串输入结束
3、能够删除已经输入的字符
我们输入字符的时候是从左往右,但是删除时是先删除最右边的字符,这说明字符串应该用栈的形式存储,每次输入和删除一个字符的时候,应该将栈中的字符串显示到屏幕上,按下回车后可以在字符串中加入0,代表字符串结束。
程序的处理过程如下:
1、调用int16h读取键盘输入
2、如果是字符,进入字符栈,显示字符栈中所有的字符,然后继续执行1
3、如果是退格键,从字符栈中弹出一个字符,显示字符栈中所有的字符,继续执行1
4、如果是enter就向字符栈中压入0,然后返回
我们先把字符栈的入栈、出栈和显示栈中的内容写成子程序,参数说明:
ah是功能号,0表示入栈,1表示出栈,2表示显示。ds:si指向字符栈空间,对0号功能,al是入栈字符;对1号功能,al是返回的字符;对于2号功能,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 如果ah大于2跳转到sret结束子程序
mov bl,ah
mov bh,0
add bx,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 如果栈为空则直接跳转到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
mov di,ax
add dl,dl
mov dh,0
add di,dx
mov bx,0 初始化bx为0
charshows: cmp bx,top 比较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
完整程序如下:
getstr: push ax
getstrs:mov ah,0
int 16h 读取键盘缓冲区
cmp al,20h
jb nochar ascii码低于20h说明不是字符转到nochar
mov ah,0
call charstack 如果是字符就将其入栈
mov ah,2
call charstack 显示字符串
jmp getstrs
nochar: cmp ah,0eh
je backspace 如果扫描码为0eh就转到backspace
cmp ah,1ch
je enter 如果扫描码为1ch就转到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
用int13h中断例程对磁盘进行读写
BIOS提供的访问磁盘的中断例程为int 13h,读取0面0道1扇区的内容到0:200的程序如下:
mov ax,0
mov es,ax
mov bx,200h es:bx指向接受数据的内存区
mov al,1 读取的扇区数
mov ch,0 磁道号为0
mov cl,1 扇区号为1
mov dl,0
mov dh,0 磁头号,也就是面号为0
mov ah,2 2功能代表读扇区
int 13h
dl是驱动器号,对于软驱来讲0是软驱A,1是软驱B,对于硬驱来讲,80h是硬盘C,81h是硬盘D。
如果读取成功,则ah为0,al为读入的扇区数,如果读取失败,ah里面是出错代码。
如果是向磁盘写入,只需要将ah代表的功能号改为3即可,就可以将0:200的内容写入磁盘对应位置。