Bongodam
Bongodam

Reputation: 27

8086 Assembly keyboard ISR implementation

I can't understand why the keyboard interrupt service routine I wrote for my program (should print "hello world" each time I press a key) only occurs once when I execute the .exe on dosbox. Here is the code:

  NAME keyb

PILE    SEGMENT STACK
    db      20 dup ('LA PILE ')
PILE    ENDS


DONNEE SEGMENT
message db "Hello wolrd !$"
DONNEE ENDS

PROGRAMME SEGMENT
ASSUME CS:PROGRAMME , DS:DONNEE, ES:NOTHING, SS:PILE
debut:

    mov ax,DONNEE
    mov ds,ax

    cli

    xor ax,ax
    mov es, ax ; load ES with segment address of interrupt pointer table
    mov bx, 24h ; load BX with interrupt type
    mov word ptr es:[bx], OFFSET SUBR ; isr address
    mov word ptr es:[bx]+2, SEG SUBR ; isr segment
    mov ax, 01h
    sti

BOUCLE: jmp BOUCLE

SUBR PROC NEAR
    cli
    mov ah,9
    mov dx,OFFSET message
    int 21h
    sti

    IRET
SUBR ENDP

PROGRAMME ENDS

    END debut

I tried a few things, like push-ing and pop-ing registers, using another interrupt (system clock 08h), but none of them worked. I know that the ISR runs at least once because the message "hello world" appears on screen, but it should print every time I press a key and I have no idea why it doesn't.

How can I resolve this?

Upvotes: 2

Views: 1703

Answers (2)

rkhb
rkhb

Reputation: 14409

You have to do some work to "free" keyboard and interrupt. Take a look here. The easiest way is to jump to the old IRQ-handler at the end of your own handler:

NAME keyb

PILE    SEGMENT STACK
    db      20 dup ('LA PILE ')
PILE    ENDS

DONNEE SEGMENT
message db "Hello wolrd !$"
oldvec dw 0, 0
DONNEE ENDS

PROGRAMME SEGMENT
ASSUME CS:PROGRAMME , DS:DONNEE, ES:NOTHING, SS:PILE
debut:

    mov ax,DONNEE
    mov ds,ax

    cli

    xor ax,ax
    mov es, ax ; load ES with segment address of interrupt pointer table
    mov bx, 24h ; load BX with interrupt type

    ; Store the old IRQ vector
    mov ax, es:[bx]
    mov oldvec, ax
    mov ax, es:[bx+2]
    mov oldvec + 2, ax

    mov word ptr es:[bx], OFFSET SUBR ; isr address
    mov word ptr es:[bx]+2, SEG SUBR ; isr segment
    mov ax, 01h
    sti

BOUCLE: jmp BOUCLE

SUBR PROC NEAR
    push ax
    push dx

    mov ah,9
    mov dx,OFFSET message
    int 21h

    pop dx
    pop ax    

    jmp dword ptr [oldvec]
SUBR ENDP

PROGRAMME ENDS

END debut

Upvotes: 1

enhzflep
enhzflep

Reputation: 13109

Basically put, unless you call the default handler after you've done your work, you'll have to tell the PIC (programmable interrupt controller) that you're done - the default handler will do this for you - sending the EOI (end of interrupt signal) to the PIC(s). The PIC wont fire the interrupt again until you tell it that you've done with the current one.

Code to do this in 32 bit protected mode looks something like this. Do note, that I use a generic handler for all IRQs, just handing off to the appropriately registered user callback function. I've included both.

First, the generic IRQ handler

irq_handler:
    push    ebp
    mov     ebp, esp
    add     ebp, 8


    mov     eax, [ebp +registers_t.int_no]

    cmp     eax, IRQ7                   ; this just dumps spurious IRQ7 interrupts
    je      .irqHandlerDone

    cmp     eax, IRQ8                   ; if it's IRQ0 - IRQ7, the first controller fired the int, otherwise the slave controller did
    jb      .slaveResetDone
.resetSlave:
    mov     al,20H      ; send End-Of-Interrupt signal
    out     0xA0,al     ; to the 8259 _slave_ Programmable Interrupt Controller
.slaveResetDone:
.resetMaster:
    mov     al, 0x20    ; send End-Of-Interrupt signal
    out     0x20, al    ; to the 8259 master Programmable Interrupt Controller

    mov     eax, [ebp + registers_t.int_no]
    shl     eax, 2                              ; x4
    mov     esi, interrupt_handlers
    add     esi, eax                            ; esi --> interrupt_handlers[int_no]
    cmp     dword [esi], 0
    je      .irqHandlerDone

    call    [esi]
.irqHandlerDone:
    pop     ebp
    ret

Next, the IRQ1 (keyboard) handler. The registration function simply copies the offset of the function into a table (interrupt_handlers) of 32 bit addresses. I'm in flat-memory mode (4GB addressable, ES already holds the segment selector for a writeable data segment. 0xB8000 + 79*2 simply points to the character on the top-right of a 80x25 mode3 text screen)

; keyboard IRQ handler
irq1Handler:
    push    ebp
    mov     ebp, esp
    add     ebp, 8+4

    in      al, 0x60
    mov     bl, al
    mov     byte [port60], al

    in      al, 0x61
    mov     ah, al
    or      al, 0x80
    out     0x61, al
    xchg    ah, al
    out     0x61, al

    and     bl, 0x80
    jnz     .done

    pusha
    ;mov        al, [port60]
    ;call   outputChar

    mov     edi, 0xB8000 + 79*2
    mov     al, [port60]
    mov     [es:edi], al

    popa
.done:  
    pop     ebp
    ret
port60  db  0

The code draws from James M's tutorial, here: http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html

There is much that can be read about interfacing to hardware over at OSDev.org - http://wiki.osdev.org/Main_Page

UPDATE: Here's a 16bit ISR that functions in DosBox.

;-----------------------------------------------------
; handles int 0x09
;-----------------------------------------------------
keyhandler:
    cli
    pusha
    in al, 0x60                     ; get key data
    mov bl, al                      ; save it
    mov byte [port60], al

    in al, 0x61                     ; keybrd control
    mov ah, al
    or al, 0x80                     ; disable bit 7
    out 0x61, al                    ; send it back
    xchg ah, al                     ; get original
    out 0x61, al                    ; send that back

    mov al, 0x20                    ; End of Interrupt
    out 0x20, al                    ;

    and bl, 0x80                    ; key released
    jnz done                        ; don't repeat

    mov al, [port60]
    ;
    ; do something with the scan-code here
    ;
done:
    popa
    iret
port60    db    0        ; where we'll store the scan-code

Upvotes: 1

Related Questions