2012-05-09 12 views
7

Tomé x86 asamblea como hobby el pasado enero para poder hacer juegos que funcionaran en computadoras antiguas con 8086 como PCj y Tandy 1000, pero los libros que encontré no enseñan exactamente mucho sobre ese tema específico. Aunque algunas interrupciones de dos y biografías hacen el trabajo, están lejos de ser perfectas.¿Cómo verificar el estado de las teclas en el ensamblaje x86?

Mi problema principal es leer el estado del teclado para las teclas presionadas sin detener el programa. Encontré algunos métodos, pero son muy limitados. INT 21h, AH 0Ch lee la última tecla presionada, pero en una edición de texto. No solo lee solo una clave a la vez, sino que la detección de impacto similar a un bloc de notas hace que no se sepa cuánto tiempo se mantuvo presionada la tecla. También he visto referencias a los puertos de 60h a 64h durante mis viajes a Google, pero es solo eso, referencias. Las explicaciones reales y el código de trabajo son prácticamente inexistentes. O tal vez soy tan malo en el uso de los motores de búsqueda.

Lo que necesito saber es si una tecla está presionada o no. La mejor solución sería tener un buffer/matriz de todas las teclas del teclado y leer su estado; 1 significa que está abajo, 0 significa que no. O simplemente tener acceso a una lista de las últimas claves para haber sido golpeado y liberado sería bueno (con una forma de borrar ese buffer, por supuesto). ¿Alguien puede señalarme en la dirección correcta?

Edit: En primer lugar, debería haber mencionado que utilizo Borland TASM. Ahora compilé tu código y funciona muy bien y todo, a pesar de que soy casi tímido para admitir que no entiendo ni la mitad. Traté de hacerlo compatible con TASM, pero todo lo que hace es crear basura en la pantalla y congelar.

Esto es lo que se me ocurrió;

.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 

No estoy seguro si incluso codifiqué la interrupción correcta. Y diablos si sé cómo funcionan los puertos 060h - 064h.

+0

Su principal problema es que usted está haciendo un programa mientras .EXE se supone que el código se compila en un programa .COM. Ver la respuesta actualizada. –

+0

Tetris OS de trabajo que hace lo que quiere: https://github.com/programble/tetrasm –

Respuesta

3

He aquí cómo usted puede hacerlo:

; 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, "$" 

Ejecútelo en DOS/Win9x/NT/2K/XP/32 bits Vista/7 o DosBox.

ACTUALIZACIÓN: TASM versión:

; 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

En primer lugar, debería haber mencionado que utilizo Borland TASM. Ahora compilé tu código y funciona muy bien y todo, a pesar de que soy casi tímido para admitir que no entiendo ni la mitad. Traté de hacerlo compatible con TASM, pero todo lo que hace es crear basura en la pantalla y congelar. – DieJay

+0

¡Sí! ¡Funciona! Lo modifiqué para que la estructura se ajuste a la de mi proyecto actual y aún no se rompió, así que es perfecto. No entiendo exactamente en detalles cómo funciona eso (para los puertos de todos modos), pero mientras lo haga, no me quejaré. ¡Muchísimas gracias! ¡Ahora puedo hacer juegos de acción en tiempo real! = D – DieJay

0

Por lo general, para sistemas antiguos como este, las personas usaban el BIOS un poco como un conjunto de funciones de biblioteca pre-aprovisionadas, donde cosas como las funciones del teclado solo se usan si les conviene. En su caso, los servicios de teclado BIOS no son convenientes, por lo que no los usa.

En su lugar, desea reemplazar el controlador de interrupción de teclado BIOS con su propio controlador de interrupción de teclado e implementar su propio controlador de teclado. El teclado utiliza IRQ1, que es la interrupción 9. La tabla de vectores de interrupción comienza en 0x0000: 0x0000, por lo que querrá obtener los 4 bytes en 0x0000: 9 * 4 = 0x0000: 0x0024 y almacenarlos en algún lugar (para que pueda volver a colocar las cosas) a la normalidad cuando finaliza su software) y coloque la dirección (desplazamiento y segmento) de su controlador de teclado IRQ allí.

Para escribir su propio controlador de teclado, querrá comenzar por comprender que hay 2 piezas de hardware involucradas. Está el chip controlador del teclado (o "controlador PS/2") en la computadora que habla (mediante comunicación en serie) a un chip dentro del teclado.

Para obtener información sobre el chip controlador de teclado, ver algo como http://wiki.osdev.org/%228042%22_PS/2_Controller

Para obtener información sobre el chip dentro del propio teclado, ver algo como http://wiki.osdev.org/PS/2_Keyboard

0

Ejemplo de sondeo utilizando el teclado 60h puerto y el puerto 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 
Cuestiones relacionadas