2012-05-09 11 views
7

Ho preso l'assemblaggio x86 come passatempo il prossimo gennaio, così ho potuto creare giochi che funzionassero su vecchi computer con tecnologia 8086 come PCj e Tandy 1000, ma i libri che ho trovato non insegnano esattamente molto su quell'argomento specifico. Mentre alcuni dos e bios interrompono il lavoro, sono tutt'altro che perfetti.Come verificare lo stato delle chiavi nell'assemblaggio x86?

Il mio problema principale è la lettura dello stato della tastiera per i tasti premuti senza interrompere il programma. Ho trovato alcuni metodi, ma sono molto limitati. INT 21h, AH 0Ch legge l'ultimo tasto premuto, ma in modo testo-edizione. Non solo legge solo una chiave alla volta, ma la rilevazione del colpo simile a un blocco note rende impossibile sapere per quanto tempo il tasto è stato trattenuto. Ho visto anche riferimenti alle porte da 60h a 64h durante i miei viaggi su Google, ma è solo questo, riferimenti. Le spiegazioni effettive e il codice di lavoro sono praticamente inesistenti. O forse sono così male nell'usare i motori di ricerca.

Quello che devo sapere è se un tasto è premuto o meno. La soluzione migliore sarebbe avere un buffer/array di tutti i tasti della tastiera e leggere il suo stato; 1 significa che è inattivo, 0 significa che non lo è. O semplicemente avere accesso a un elenco delle ultime chiavi che sono state colpite e rilasciate sarebbe bello (con un modo per cancellare quel buffer, ovviamente). Qualcuno può indicarmi la giusta direzione?

Modifica: Prima di tutto, avrei dovuto dire che uso Borland TASM. Ora ho compilato il tuo codice e funziona alla grande e tutto, anche se sono quasi timido ad ammettere che non ne capisco la metà. Ho provato a renderlo compatibile con TASM, ma tutto ciò che fa è creare immondizia sullo schermo e congelare.

Ecco cosa mi è venuto in mente;

.MODEL TINY 
.STACK 256 

.DATA 
kbdbuf DB 128 DUP (0) 

msg1 db "Press and hold ESC", 13, 10, "$" 
msg2 db "ESC pressed, release ESC", 13, 10, "$" 
msg3 db "ESC released", 13, 10, "$" 

.CODE 
main PROC 
    org 0100h 
    mov ax, @data 
    mov ds, ax 

    xor  ax, ax 
    mov  es, ax 

    cli       ; update ISR address w/ ints disabled 
    push word [es:9*4+2]  ; preserve ISR address 
    push word [es:9*4] 
    lea si, irq1isr 
    mov  word [es:9*4], si ; requires a register 
    mov  [es:9*4+2],cs 
    sti 

     mov  ah, 9 
     lea  dx, msg1 
     int  021h    ; print "Press and hold ESC" 

    test1: 
     mov  al, [kbdbuf + 1] ; check Escape key state (Esc scan code = 1) 
     or  al, al 
     jz  test1    ; wait until it's nonzero (pressed/held) 

     lea  dx, msg2 
     int  021h    ; print "ESC pressed, release ESC" 

    test2: 
     mov  al, [kbdbuf + 1] ; check Escape key state (Esc scan code = 1) 
     or  al, al 
     jnz  test2    ; wait until it's zero (released/not pressed) 

     lea  dx, msg3   ; print "ESC released" 
     int  021h 

    cli       ; update ISR address w/ ints disabled 
    pop  word [es:9*4]  ; restore ISR address 
    pop  word [es:9*4+2] 
    sti 

    ret 

    irq1isr: 
    push ax bx 

    ; read keyboard scan code 
    in  al, 060h 

    ; update keyboard state 
    xor  bh, bh 
    mov  bl, al 
    and  bl, 07Fh   ; bx = scan code 
    shr  al, 7    ; al = 0 if pressed, 1 if released 
    xor  al, 1    ; al = 1 if pressed, 0 if released 
    mov  [cs:bx+kbdbuf], al 

    ; send EOI to XT keyboard 
    in  al, 061h 
    mov  ah, al 
    or  al, 080h 
    out  061h, al 
    mov  al, ah 
    out  061h, al 

    ; send EOI to master PIC 
    mov  al, 020h 
    out  020h, al 

    pop  bx ax 
    iret 
main ENDP 

END main 

Non sono sicuro di aver anche corretto l'interrupt. E diamine se so come funzionano le porte 060h - 064h.

+0

Il tuo problema principale è che si sta facendo un programma eseguibile, mentre il codice dovrebbe essere compilato in un programma .COM. Vedi la risposta aggiornata. –

+0

Sistema operativo Tetris funzionante che fa ciò che vuoi: https://github.com/programble/tetrasm –

risposta

3

Ecco come si può fare:

; compile with NASM: nasm.exe -f bin kbd.asm -o kbd.com 

bits 16 
org 0x100 

    xor  ax, ax 
    mov  es, ax 

    cli       ; update ISR address w/ ints disabled 
    push word [es:9*4+2]  ; preserve ISR address 
    push word [es:9*4] 
    mov  word [es:9*4], irq1isr 
    mov  [es:9*4+2],cs 
    sti 

    call test 

    cli       ; update ISR address w/ ints disabled 
    pop  word [es:9*4]  ; restore ISR address 
    pop  word [es:9*4+2] 
    sti 

    ret 

test: 
    mov  ah, 9 
    mov  dx, msg1 
    int  0x21    ; print "Press and hold ESC" 

test1: 
    mov  al, [kbdbuf + 1] ; check Escape key state (Esc scan code = 1) 
    or  al, al 
    jz  test1    ; wait until it's nonzero (pressed/held) 

    mov  dx, msg2 
    int  0x21    ; print "ESC pressed, release ESC" 

test2: 
    mov  al, [kbdbuf + 1] ; check Escape key state (Esc scan code = 1) 
    or  al, al 
    jnz  test2    ; wait until it's zero (released/not pressed) 

    mov  dx, msg3   ; print "ESC released" 
    int  0x21 

    ret 

irq1isr: 
    pusha 

    ; read keyboard scan code 
    in  al, 0x60 

    ; update keyboard state 
    xor  bh, bh 
    mov  bl, al 
    and  bl, 0x7F   ; bx = scan code 
    shr  al, 7    ; al = 0 if pressed, 1 if released 
    xor  al, 1    ; al = 1 if pressed, 0 if released 
    mov  [cs:bx+kbdbuf], al 

    ; send EOI to XT keyboard 
    in  al, 0x61 
    mov  ah, al 
    or  al, 0x80 
    out  0x61, al 
    mov  al, ah 
    out  0x61, al 

    ; send EOI to master PIC 
    mov  al, 0x20 
    out  0x20, al 

    popa 
    iret 

kbdbuf: 
    times 128 db 0 

msg1 db "Press and hold ESC", 13, 10, "$" 
msg2 db "ESC pressed, release ESC", 13, 10, "$" 
msg3 db "ESC released", 13, 10, "$" 

Eseguirlo in DOS/Win9x/NT/2K/XP/32-bit Vista/7 o DosBox.

UPDATE: versione TASM:

; file: kbdt.asm 
; compile with TASM/TLINK: 
; tasm.exe kbdt.asm 
; tlink.exe /t kbdt.obj 

.286 

code segment use16 
assume cs:code, ds:code, ss:code 
org 100h 

main: 
    xor  ax, ax 
    mov  es, ax 

    cli       ; update ISR address w/ ints disabled 
    push word ptr es:[9*4+2]  ; preserve ISR address 
    push word ptr es:[9*4] 
    mov  word ptr es:[9*4], offset irq1isr 
    mov  es:[9*4+2],cs 
    sti 

    call test0 

    cli       ; update ISR address w/ ints disabled 
    pop  word ptr es:[9*4] ; restore ISR address 
    pop  word ptr es:[9*4+2] 
    sti 

    ret 

test0: 
    mov  ah, 9 
    mov  dx, offset msg1 
    int  21h     ; print "Press and hold ESC" 

test1: 
    mov  al, [kbdbuf + 1] ; check Escape key state (Esc scan code = 1) 
    or  al, al 
    jz  test1    ; wait until it's nonzero (pressed/held) 

    mov  dx, offset msg2 
    int  21h     ; print "ESC pressed, release ESC" 

test2: 
    mov  al, [kbdbuf + 1] ; check Escape key state (Esc scan code = 1) 
    or  al, al 
    jnz  test2    ; wait until it's zero (released/not pressed) 

    mov  dx, offset msg3  ; print "ESC released" 
    int  21h 

    ret 

irq1isr: 
    pusha 

    ; read keyboard scan code 
    in  al, 60h 

    ; update keyboard state 
    xor  bh, bh 
    mov  bl, al 
    and  bl, 7Fh    ; bx = scan code 
    shr  al, 7    ; al = 0 if pressed, 1 if released 
    xor  al, 1    ; al = 1 if pressed, 0 if released 
    mov  cs:[bx+kbdbuf], al 

    ; send EOI to XT keyboard 
    in  al, 61h 
    mov  ah, al 
    or  al, 80h 
    out  61h, al 
    mov  al, ah 
    out  61h, al 

    ; send EOI to master PIC 
    mov  al, 20h 
    out  20h, al 

    popa 
    iret 

kbdbuf  db 128 dup (0) 

msg1 db "Press and hold ESC", 13, 10, "$" 
msg2 db "ESC pressed, release ESC", 13, 10, "$" 
msg3 db "ESC released", 13, 10, "$" 

code ends 

end main 
+0

Prima di tutto, avrei dovuto dire che uso Borland TASM. Ora ho compilato il tuo codice e funziona alla grande e tutto, anche se sono quasi timido ad ammettere che non ne capisco la metà. Ho provato a renderlo compatibile con TASM, ma tutto ciò che fa è creare immondizia sullo schermo e congelare. – DieJay

+0

Sì! Funziona! L'ho modificato in modo che la struttura si adattava a quella del mio progetto attuale e non si è ancora interrotta, quindi è semplicemente perfetta. Non capisco esattamente nei dettagli come funziona (per le porte comunque), ma finché lo fa, non mi lamento. Grazie MOLTO! Ora posso fare giochi d'azione in tempo reale! = D – DieJay

0

In genere per i vecchi sistemi come questo la gente utilizzava il BIOS un po 'come un insieme di funzioni di libreria pre-fornite, dove cose come le funzioni della tastiera vengono utilizzate solo se sono convenienti. Nel tuo caso i servizi di tastiera del BIOS non sono convenienti, quindi non li usi.

Invece, si desidera sostituire il gestore di interrupt di tastiera BIOS con il proprio gestore di interrupt di tastiera e implementare il proprio driver di tastiera. La tastiera utilizza IRQ1, che è l'interrupt 9. La tabella vettoriale di interrupt inizia da 0x0000: 0x0000 quindi vorresti ottenere i 4 byte a 0x0000: 9 * 4 = 0x0000: 0x0024 e memorizzarli da qualche parte (quindi puoi rimettere le cose a posto normale quando il tuo software esce) e metti invece l'indirizzo (offset, quindi segmento) del tuo gestore IRQ della tastiera.

Per scrivere il proprio driver per tastiera, è necessario iniziare comprendendo che sono coinvolti 2 componenti hardware. C'è il chip del controller della tastiera (o "controller PS/2") nel computer che parla (tramite la comunicazione seriale) con un chip all'interno della tastiera stessa.

Per informazioni sul chip controller della tastiera, vedere qualcosa di simile http://wiki.osdev.org/%228042%22_PS/2_Controller

Per informazioni sul chip all'interno della tastiera stessa, vedere qualcosa di simile http://wiki.osdev.org/PS/2_Keyboard

0

Esempio di polling della tastiera utilizzando 60h porta e la porta 64h:

 cli   ; stop software-Interrupts 
     mov al, 2  ; stop IRQ 1 
     out 21h, al 
     sti 
P1: 
     in al, 64h  ; get Status 
     test al, 1  ; is there something in the outputbuffer? 
     jz P1 
     test al, 20h  ; it is a byte from the PS2-Mouse? 
     jnz P1 
     in al, 60h  ; get a key 
     cmp al, 1  ; Escape-key? 
     jz XRAUS  ; then goto end 
;─────────────────────────────────────────────────────────────── 
     mov si, OFFSET SONTAB ; get the offsetaddress of our special-key table 
     mov cl, Extablen  ; lenght 
XSUCH: cmp al, [si] 
     jz XFOUND 
     lea si, [si+1]   ; instead of "inc si" 
     dec cl 
     jnz XSUCH 
;─────────────────────────────────────────────────────────────── 
     mov si, OFFSET TASTTAB ; get the offsetaddress of our key table 
     mov cx, tablen 
     mov bx, OFFSET TEXTTAB ; our corresponding ASCII table 
SUCH: cmp al, [si] 
     jz short FOUND 
     lea si, [si+1] 
     dec cx 
     jnz SUCH 
     jmp P1 
;─────────────────────────────────────────────────────────────── 
XRAUS: in al, 60h ; clear outputbuffer 
     cli 
     xor al, al ; enable IRQ 1 
     out 21h, al 
     sti 
     mov ah, 1 ; clear buffer in the ram 
     int 16h 
; ...some more instructions 
;─────────────────────────────────────────────────────────────── 
FOUND: mov si, tablen ; Length 
     sub si, cx 
     xor ecx, ecx 
     mov cl, [bx+si] ; get the ASCII from our table 
; ...some more instructions 
;─────────────────────────────────────────────────────────────── 
XFOUND: 
; Tab,shift li.,shift re.,HOME,UP,LEFT,RIGHT,END,DOWN 
     cmp cl, 1  ; DOWN-key 
     jnz short ... ; jump to next 
     .... 
     .... 
     cmp cl, 9  ; Tab-key 
     jnz P1 
; ...some more instructions 
:------------------------Data area---------------------- 
TASTTAB DB 02h,03h,04h,05h,06h,07h,08h,09h,0Ah,0Bh,0Ch,0Dh 
     DB 10h,11h,12h,13h,14h,15h,16h,17h,18h,19h,1Ah,1Bh,1Eh,1Fh 
     DB 20h,21h,22h,23h,24h,25h,26h,27h,28h,29h,2Bh,2Ch,2Dh,2Eh,2Fh 
     DB 30h,31h,32h,33h,34h,35h,39h 
     DB 56h 
tablen = ($-TASTTAB) 
TEXTTAB DB "1234567890ß'"  ; with some german letters inside 
     DB "qwertzuiopü+as" 
     DB "dfghjklöä^#yxcv" 
     DB "bnm,.- " 
     DB "<" 
Textablen = ($-TEXTTAB) 
;--------------------------------------------------------------------------- 
; Tab,shift left.,shift rigth.,HOME,UP,LEFT,RIGHT,END,DOWN 
;---------- 
SONTAB DB 0Fh,2Ah,36h,47h,48h,4Bh,4Dh,4Fh,50h 
Extablen = ($-SONTAB) 
     DB 0,0,0 ; for data_alignment of following entries 
Problemi correlati