题目
扩充键盘处理功能程序
在示例3.6的基础上,增加left_shift和right_shift键的功能,即在按下left_shift键或right_shift键的同时,又按下0-9或a-z等键,则CPU显示的是键的上档符号或大写字母。
思路
和上一题类似,这一题增加处理的功能就是在kbint中断处理程序中判断是否按下了left_shift键或right_shift键。如何判断是否按下shift键呢,我们可以把按动状态记录在一个标志单元(kbflag1中),设按下right_shift键或按下left_shift键,kbflag1置1,放开左或右shift键,kbflag恢复0。
当kbflag为1时,再按下其他数字键或字母键,则应转化为在shiftab表格里面查找。
代码
;-----------------------
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
kbflag1 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.'
shiftab db 0,0,'!@#$%^&*()_+',0,0
db 'QWERTYUIOP{}',0Dh,0
db 'ASDFGHJKL:"',0,0,0
db 'ZXCVBNM<>?',0,0,0
db ' ',26 dup(0)
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 断码
;以下为按下的情况
;判断是否为左Shift键
cmp al,2ah
jnz shift5 ;不是左shift键
cmp kbflag1,1
jz shift1
mov kbflag1,1 ;标志位置1
jmp shift1
shift5:
cmp al,36h ;不是左shift,判断是否为右shift键
jnz shift2
cmp kbflag1,1
jz shift1
mov kbflag1,1
jmp shift1
shift2:
cmp kbflag1,1
jnz shift3
lea bx,shiftab ;shiftab换码
xlat
jmp shift4
shift3:
lea bx,scantab
xlat
shift4:
cmp al,0 ;判断是否为其他控制符
jnz kbint4
mov 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:
cmp al,0aah
jnz shift6
mov kbflag1,0
shift6:
cmp al,0b6h
jnz shift1
mov kbflag1,0
shift1:
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