Search code examples
assemblykeyboardx86-16bios

The controls of my game freeze after the first keypress with int 16h / ah=1


I am coding a game in assembly 8086. I fixed the issue when the game didn't open, but I can't fix the controls.

The ESC key works. When I press it, it goes to _QUIT function, but if any other key was pressed before that, the controls freeze and don't react on any key.

Is there something wrong with my function?

I tried to change the AL register to AH, but it didn't work.

_KEYCHECK:
        mov ah,01h
        int 16h

        cmp al,1Bh      ;ESC
        je _QUIT
        cmp al,48h      ;UP
        je _PLAYER.UP
        cmp al,50h      ;DOWN
        je _PLAYER.DOWN
        cmp al,4Bh      ;LEFT
        je _PLAYER.LEFT
        cmp al,4Dh      ;RIGHT
        je _PLAYER.RIGHT
        ret

Solution

  • Your _KEYCHECK function is using the BIOS.ReadKeyboardStatus function.

    It will inform you about the availability of a keyboard key by setting the ZeroFlag (ZF) if no key is available, or by clearing the ZeroFlag if a key is waiting. In the latter case you will also receive the key's ASCII code and scancode.
    The important thing here is that a key that is reported available stays in the keyboard buffer. The info that you get in AL and AH is just a preview not the actual key (in a sense). This explains your observation:

    ... but if any other key was pressed before that the controls frees ...

    The solution is to remove the key from the keyboard buffer. That's what BIOS.ReadKeyboardCharacter does. If a key is waiting it will return very quickly with the key removed from the buffer. If no key is available it will wait until one comes available and then return with that key removed from the buffer.

    _KEYCHECK:
        mov     ah, 01h     ; BIOS.ReadKeyboardStatus
        int     16h         ; -> AX ZF
        jz      NoKeyAvailable
        mov     ah, 00h     ; BIOS.ReadKeyboardCharacter
        int     16h         ; -> AX
    
        cmp     al, 1Bh      ;ESC
        je      _QUIT
        cmp     ah, 48h      ;UP
        je      _PLAYER.UP
        cmp     ah, 50h      ;DOWN
        je      _PLAYER.DOWN
        cmp     ah, 4Bh      ;LEFT
        je      _PLAYER.LEFT
        cmp     ah, 4Dh      ;RIGHT
        je      _PLAYER.RIGHT
    NoKeyAvailable:
        ret
    

    Please note:

    • You didn't actually inspect the ZeroFlag to find out if the info in AL and AH was valid.
    • The number 1Bh (ESC) is an ASCII code and has to be checked from AL but all the other codes 48h (UP), 50h (DOWN), 4Bh (LEFT), and 4Dh (RIGHT) are scancodes and thus have to be checked from AH.