题目
键盘处理程序
思路
该程序修改了键盘I/O,09号中断,并对键盘的按下和弹起进行中断检测,并把来自键盘的83个键的扫描码转换成相应的ascii字符。
我们要知道事有:
1、当键盘上“按下”或“放开”一个键时,如果键盘中断是允许的,就会产生一个9号中断
2、键盘触点电路按照16×8=128矩阵排列每个按键分为“按下”和“放开”两种情况,
通码:最高位为0
断码:最高位为1
3、扫描码:一个字节,8位(256种情况),入60H号端口寄存器
我们要做的事就是
主程序:
- (1) 保存原来的09号中断向量
- (2) 设置中断向量kint
- (3) 设置中断屏蔽字
-
(4)forever循环:
call kbget:检测并等待键盘中断,如果有键盘输入,则从缓冲区buffer取出字符并进行队列管理。kbflag用于判断是否为功能键,这里对功能键不做任何处理,如果是功能键则退出,如果不是功能键则显示,继续输入。如果是回车则外加换行。
键盘中断程序kint:
- (1)保存寄存器的内容
- (2) 取得原来60H的扫描码并保存
- (3) 取得控制端口,应答键盘,应答复位
- (4) 根据扫描码获取ascii码
- (5)获取ascii码后进行相关功能操作,加入缓冲区
- (6)EOI,中断返回
关于循环队列的几种工作状态:
代码
;-----------------------
stack segment
db 256 dup(0)
stack ends
;-----------------------
datasg segment
buffer db 16 dup(0)
bufpt1 dw 0
bufpt2 dw 0
;bufpt1 = bufpt2 ,the buffer is empty 16个字的缓冲区
kbflag db 0
prompt db '---kbd_io program begin---' , 0dh , 0ah , '$'
scantab db 0,0,'1234567890-=',8,0
db 'qwertyuiop[]',0dh,0
db 'asdfghjkl;',0,0,0,0
db 'zxcvbnm,./',0,0,0
db ' ',0,0,0,0,0,0,0,0,0,0,0,0,0
db '789-456+1230.'
oldcs9 dw ?
oldip9 dw ?
datasg ends
;-------------------------
codesg segment
main proc far
assume cs:codesg,ds:datasg
start:
push ds
mov ax,0
push ax
mov ax,datasg
mov ds,ax
cli ;关中断
;第一步 保存中断向量
mov ah,09
mov ah,35h
int 21h
mov oldcs9,es
mov oldip9,bx
;第二步 设置中断向量
push ds ;保护一下ds,因为设置中断时用到ds,ax
mov dx,offset kbint
mov ax,seg kbint
mov ds,ax
mov al,09
mov ah,25h
int 21h
pop ds
;第三步 设置中断屏蔽字
in al,21h
and al,0fdh
out 21h,al
mov dx,offset prompt ;显示提示'---kbd_io program begin---'
mov ah,9
int 21h
sti ;保险起见 , 在设置中断的时候关中断后在开中断 if
forever:
call kbget ;wait get a key
test kbflag,80h ;判断是否为功能键
jnz endint
push ax ;如果不是功能键显示字符
call dispchar ;display the character
pop ax
cmp al,0dh ;如果是换行,则换到下一行
jnz forever
mov al,0ah
call dispchar ;display CR/LF
jmp forever
;第七步 设置回原中断
endint:
mov dx,oldip9
mov ax,oldcs9
mov ds,ax
mov al,09h
mov ah,25h
int 21h
exit:
ret
main endp
;----------主程序结束-------------
;----------kbget-----------------
kbget proc near
push bx
cli ;关中断,在处理缓冲区时防止接受字符
mov bx,bufpt1
cmp bx,bufpt2
jnz kbget2 ;没满,继续接受字符
cmp kbflag,0
jnz kbget3
sti
pop bx
jmp kbget
kbget2:
mov al,[buffer+bx] ;get ascii code
inc bx ;inc a buffer pointer
cmp bx,16 ;at end of buffer ?
jc kbget3 ;no,continue
mov bx,0 ;reset to buf begining 相当于是一个循环队列
kbget3:
mov bufpt1,bx
pop bx
ret
kbget endp
;-----------------------
;-----------kbint-------接管中断
kbint proc far
;第四步 保护寄存器内容
push bx
push ax
;第五步 执行中断处理程序(如果允许中断嵌套,则sti,cli)
in al,60h ;取得扫描码
push ax ;save it
in al,61h ;取得控制端口
or al,80h ;应答键盘,最高位置1
out 61h,al
and al,7fh ;应答复位 最高位置0
out 61h,al
pop ax ;恢复扫描码
test al,80h ;按下或松开
jnz kbint2 ;如果松开,return 断码
mov bx,offset scantab
xlat scantab ;根据扫描码获得ASCII码
cmp al,0 ;判断是否为功能键 scantab功能键都设为0
jnz kbint4 ;不是功能键
mov kbflag,80h ;如果是功能键,kbflag=80h
jmp kbint2
kbint4:
mov bx,bufpt2 ;取出尾指针
mov [buffer+bx],al ;ASCII fill in buffer (al保存着扫描码)
inc bx
cmp bx,16 ;是最后一个?
jc kbint3 ;no
mov bx,0 ;reset to buf beginning
kbint3:
cmp bx,bufpt1 ;缓冲区是否已满?
jz kbint2 ;满了,lose character
mov bufpt2,bx ;保存尾指针
;第六步 送中断结束命令EOI给中断命令程序,恢复寄存器,返回
kbint2:
cli
mov al,20h ;相当于EOI 允许同或低级的中断
out 20h,al
pop ax
pop bx
sti
iret ;中断返回
kbint endp
;-------------dispchar-------------
dispchar proc near
;push bx
;mov bx,0
;mov ah,0eh
;int 10h ;call video routine
;pop bx
push dx
mov dl,al
mov ah,02
int 21h
pop dx
ret
dispchar endp
codesg ends
end start
结果
体验了才知道!