汇编语言 | 定制键盘输入的处理过程
汇编语言 | 定制键盘输入的处理过程
一、键盘输入的处理过程
- 键盘产生扫描码
- 扫描码送入60h端口
- 引发9号中断
- CPU执行int 9中断例程,处理键盘输入
1.~3.由硬件系统用完成。4.中的int 9中断例程可以由DOS系统提供,也可以按照开发需求定制处理键盘的输入。
二、编程任务分析
-
在屏幕中间依次显示'a'~'z',并可以让人看清
-
在显示的过程中按下Esc键后改变显示的颜色
三、工作策略
-
尽可能忽略硬件处理细节,充分利用BIOS提供的int 9中断例程处理硬件细节。
-
在改写后的中断例程中满足特定要求,并能调用BIOS的原int 9中断例程
四、实现
(1)依次显示'a'~'z':
; 显示'a'-'z'
mov ax,0b800h
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah ; 操作显存,找到中间位置
call delay
inc ah
cmp ah,'z'
jna s
mov ax,0
mov es,ax
但是这样做字母切换太快,在屏幕上无法看清每个字母的显示。需要在每个字母显示后延时一段时间。
(2)延时程序:
用两个16位寄存器来存放32位的循环次数。一共循环10,0000h次。
; 定义延迟程序
delay:
push ax
push dx
mov dx,10h
mov ax,0
s1:
sub ax,1
sbb dx,0
cmp ax,0
jne s1
cmp dx,0
jne s1
pop dx
pop ax
ret
(3)按下Esc键后改变显示的颜色
键盘输入到达60h端口后会引发9号中断,CPU会转去执行int 9中断例程。
(1) 从60h端口读出键盘的输入
(2) 调用BIOS的int 9中断例程,处理硬件细节
-
将中断向量表中的int 9中断例程的入口地址改为自编的中断处理程序的入口地址
-
在新中断例程中调用原有的int 9中断例程,因此仍需保存原来的int 9中断例程的地址
解决方案:将原来int 9中断例程的偏移地址和段地址保存在ds:[0]和ds:[2]单元中。
; 保存旧中断例程入口
mov ax,0
mov es,ax
push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2]
; 设置新的中断向量的位置
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
调用原int 9指令的中断例程:
-
标志寄存器入栈
-
IF=0, TF=0
-
CS、IP入栈
-
(IP) = ((ds)*16 + 0)
(CS) = ((ds)*16 + 2)
pushf ; 标志寄存器入栈
; IF=0, TF=0
pushf
pop bx
and bh,11111100b
push bx
popf
; CS、IP入栈 (IP) = ((ds)*16 + 0) (CS) = ((ds)*16 + 2)
call dword ptr ds:[0]
(3) 判断是否为Esc的扫描码,如果是则改变显示颜色。否则直接返回。
; 定义中断例程
int9:
push ax
push bx
push es
in al,60h
pushf
pushf
pop bx
and bh,11111100b
push bx
popf
call dword ptr ds:[0]
cmp al,1 ; ESC扫描码1
jne int9ret
; 改变颜色
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1]
int9ret:
pop es
pop bx
pop ax
iret
五、完整代码
; 编写int 9中断例程改变显示的颜色
; (1)从60h端口读出键盘的输入 in al,60h
; (2)调用BIOS的int 9中断例程, 处理硬件细节
; (3)判断是否为Esc扫描码 如果是则改变显示的颜色后返回
; 如果不是则直接返回
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0,0
data ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
; 更改中断例程的入口地址
mov ax,0
mov es,ax
push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2]
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
; 显示'a'-'z'
mov ax,0b800h
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah
call delay
inc ah
cmp ah,'z'
jna s
mov ax,0
mov es,ax
; 恢复原来的地址
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
mov ax,4c00h
int 21h
; 定义延迟程序
delay:
push ax
push dx
mov dx,10h
mov ax,0
s1:
sub ax,1
sbb dx,0
cmp ax,0
jne s1
cmp dx,0
jne s1
pop dx
pop ax
ret
; 定义中断例程
int9:
push ax
push bx
push es
in al,60h
pushf
pushf
pop bx
and bh,11111100b
push bx
popf
call dword ptr ds:[0]
cmp al,1 ; ESC扫描码为1
jne int9ret
; 改变颜色
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1] ; 高8位存放的是字符的属性
int9ret:
pop es
pop bx
pop ax
iret
code ends
end start