Question

I have problem with 09h interrupt. I'm hooking int vector and set my handler for 09h and it works properly. But when I return basic handler to int vector and terminate program DOS prints full keyboard buffer and then any key pressing doesn't work.

My simple program prints scan codes for pressed/unpressed keys.

    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

Here is DOS Box screenshoot https://www.dropbox.com/s/v1olsalhzgnyyfv/int09h_problem.png

Was it helpful?

Solution

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.

  1. The interrupt handler doesn't toggle port 61h bit 7; this could be the reason why the keyboard events re-appear.
  2. The interrupt handler doesn't re-enable the interrupt controller by writing 20h to port 20h.
  3. The exit branch doesn't pop dx ax cx bx and ends in ret (should be iret); upon return it might jump anywhere.
  4. The interrupt handler modifies registers (especially in tohex) without restoring their original value: es, si, di.
  5. The interrupt handler assumes registers to have a certain value: ds and the direction flag (which affects stosb); that's a dangerous assumption.
  6. 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.

  1. The interrupt handler should not terminate the program (DOS function 4Ch); just set a flag in memory to instruct the main thread to exit.
  2. 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

OTHER TIPS

Pressed keys still remain in buffer. You need to call original int 09h to check whether there are keys awaiting and fetch them using the same original int 09h (GetKey).

Other possibility is to end interrupt call without calling original int 09h. But this solution needs to cancel itself on one of the keys (or it will hang computer/DOSbox) and support port 20h handling (as I remember this port needs to know that app/BIOS fetched the key).

You can make app that fetches keys and prints scancodes without changing interrupt address to your handler. But it should exit itself on one of the keys (to avoid printing endlessly - as you made checking for 81h key).

Also, you can watch 60h port for scancodes and get more (Ctrl, Alt, Shift) even with distinction left-right (other port, as I remember 64h). Don't write to these ports! You may want to look to old TechHelp app (there was 4.1 version).

More info is available here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top