Saturday, April 20, 2024 | Toby Opferman
 

Keyboard Programming

Toby Opferman
http://www.opferman.net
programming@opferman.net



                           Using The Keyboard

        Using the keyboard is the MOST common input for the PC and
other computers.  Although, even with GUI's as the mouse is slowly
replacing the keyboard you still need the keyboard to type (Unless
you get Voice Recognition).  It seems trivial to have a tutorial
on something as easy to use as the keyboard, but this is not just
about calling 'getch()' or  'scanf'.  I will explain how to use
the keyboard more effiently.


        There are a number of ways to get input from the keyboard
in DOS and we will go over a few of them.  One of them is
INT 16h - the BIOS Keyboard interrupt.

XOR AH, AH
INT 16h     ; This is Equal to getch() in C

This will wait for the user to type in a single key.
and return it in AX.  AH = ScanCode, AL = ASCII Code

MOV AH, 1
INT 16h     ; This is the same as kbhit() in C
JZ NO_KEY

This will set the 0 flag if no key is in the buffer,
and if there is one, it will return the key in AX same
as Function 00h in the first example, EXCPT, it will NOT
remove it from the keyboard buffer.  You MUST call a
function such as the first to remove it from the buffer, or
else your buffer will reach it's maximum fill and the keyboard
will just beep.

You may want to use DOS to read in characters.  DOS's interrupt is
INT 21h.

MOV AH, 1
INT 21h

AL = Character Read

And there are a few others that you can look up if you
are interested but they all read a character in.  Some read with echo
and some read with out echo.  Echo means the character read in is displayed
on the screen at the same time, like getche() in C.

But, one of the more useful functions of INT 21h is
MOV AH, 0Ch
INT 21h

Clear Keyboar Buffer.

If your buffer gets full, just call this interrupt to clear it. OR, clear
the keyboard buffer manually as we will see how to do a little bit later in
this tutor.


Another way is to read the keyboard ports.
Ports 60h-64h


The BEST thing you could possibly do with the ports is hook interrupt 9h.
Everytime a key is pressed, INT 9h is called.  Everytime a key is
release INT 9h is called.  INT 9h is the software for the hardware interrupt
of the keyboard.

KeyList db 128 DUP(0)
The Above creates an array of 128 bytes that will represent the
Key Scan Codes

KeyList[ScanCode] Will get you the status of the key (C Syntax)
0 = Not Pressed
1 = Pressed

Temp  dw 0
Press db 0

INT9HANDLER PROC
    PUSHA          ; Save Registers & Enable Interrupts
    STI
    IN AL, 0x60    ; Get Key
    XOR AH, AH
    MOV Temp, AX   ; Put Scan Code in Temp

    IN AL, 0x61
    OR AL, 0x82    ; Set Bits to Reset KB Flip Flop
    OUT 0x61, AL

    AND AL, 0x7F   ; Finish the Reset
    OUT 0x61, AL

    MOV AL, 0x20   ; Tell PIC Enable Interrupts
    OUT 0x20, AL

    XOR AL, AL
    MOV Press, AL  ; Set Press = 0, Meaning a Unpress Code

    CMP Temp, 128
    JL  SHORT UnpressCode

    INC AX
    MOV Press, AL  ; Set Press = 1, Meaning a Press Code
    JMP DONE

UnpressCode:
    MOV AX, 128
    SUB Temp, AX   ; Set Temp to ScanCode < 128

DONE:
    MOV BX, Temp   ; Get Index to ScanCode Array

    MOV SI, Offset KeyList
    MOV DS:[SI+BX], Press  ; Set Key To Press or UnPress

    POPA
    IRET     ; End Interrupt Handler
ENDP INT9HANDLER


The Above Interrupt will service keyboard presses.  It will set
a list of keys to Pressed (1) or Not Pressed (0) According to their
scan code.  This is one of the fastest, if not the fastest way
to detect keys on the keyboard.  As soon as they are pressed, this
ISR is serviced.  In your code, all you have to do is poll your hot keys
with a simple test or cmp and continue processing.  No waiting,
no lagtime interrupts, just the check of a memory location. Remeber,
the scan codes are raw and do not apply to any shift state.  You
will have to read the Keyboard Status Flags to find out the shift state
of a key.

Flag1   0040:0017h
BIT      Meaning If Set to 1
7        INSert Active
6        Caps Lock Active
5        Num Lock Active
4        Scroll Lock Active
3        Either ALT Pressed
2        Either CNTRL Pressed
1        Left Shift Pressed
0        Right Shift Pressed

Flag2   0040:0018h
BIT      Meaning If Set to 1
7        INSert Pressed
6        Caps Lock Pressed
5        Num Lock Pressed
4        Scroll Lock Pressed
3        Pause State Active
2        Sys Req Pressed
1        Left Alt Pressed
0        Left Cntrl Pressed

To Get Both Bytes:

 MOV AX, 40h    ; BIOS Segment
 PUSH DS        ; Save Data Seg
 MOV DS, AX     ; Set BIOS Segment
 MOV DI, 17h    ; Offset 17h & 18h
 MOV AX, [DI]   ; Read Word
 POP DS         ; Restore



And finally, how to manipulate the keyboard buffer manually.
The Keyboard buffer is at Segment 0040h
The Start of the buffer is at 80h, 1Ah is pointing to the First Character
(Or Next Character)
in the buffer and 1Ch is pointing to the next free slot.
To Clear the buffer do the following
The Keyboard buffer is at 1Eh (16 Bytes Circular) Standard, but
you can change that by changing the values in the keyboard pointers at
80h, 81h, 1Ah and 1Ch.

To Clear the Keyboard Buffer, just do this:

 .186
 PUSH 40h
 POP DS        ; Set Segment
 MOV DI, 80h
 MOV BX, [DI]  ; Get First Memory Location
 MOV DI, 1Ah
 MOV [DI], BX  ; Set 1Ah To First Location
 MOV DI, 1Ch
 MOV [DI], BX  ; Set 1Ch To First Location


After all is well, you can just check the value of 1ah vs 1ch.   Once
the values are differnt, get the value at 1ah and get the value pointed
to by that value and you have the ascii value.  Then just set 1ch and 1ah
equal to each other again using 80h or setting 1ch equal to 1ah.
Here is an example:

GETKEY:
  MOV SI, 1Ah   ; Pointer to first/next key
  MOV DI, 1Ch   ; Pointer to next free locatoin
  MOV BX, [SI]  ; Get Address

  CMP BX, [DI]  ; Compare Values
  JE SHORT GETKEY  ; If Equal, no keys

                 ; Key In Buffer
  MOV AL, [BX]   ; Get key
  MOV [DI], BX   ; Clear buffer
  JMP SHORT GETKEY  ; Do it Again

Even though the above is an endless loop, you get the idea.


NOTE: For more information on any of the interrupts/ports used in this Tutor,
get an interrupt/port list such as Ralf Brown's Interrupt List off the web
or buy a book.
 
About Toby Opferman

Professional software engineer with over 15 years...

Learn more »
Codeproject Articles

Programming related articles...

Articles »
Resume

Resume »
Contact

Email: codeproject(at)opferman(dot)com