2

Is it possible to detect and collect keyboard makes and brakes faster than just reading from hardware port 60h?

Whenever I press a key, let's say the 'W' key, then very quickly press another key, the break code for the 'W' key is still returned by port 60h.

In the game i am writing, this has the effect of locking the player sprite in place when a user tries to quickly change direction.

I have tried using int 16h function 01h along with int 16h function 00, but it's very choppy and slow compared to port 60h.

Here's my input code using port 60h. I just pass a scancode into bp. All my procedures that require user input check the scancode in bp.

HANDLE_INPUT PROC

;CLEARS THE KEYBOARD TYPEHEAD BUFFER AND COLLECTS A SCANCODE 

;ALTERS BP

    push ax
    push es

    mov ax, 40h                
    mov es, ax                  ;access keyboard data area via segment 40h
    mov WORD PTR es:[1ah], 1eh  ;set the kbd buff head to start of buff
    mov WORD PTR es:[1ch], 1eh  ;set the kbd buff tail to same as buff head
                                ;the keyboard typehead buffer is now cleared
    xor ah, ah
    in al, 60h                  ;al -> scancode
    mov bp, ax                  ;bp -> scancode, accessible globally   

    pop es
    pop ax
    ret


HANDLE_INPUT ENDP

And here's the alternate version using int 16h, doesn't work nearly as well as the above version that uses port 60h.

HANDLE_INPUT PROC

;COLLECTS A SCANCODE 

;ALTERS BP

    push ax

    xor bp, bp    ;clear out bp
    mov ah, 1     ;Function 1, check key status.
    int 16h       ;Is a key ready? 
    jz NO_KEY     ;If zf cleared, then no.
    xor ah, ah    ;Otherwise, a key is waiting.
    int 16h       ;ah -> scancode
    xor al, al    
    xchg al, ah   ;ax -> scancode
    mov bp, ax    ;bp -> scancode, accessible globally


NO_KEY:

    pop ax
    ret


HANDLE_INPUT ENDP
bad
  • 939
  • 6
  • 18
  • 3
    If polling port 0x60 you should wait until bit 0 of the status register is 1 before attempting to retrieve an event. If replacing the keyboard interrupt be aware that may cause issues for DOS functions that rely on keyboard data (may not be an issue for your code). Advantage to using interrupts is you don't need to wait for the Status register bit 0 to become 1 (just remember to send an End of Interrupt) to the master PIC (send 0x20 to port 0x20). – Michael Petch Dec 13 '17 at 05:55
  • 4
    The problem with polling is that you are competing with the IRQ 1 ISR. Usually, int 16h is good enough for any game but it has some latency of course. Beware that if you wanted to write an ISR that is as fast as possible you will end up with a circular buffer solution, that's exactly what the BIOS already does. Alternatively, you can process each scan code in the ISR but that would make it *very* slow. If you return the EOI before finishing processing a scan code, you must then decide what to do if a scan code arrives in the middle of the processing routine. – Margaret Bloom Dec 13 '17 at 10:21
  • Masking the IRQ1 (or disabling it at the 8042 level) and correctly using polling may be the easier solution if you can afford a [busy wait loop](https://en.wikipedia.org/wiki/Busy_waiting). – Margaret Bloom Dec 13 '17 at 10:21
  • While you wrote you are not interested to create full interrupt, I have one example in previous answer to a question: https://stackoverflow.com/a/47115465/4271923 - IMO should be reasonably easy to adapt for you, as it's dealing exactly with 0/1 flags for particular key being pressed, for a game (the interrupt installation/restoration code is inside the question code, my answer has only the interrupt handler, so you will have to study it a bit and find out how to connect it everything together). – Ped7g Dec 13 '17 at 11:25

1 Answers1

1

To answer my own question, scancodes provided via port 60h were not actually too slow, they were too fast.

In the following example given in my question: "Whenever I press a key, let's say the 'W' key, then very quickly press another key, the break code for the 'W' key is still returned by port 60h."

I thought the reason port 60h still returned the break code for the 'W' was due to the keyboard controller not having enough time to register the fact that I had struck a new key.

Actually, I was pressing the other key first, and the make code for the key was indeed being returned. Then, a split second later, my finger lifted from the 'W' key overwriting the byte returned by port 60h with the 'W' break code.

The solution: Only honor break codes if they correspond with the make code already stored in bp.

New HANDLE_INPUT procedure, now working much better:

HANDLE_INPUT PROC

;CLEARS THE KEYBOARD TYPEHEAD BUFFER AND COLLECTS A SCANCODE 

;ALTERS BP

    push ax
    push bx
    push es

    mov ax, 40h                
    mov es, ax                  ;access keyboard data area via segment 40h
    mov WORD PTR es:[1ah], 1eh  ;set the kbd buff head to start of buff
    mov WORD PTR es:[1ch], 1eh  ;set the kbd buff tail to same as buff head
                                ;the keyboard typehead buffer is now cleared
    xor ah, ah
    in al, 60h                  ;al -> scancode
    test al, 80h                ;Is a break code in al?
    jz ACCEPT_KEY               ;If not, accept it. 
                                ;If so, check to see if it's the break code
                                ;that corresponds with the make code in bp.
    mov bx, bp                  ;bx -> make code   
    or bl, 80h                  ;change make code into it's break code  
    cmp bl, al                  ;Do the new and old break codes match?
    je ACCEPT_KEY               ;If so, accept the break code.
    pop es                      ;If not, bp retains old make code.
    pop bx
    pop ax
    ret

ACCEPT_KEY: 
    mov bp, ax                  ;bp -> scancode, accessible globally

    pop es
    pop bx
    pop ax
    ret


HANDLE_INPUT ENDP
bad
  • 939
  • 6
  • 18