Recently for the Fruitjam-286 emulator, I needed to create a good mapping from USB HID codes to XT codes.
There are many resources online including Keyboard Scancodes (Andries Brouwer) which is a nice HTML table.
I converted the table into a csv file, and then used a bespoke Python program to convert it to a format for the emulator.
First, my program opens the csv file and checks its structure
import csv
with open("keymaps.csv") as f:
rows = [[el.strip() for el in row] for row in csv.reader(f)]
header = rows[0]
del rows[0]
assert header[1] == "USB"
assert header[2] == "Set 1"
assert header[-1] == "keycap"
It defines a function to convert an XT keycode sequence like (hex) 49 or (hex) e0-1c into a C string for the "make" sequence and a second C string for a "break" sequence (the two always differ by setting the 0x80 bit of the last byte, or at least I assumed so).
dq = "\""
def tohexstr(dashed):
hexes = [int(x, 16) for x in dashed.split("-")]
s1 = dq + "".join(f"\\x{x:02x}" for x in hexes) + dq
hexes[-1] |= 0x80
s2 = dq + "".join(f"\\x{x:02x}" for x in hexes) + dq
return f"{{ {s1:<16}, {s2:<16} }}"
Then I just have to print some declarations and then output one line of C code for each row in the table with an associated USB keycode. There's even an untested special-case for the Pause key, which per documentation emits a 6 byte press sequence and no release sequence.
print("typedef struct { const char *press, const char *release } usb_to_xt;")
print("static const usb_to_xt conversion[] = {")
for row in rows:
u = int(row[1])
if u == 0: continue
x = tohexstr(row[2])
c = row[-1]
if c == "Pause":
x = '{ "\\xe1\\x1d\\x45\\xe1\\x9d\\xc5", "" }'
print(f" [{u:3d}] = {x}, // {c}")
print("};")
This same table could be useful as one part of a variety of converters, such as XT->USB, USB->PS/2, etc.
The resulting table looks something like this. I just pasted it into my source file and made some manual edits there. A better person would have put the csv file & python script under revision control, and arranged to build a C header from the Python script as a part of the build process:
typedef struct { const char *d[2]; } usb_to_xt;
static const usb_to_xt conversion[] = {
[ 53] = { "\x29" , "\xa9" }, // ` ~
[ 30] = { "\x02" , "\x82" }, // 1 !
[ 31] = { "\x03" , "\x83" }, // 2 @
[ 32] = { "\x04" , "\x84" }, // 3 #
[ 33] = { "\x05" , "\x85" }, // 4 $
[ 34] = { "\x06" , "\x86" }, // 5 % E
[ 35] = { "\x07" , "\x87" }, // 6 ^
[ 36] = { "\x08" , "\x88" }, // 7 &
[ 37] = { "\x09" , "\x89" }, // 8 *
[ 38] = { "\x0a" , "\x8a" }, // 9 (
[ 39] = { "\x0b" , "\x8b" }, // 0 )
[ 45] = { "\x0c" , "\x8c" }, // - _
[ 46] = { "\x0d" , "\x8d" }, // = +
[ 42] = { "\x0e" , "\x8e" }, // Backspace
This page (USB, XT, PS2 keycode cross reference in CSV) was last updated on October 18, 2025.