我有09h中断的问题。我挂钩int vector并设置我的处理程序09h,它正常工作。但是,当我将基本处理程序返回到int vector并终止程序时,DOS打印完整的键盘缓冲区,然后任何按键操作都不起作用。
我的简单程序打印按下/未按下键的扫描码。
model tiny
.code
.386
org 100h
main:
xor ax, ax
push ax
pop es
jmp start
handler:
push bx cx ax dx
in al, 60h
cmp al, 81h
je hand_end
call tohex
lea dx, nl
mov ah, 09h
int 21h
in al,61H ;get value of keyboard control lines
mov ah,al ; save it
or al,80h ;set the "enable kbd" bit
out 61H,al ; and write it out the control port
xchg ah,al ;fetch the original control port value
out 61H,al ; and write it back
mov al, 20h ;send End-Of-Interrupt signal
out 20h, al ; to the 8259 Interrupt Controller
pop dx ax cx bx
jmp dword ptr cs:[vec]
hand_end:
pop dx ax cx bx
jmp exit
start:
cli
mov ax, 3509h
int 21h
mov word ptr [vec], bx
mov word ptr [vec + 2], es
mov ax, 2509h
lea dx, handler
int 21h
sti
lp:
jmp lp
exit:
; returning iVector
cli
push ds
mov ax, 2509h
mov ds, word ptr [vec + 2]
mov dx, word ptr [vec]
int 21h
pop ds
sti
mov ax, 4c00h
int 21h
proc tohex
pusha
push es
mov ah, al
mov cx, 2
push ds
pop es
lea di, hex
hxlp:
shr al, 4
lea bx, hx_table
xlat
stosb
shl ah, 4
mov al, ah
loop hxlp
lea dx, hex
mov ah, 09h
int 21h
pop es
popa
ret
endp tohex
nl db 13,10,'$'
vec dd ?,'$'
hex db ?,?,'$'
hx_table db '0123456789ABCDEF'
end main
这是DOS Box屏幕截图https://www.dropbox.com/s/v1olsalhzgnyyfv/int09h_problem.png
答案 0 :(得分:2)
你的源代码有很多东西让我皱眉;我不知道DOS Box是多么宽容,但是在安装了MS-DOS的真正的IBM-PC兼容机器上,我希望这个程序能够挂起。
exit
分支不pop dx ax cx bx
,以ret
结尾(应为iret
);返回时,它可能会任何地方。tohex
)而不恢复其原始值:es
,si
,di
。ds
和方向标志(影响stosb
);这是一个危险的假设。tohex
的输出推送到键盘缓冲区,而不是自己打印。让主线程调用DOS中断01h,使键盘缓冲区的内容回显到屏幕。在那里恢复中断向量;这不是中断处理程序本身应该做的事情。修改强> 谢谢你的编辑。似乎还有两个单独的问题。
有趣的是,原始的Win95机器上没有出现问题;只在DOSBox上!有人可能会认为这是DOSBox中的一个错误,但是当之前没有人抱怨时,通常意味着你正在做一些与众不同的事情。
解决方案: not 在您自己的处理程序结束时调用原始中断处理程序;只需iret
。
在Win95和DOSBos上都会出现此问题;当用户按 Esc 时,代码中有两个错误。
以下两条说明顺序错误; loading ds
将替换下一条指令的操作数的地址。
mov ds, word ptr [vec + 2]
mov dx, word ptr [vec]
我做了一些重构,并想出了以下内容;适用于DOSBox和Win95。
然而,这种方法仍然远非理想;调用DOS函数09h从中断处理程序中打印一个字符串似乎非常有害。如果主线程本身会调用DOS函数(在实际应用程序中并不常见),整个程序可能因DOS不可重入而崩溃和烧毁。
对于寻址模式中微妙的语法差异感到抱歉;我使用了一个古老的Borland汇编程序。
IDEAL
MODEL tiny
CODESEG
ORG 100h
main:
cli
mov ax,3509h
int 21h
mov [WORD LOW vec],bx
mov [WORD HIGH vec],es
mov ax,2509h
mov dx,OFFSET handler
int 21h
sti
lp:
;mov ah,01h
;int 21h ; uncomment this and the program will crash
mov al,[scancode]
cmp al,81h
jne lp
push ds
mov ax,2509h
mov dx,[WORD LOW vec]
mov ds,[WORD HIGH vec]
int 21h
pop ds
mov ax,4C00h
int 21h
handler:
push ax
push bx
push cx
push dx
push ds
mov ax,cs
mov ds,ax
in al,60h
mov [scancode],al
in al,61h
or al,80h
out 61h,al
and al,7Fh
out 61h,al
mov al,20h
out 20h,al
call tohex
pop ds
pop dx
pop cx
pop bx
pop ax
iret
tohex:
mov bx,OFFSET hx_table
mov al,[scancode]
and al,0Fh
xlat
mov ah,al
mov al,[scancode]
shr al,4
xlat
mov [WORD PTR hex],ax
mov dx,OFFSET hex
mov ah,09h
int 21h
ret
DATASEG
hex db ?,?,13,10,'$'
hx_table db '0123456789ABCDEF'
scancode db 0
UDATASEG
vec dd ?
END main
答案 1 :(得分:1)
按下的键仍保留在缓冲区中。你需要调用原始的int 09h来检查是否有等待的密钥并使用相同的原始int 09h(GetKey)获取它们。
其他可能性是在不调用原始int 09h的情况下结束中断调用。但是这个解决方案需要在其中一个键上取消(或者它会挂起计算机/ DOSbox)并支持端口20h处理(因为我记得这个端口需要知道app / BIOS获取了密钥)。
您可以创建获取密钥并打印扫描码的应用程序,而无需更改处理程序的中断地址。但它应该在其中一个键上退出(以避免无休止地打印 - 当你检查81h键时)。
此外,您可以观看60小时的扫描码端口,并获得更多(Ctrl,Alt,Shift),即使左右区别(其他端口,我记得64小时)。不要写这些端口!您可能希望查看旧的TechHelp应用程序(有4.1版本)。
有更多信息here。