programmable keyboard interceptor and hardware control system

Keyman64 Rev.4

The keyman64 is a programmable keyboard interceptor and hardware control system for computers equipped with a simple 64-key matrix keyboard.

It is installed between the keyboard and the computer, continually scanning the keyboard matrix and relaying the keyboard state to the computer using a crosspoint switch. To the computer, the crosspoint switch matrix looks just like a physical keyboard, while the keyman64 gains the ability to intercept keystrokes and control the matrix seen by the computer.

The keyman64 can be configured to intercept special key combinations and invoke arbitrary sequences of commands to alter the state of sixteen general purpose control lines provided on the board. These lines can be used to control additional hardware instead of using physical buttons or switches.

Additional features include the ability to send predefined keyboard macros or to redefine the keyboard layout. Commands can also be sent from a remote PC via USB, or send on simple serial wire interface, allowing remote control from either a PC or another microcontroller or similar device.

For some concrete examples of what the keyman64 can do, see the configuration examples.


Basic mode of operation

During normal operation, the device simply relays all keyboard events to the computer, except when a special key defined as the meta key is held down. As long as this key is held down, any additional key presses invoke the user-defined command sequences bound to the respective key. When the meta key is released again, the device continues to relay keyboard events to the computer.

If no special key has been pressed while the meta key was down, a press of the meta key itself is simulated on the matrix seen by the computer. This way the meta key is not lost, it can still be pressed as usual in order to type the associated character. The only difference is that the computer will notice the simulated key down and up events only after the physical key has been released.

This scheme works well if the meta key only needs to be pressed once, but not if the meta key is supposed to be held down for a longer period of time. But this can still be accomplished by using a key combination invoking the down command for the meta key itself, for example:


Assuming that the arrow-left key is used as the meta key, this binding can be invoked by holding down arrow-left and pressing m. The virtual arrow-left key now appears held down to the computer as long as the physical meta key is kept down. Once the physical meta key is released, the device beings scanning and relaying the physical keyboard state again, effectively releasing the virtual meta key seen by the computer.

The meta key defaults to the arrow-left key. To change it to a different key, use the meta command described below.


A simple commandline configuration and control utility is provided. Its main purpose is to convert a plain text configuration file into a binary format. The binary configuration file is flashed to the Atmegas EEPROM Memory. See Configuration for details.

In Addition, the utility allows remote control of the keyman64 via USB. Arbitrary commands can be send via USB and will be immediately executed on the keyman64.

Hardware design

The device is based on an Atmega1284p, a crosspoint switch IC and some discrete 74xx logic ICs. Supported crosspoint switches are the CD74HC22106 or the MT8808 in either DIP or PLCC package. A USB socket is provided to allow easy configuration and firmware updates using a USB bootloader preinstalled on the Atmega. Plese see design documents in the source distribution and the relevant sections in this document for further details.


In theory, the device should work with any computer using a passive 8x8 matrix keyboard. However, the layout of the connectors and the keynames follow those of the C64. Also the type command will use key combinations specific to the C64 to type the ASCII characters given as the argument. If you need software support for a different homecomputer, please feel free to post a feature request on the github issue tracker, preferably providing the required information.




The source distribution includes the sources for the firmware and the configuration utility as well as schematics and pcb layouts in KiCad format for building the hardware.

Latest stable is keyman64-1.6.tar.gz.

All releases can be found under /download/keyman64

Latest developments are available via github:

git clone

The Changelog lists all changes in detail.




Revision 3

The USB level conversion circuit is not correct in this and all previous revisions. This may result in missing USB functionality. It obviously depends on the specific type of 3.6V zener diodes used. While the diodes I have shipped with revisions 1 and 2 seem to have worked (more by accident than by design), the ones shipped with revision 3 boards have failed to work properly, so I have only discovered this error after shipping revision 3.

In order to fix an already assembled board or when assembling the board, the diodes have to be soldered in like this:

As you can see, the cathodes have to go directly to the D- and D+ lines from the USB socket. You can solder them directly to the leads of the 68Ω resistors, given that you installed them as suggested by the silkscreen.

This error will be corrected with the upcoming revision 4. All pending orders will of course include the corrected revision.

Revision 2

The negative voltage generator ICL7660 is not required, although it does not hurt to leave it in. It has been removed in favor of an alternative PLCC socket in Revision 3.

Also, the mounting holes added in this revision are 3mm in diameter, which makes them unsuitable for mounting using common self-adhesive pcb holders, which require at least 4mm holes. The holes have been changed to 4mm in Revision 3.

Ordering Assembly Kits

I’m offering assembly kits including the keyman64 pcb, the preprogrammed Atmega and all required components (except connection cables) for 30€ each. Worldwide shipping via airmail is free of charge.

You can order kits via email at

To place an order, please include the word “keyman64” in your subject. State your full name, the number of kits you want and your complete international shipping address. You will then receive an email containing payment information (bank transfer only).

Please note that you will have to pay in advance to confirm your order. Since I’m doing all of this in my spare time and on a short budget, I can’t afford to keep complete assembly kits on stock. I need to buy them in multiples of 25 in order to keep the price. This means that it could take quite some time (depending on demand) until you get your kits. However, I will inform you about the status of your order in regular intervals.

Building the hardware

Please refer to the list of parts and the placement on board below below.

Power supply

+5V must be supplied to the center pin of J1. You can either supply +5V from the computers main board directly to this pin, or use a jumper bridge to supply power from pin 4 of P1 (the mainboard keyboard socket) or from the USB socket. To supply power from the mainboard socket, bridge the left and center pin of J1. To supply power from USB, bridge the center and right pin of J1.

Supplying power from the USB socket is only recommended for standalone operation of the keyman64, i.e. when P1 it is not connected to the io ports of a computer and none of the external devices connected to the keyman64 are powered from a different source. In general, all devices involved (the computer, the keyman64 and the devices controlled by it) must be powered from the same source. For example, if the keyman64 was configured to source power over USB and the computer is powered on without the USB cable being connected, the keyman64 might still sink current over the ESD-protection diodes of the Atmels io ports from the io ports of the computer or the external devices, which might cause severe damage to any of the io ports involved.


The pin layouts of P1 and P2 follow the layout of the C64 keyboard connector. The RESTORE line is simply passed through on the board.

List of parts

Reference Type Value Package/RM
C1 Ceramic capacitor 18pF 2.5mm
C2 Ceramic capacitor 18pF 2.5mm
C6 Ceramic/film capacitor 100nF 2.5mm
C7 Ceramic/film capacitor 100nF 2.5mm
C8 Ceramic/film capacitor 100nF 2.5mm
C9 Ceramic/film capacitor 100nF 2.5mm
C10 Ceramic/film capacitor 100nF 2.5mm
C11 Ceramic/film capacitor 100nF 2.5mm
D1 Zener Diode 3.6V DO-204
D2 Zener Diode 3.6V DO-204
R1 Precision Resistor 1k5 6.5mm, ∅ 2.5mm
R2 Precision Resistor 68 6.5mm, ∅ 2.5mm
R3 Precision Resistor 68 6.5mm, ∅ 2.5mm
P3 USB Mini-B Socket - Through-Hole
SW1 Push Button - 6x6mm print
SW2 Push Button - 6x6mm print
X1 Quartz Crystal 16Mhz HC49/U-S
U1 74HC22106 or MT8808 - DIP28
U2 Atmega1284P - DIP40
U3 74HC00 - DIP14
U4 74HC4051 - DIP16
U5 74HC4520 - DIP16
U6 74HC4051 - DIP16
U7 74HC22106 or MT8808 - PLCC28

For the crosspoint switch you can either use U1 for a DIP28 package or U7 for a PLCC28 package version.

Sockets not included.

Placement on board

Installing the firmware

If you bought an assembly kit from me, the Atmega is already programmed with the latest firmware and bootloader versions and is ready to use.

Otherwise, if you have no means of initially programming the Atmega yourself then you can send it to me and I will prepare it for you. Just drop me a line at

Installing the combined firmware image

The easiest way to bootstrap the Atmega is to install the combined binary image. The image contains both the bootloader and the application part of the firmware. If you have a programming device capable of programming the complete PROGMEM area from a single binary image or intel hex file (e.g. the TL866), use this method.

Note that you also have to check and eventually program the fuses of the Atmega as well. The required fuse values are:

    Low:      0xd7
    High:     0xd0
    Extended: 0xfc

This corresponds to the following fuses programmed: SUT1, CKSEL3, SPIEN, EESAVE, BOOTSZ0, BOOTSZ1, BOOTRST, BODLEVEL0, BODLEVEL1. All other fuses must remain unprogrammed.

Installing the bootloader manually

The Atmega1284p must contain the USBaspLoader, which must be uploaded to the Atmega via ISP prior to installing it on the board.

There is a preconfigured version of the bootloader in the source tarball.

Change to the bootloader directory and edit the and adjust the PROGRAMMER settings to according to your setup. Then issue

 $ make flash fuse

The Atmega can now be installed on the device. When entering the bootloader, it identifies itself to the host like this:

16c0:05dc Van Ooijen Technische Informatica shared ID for use with libusb

It can be accessed using avrdude -p m1284p -c usbasp <commands...>

Note that you may need to configure the USB devices on your system beforehand.

Entering the bootloader

To manually enter the bootloader, hold down the BOOT Button and press the RESET button, then release the BOOT button again.

Once the device is installed you can configure a key combination using the boot command. For example, by using

b: boot

you can enter the bootloader simply by pressing <meta>-b.

You can also just send the boot command via USB from the PC:

$ keyman64 boot

Updating the bootloader

Eventually firmware updates will require an update of the bootloader already installed on the device.

For this purpose, an updating application is provided in keyman64-bootloader-updater-1.6.hex.

To upgrade, connect and power on the keyman64 and enter the existing bootloader, then run

$ avrdude -p m1284p -c usbasp -U flash:w:keyman64-bootloader-updater-1.6.hex

This will overwrite the existing application code (i.e. the keyman64 application), run the updater application and thus update the bootloader.

After this, manually enter the bootloader and proceed by reinstalling the keyman64 application as described in the following section.

Installing and updating the firmware

Once the Atmega is equipped with a USB bootloader the application part of the firmware can be installed with the following command:

$ avrdude -p m1284p -c usbasp -U flash:w:keyman64-firmware-1.6.hex

If you have build the firmware from source you can use the program target of the toplevel Makefile as well.

Since version 1.4 the firmware can also be updated using the update command:

$ keyman64 update keyman64-firmware-<version>.bin

This will automatically enter the bootloader and update the firmware.

Since version 1.5 the update command also accepts firmware images in Intel HEX format. The file extensions .bin and .hex are recognized and determine the respective file format.

Since version 1.6 the update command accepts a configuration file as an optional second argument. If a configuration file is specified, the update command will first disable the existing configuration on the device, then update the firmware and finally recreate and flash the configuration from the specified file to the device. This is required for updates which change the binary configuration format. If this procedure is necessary, it will be explicitly stated in the release notes and Changelog.

Configuring USB devices on the PC

The keyman64 implements two separate USB devices, a remote control device and a bootloader device.

During normal operation, the remote control device will be active and accessible from the PC using the configuration and control utility. This device will identify itself to the host using the following properties:

Vendor OpenMoko, Inc.
Manufacturer Henning Bekel
Device Keyman64
Vendor ID 1d50
Product ID 60e9

While in bootloader mode, the bootloader device will be accessible from the PC. This device will identify itself to the host using the following properties:

Vendor Van Ooijen Technische Informatica shared ID for use with libusb
Device USBasp
Vendor ID 16c0
Product ID 05dc


On Linux, the required udev rules are installed alongside the configuration and control utility. After installation, issue

# udevadm control --reload-rules

When in bootloader mode, the bootloader device will be symlinked to /dev/usbasp. During normal operation, the keyman64 control device will be symlinked to /dev/keyman64. These symlinks are created with file permissions 0666, allowing access for any user.


Once you connect one of the usb devices for the first time, Windows will insist on trying to download and install a driver for these devices. Since both devices are general purpose USB devices, this is futile, since there simply are no drivers for windows to install. You must abort the windows driver installation dialog to prevent windows from permanently marking the device as unusable due to its perceived lack of “proper” drivers.

Instead, use the Zadig tool to generate and install minimal drivers that simply associate the devices with the subsystems required for general purpose access.

Run Zadig, connect the usb cable to the keyman64 and power up the device. Zadig should now detect the Keyman64 device. Install the WinUSB driver for this device.

Now enter the bootloader. The Zadig tool should now detect the USBasp device. For use with avrdude, install the libusb-win32 driver for this device. Also make sure that the libusb0.dll is present on your system (it should come with avrdude). If not, follow the link for libusb-win32 shown in the “More information” section of the Zadig window and install libusb-win32 on your system.

Installing the configuration and control utility


For the windows version, use the binary installer package. This will also install the required libusb-1.0.dll. The installation directory will be added to your PATH environment variable.


Linux & MacOSX

libusb-1.0 and corresponding development packages (if any) need to be installed on your system.

Extract the tarball, change to the source directory and type make.

Use make install to install the keyman64 binary into /usr/local/bin. The PREFIX variable can be used to install with a different prefix, e.g. use make PREFIX=/usr install to install into /usr/bin instead. The DESTDIR variable can be used for a staged install.

On linux, the udev rules required for the USB devices will be installed to /etc/udev/rules.d as well. As root, run

# udevadm control --reload-rules

after installation.


If you’re using cygwin you can build a windows binary in the same way as under Linux or MacOSX. The required packages are libusb-1.0 and libusb-1.0-devel. Note that the resulting binary will still need cygwin dlls to be present.

A native win32 binary can be build using mingw32 under Linux or Cygwin. If necessary, edit the Makefile and adjust the MINGW32 variable to the proper prefix for your toolchain. Then use make win32 to build keyman64.exe.


The configuration file is a plain text file containing configuration commands and keybindings in a human readable format. Using the keyman64 utility this file is converted to a binary format which can be flashed into the EEPROM memory of the Microcontroller. See Transferring the configuration.


Whitespace and comments

Empty lines as well as leading and trailing whitespace characters are ignored. Whitespace is only significant to separate individual tokens and keywords.

Comments begin with a hash character # and continue for the remainder of the current line.


All keywords, like command and key names, as well as user defined symbols are case-insensitive. E.g. CLEAR is equivalent to clear, SPACE is equivalent to space and so on.

Commands and bindings

Each line must contain one command specification, optionally prefixed by a key specification:

[<key>:][<policy>] <command> [<arguments>]

If no key specification is given, the command will be executed on powerup/reset of the microcontroller, and can thus be used to configure the initial state of the device.

If a key specification is given, the command will be bound to the specified key.

If multiple commands are bound to the same key, the commands will be executed in sequence, in the order in which they appear in the configuration file.

Command sequences can also be bound to “slots” by using numeric key specifications between $40 and $F9. These command sequences can not be invoked directly by a key combination, but may be invoked indirectly via the exec command.

The optional policy argument can be set to either 0 or 1, meaning even or odd. If even, the command will only be executed on the second, fourth, sixth etc. time the key is pressed. If odd, the command will only be executed on the first, third, fifth etc. time the key is pressed.


Keys can be specified by using literal key names like DEL, A, RUNSTOP or ONE. To obtain a list of valid key names and their synonyms, run keyman64 --keys.

Keys may also be specified by number, denoting the key position in the C64 keyboard matrix. Keys are numbered from left to right, from row 0, column 0 down to row 7, column 7.

Thus $00 corresponds to the DEL key, $3f denotes the RUNSTOP key.

Key numbers may be given in decimal or hexadecimal notation. Hexadecimal numbers must be prefixed with a dollar sign $ or the literal string 0x.


The sixteen control lines are organized into two 8-bit wide ports. For commands modifying the state of these control lines, the respective port can be specified using the keyword port, followed by the literal string a or b:

port [a|b]


Commands modifying the state of the control lines can be limited to individual bits or ranges of successive bits. Bits are denoted by a decimal number in the range 0-7.

bit <n>

bits <s>-<e>

Where n denotes an individual and s and e denote the start and end bit of a bitrange (inclusive).

Short notation for ports and bits

As of version 1.6 control lines and ranges of control lines can be specified with a short notation combining the existing notations for ports and bits:


Thus expressions like port a bit 3 or port b bits 0-2 can be shortened to a3 or b0-2 respectively.


Durations can be specified by a series of decimal values and unit specifiers:


Available unit specifiers are d, h, m, s, ms for days, hours, minutes, seconds and milliseconds respectively. If no unit specifier is given, the preceding value is interpreted as milliseconds.

The maximum possible duration is 232 milliseconds, about 49 days. Specifications that exceed this value will be truncated accordingly.


1m30s = one minute and thirty seconds

1s500 = one second and fivehundred milliseconds

10 = ten milliseconds


Values can be specified in hexadecimal or binary notation. Hexadecimal values need to be prefixed with $. Binary values need to be prefixed with %


Custom symbols can improve the readability of the configuration file. They are defined using simple name/value pairs.

<name> = <value>

Symbol names must consist of alphanumeric characters, digits and underscores only. If a symbol name equals one of the reserved key or command names, a corresponding error message will be issued.

Once a symbol has been defined, any subsequent occurrence is replaced by its literal value. If a symbol is used before it is defined, an error message will be generated.

For example, this configuration

    r: clear port a bit 0
    r: sleep 10
    r: tristate port a bit 0

can be made easier to read and change:

    RESETLINE = port a bit 0
    RESET = r
    RESET: sleep 10
    RESET: tristate RESETLINE

Commands for configuration and maintenance


using <8808|22106>

Specify which crosspoint switch IC is installed in the device. Either a CD74HC22106 or an MT8808 may be used. Default is 8808.


speed <fast|slow>

Specify the speed at which the keyboard matrix is scanned. Fast mode is the default and should work well in most setups. Slow mode is required when the length of the cable is significantly longer than the stock keyboard cable, e.g. when installing the device in a Commodore SX64.

Note that selecting slow mode will not impact the responsiveness of the keyboard in any way. Both modes are still quite fast in scanning the keyboard.


expand ports=<n> clock=<line> data=<line> latch=<line> enable=<line>

Configures a port expansion of <n> daisy chained 74595 serial shift registers, where the CLOCK, DATA, LATCH and ENABLE lines of the first shift register in the chain are connected to the respective native port lines.

Note that in this case the port lines need to be specified in short notation.

See port expansion for details.


meta <key>

The meta command defines the meta key to be used. Default is ARROWLEFT.



Enter the bootloader and expect configuration or firmware updates via USB. The device will remain bootloader mode until a new configuration or firmware has been programmed or the reset button has been pressed.

When sending the boot command via the remote USB interface, e.g. by executing keyman64 boot on the PC, the firmware will immediately jump into the bootloader without properly terminating the current USB communication properly, which will result in the following non-critical error message:

error: could send usb control message: Pipe error

This message can be ignored as long as the AVR has successfully entered the bootloader.



Permanently saves the current state of the control lines to eeprom. This state can be restored later using the restore command.



Restore the state of the control lines that were stored in eeprom by a preceding save command. If no state has been saved before, all lines are set to tristate.



Temporarily memorize the current state of the control lines to RAM. This state can be restored later using the recall command.



Restore the state of the control lines that was stored in ram by a preceding memorize command. If no state has been memorized before, all lines are set to tristate.

Commands for modifying control lines


set <port> [<bits>] [to <value>]

Sets the specified bits of the specified port to the specified value. If value is omitted, it defaults to setting all bits to 1 (high). If bits are omitted, sets all bits of the specified port.


set port a bit 3 to 1 – sets bit 3 of port a to high

set port b bits 3-4 to 2 – sets bit 3 to low and bit 4 to high on port b

set port a – sets bits 0-7 to high


clear <port> [<bits>]

Clears the specified bits of the specified ports, i.e. sets them to 0 (low). If bits are omitted, clears all bits of the specified port.


tristate <port> [<bits>]

Tristates the specified bits on the specified port. If bits are omitted, tristates all bits on the specified port.

Tristating a line means setting it to a high-impedance state which is neither high nor low. This is equivalent to physically disconnecting the line.


invert <port> [<bits>]

Inverts the state of the specified bits on the specified ports. If bits are omitted, inverts all bits of the specified port. If a bit has been set to tristate beforehand, it will be set to output high.


increment <port> [<bits>]

Increment the state of the specified bits on the specified port by one. If bits are omitted, increments the state of all bits on the specified port.

This can be used to implement a binary counter on a port or a specific bit range on a port. If all bits are already set (e.g. the counter has reached its maximum) then all bits are cleared again.

Example of a four-bit binary up-counter in the lower part of port a:

increment port a bits 0-3


decrement <port> [<bits>]

Decrements the state of the specified bits. Works similar to the increment command.

Commands for controlling the keyboard matrix


down <key>

Holds down the key on the matrix seen by the computer. The key will remain held down until an up command for the key is executed or the meta key is released.


up <key>

Releases the specified key on the matrix seen by the computer. The key will remain released until a down command for the key is executed or the meta key is released.


press <key>

Presses the specified key on the matrix seen by the computer, i.e. the key is held down for 20 milliseconds and then released again. This is a shortcut for

down <key>
sleep 20
up <key>


type <string>

Types the specified string on the matrix seen by the computer. To insert a press of the RETURN key, add the escape sequence \r.

Lower case ASCII characters will cause the corresponding key to be pressed without modifiers. Upper case and special characters will be pressed in conjunction with the required modifier to type the correct character.

To type PETSCII characters that are not present in the ASCII character set, you can simply use the ASCII character at the respective position in the ASCII table. For example, the pound symbol, which is at position 92 in the PETSCII table, can be specified by using the corresponding ASCII backslash character.

Alternatively, any PETSCII character can be typed by inserting it using a numeric escape sequence, specified in either decimal or hexadecimal notation:

\xnn   -- hexadecimal notation, e.g. \x0a
\{ddd} -- decimal notation, e.g. \{147}

In addition, the following escape sequences are predefined:

\\ -- literal backslash, (0x5c) (pound-symbol on the C64)
\r -- return (0x0d), equivalent to pressing RETURN
\n -- newline (0xa), equivalent to pressing SHIFT-RETURN
\f -- form feed (0x0c), equivalent to pressing SHIFT-CLRHOME

Note that the keyman can only type those characters that can actually be typed on a real C64 keyboard. This excludes the PETSCII-codes for explicitly setting the character set (upper or mixed case, codes 14 and 142) and for locking/unlocking manual switching of character sets (codes 8 and 9).

Example (on the C64):

type load"*",8,1\r – types LOAD"*",8,1 and presses RETURN


swap <key> <key>

Swaps the specified keys against each other before relaying them to the computer. This can be used to redefine the keyboard layout, e.g. to switch to a QWERTZ layout on the C64:

swap Z Y

Note that this applies only to the keys relayed to the computer, not to the keys interpreted as a part of a key binding. Commands bound to the physical key Z can still be invoked by pressing <meta>-Z in this example.


map <port> <bit> to <key>

Maps the specified control pin to a key. The control pin becomes an active-low input pin and will hold down the specified key when low.

Note that once mapped, a pin should not be modified by other commands in any way.



Interactively set the password used for unlocking the keyboard after it has been locked using the lock command. The C64 needs to be in direct mode for this as the keyman prompts for the password by simply typing on the keyboard.

The password needs to be entered twice. If both passwords match, the password is permanently stored in EEPROM memory.

To clear the password, simply enter an empty password, i.e. hit return twice.



If a password has been set using the password command, the keyboard will be locked and no keys will be passed through to the computer until the user enters the correct password followed by RETURN.

Any commands bound to the virtual key LOCKED will be executed before locking the keyboard. Likewise, any commands bound to the virtual key UNLOCKED will be executed before unlocking the keyboard again.

Commands for controlling execution


exec <key>|<slot>

Executes the command sequence bound to the specified key or slot number. Execution of the current command sequence will resume after the specified sequence has been executed.


sleep <duration>

Suspend execution of the current command sequence for the specified duration. No keyboard processing will occur during sleep, e.g. key presses will neither be relayed nor interpreted as bindings.


<key>: requires <n>

This directive instructs the keyman to execute the command sequence bound to the respective key only after n consecutive presses of the key have been registered while the meta key is being held down.

This is useful to protect against accidental executions of a disruptive action, such as resetting the machine while having unsaved changes in an editor. For example, the following reset sequence requires three presses to execute:

r: requires 3
r: clear port a bit 0
r: sleep 20
r: tristate port a bit 0

This only applies to interactive use, i.e. exec r will still run the above sequence immediately.

Commands for displaying information



Types out the current firmware version and build date. Note that the C64 has to be in direct mode for the text to appear on screen.



Types out the current state of the control lines. Note that the C64 has to be in direct mode for the text to appear on screen.

The state of the control lines will be displayed similar to this:

A XXXX0010
B 11111001

Where 0 is low, 1 is high and X is tristate.

Configuration and control utility

$ keyman64 --help

Keyman64 v1.6
Copyright (C) 2016 Henning Bekel.
License GPLv3: GNU GPL version 3 <>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

      keyman64 [<options>] convert [<infile>|-] [<outfile>|-]
      keyman64 [<options>] configure [<infile>]
      keyman64 [<options>] update <firmware> [<config>]
      keyman64 [<options>] reset
      keyman64 [<options>] <command>
      keyman64 [<options>] <script>|-

           -v, --version  : print version information
           -h, --help     : print this help text
           -d, --device   : specify usb device (default: /dev/keyman64)
           -D, --delay    : delay in ms between commands
           -k, --keys     : list key names and synonyms
           -p, --preserve : deprecated as of version 1.5
           -i, --identify : request firmware identification via USB

           <infile>   : input file, format is autodetected
           <outfile>  : output file, format determined by extension
           <script>   : script file containing keyman64 commands
           <firmware> : binary or ihex firmware image (.bin or .hex)

           *.conf : plain text config file format
           *.bin  : binary file format (default)

           Optional arguments default to stdin or stdout

            (any valid keyman64 command)

Transferring the Configuration

Version 1.4 and later

As of version 1.4 the configuration file can be converted and flashed to the keyman64 in one step using the configure command:

$ keyman64 configure keyman64.conf

This will parse the configuration file keyman64.conf, convert it to a binary format and transfer it to the Atmega’s eeprom memory. When finished, the keyman64 will be reset and the new configuration should become effective.

The previous method described below can still be used alternatively.

Versions prior to 1.4

The convert command of the configuration and control utility is used to convert the configuration file to a binary format which can then be written to the Atmega1284p’s EEPROM memory over USB.

Assuming you have written a configuration file named keyman64.conf, you can convert it using

$ keyman64 convert keyman64.conf keyman64.bin

If no errors are encountered this results in the file keyman64.bin.

Make the keyman64 enter the bootloader.

Then use avrdude to write the configuration to the EEPROM memory:

$ avrdude -p m1284p -c usbasp -U eeprom:w:keyman64.bin:r

After the configuration has been transfered, the keyman64 will reset and the new configuration should be in effect.

Please note that the bootloader will reset the AVR immediately after the update operation has completed, even before the current USB communication between the bootloader and avrdude has been completed. This results in the following non-critical error message from avrdude:

error: usbasp_transmit: usb_control_msg: sending control message failed

This message can be ignored as long as avrdude reports that it has successfully written and verified eeprom contents.

Preserving saved state when updating the configuration

Note that the –preserve option is no longer necessary in versions 1.5 and later, since saved state is no longer overwritten when transferring a new configuration.

If the current eeprom contains a saved control state, this state will be lost if a new binary configuration is created using the convert command, since the eeprom will be flashed with new values.

In order to preserve a saved state, the --preserve option may be used in conjunction with the convert command. The utility will then try to obtain the currently saved state of the control lines via USB and insert this state into the newly created binary configuration. Thus the device has to powered up and connected via USB.

Note that when using the configure command described above, any saved state will be preserved automatically.

Remote control

Any command can be send from the PC via USB to the keyman64, where it will be executed immediately.

A single command can be executed by passing it directly on the commandline, e.g.

$ keyman64 press F5

If no arguments are specified on the commandline, the keyman64 utility will read commands from STDIN, where one command can be given on each line. Note that the commands will get parsed and executed after STDIN has been closed, e.g. all commands have been entered/piped in:

$ keyman64
reading commands from stdin...
> type load"$",8\r
> sleep 5s
> type list\r
> ^D

If the first argument to the keyman64 utility is a file, commands will be read from this file and subsequently sent to the keyman64.

Thus you can create a file containing a script like the following:

exec rightshift
press f5
press cursordown
press cursordown
press cursordown
press return

and run this script via

$ keyman64 --delay 250 script.txt

The --delay option will add an additional delay of 250ms after each command. This avoids having to add explicit sleep commands in the script.

On Linux and MacOSX, the she-bang mechanism can be used to create an executable script. Just make the script executable and add an appropriate she-bang line at the top, e.g.

#!/usr/bin/keyman64 --delay 250
press f5
press cursordown
press return

then chmod +x the script and you can execute it like any other command.

Port expansion

As of version 1.6 the number of available control lines can be expanded by connecting one or more daisy chained 74595 serial shift registers. Four of the existing control lines need to be connected to the first (or only) 74595, while each 74595 added to the chain will add another 8-bit control port.

The 74595 is available on easily chainable breakout boards from various sources, for example the Artekit shop offers a breakout board well suited for this purpose.

The expand configuration directive is used to tell the keyman64 about the number of available additional ports and which of its native control lines are connected to the required pins of the first shift register. Once configured, the expanded ports will be labeled alphabetically beginning with the letter “c”. They be used just like the existing ports a and b in other configuration directives and commands.

The expand directive names the required shift register pins as CLOCK, DATA, LATCH and ENABLE. Depending on the datasheet or breakout-board used, these pins may be named differently:

Keyman64 74595 pin number Description Other common names on breakout-boards or in datasheets
CLOCK 11 shift register clock input CLK, SCK, SHCP, SRCLK
DATA 14 serial data input DS, SER, SER-IN
LATCH 12 storage register clock input STCP, RCLK
ENABLE 13 output enable input (active low) OE, EN
RESET 10 serial clear input (active low) MR, CLR, SRCLR

The ENABLE and RESET signals should be pulled up via a 4.7k resistor. (This is already the case for the artekit board linked above).

When the keyman starts up, all expanded ports will be initially tristated. Once any of the expanded port lines is set or modified by a command, all registers will be enabled, and all port lines whose state has not explicitly defined yet will be driven low by default.

Note that the virtual ports cannot be used as inputs and cannot be put into tristate, thus the commands map and tristate are not allowed to be used with expanded port lines. The configuration utility will issue an appropriate error message in this case.


This illustration shows how to wire one or more 74595 breakout boards. Here the lower four control lines of port b are connected to the 74595.

The corresponding expand directive for using two additional ports is thus:

expand PORTS=2 DATA=b0 CLOCK=b1 LATCH=b2 ENABLE=b3

Any command following this directive in the configuration file can now refer to the additional ports as ports c and d, e.g.

X: set port d bit 5
Y: inc port c bits 0-1

Serial interface

The P4 connector offers a simple serial interface allowing remote control of the keyman64 by external hardware. The left pin provides the input for an active-low clock signal (denoted as /CLK on the board). The right pin provides the data input (denoted DATA). On the falling edge of the clock signal the value present on the data input is transferred to the keyman64.

Each command consists of a command-byte, followed by one or more argument bytes. Bytes are transferred individually in little-endian order, i.e. the least significant bit is send first. For example, to press RUNSTOP, the actual command looks like this:

00000100 00111111 => 0x4 (command) 0x3f (argument)

but must be transferred like this:

00100000 11111100 => command, then argument, both lsb first.

The following commands are available:

  • 00000001 <key> : execute command sequence bound to key or slot number
  • 00000010 <key> : hold the specified key down
  • 00000011 <key> : release the specified key
  • 00000100 <key> : press the specified key (down for 20ms, release)
  • 00000101 <pin> <key>: (re)map the specified pin to the specified key
  • 00000111 <code> : type the specified PETSCII code

The pin for the map command is denoted by bits 0-2 and the port is denoted by bit 3 (low = port a, high = port b).

Note that if a key is held down via the serial interface it will remain in this state until explicitly released via the serial interface again. The state of the physical key will be ignored during this time.

Connecting the Serial interface to the C64

One possible way of using the serial interface is to connect it to the C64 via bits 3 and 4 of the 6510 IO port. These are available as “Cassette Sense” and “Cassette Write” at the tapeport (assuming you don’t use the tapeport for anything else). “Cassette Sense” will be used as the /CLK signal and “Cassette Write” as the DATA signal.

The source distribution contains the file serial.h and serial.asm in KickAssembler3 format. These files can be used to issue serial commands from the C64 using the 6510 IO port.

For example, the following code will execute the command sequence bound to <meta>-RUNSTOP:

.import source "serial.h"

jsr    // configure the tapeport lines for output

lda #Command.exec  // byte to send
jsr serial.write   // right shift the bits out (i.e. lsb first)

lda #$3f           // This is the "RUNSTOP" key
jsr serial.write

jsr serial.close   // Reset tapeport lines to default state (input)


.import source "serial.asm"

Configuration examples

Switching the kernal and performing a reset

Let’s assume you have a dual-kernal adapter installed in your C64. These adapters usually come with a switch that controls the highest address line of the eprom and thus chooses the kernal that is seen by the C64. You are expected to drill a hole in your C64 case to install the switch.

Using keyman64 you can avoid drilling a hole in your precious case.

Simply remove the switch and connect the highest address line of the eprom to one of the 16 control lines of the keyman64. For this example, we’ll assume that we’re using the highest line of the first control port (port a bit 7).

Create a configuration file with the following contents:

clear port a
clear port b

k: invert port a bit 7

Convert and transfer it using the keyman64 command line tool:

keyman64 configure example.conf

After the configuration is written, the keyman64 will reset. First it executes any command not bound to a key. This allows us to set the initial state of the control lines. In this case, all lines are initially pulled low using the clear command).

Now the key combination <meta>-k will invert the state of the eprom address line, effectively switching back and forth between the two kernal images on each invocation.

While this does the job on the hardware level, it might still cause the C64 to crash, since you might swap the kernal contents while kernal code is being executed. So it would be nice to also reset the C64 after switching the kernal rom.

We’ll connect the C64 reset line to the first line of the first control port and change the configuration to:

tristate port a bit 0
clear port a bits 1-7
clear port b

r: clear port a bit 0
r: sleep 10
r: tristate port a bit 0

k: invert port a bit 7
k: exec r

Now if we press <meta>-r, the reset line will be driven from tristate to low for 10 milliseconds and is then tristated again, effectively causing the C64 to reset.

And if we press <meta>-k, the kernal will be switched just like before, but then the reset sequence bound to <meta>-r will be executed in addition.

Thus we can switch the kernal and immediately reset the C64 simply by pressing <meta>-k instead of having to turn off the C64, flip a switch and turn the C64 back on every time we want to change the kernal.

Holding down a key during reset

Some expansion port modules execute special functions when a key is held down during reset. Things like this can be simulated as well. The following binding performs a reset while holding down the RUNSTOP key:

u: down runstop
u: clear port a bit 0    
u: sleep 10
u: tristate port a bit 0
u: sleep 1s
u: up runstop

Defining keyboard macros

The type command can be bound to a key as well:

d: type load"$",8\r

Now pressing <meta>-d will type load"$",8<return>. The \r escape sequence denotes a newline in this context.

Remapping the keyboard

The swap command can be used to virtually swap two keys on the keyboard. For example, to change the keyboard layout to QWERTZ, just add

swap z y

to your configuration.

If you want to switch between both layouts on the fly, you can simply bind the swap command to a key:

y: swap z y


Copyright (C) 2016 Henning bekel <>

Hardware licensed under CERN OHL v.1.2, see ./hardware/LICENSE.txt
Software and firmware licensed under GNU GPLv3, see ./LICENSE

The software contains MIT licensed code from for reading Intel HEX files, copyright (C) 2013-2015 Kimmo Kulovesi, see ./intelhex/LICENSE

The firmware contains the V-USB Driver (, (C)2008 Objective Development GmbH.

Distributed in the ./bootloader directory is the USBasp bootloader, (C)2013 Stephan Baerwolf ( and (C)2008 Objective Development GmbH (

In compliance with the terms of the (identical) V-USB driver and USBasp bootloader licenses (./firmware/usbdrv/License.txt and ./bootloader/License.txt, the entire project is published under

In addition, this distribution contains

The USB Product and Vendor IDs for the Keyman64 USB device have kindly been provided by OpenMoko, Inc (