Search (using Google):  Web Karig

 

12 June 2004

Keyboard scan codes

Before I get started, I'd like to announce that you can now do a full-text search of this site via Google. Each page on this site has a search box at the top — just click the box, type in your keyword, and hit Enter. (I found the code to do this here.)

Ports 0x60 and 0x64

My latest project is an experiment in keyboard support. It simply prints a scan code to the screen when you press a key.

A scan code is a series of bytes (or, more typically, a single byte) that marks a keyboard event — that is, the pressing or releasing of a key. Whenever you press down on a key, and whenever you let up on the key (or "release" it), the keyboard responds by sending a signal to the keyboard controller inside the computer. The keyboard controller is connected to the system via the I/O ports 0x60 and 0x64. To see if there has been a keyboard event, you read a byte from port 0x64 and test whether the low bit, bit 0, is set. If the bit is set, that means there has been a keyboard event, and therefore there is data to be read from port 0x60.

		in	al, 0x64 ; Get the keyboard status.
		test	al, 1    ; Test bit zero.
		jz	no_event ; If the bit is clear, then no key
			         ;   has been pressed or released.

		; KEYBOARD EVENT. HANDLE IT HERE.

So you grab the first byte from port 0x60. What you do depends on what the byte is.

There are actually three sets of scan codes. On startup, the keyboard controller emits scan codes from Set 1. When reading Set 1 scan codes, you have to watch out for scan codes two or more bytes long (the first byte being either 0xE0 or 0xE1), so if AL contains a value 0xE0 or higher, you'd keep getting bytes from port 0x64 until there are no more. If the byte is less than 0xE0 but greater than 0x7F — in other words, if bit 7 is set — then the keyboard event is the release of a key, not a keypress.

The code

The code is in a zip file. The relevant lines of code are in BOOT.ASM, after the boot-sector code, in the section marked SYSTEM LOADER. The code simply reads a byte from port 0x60 whenever port 0x64 says there is a byte to be read. If you run the code, you'll see that there is data whenever you press a key.

I've been trying over the past several days to do something more sophisticated, namely to display all bytes of each scan code in a row whenever you press a key that generates a multibyte scan code, such as Print-Screen or Pause. However, I couldn't get the code to work that way, so I simplified the code. It now just dumps the latest byte into a given address and dumps the address, and as a result, what you see is the last byte of the scan code of the key you pressed, followed by fifteen null bytes.

The relevant code is here:

; ------ Wait for key event.

	.1:	in	al, 0x64
		test	al, 1
		jz	.1

; ------ Gather scan code byte.

	.2:	in	al, 0x60
		mov	[scanned], al

; ------ Dump scan code to screen.

		_lit	3 ; row
		_lit	scanned
		call	dump16
		_drop
		_drop
		call	refresh
		jmp	.1

The data is here. Scanned was originally going to be a buffer for multibyte scan codes.

msg:		db	"Hit a key to see its scancode below."
len:		equ	$-msg

scanned:	db	0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0

I'm sick of staring at this code, so I'm posting this page as is. I'll eventually work on more sophisticated keyboard code — code that will watch for bytes 0xE0 and 0xE1, which are the bytes that signal that a multibyte scan code is coming through and that the system needs to wait for the next one, three, or five bytes before determining what the key is. (That was the problem I was having. The keyboard acts as if it has a one-byte buffer and will dole out only one byte of a long scan code at a time, and I was trying to use the ports to determine when I had received all of the scan code, and this wasn't working for me.)

Time to decompress now.

Check the index for other entries.