上篇文中我们已经实现了int9中断例程的编写,根据课后检测点我们对上文代码做出优化。代码如下:
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
;保存原有int 9号中断处理程序的入口地址
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
mov ax, 0b800h
mov es, ax
mov ah, 'a'
s1: mov es:[160*12+40*2], ah
call delay
inc ah
cmp ah, 'z'
jna s1
;为了不影响后续的键盘输入处理,需要将int 9号中断处理程序的入口地址恢复为原来的地址
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 ax, 0
mov dx, 1000h ;//cpu循环执行10000 000h次
s2:sub ax, 1
sbb dx, 0
cmp ax, 0
jne s2
cmp dx, 0
jne s2
pop dx
pop ax
ret
;-----------新的int 9中断例程-------------
int9: push ax
push bx
push es
in al, 60h ;从端口60h读出键盘的输入
pushf ;标志寄存器入栈
;设置IF = 0, TF = 0
pushf
pop bx
and bh, 11111100B
push bx
popf
;CS, IP入栈,IP = n*4, CS = n*4+2
call dword ptr ds:[0] ;对int指令进行模拟,调用原来的int 9中断例程
cmp al, 1 ;esc键的扫描码为1
jne int9ret
mov ax, 0b800h
mov es, ax
inc byte ptr es:[160*12+40*2 + 1] ;属性值加1改变颜色
int9ret:pop es
pop bx
pop ax
iret
code ends
end start
仔细分析上面代码,可以发现,程序在进入编写的中断例程之前IF和TF都已经置0,对于程序段
pushf ;标志寄存器入栈
;设置IF = 0, TF = 0
pushf
pop bx
and bh, 11111100B
push bx
popf
;CS, IP入栈,IP = n*4, CS = n*4+2
call dword ptr ds:[0] ;对int指令进行模拟,调用原来的int 9中断例程
可以精简为:
pushf ;标志寄存器入栈
;CS, IP入栈,IP = n*4, CS = n*4+2
call dword ptr ds:[0] ;对int指令进行模拟,调用原来的int 9中断例程
另外,在主程序执行过程中,如果在执行设置int9中断例程的段地址和偏移地址的指令之间发生了键盘中断,则cpu将转去一个错误的地址执行,将发生错误,因此针对这一时间段,我们可以使用sti个cli指令来显示中断事件的处理。修改的代码如下:
cli ;禁止中断发生
mov word ptr es:[9*4], offset int9
mov es:[9*4+2], cs
sti ;允许中断发生
;为了不影响后续的键盘输入处理,需要将int 9号中断处理程序的入口地址恢复为原来的地址
cli
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
sti