今天我们来实现个功能按F1 不断改变屏幕颜色
概念:
按键盘会产生扫描码
- 通码 按住不放
- 断码 弹起,通码+80H
int9中断对应的端口为60H,也就是我们按下键盘,就可以从60H中读取到扫描码
扫描码的对照表可以参考这篇文章:
键盘按键的各种编码对照表
如果我们直接改变int9,那么硬件层面的一些其它操作细节会就无法继续了,比如控制键的状态信息的填充,比如后续硬件的应答事件。所以这里我们需要做个事情就是在调用int9之前做一个拦截。也就是我们先从60H端口中把数据读走,然后在调用它原来的中断,做后续硬件事件发出应答。
代码如下:
DATAS SEGMENT
;此处输入数据段代码
DATAS ENDS
STACKS SEGMENT
;此处输入堆栈段代码
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
call cpy_new_int9 ;设置新的in9代码到安全区
call save_old_int9 ;保存旧的移动旧的int9向量到0:200位置
call set_new_int9 ;设置新得int9到对应位置。
MOV AH,4CH
INT 21H
;============================================
set_new_int9:
mov bx,0
mov es,bx
cli
mov word ptr es:[9*4],7E00H ;低2个字节给ip
mov word ptr es:[9*4+2],0 ;高2个字节给cs
sti
ret
;============================================
save_old_int9:
mov bx,0
mov es,bx
cli
push es:[9*4] ;ip
push es:[9*4+2] ;cs
pop es:[202H] ;cs
pop es:[200H] ;ip
sti
ret
;============================================
new_int9:
push ax
in al,60H ;从端口读取数据
pushf ;保存标志位
call dword ptr cs:[200H] ;调用原中断
cmp al,3BH ;读到得内容与F1比较
jne int9Ret ;不相等则直接退出,相等执行改变屏幕颜色
call change_screen_color
int9Ret:
pop ax
iret
;============================================
change_screen_color:
push bx
push es
push cx
mov bx,0B800H
mov es,bx
mov bx,1 ;奇数位保存颜色属性信息
mov cx,2000 ;整个屏幕2000个字符
changeColor:
inc byte ptr es:[bx]
add bx,2
loop changeColor
pop cx
pop es
pop bx
ret
new_int9_end: nop
;============================================
cpy_new_int9:
mov bx,0
mov es,bx
mov di,7E00H
mov bx,cs
mov ds,bx
mov si,OFFSET new_int9
mov cx,OFFSET new_int9_end-OFFSET new_int9
cld
rep movsb
ret
;============================================
CODES ENDS
END START
先来看下效果:
多按几下F1 可以看到颜色不断在变化。
这里着重介绍下这两行代码,怎么达到栈平衡
pushf
call dword ptr cs:[200H]
当我们调用我们自己得中断得时候
实际执行了
pushf
push cs
push ip
然后我们又执行了
pushf
接着调用call得时候
实际执行了
push cs
push ip
所以现在栈里面是
pushf
push cs
push ip
pushf
push cs
push ip
而调用完原中断之后就执行了
pop ip
pop cs
popf
最后执行我们自己得iret得时候又把栈内容pop出来了所以达到栈平衡了。