Your source code has a number of things that made me frown; I don't know how forgiving DOS Box is, but on a real IBM-PC compatible machine with MS-DOS installed, I would expect this program to hang.
- The interrupt handler doesn't toggle port 61h bit 7; this could be the reason why the keyboard events re-appear.
- The interrupt handler doesn't re-enable the interrupt controller by writing 20h to port 20h.
- The
exit
branch doesn'tpop dx ax cx bx
and ends inret
(should beiret
); upon return it might jump anywhere. - The interrupt handler modifies registers (especially in
tohex
) without restoring their original value:es
,si
,di
. - The interrupt handler assumes registers to have a certain value:
ds
and the direction flag (which affectsstosb
); that's a dangerous assumption. - DOS is not re-entrant; I'm not sure it is allowed to call DOS interrupts from an interrupt handler. Consider pushing the output of
tohex
into the keyboard buffer, instead of printing them yourself. Let the main thread call DOS interrupt 01h so that the contents of the keyboard buffer are echoed to the screen. Restore the interrupt vector there; this is not something the interrupt handler itself should be doing.
EDIT: Thanks for the edit. It seems there are two separate issues remaining.
1. Earlier key presses are dumped to screen when program terminates
Funny thing is, the problem did not occur on an original Win95 machine; only on DOSBox! One might consider this to be a bug in DOSBox, but when nobody complained before, it typically means you are doing something out of the ordinary.
Solution: do not call the original interrupt handler at the end of your own handler; just iret
instead.
2. Keyboard seems dead after program terminates
This problem occurs both on Win95 and on DOSBos; there are two bugs in the code that is called when the user presses Esc.
- The interrupt handler should not terminate the program (DOS function 4Ch); just set a flag in memory to instruct the main thread to exit.
The following two instructions are in the wrong order; loading
ds
will displace the address of the operand of the next instruction.mov ds, word ptr [vec + 2] mov dx, word ptr [vec]
I did some refactoring, and came up with the following; works in DOSBox and Win95.
However, the approach is still far from ideal; calling DOS function 09h to print a string from within the interrupt handler seems pretty harmful. If the main thread would call DOS functions itself (not uncommon in a real-life application), the whole program could crash and burn due to DOS being not re-entrant.
Sorry for the subtle syntax differences in addressing modes; I used an old Borland assembler.
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