Communication to the microcontroller in the keyboard unit is via the
bytes at I/O addresses 60h and 64h. Bits zero and one in the status byte
at port 64h provide the necessary handshaking control for these ports. Before
writing any data to these ports, bit zero of port 64h must be zero; data
is available for reading from port 60h when bit one of port 64h contains
a one. The keyboard enable and disable bits in the command byte (port 64h)
determine whether the keyboard is active and whether the keyboard will interrupt
the system when the user presses (or releases) a key, etc.
Bytes written to port 60h are sent to the keyboard microcontroller and bytes written to port 64h are sent to the on-board microcontroller. Bytes read from port 60h generally come from the keyboard, although you can program the on-board microcontroller to return certain values at this port, as well. The following tables lists the commands sent to the keyboard microcontroller and the values you can expect back. The following table lists the allowable commands you can write to port 64h:
|20||Transmit keyboard controller's command byte to system as a scan code at port 60h.|
|60||The next byte written to port 60h will be stored in the keyboard controller's command byte.|
|A4||Test if a password is installed (PS/2 only). Result comes back in port 60h. 0FAh means a password is installed, 0F1h means no password.|
|A5||Transmit password (PS/2 only). Starts receipt of password. The next sequence of scan codes written to port 60h, ending with a zero byte, are the new password.|
|A6||Password match. Characters from the keyboard are compared to password until a match occurs.|
|A7||Disable mouse device (PS/2 only). Identical to setting bit five of the command byte.|
|A8||Enable mouse device (PS/2 only). Identical to clearing bit five of the command byte.|
|A9||Test mouse device. Returns 0 if okay, 1 or 2 if there is a stuck clock, 3 or 4 if there is a stuck data line. Results come back in port 60h.|
|AA||Initiates self-test. Returns 55h in port 60h if successful.|
|AB||Keyboard interface test. Tests the keyboard interface. Returns 0 if okay, 1 or 2 if there is a stuck clock, 3 or 4 if there is a stuck data line. Results come back in port 60h.|
|AC||Diagnostic. Returns 16 bytes from the keyboard's microcontroller chip. Not available on PS/2 systems.|
|AD||Disable keyboard. Same operation as setting bit four of the command register.|
|AE||Enable keyboard. Same operation as clearing bit four of the command register.|
|C0||Read keyboard input port to port 60h. This input
port contains the following values:|
bit 7: Keyboard inhibit keyswitch (0 = inhibit, 1 = enabled).
bit 6: Display switch (0=color, 1=mono).
bit 5: Manufacturing jumper.
bit 4: System board RAM (always 1).
bits 0-3: undefined.
|C1||Copy input port (above) bits 0-3 to status bits 4-7. (PS/2 only)|
|C2||Copy input pot (above) bits 4-7 to status port bits 4-7. (PS/2 only).|
|D0||Copy microcontroller output port value to port 60h (see definition below).|
|D1||Write the next data
byte written to port 60h to the microcontroller output port. This port has
the following definition:|
bit 7: Keyboard data.
bit 6: Keyboard clock.
bit 5: Input buffer empty flag.
bit 4: Output buffer full flag.
bit 3: Undefined.
bit 2: Undefined.
bit 1: Gate A20 line.
bit 0: System reset (if zero).
Note: writing a zero to bit zero will reset the machine.
Writing a one to bit one combines address lines 19 and 20 on the PC's address bus.
|D2||Write keyboard buffer. The keyboard controller returns the next value sent to port 60h as though a keypress produced that value. (PS/2 only).|
|D3||Write mouse buffer. The keyboard controller returns the next value sent to port 60h as though a mouse operation produced that value. (PS/2 only).|
|D4||Writes the next data byte (60h) to the mouse (auxiliary) device. (PS/2 only).|
|E0||Read test inputs. Returns in port 60h the status of the keyboard serial lines. Bit zero contains the keyboard clock input, bit one contains the keyboard data input.|
|Fx||Pulse output port (see definition for D1). Bits 0-3 of the keyboard controller command byte are pulsed onto the output port. Resets the system if bit zero is a zero.|
The system transmits bytes written to I/O port 60h directly to the keyboard's
microcontroller. Bit zero of the status register must contain a zero before
writing any data to this port. The commands the keyboard recognizes are
|ED||Send LED bits. The next byte
written to port 60h updates the LEDs on the keyboard. The parameter (next)
bits 3-7: Must be zero.
bit 2: Capslock LED (1 = on, 0 = off).
bit 1: Numlock LED (1 = on, 0 = off).
bit 0: Scroll lock LED (1 = on, 0 = off).
|EE||Echo commands. Returns 0EEh in port 60h as a diagnostic aid.|
|F0||Select alternate scan
code set (PS/2 only). The next byte written to port 60h selects one of the
00: Report current scan code set in use (next value read from port 60h).
01: Select scan code set #1 (standard PC/AT scan code set).
02: Select scan code set #2.
03: Select scan code set #3.
|F2||Send two-byte keyboard ID code as the next two bytes read from port 60h (PS/2 only).|
Autorepeat delay and repeat rate. Next byte written to port 60h determines
bit 7: must be zero
bits 5,6: Delay. 00- 1/4 sec, 01- 1/2 sec, 10- 3/4 sec, 11- 1 sec.
bits 0-4: Repeat rate. 0- approx 30 chars/sec to 1Fh- approx 2 chars/sec.
|F5||Reset to power on condition and wait for enable command.|
|F6||Reset to power on condition and begin scanning keyboard.|
|F7||Make all keys autorepeat (PS/2 only).|
|F8||Set all keys to generate an up code and a down code (PS/2 only).|
|F9||Set all keys to generate an up code only (PS/2 only).|
|FA||Set all keys to autorepeat and generate up and down codes (PS/2 only).|
|FB||Set an individual key to autorepeat. Next byte contains the scan code of the desired key. (PS/2 only).|
|FC||Set an individual key to generate up and down codes. Next byte contains the scan code of the desired key. (PS/2 only).|
|FD||Set an individual key to generate only down codes. Next byte contains the scan code of the desired key. (PS/2 only).|
|FE||Resend last result. Use this command if there is an error receiving data.|
|FF||Reset keyboard to power on state and start the self-test.|
; LEDSHOW.ASM ; ; This short TSR creates a light show on the keyboard's LEDs. For space ; reasons, this code does not implement a multiplex handler nor can you ; remove this TSR once installed. See the chapter on resident programs ; for details on how to do this. ; ; cseg and EndResident must occur before the standard library segments! cseg segment para public 'code' cseg ends ; Marker segment, to find the end of the resident section. EndResident segment para public 'Resident' EndResident ends .xlist include stdlib.a includelib stdlib.lib .list byp equ <byte ptr> cseg segment para public 'code' assume cs:cseg, ds:cseg ; SetCmd- Sends the command byte in the AL register to the 8042 ; keyboard microcontroller chip (command register at ; port 64h). SetCmd proc near push cx push ax ;Save command value. cli ;Critical region, no ints now. ; Wait until the 8042 is done processing the current command. xor cx, cx ;Allow 65,536 times thru loop. Wait4Empty: in al, 64h ;Read keyboard status register. test al, 10b ;Input buffer full? loopnz Wait4Empty ;If so, wait until empty. ; Okay, send the command to the 8042: pop ax ;Retrieve command. out 64h, al sti ;Okay, ints can happen again. pop cx ret SetCmd endp ; SendCmd- The following routine sends a command or data byte to the ; keyboard data port (port 60h). SendCmd proc near push ds push bx push cx mov cx, 40h mov ds, cx mov bx, ax ;Save data byte mov al, 0ADh ;Disable kbd for now. call SetCmd cli ;Disable ints while accessing HW. ; Wait until the 8042 is done processing the current command. xor cx, cx ;Allow 65,536 times thru loop. Wait4Empty: in al, 64h ;Read keyboard status register. test al, 10b ;Input buffer full? loopnz Wait4Empty ;If so, wait until empty. ; Okay, send the data to port 60h mov al, bl out 60h, al mov al, 0AEh ;Reenable keyboard. call SetCmd sti ;Allow interrupts now. pop cx pop bx pop ds ret SendCmd endp ; SetLEDs- Writes the value in AL to the LEDs on the keyboard. ; Bits 0..2 correspond to scroll, num, and caps lock, ; respectively. SetLEDs proc near push ax push cx mov ah, al ;Save LED bits. mov al, 0EDh ;8042 set LEDs cmd. call SendCmd ;Send the command to 8042. mov al, ah ;Get parameter byte call SendCmd ;Send parameter to the 8042. pop cx pop ax ret SetLEDs endp ; MyInt1C- Every 1/4 seconds (every 4th call) this routine ; rotates the LEDs to produce an interesting light show. CallsPerIter equ 4 CallCnt byte CallsPerIter LEDIndex word LEDTable LEDTable byte 111b, 110b, 101b, 011b,111b, 110b, 101b, 011b byte 111b, 110b, 101b, 011b,111b, 110b, 101b, 011b byte 111b, 110b, 101b, 011b,111b, 110b, 101b, 011b byte 111b, 110b, 101b, 011b,111b, 110b, 101b, 011b byte 000b, 100b, 010b, 001b, 000b, 100b, 010b, 001b byte 000b, 100b, 010b, 001b, 000b, 100b, 010b, 001b byte 000b, 100b, 010b, 001b, 000b, 100b, 010b, 001b byte 000b, 100b, 010b, 001b, 000b, 100b, 010b, 001b byte 000b, 001b, 010b, 100b, 000b, 001b, 010b, 100b byte 000b, 001b, 010b, 100b, 000b, 001b, 010b, 100b byte 000b, 001b, 010b, 100b, 000b, 001b, 010b, 100b byte 000b, 001b, 010b, 100b, 000b, 001b, 010b, 100b byte 010b, 001b, 010b, 100b, 010b, 001b, 010b, 100b byte 010b, 001b, 010b, 100b, 010b, 001b, 010b, 100b byte 010b, 001b, 010b, 100b, 010b, 001b, 010b, 100b byte 010b, 001b, 010b, 100b, 010b, 001b, 010b, 100b byte 000b, 111b, 000b, 111b, 000b, 111b, 000b, 111b byte 000b, 111b, 000b, 111b, 000b, 111b, 000b, 111b byte 000b, 111b, 000b, 111b, 000b, 111b, 000b, 111b byte 000b, 111b, 000b, 111b, 000b, 111b, 000b, 111b TableEnd equ this byte OldInt1C dword ? MyInt1C proc far assume ds:cseg push ds push ax push bx mov ax, cs mov ds, ax dec CallCnt jne NotYet mov CallCnt, CallsPerIter ;Reset call count. mov bx, LEDIndex mov al, [bx] call SetLEDs inc bx cmp bx, offset TableEnd jne SetTbl lea bx, LEDTable SetTbl: mov LEDIndex, bx NotYet: pop bx pop ax pop ds jmp cs:OldInt1C MyInt1C endp Main proc mov ax, cseg mov ds, ax print byte "LED Light Show",cr,lf byte "Installing....",cr,lf,0 ; Patch into the INT 1Ch interrupt vector. Note that the ; statements above have made cseg the current data segment, ; so we can store the old INT 1Ch values directly into ; the OldInt1C variable. cli ;Turn off interrupts! mov ax, 0 mov es, ax mov ax, es:[1Ch*4] mov word ptr OldInt1C, ax mov ax, es:[1Ch*4 + 2] mov word ptr OldInt1C+2, ax mov es:[1Ch*4], offset MyInt1C mov es:[1Ch*4+2], cs sti ;Okay, ints back on. ; We're hooked up, the only thing that remains is to terminate and ; stay resident. print byte "Installed.",cr,lf,0 mov ah, 62h ;Get this program's PSP int 21h ; value. mov dx, EndResident ;Compute size of program. sub dx, bx mov ax, 3100h ;DOS TSR command. int 21h Main endp cseg ends sseg segment para stack 'stack' stk db 1024 dup ("stack ") sseg ends zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end MainThe keyboard microcontroller also sends data to the on-board microcontroller for processing and release to the system through port 60h. Most of these values are key press scan codes (up or down codes), but the keyboard transmits several other values as well. A well designed keyboard interrupt service routine should be able to handle (or at least ignore) the non-scan code values. Any particular, any program that sends commands to the keyboard needs to be able to handle the resend and acknowledge commands that the keyboard microcontroller returns in port 60h. The keyboard microcontroller sends the following values to the system:
|00||Data overrun. System sends a zero byte as the last value when the keyboard controller's internal buffer overflows.|
|Scan codes for key presses. The positive values are down codes, the negative values (H.O. bit set) are up codes.|
|83AB||Keyboard ID code returned in response to the F2 command (PS/2 only).|
|AA||Returned during basic assurance test after reset. Also the up code for the left shift key.|
|EE||Returned by the ECHO command.|
|F0||Prefix to certain up codes (N/A on PS/2).|
|FA||Keyboard acknowledge to keyboard commands other than resend or ECHO.|
|FC||Basic assurance test failed (PS/2 only).|
|FD||Diagnostic failure (not available on PS/2).|
|FE||Resend. Keyboard requests the system to resend the last command.|
|FF||Key error (PS/2 only).|