8086 Реализация на ISR на асемблирана клавиатура

Не мога да разбера защо рутинната услуга за прекъсване на клавиатурата, която написах за моята програма (трябва да отпечатва "hello world" всеки път, когато натисна клавиш), се появява само веднъж, когато изпълня .exe на dosbox. Ето кода:

  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

Опитах няколко неща, като натискане и изскачане на регистри, използвайки друго прекъсване (системен часовник 08h), но нито едно от тях не работи. Знам, че ISR се изпълнява поне веднъж, защото съобщението „здравей свят“ се появява на екрана, но трябва да се отпечатва всеки път, когато натисна клавиш и нямам представа защо не го прави.

Как мога да разреша това?


person Bongodam    schedule 13.10.2015    source източник


Отговори (2)


Трябва да свършите малко работа, за да "освободите" клавиатурата и да прекъснете. Разгледайте тук. Най-лесният начин е да преминете към стария IRQ манипулатор в края на вашия собствен манипулатор:

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
person rkhb    schedule 13.10.2015

По принцип казано, освен ако не извикате манипулатора по подразбиране, след като сте свършили работата си, ще трябва да кажете на PIC (програмируем контролер за прекъсване), че сте готови - манипулаторът по подразбиране ще направи това вместо вас - изпращане на EOI (край на сигнал за прекъсване) към PIC(ите). PIC няма да задейства отново прекъсването, докато не му кажете, че сте приключили с текущото.

Кодът за извършване на това в 32-битов защитен режим изглежда по следния начин. Имайте предвид, че използвам общ манипулатор за всички IRQ, като просто предавам на подходящо регистрираната потребителска функция за обратно извикване. Включил съм и двете.

Първо, общият манипулатор на IRQ

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

След това манипулаторът на IRQ1 (клавиатура). Функцията за регистрация просто копира отместването на функцията в таблица (interrupt_handlers) с 32-битови адреси. Аз съм в режим на плоска памет (4GB адресируеми, ES вече държи селектора на сегменти за записваем сегмент от данни. 0xB8000 + 79*2 просто сочи към знака в горния десен ъгъл на текстов екран 80x25 mode3)

; 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

Кодът е взет от урока на James M, тук: http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html

Има много, което може да се прочете за взаимодействието с хардуера на OSDev.org - http://wiki.osdev.org/Main_Page

АКТУАЛИЗАЦИЯ: Ето 16-битов ISR, който функционира в 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
person enhzflep    schedule 14.10.2015