Search code examples
assemblydosx86-16

Check for keypress without blocking if no key was pressed


I'm creating an application to view the current time and I created a loop to update the time each second. The loop looks like this

UPDATE:
;The code to be re-executed
JMP UPDATE

but I cant end it. When I use

MOV AH, 00H
INT 21H

to capture any pressed key, the application stops and waits for a keypress. I don't want that to happen; the application should go normally and end the loop when the user hits escape

What is the best way to do this?


Solution

  • Whenever during DOS programming you are looking for some service, have a look at Ralf Brown's interrupt list.

    Particularly, keyboard related services fall under the int 16h category.
    Int 16/AH=01h is CHECK FOR KEYSTROKE which is exactly what you need: after it returns, the ZF1 is set2 if no keystroke was available; also AL and AH contain the ASCII and scan code of the key pressed.

    Alone however it is not enough as it doesn't remove the keystroke from the buffer, so if the user presses ABESC, using CHECK FOR KEYSTROKE alone would always return that A is available.
    You can use Int 16/AH=00h, GET KEYSTROKE to read and remove a keystroke from the buffer without echoing it.

    You can also use Int 21/AH=01h to read a character and echo it3, note that despite what is stated in your question, Int 21/AH=00h is totally unrelated to this task as it is TERMINATE PROGRAM.

    Finally the ASCII code for ESC is 27 or 1bh.


    Here a sample COM program that loops until ESC is pressed.

    BITS 16
    ORG 100h
    
    _loop:
    
     ;
     ; L O O P   S T U F F
     ;
    
     ;Show a greeting message
    
     mov ah, 09h
     mov dx, strGreetings
     int 21h
    
    
    
     ;
     ; K E Y S   C H E C K
     ;
    
    
     ;Check for a keystroke
    
     mov ah, 01h
     int 16h
    jz _loop                               ;ZF is set if no keystroke available
    
     ;A keystroke is present, remove it from the buffer
     ;so that we always check the last key pressed by the user
    
     xor ah, ah
     int 16h
    
     ;AL = ASCII code     
     ;AH = Scancode
    
     ;Check the key was ESC
    
     cmp al, ESC_ASCII_CODE 
    jne _loop
    
     ;
     ; T E R M I N A T I O N
     ;
    
     mov ax, 4c00h
     int 21h
    
     ;[ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ]
     ;   [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ]
     ;[ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ]
     ;
     ;D A T A
     ;
    
     strGreetings db "Hello!", 13, 10, 24h
    
     ;[ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ]
     ;   [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ]
     ;[ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ] [ o ]
     ;
     ;E Q U A L S
     ;
    
     ESC_ASCII_CODE EQU 27
    

    1 The zero flag, you can jump depending on it with jz/je (jump if it is set) or jnz/jne (jump if it is clear).
    2 Think of this as: Zero was set because there was zero keystrokes.
    3 Though I believe that's not really appropriate for non printable chars.