汇编语言 | 定制键盘输入的处理过程

汇编语言 | 定制键盘输入的处理过程

一、键盘输入的处理过程

  1. 键盘产生扫描码
  2. 扫描码送入60h端口
  3. 引发9号中断
  4. 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指令的中断例程:

  1. 标志寄存器入栈

  2. IF=0, TF=0

  3. CS、IP入栈

  4. (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
posted @ 2021-02-11 12:11  hellcat9  阅读(552)  评论(0编辑  收藏  举报