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.