检测点15.1
(1)仔细分析一下上面的int 9中断例程,看看是否可以精简一下?
其实在我们的int 9中断例程中,模拟int指令调用原int 9中断例程的程序段是可以精简的,因为在进入中断例程后,IF和TF都已经置零,没有必要再进行设置了。对于程序段:
pushf ;标志寄存器值入栈
pushf ;标志寄存器值入栈
pop ax ;标志寄存器值弹栈到ax中
and ah, 11111100B ;标志寄存器值第八位与第九位(IF、TF) 按位与
push ax ;将修改后的ax第值入栈
popf ;将修改后的值弹栈到标志寄存器中,此时第IF & TF位值为0
call word ptr ds:[0] ;调用原int9中断例程
可以精简为:
- pushf
- call word ptr ds:[0]
即免去了IF与TF位赋值位0第操作。(第一条第pushf是为了配合中断例程返回时的iret指令的popf操作)
(2)仔细分析上面程序中的主程序,看看有什么潜在的问题?
在主程序中,如果在执行设置int 9中断例程的段地址和偏移地址的指令之间,发生了键盘中断,则CPU将转去一个错误的地址执行,将发生错误。找出这样的程序段,改写它们,排除潜在的问题。
提示:注意sti和cli指令的用法。
解:问题描述中已经给出了解法:通过sti与cli使设置int9中断例程的段地址和偏移地址的过程是不可被中断的。
1.中断向量表中注册新的int9中断例程时
cli
mov word ptr es:[9*4],offset int9
mov word ptr es:[9*4+2],cs
sti
2.复原int9中断例程在中断向量表中的源地址
cli
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
sti
实验15:安装新的int 9中断例程
assume cs:codesg
codesg segment
start:
push cs ;int 9中断例程的安装程序
pop ds
mov si,offset int9
mov ax,0000H
mov es,ax
mov di,204H ;预留2个字的大小存储原int9例程的内存位置
mov cx,offset int9end - offset int9
cld
rep movsb
mov ax,0000H ;在预留的两个字的空间中[0:200, 0:203],保留原来的例程的入口地址
mov es,ax
push es:[9*4] ;原例程的ip压栈
pop es:[200H] ;弹栈到0:200H
push es:[9*4+2] ;原例程的cs压栈
pop es:[202H] ;弹栈到0:202H处
cli ;设置中断向量表,指向新的int9例程
mov word ptr es:[9*4],204H
mov word ptr es:[9*4+2],0000H
sti
mov ax,4c00H
int 21H
;---
;子程序名称:int9
;功能:int9例程的扩展,当A键松开时,屏幕展示满字符A
;入口参数:无
;返回值:无
;---
int9:
push ax ;保存寄存器值
push bx
push cx
push es
in al,60H ;读取60H端口的扫描码
pushf ;标志寄存器值栈,需要配合原int9中断例程中的iret指令
call dword ptr cs:[200H] ;调用原int9中断例程(注:原int9中断例程的cs与ip在cs:[200H]处)
cmp al,9EH ;A键的通码为1EH,断码为9EH。对比键入扫描码
jne short int9ret ;如果不是A键的断码,则结束
mov ax,0b800H ;满屏显示A字符
mov es,ax
mov bx,0
mov cx,2000 ;80列*25行共2000个字符
writeA:
mov byte ptr es:[bx],41H
add bx,2
loop writeA
int9ret:
pop es ;还原寄存器值
pop cx
pop bx
pop ax
iret
int9end: nop
codesg ends
end start