]> git.friedersdorff.com Git - max/tmk_keyboard.git/commitdiff
ibmpc_usb: Add IBM PC Keyobard converter
authortmk <hasu@tmk-kbd.com>
Wed, 4 Dec 2019 02:35:48 +0000 (11:35 +0900)
committertmk <hasu@tmk-kbd.com>
Wed, 4 Dec 2019 02:35:48 +0000 (11:35 +0900)
converter/ibmpc_usb/Makefile [new file with mode: 0644]
converter/ibmpc_usb/Makefile.atmega32u2 [new file with mode: 0644]
converter/ibmpc_usb/Makefile.atmega32u4 [new file with mode: 0644]
converter/ibmpc_usb/README.md [new file with mode: 0644]
converter/ibmpc_usb/config.h [new file with mode: 0644]
converter/ibmpc_usb/ibmpc_usb.c [new file with mode: 0644]
converter/ibmpc_usb/ibmpc_usb.h [new file with mode: 0644]
converter/ibmpc_usb/unimap_plain.c [new file with mode: 0644]
converter/ibmpc_usb/unimap_trans.h [new file with mode: 0644]

diff --git a/converter/ibmpc_usb/Makefile b/converter/ibmpc_usb/Makefile
new file mode 100644 (file)
index 0000000..636cb32
--- /dev/null
@@ -0,0 +1,114 @@
+# Target file name (without extension).
+TARGET ?= ibmpc_usb
+
+# Directory common source filess exist
+TMK_DIR ?= ../../tmk_core
+
+# Directory keyboard dependent files exist
+TARGET_DIR ?= .
+
+# project specific files
+SRC ?= protocol/ibmpc.c \
+       ibmpc_usb.c
+
+CONFIG_H ?= config.h
+
+
+# MCU name
+MCU ?= atmega32u4
+
+# Processor frequency.
+#     This will define a symbol, F_CPU, in all source code files equal to the
+#     processor frequency in Hz. You can then use this symbol in your source code to
+#     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
+#     automatically to create a 32-bit value in your source code.
+#
+#     This will be an integer division of F_USB below, as it is sourced by
+#     F_USB after it has run through any CPU prescalers. Note that this value
+#     does not *change* the processor frequency - it should merely be updated to
+#     reflect the processor speed set externally so that the code can use accurate
+#     software delays.
+F_CPU ?= 16000000
+
+
+#
+# LUFA specific
+#
+# Target architecture (see library "Board Types" documentation).
+ARCH ?= AVR8
+
+# Input clock frequency.
+#     This will define a symbol, F_USB, in all source code files equal to the
+#     input clock frequency (before any prescaling is performed) in Hz. This value may
+#     differ from F_CPU if prescaling is used on the latter, and is required as the
+#     raw input clock is fed directly to the PLL sections of the AVR for high speed
+#     clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
+#     at the end, this will be done automatically to create a 32-bit value in your
+#     source code.
+#
+#     If no clock division is performed on the input clock inside the AVR (via the
+#     CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
+F_USB ?= $(F_CPU)
+
+# Interrupt driven control endpoint task
+# Not work with suart debug
+#OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
+
+# This improves response of keyboard when wakeup
+OPT_DEFS += -DSUSPEND_MODE_STANDBY
+
+# Boot Section Size in *bytes*
+#   Teensy halfKay   512
+#   Teensy++ halfKay 1024
+#   Atmel DFU loader 4096
+#   LUFA bootloader  4096
+#   USBaspLoader     2048
+BOOTLOADER_SIZE ?= 4096
+OPT_DEFS += -DBOOTLOADER_SIZE=$(BOOTLOADER_SIZE)
+
+
+# Build Options
+#   comment out to disable the options.
+#
+BOOTMAGIC_ENABLE ?= no # Virtual DIP switch configuration(+1000)
+MOUSEKEY_ENABLE ?= no  # Mouse keys(+4700)
+EXTRAKEY_ENABLE ?= yes # Audio control and System control(+450)
+CONSOLE_ENABLE ?= yes  # Console for debug(+400)
+COMMAND_ENABLE ?= yes    # Commands for debug and configuration
+NKRO_ENABLE ?= yes     # USB Nkey Rollover
+
+KEYMAP_SECTION_ENABLE = yes
+UNIMAP_ENABLE = yes
+
+
+# Optimize size but this may cause error "relocation truncated to fit"
+#EXTRALDFLAGS = -Wl,--relax
+
+
+#
+# Keymap file
+#
+ifdef UNIMAP_ENABLE
+    KEYMAP_FILE = unimap
+else
+    ifdef ACTIONMAP_ENABLE
+       KEYMAP_FILE = actionmap
+    else
+       KEYMAP_FILE = keymap
+    endif
+endif
+ifdef KEYMAP
+    SRC := $(KEYMAP_FILE)_$(KEYMAP).c $(SRC)
+else
+    SRC := $(KEYMAP_FILE)_plain.c $(SRC)
+endif
+
+
+# Search Path
+VPATH += $(TARGET_DIR)
+VPATH += $(TMK_DIR)
+
+include $(TMK_DIR)/protocol.mk
+include $(TMK_DIR)/protocol/lufa.mk
+include $(TMK_DIR)/common.mk
+include $(TMK_DIR)/rules.mk
diff --git a/converter/ibmpc_usb/Makefile.atmega32u2 b/converter/ibmpc_usb/Makefile.atmega32u2
new file mode 100644 (file)
index 0000000..53475b3
--- /dev/null
@@ -0,0 +1,3 @@
+MCU = atmega32u2
+
+include Makefile
diff --git a/converter/ibmpc_usb/Makefile.atmega32u4 b/converter/ibmpc_usb/Makefile.atmega32u4
new file mode 100644 (file)
index 0000000..ba4ec74
--- /dev/null
@@ -0,0 +1,3 @@
+MCU = atmega32u4
+
+include Makefile
diff --git a/converter/ibmpc_usb/README.md b/converter/ibmpc_usb/README.md
new file mode 100644 (file)
index 0000000..7b24866
--- /dev/null
@@ -0,0 +1,168 @@
+IBM PC Keyboard Converter
+=========================
+The converter translates IBM PC keyboard protocols to use classic keyboards with modern computer with USB ports.
+It supports both IBM XT and AT protocols, and all of scan code set 1, 2 and 3 with one firmware.
+
+This is not finish project and still work in progress as of 2019-12-02. Test in the field and feedback from users are needed to improve
+
+This project is intended to integrated existent TMK XT, PS/2 and Terminal converters.
+
+- IBM XT converter: https://geekhack.org/index.php?topic=94649.0
+- PS/2 converter: https://geekhack.org/index.php?topic=14618.0
+- IBM Terminal converter: http://geekhack.org/index.php?topic=27272.0
+
+
+
+Keyboard supported
+------------------
+- PC XT keyboard of IBM 5150 5160
+  - 83-key: 1501100 1501105
+- PC AT keyboard of IBM 5170
+  - 84-key: 6450200 6450225
+- PC Terminal keyboard of IBM 5271(3270 PC)
+  - 122-key: 6110344 6110345 1397000
+  - 102-key: 1390680 1395764 1392595
+- PS/2 keyboards(AT+CodeSet2)
+- Clones of above models
+
+
+
+Hardware
+--------
+Firmware supports ATMega32u4 and ATMega32u2 by default, Teensy2 or ProMicro can be used.
+Wire controller pins below to keyboard signals, besides VCC and GND. This is compatible for Soarer's converter.
+
+- Data    PD0
+- Clock   PD1
+- Reset   PB6 or PB7 (For some of XT keyboards. Not needed for AT, PS/2 and Terminal)
+
+Pull up resistors of 1-4.7K Ohm on both Data and Clock line are recommended, without them it won't work in some cases.
+
+### Reset
+Old Type-1 IBM XT keyboard and some of XT clones need this to reset its controller on startup. Many of IBM XT keyboards available are Type-2 and don't need the reset pin.
+
+See this for Type-1 vs Type-2:
+https://vintagecomputer.ca/ibm-pc-model-f-keyboard-type-1-vs-type-2/
+
+As for clones Zenith Z-150 XT and  Leading Edge DC-2014 are known to need this.
+
+### Connector pinouts
+#### XT
+- http://www.kbdbabel.org/conn/kbd_connector_ibmpc.png
+- https://allpinouts.org/pinouts/connectors/input_device/keyboard-xt-5-pin/
+
+#### AT
+- http://www.kbdbabel.org/conn/kbd_connector_ps2.png
+- https://old.pinouts.ru/InputCables/KeyboardPC5_pinout.shtml
+
+#### PS/2
+- https://pinouts.ru/InputCables/KeyboardPC6_pinout.shtml
+
+#### Terminal
+- http://www.kbdbabel.org/conn/kbd_connector_ibmterm.png
+- http://www.kbdbabel.org/conn/kbd_connector_ibm3179_318x_319x.png
+
+
+
+Source Code
+-----------
+https://github.com/tmk/tmk_keyboard/tree/master/converter/ibmpc_usb
+
+
+
+Build Firmware
+--------------
+
+    $ cd converter/ibmpc_usb
+    $ make clean
+    $ make
+
+
+
+Keyboard discrimination
+-----------------------
+This section explains how the converter determines proper protocol and scan code set for keyboard. The converter need to do that before starting to receive and translate scan codes from keyboard.
+
+### Keyboard ID
+After startup the converter sends 0xF2 command to get keyboard ID and sees how the keyboard responds to the command.
+
+Response from keyboard:
+
+- XT keyboard doesn't support any command and returns no response.
+- AT keyboard should respond with 0xFA to the command but returns no keyboard ID.
+- PS/2 keyboard should respond with 0xFA to the command, followd by keyboard ID, such as 0xAB86.
+- Terminal keyboard should respond with 0xFA to the command, followed by keyboard ID, such as 0xBFBF.
+
+Now we can dscriminate the keyboard and determine suitable protocol and scan code set as described below.
+
+### Protocol
+- Signals from XT keyboard are recognized by XT protocol.
+- Signals from AT, PS/2 and Terminal keyboard are recognized by AT protocol.
+
+### Scan code Set
+- Scan codes from XT keyboard are handled as CodeSet1.
+- Scan codes from AT and PS/2 keyboard are handled as CodeSet2.
+- Scan codes from Terminal keyhboard are handled as CodeSet3.
+
+
+
+Debug
+-----
+Use hid_listen to see debug outputs from the converter.
+
+https://www.pjrc.com/teensy/hid_listen.html
+
+
+
+Resources
+---------
+IBM PC Keyboard Protocol Resources:
+
+[a] [Microsoft USB HID to PS/2 Translation Table - Scan Code Set 1 and 2](
+http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf)
+
+[b] [Microsoft Keyboard Scan Code Specification - Special rules of Scan Code Set 1 and 2](
+http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc)
+
+[1] [PS/2 Reference Manuals - Collection of IBM Personal System/2 documents](
+http://www.mcamafia.de/pdf/pdfref.htm)
+
+[2] [Keyboard and Auxiliary Device Controller - Signal Timing and Format](
+http://www.mcamafia.de/pdf/ibm_hitrc07.pdf)
+
+[3] [Keyboards(101- and 102-key) - Keyboard Layout, Scan Code Set, POR, and Commands](
+http://www.mcamafia.de/pdf/ibm_hitrc11.pdf)
+
+[4] [IBM PC XT Keyboard Protocol](
+https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol)
+
+[5] [IBM Keyboard Scan Code by John Elliott - 83-key, 84-key, 102-key and 122-key](
+https://www.seasip.info/VintagePC/index.html)
+
+[6] [IBM 1391406 Keyboard - Scan Code Set 2 of 102-key PS/2 keyboard](
+https://www.seasip.info/VintagePC/ibm_1391406.html)
+
+[7] [The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard](
+https://www.seasip.info/VintagePC/ibm_6110344.html)
+
+[y] [TrackPoint Engineering Specifications for version 3E](
+https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html)
+
+[z] [Soarer's XT/AT/PS2/Terminal to USB converter](
+https://geekhack.org/index.php?topic=17458.0)
+
+
+
+TODO
+----
+### Reset method for rescue
+For converter without accesible reset button when magickey combo doesn't work.
+
+Check pin status at powerup:
+
+- if Data and/or Clock are pull down to GND
+
+### Force protocol and scan code set
+Keyboard discrimination may fail and you have to configure them by hand.
+
+### Add AT90usb1286 support for Teensy2++
diff --git a/converter/ibmpc_usb/config.h b/converter/ibmpc_usb/config.h
new file mode 100644 (file)
index 0000000..fb204ec
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+Copyright 2019 Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <avr/interrupt.h>
+
+#define VENDOR_ID       0xFEED
+#define PRODUCT_ID      0x1bee
+#define DEVICE_VER      0x0001
+#define MANUFACTURER    t.m.k.
+#define PRODUCT         IBM PC keyboard converter
+#define DESCRIPTION     convert IBM PC keyboard to USB
+
+
+/* matrix size */
+#define MATRIX_ROWS 16  // keycode bit: 6-3
+#define MATRIX_COLS 8   // keycode bit: 2-0
+
+
+/* key combination for command */
+#define IS_COMMAND() ( \
+    keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) || \
+    keyboard_report->mods == (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT)) \
+)
+
+
+/*
+ * Pin and interrupt configuration
+ */
+#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32U2__)
+/* uses INT1 for clock line */
+#define IBMPC_CLOCK_PORT  PORTD
+#define IBMPC_CLOCK_PIN   PIND
+#define IBMPC_CLOCK_DDR   DDRD
+#define IBMPC_CLOCK_BIT   1
+#define IBMPC_DATA_PORT   PORTD
+#define IBMPC_DATA_PIN    PIND
+#define IBMPC_DATA_DDR    DDRD
+#define IBMPC_DATA_BIT    0
+#define IBMPC_RST_PORT    PORTB
+#define IBMPC_RST_PIN     PINB
+#define IBMPC_RST_DDR     DDRB
+#define IBMPC_RST_BIT1    6
+#define IBMPC_RST_BIT2    7
+
+/* reset for XT keyboard: low pulse for 500ms and after that HiZ for safety */
+#define IBMPC_RESET() do { \
+    IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT1);  \
+    IBMPC_RST_DDR  |=  (1<<IBMPC_RST_BIT1);  \
+    IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT2);  \
+    IBMPC_RST_DDR  |=  (1<<IBMPC_RST_BIT2);  \
+    _delay_ms(500);                   \
+    IBMPC_RST_DDR  &= ~(1<<IBMPC_RST_BIT1);  \
+    IBMPC_RST_DDR  &= ~(1<<IBMPC_RST_BIT2);  \
+} while (0)
+
+#define IBMPC_INT_INIT()  do {  \
+    EICRA |= ((1<<ISC11) |      \
+              (0<<ISC10));      \
+} while (0)
+
+#define IBMPC_INT_ON()  do {    \
+    EIMSK |= (1<<INT1);         \
+} while (0)
+
+#define IBMPC_INT_OFF() do {    \
+    EIMSK &= ~(1<<INT1);        \
+} while (0)
+
+#define IBMPC_INT_VECT    INT1_vect
+
+#else
+#error "No pin configuration in config.h"
+#endif
+
+#endif
diff --git a/converter/ibmpc_usb/ibmpc_usb.c b/converter/ibmpc_usb/ibmpc_usb.c
new file mode 100644 (file)
index 0000000..87179ca
--- /dev/null
@@ -0,0 +1,853 @@
+/*
+Copyright 2019 Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "print.h"
+#include "util.h"
+#include "debug.h"
+#include "ibmpc.h"
+#include "host.h"
+#include "led.h"
+#include "matrix.h"
+#include "timer.h"
+#include "action.h"
+#include "ibmpc_usb.h"
+
+
+static void matrix_make(uint8_t code);
+static void matrix_break(uint8_t code);
+
+static int8_t process_cs1(void);
+static int8_t process_cs2(void);
+static int8_t process_cs3(void);
+
+
+static uint8_t matrix[MATRIX_ROWS];
+#define ROW(code)      ((code>>3)&0x0F)
+#define COL(code)      (code&0x07)
+
+static int16_t read_wait(uint16_t wait_ms)
+{
+    uint16_t start = timer_read();
+    int16_t code;
+    while ((code = ibmpc_host_recv()) == -1 && timer_elapsed(start) < wait_ms);
+    return code;
+}
+
+static uint16_t read_keyboard_id(void)
+{
+    uint16_t id = 0;
+    int16_t  code = 0;
+
+    // Disable
+    code = ibmpc_host_send(0xF5);
+
+    // Read ID
+    code = ibmpc_host_send(0xF2);
+    if (code == -1)  return 0xFFFF;     // XT or No keyboard
+    if (code != 0xFA) return 0xFFFE;    // Broken PS/2?
+
+    code = read_wait(1000);
+    if (code == -1)  return 0x0000;     // AT
+    id = (code & 0xFF)<<8;
+
+    code = read_wait(1000);
+    id |= code & 0xFF;
+
+    // Enable
+    code = ibmpc_host_send(0xF4);
+
+    return id;
+}
+
+void matrix_init(void)
+{
+    debug_enable = true;
+    ibmpc_host_init();
+
+    // hard reset for XT keyboard
+    IBMPC_RESET();
+
+    // initialize matrix state: all keys off
+    for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
+
+    return;
+}
+
+/*
+ * keyboard recognition
+ *
+ * 1. Send F2 to get keyboard ID
+ *      a. no ACK(FA): XT keyobard
+ *      b. ACK but no ID: 84-key AT keyboard CodeSet2
+ *      c. ID is AB 83: PS/2 keyboard CodeSet2
+ *      d. ID is BF BF: Terminal keyboard CodeSet3
+ *      e. error on recv: maybe broken PS/2
+ */
+uint16_t keyboard_id = 0x0000;
+keyboard_kind_t keyboard_kind = NONE;
+uint8_t matrix_scan(void)
+{
+    // scan code reading states
+    static enum {
+        INIT,
+        WAIT_STARTUP,
+        READ_ID,
+        LED_SET,
+        LOOP,
+        END
+    } state = INIT;
+    static uint16_t last_time;
+
+
+    if (ibmpc_error) {
+        xprintf("err: %02X\n", ibmpc_error);
+
+        // when recv error, neither send error nor buffer full
+        if (!(ibmpc_error & (IBMPC_ERR_SEND | IBMPC_ERR_FULL))) {
+            // keyboard init again
+            if (state == LOOP) {
+                xprintf("init\n");
+                state = INIT;
+            }
+        }
+
+        // clear or process error
+        ibmpc_error = IBMPC_ERR_NONE;
+    }
+
+    switch (state) {
+        case INIT:
+            ibmpc_protocol = IBMPC_PROTOCOL_AT;
+            keyboard_kind = NONE;
+            keyboard_id = 0x0000;
+            last_time = timer_read();
+            state = WAIT_STARTUP;
+            matrix_clear();
+            clear_keyboard();
+            break;
+        case WAIT_STARTUP:
+            // read and ignore BAT code and other codes when power-up
+            ibmpc_host_recv();
+            if (timer_elapsed(last_time) > 1000) {
+                state = READ_ID;
+            }
+            break;
+        case READ_ID:
+            keyboard_id = read_keyboard_id();
+            if (ibmpc_error) {
+                xprintf("err: %02X\n", ibmpc_error);
+                ibmpc_error = IBMPC_ERR_NONE;
+            }
+            xprintf("ID: %04X\n", keyboard_id);
+            if (0xAB00 == (keyboard_id & 0xFF00)) {
+                // CodeSet2 PS/2
+                keyboard_kind = PC_AT;
+            } else if (0xBF00 == (keyboard_id & 0xFF00)) {
+                // CodeSet3 Terminal
+                keyboard_kind = PC_TERMINAL;
+            } else if (0x0000 == keyboard_id) {
+                // CodeSet2 AT
+                keyboard_kind = PC_AT;
+            } else if (0xFFFF == keyboard_id) {
+                // CodeSet1 XT
+                keyboard_kind = PC_XT;
+            } else if (0xFFFE == keyboard_id) {
+                // CodeSet2 PS/2 fails to response?
+                keyboard_kind = PC_AT;
+            } else if (0x00FF == keyboard_id) {
+                // Mouse is not supported
+                xprintf("Mouse: not supported\n");
+                keyboard_kind = NONE;
+            } else {
+                keyboard_kind = PC_AT;
+            }
+
+            // protocol
+            if (keyboard_kind == PC_XT) {
+                xprintf("kbd: XT\n");
+                ibmpc_protocol = IBMPC_PROTOCOL_XT;
+            } else if (keyboard_kind == PC_AT) {
+                xprintf("kbd: AT\n");
+                ibmpc_protocol = IBMPC_PROTOCOL_AT;
+            } else if (keyboard_kind == PC_TERMINAL) {
+                xprintf("kbd: Terminal\n");
+                ibmpc_protocol = IBMPC_PROTOCOL_AT;
+                // Set all keys - make/break [3]p.23
+                ibmpc_host_send(0xF8);
+            } else {
+                xprintf("kbd: Unknown\n");
+                ibmpc_protocol = IBMPC_PROTOCOL_AT;
+            }
+            state = LED_SET;
+            break;
+        case LED_SET:
+            led_set(host_keyboard_leds());
+            state = LOOP;
+        case LOOP:
+            switch (keyboard_kind) {
+                case PC_XT:
+                    process_cs1();
+                    break;
+                case PC_AT:
+                    process_cs2();
+                    break;
+                case PC_TERMINAL:
+                    process_cs3();
+                    break;
+                default:
+                    break;
+            }
+            break;
+        default:
+            break;
+    }
+    return 1;
+}
+
+inline
+bool matrix_is_on(uint8_t row, uint8_t col)
+{
+    return (matrix[row] & (1<<col));
+}
+
+inline
+uint8_t matrix_get_row(uint8_t row)
+{
+    return matrix[row];
+}
+
+uint8_t matrix_key_count(void)
+{
+    uint8_t count = 0;
+    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+        count += bitpop(matrix[i]);
+    }
+    return count;
+}
+
+
+inline
+static void matrix_make(uint8_t code)
+{
+    if (!matrix_is_on(ROW(code), COL(code))) {
+        matrix[ROW(code)] |= 1<<COL(code);
+    }
+}
+
+inline
+static void matrix_break(uint8_t code)
+{
+    if (matrix_is_on(ROW(code), COL(code))) {
+        matrix[ROW(code)] &= ~(1<<COL(code));
+    }
+}
+
+void matrix_clear(void)
+{
+    for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
+}
+
+void led_set(uint8_t usb_led)
+{
+    if (keyboard_kind != PC_AT) return;
+
+    uint8_t ibmpc_led = 0;
+    if (usb_led &  (1<<USB_LED_SCROLL_LOCK))
+        ibmpc_led |= (1<<IBMPC_LED_SCROLL_LOCK);
+    if (usb_led &  (1<<USB_LED_NUM_LOCK))
+        ibmpc_led |= (1<<IBMPC_LED_NUM_LOCK);
+    if (usb_led &  (1<<USB_LED_CAPS_LOCK))
+        ibmpc_led |= (1<<IBMPC_LED_CAPS_LOCK);
+    ibmpc_host_set_led(ibmpc_led);
+}
+
+
+/*******************************************************************************
+ * XT: Scan Code Set 1
+ *
+ * See [3], [a]
+ *
+ * E0-escaped scan codes are translated into unused range of the matrix.(54-7F)
+ *
+ *     01-53: Normal codes used in original XT keyboard
+ *     54-7F: Not used in original XT keyboard
+ *
+ *         0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
+ *     50  -   -   -   -   *   *   x   x   x   x   *   *   *   *   *   *
+ *     60  *   *   *   *   x   x   x   x   x   x   x   x   x   x   x   *
+ *     70  x   *   *   x   *   *   x   *   *   x   *   x   *   x   x   *
+ *
+ * -: codes existed in original XT keyboard
+ * *: E0-escaped codes translated
+ * x: Non-espcaped codes(Some are not used in real keyboards probably)
+ *
+ * Codes assigned in range 54-7F:
+ *
+ *     50  -                60  Up*                 70  KANAx
+ *     51  -                61  Left*               71  Insert*
+ *     52  -                62  Down*               72  Delete*
+ *     53  -                63  Right*              73  ROx
+ *     54  PrintScr*        64  F13x                74  Home*
+ *     55  Pause*           65  F14x                75  End*
+ *     56  Euro2x           66  F15x                76  F24x
+ *     57  F11x             67  F16x                77  PageUp*
+ *     58  F12x             68  F17x                78  PageDown*
+ *     59  Keypad=x         69  F18x                79  HENKANx
+ *     5A  LGUI*            6A  F19x                7A  RCTL*
+ *     5B  RGUI*            6B  F20x                7B  MUHENKANx
+ *     5C  APP*             6C  F21x                7C  RALT*
+ *     5D  Mute*            6D  F22x                7D  JPYx
+ *     5E  Volume Down*     6E  F23x                7E  Keypad,x
+ *     5F  Volume Up*       6F  Keypad Enter*       7F  Keypad/ *
+ */
+static uint8_t cs1_e0code(uint8_t code) {
+    switch(code) {
+        // Original IBM XT keyboard doesn't use E0-codes probably
+        // Some XT compatilble keyobards need these keys?
+        case 0x37: return 0x54; // Print Screen
+        case 0x46: return 0x55; // Ctrl + Pause
+        case 0x5B: return 0x5A; // Left  GUI
+        case 0x5C: return 0x5B; // Right GUI
+        case 0x5D: return 0x5C; // Application
+        case 0x20: return 0x5D; // Mute
+        case 0x2E: return 0x5E; // Volume Down
+        case 0x30: return 0x5F; // Volume Up
+        case 0x48: return 0x60; // Up
+        case 0x4B: return 0x61; // Left
+        case 0x50: return 0x62; // Down
+        case 0x4D: return 0x63; // Right
+        case 0x1C: return 0x6F; // Keypad Enter
+        case 0x52: return 0x71; // Insert
+        case 0x53: return 0x72; // Delete
+        case 0x47: return 0x74; // Home
+        case 0x4F: return 0x75; // End
+        case 0x49: return 0x77; // Page Up
+        case 0x51: return 0x78; // Page Down
+        case 0x1D: return 0x7A; // Right Ctrl
+        case 0x38: return 0x7C; // Right Alt
+        case 0x35: return 0x7F; // Keypad /
+
+        // Shared matrix cell with other keys
+        case 0x5E: return 0x70; // Power (KANA)
+        case 0x5F: return 0x79; // Sleep (HENKAN)
+        case 0x63: return 0x7B; // Wake  (MUHENKAN)
+
+        default:
+           xprintf("!CS1_?!\n");
+           return code;
+    }
+    return 0x00;
+}
+
+static int8_t process_cs1(void)
+{
+    static enum {
+        INIT,
+        E0,
+        // Pause: E1 1D 45, E1 9D C5
+        E1,
+        E1_1D,
+        E1_9D,
+    } state = INIT;
+
+    uint16_t code = ibmpc_host_recv();
+    if (code == -1) {
+        return 0;
+    }
+
+    switch (state) {
+        case INIT:
+            switch (code) {
+                case 0xE0:
+                    state = E0;
+                    break;
+                case 0xE1:
+                    state = E1;
+                    break;
+                default:
+                    if (code < 0x80)
+                        matrix_make(code);
+                    else
+                        matrix_break(code & 0x7F);
+                    break;
+            }
+            break;
+        case E0:
+            switch (code) {
+                case 0x2A:
+                case 0xAA:
+                case 0x36:
+                case 0xB6:
+                    //ignore fake shift
+                    state = INIT;
+                    break;
+                default:
+                    if (code < 0x80)
+                        matrix_make(cs1_e0code(code));
+                    else
+                        matrix_break(cs1_e0code(code & 0x7F));
+                    state = INIT;
+                    break;
+            }
+            break;
+        case E1:
+            switch (code) {
+                case 0x1D:
+                    state = E1_1D;
+                    break;
+                case 0x9D:
+                    state = E1_9D;
+                    break;
+                default:
+                    state = INIT;
+                    break;
+            }
+            break;
+        case E1_1D:
+            switch (code) {
+                case 0x45:
+                    matrix_make(0x55);
+                    break;
+                default:
+                    state = INIT;
+                    break;
+            }
+            break;
+        case E1_9D:
+            switch (code) {
+                case 0x45:
+                    matrix_break(0x55);
+                    break;
+                default:
+                    state = INIT;
+                    break;
+            }
+            break;
+        default:
+            state = INIT;
+    }
+    return 0;
+}
+
+
+/*******************************************************************************
+ * AT, PS/2: Scan Code Set 2
+ *
+ * Exceptional Handling
+ * --------------------
+ * Some keys should be handled exceptionally. See [b].
+ *
+ * Scan codes are varied or prefix/postfix'd depending on modifier key state.
+ *
+ * 1) Insert, Delete, Home, End, PageUp, PageDown, Up, Down, Right, Left
+ *     a) when Num Lock is off
+ *     modifiers | make                      | break
+ *     ----------+---------------------------+----------------------
+ *     Ohter     |                    <make> | <break>
+ *     LShift    | E0 F0 12           <make> | <break>  E0 12
+ *     RShift    | E0 F0 59           <make> | <break>  E0 59
+ *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
+ *
+ *     b) when Num Lock is on
+ *     modifiers | make                      | break
+ *     ----------+---------------------------+----------------------
+ *     Other     | E0 12              <make> | <break>  E0 F0 12
+ *     Shift'd   |                    <make> | <break>
+ *
+ *     Handling: These prefix/postfix codes are ignored.
+ *
+ *
+ * 2) Keypad /
+ *     modifiers | make                      | break
+ *     ----------+---------------------------+----------------------
+ *     Ohter     |                    <make> | <break>
+ *     LShift    | E0 F0 12           <make> | <break>  E0 12
+ *     RShift    | E0 F0 59           <make> | <break>  E0 59
+ *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
+ *
+ *     Handling: These prefix/postfix codes are ignored.
+ *
+ *
+ * 3) PrintScreen
+ *     modifiers | make         | break
+ *     ----------+--------------+-----------------------------------
+ *     Other     | E0 12  E0 7C | E0 F0 7C  E0 F0 12
+ *     Shift'd   |        E0 7C | E0 F0 7C
+ *     Control'd |        E0 7C | E0 F0 7C
+ *     Alt'd     |           84 | F0 84
+ *
+ *     Handling: These prefix/postfix codes are ignored, and both scan codes
+ *               'E0 7C' and 84 are seen as PrintScreen.
+ *
+ * 4) Pause
+ *     modifiers | make(no break code)
+ *     ----------+--------------------------------------------------
+ *     Other     | E1 14 77 E1 F0 14 F0 77
+ *     Control'd | E0 7E E0 F0 7E
+ *
+ *     Handling: Both code sequences are treated as a whole.
+ *               And we need a ad hoc 'pseudo break code' hack to get the key off
+ *               because it has no break code.
+ *
+ * Notes:
+ * 'Hanguel/English'(F1) and 'Hanja'(F2) have no break code. See [a].
+ * These two Korean keys need exceptional handling and are not supported for now.
+ *
+ */
+static uint8_t cs2_e0code(uint8_t code) {
+    switch(code) {
+        // E0 prefixed codes translation See [a].
+        case 0x11: return 0x0F; // right alt
+        case 0x14: return 0x17; // right control
+        case 0x1F: return 0x19; // left GUI
+        case 0x27: return 0x1F; // right GUI
+        case 0x2F: return 0x5C; // apps
+        case 0x4A: return 0x60; // keypad /
+        case 0x5A: return 0x62; // keypad enter
+        case 0x69: return 0x27; // end
+        case 0x6B: return 0x53; // cursor left
+        case 0x6C: return 0x2F; // home
+        case 0x70: return 0x39; // insert
+        case 0x71: return 0x37; // delete
+        case 0x72: return 0x3F; // cursor down
+        case 0x74: return 0x47; // cursor right
+        case 0x75: return 0x4F; // cursor up
+        case 0x7A: return 0x56; // page down
+        case 0x7D: return 0x5E; // page up
+        case 0x7C: return 0x6F; // Print Screen
+        case 0x7E: return 0x00; // Control'd Pause
+
+        case 0x21: return 0x65; // volume down
+        case 0x32: return 0x6E; // volume up
+        case 0x23: return 0x7F; // mute
+        case 0x10: return 0x08; // (WWW search)     -> F13
+        case 0x18: return 0x10; // (WWW favourites) -> F14
+        case 0x20: return 0x18; // (WWW refresh)    -> F15
+        case 0x28: return 0x20; // (WWW stop)       -> F16
+        case 0x30: return 0x28; // (WWW forward)    -> F17
+        case 0x38: return 0x30; // (WWW back)       -> F18
+        case 0x3A: return 0x38; // (WWW home)       -> F19
+        case 0x40: return 0x40; // (my computer)    -> F20
+        case 0x48: return 0x48; // (email)          -> F21
+        case 0x2B: return 0x50; // (calculator)     -> F22
+        case 0x34: return 0x08; // (play/pause)     -> F13
+        case 0x3B: return 0x10; // (stop)           -> F14
+        case 0x15: return 0x18; // (previous track) -> F15
+        case 0x4D: return 0x20; // (next track)     -> F16
+        case 0x50: return 0x28; // (media select)   -> F17
+        case 0x5E: return 0x50; // (ACPI wake)      -> F22
+        case 0x3F: return 0x57; // (ACPI sleep)     -> F23
+        case 0x37: return 0x5F; // (ACPI power)     -> F24
+
+        // https://github.com/tmk/tmk_keyboard/pull/636
+        case 0x03: return 0x18; // Help        DEC LK411 -> F15
+        case 0x04: return 0x08; // F13         DEC LK411
+        case 0x0B: return 0x20; // Do          DEC LK411 -> F16
+        case 0x0C: return 0x10; // F14         DEC LK411
+        case 0x0D: return 0x19; // LCompose    DEC LK411 -> LGUI
+        case 0x79: return 0x6D; // KP-         DEC LK411 -> PCMM
+        case 0x83: return 0x28; // F17         DEC LK411
+        default: return (code & 0x7F);
+    }
+}
+
+static int8_t process_cs2(void)
+{
+    // scan code reading states
+    static enum {
+        INIT,
+        F0,
+        E0,
+        E0_F0,
+        // Pause
+        E1,
+        E1_14,
+        E1_F0,
+        E1_F0_14,
+        E1_F0_14_F0,
+    } state = INIT;
+
+    uint16_t code = ibmpc_host_recv();
+    if (code == -1) {
+        return 0;
+    }
+
+    switch (state) {
+        case INIT:
+            switch (code) {
+                case 0xE0:
+                    state = E0;
+                    break;
+                case 0xF0:
+                    state = F0;
+                    break;
+                case 0xE1:
+                    state = E1;
+                    break;
+                case 0x83:  // F7
+                    matrix_make(0x02);
+                    state = INIT;
+                    break;
+                case 0x84:  // Alt'd PrintScreen
+                    matrix_make(0x6F);
+                    state = INIT;
+                    break;
+                case 0x00:  // Overrun [3]p.26
+                    matrix_clear();
+                    xprintf("!CS2_OVERRUN!\n");
+                    state = INIT;
+                    break;
+                case 0xAA:  // Self-test passed
+                case 0xFC:  // Self-test failed
+                    // reset or plugin-in new keyboard
+                    state = INIT;
+                    return -1;
+                    break;
+                default:    // normal key make
+                    if (code < 0x80) {
+                        matrix_make(code);
+                    } else {
+                        matrix_clear();
+                        xprintf("!CS2_INIT!\n");
+                    }
+                    state = INIT;
+            }
+            break;
+        case E0:    // E0-Prefixed
+            switch (code) {
+                case 0x12:  // to be ignored
+                case 0x59:  // to be ignored
+                    state = INIT;
+                    break;
+                case 0xF0:
+                    state = E0_F0;
+                    break;
+                default:
+                    if (code < 0x80) {
+                        matrix_make(cs2_e0code(code));
+                    } else {
+                        matrix_clear();
+                        xprintf("!CS2_E0!\n");
+                    }
+                    state = INIT;
+            }
+            break;
+        case F0:    // Break code
+            switch (code) {
+                case 0x83:  // F7
+                    matrix_break(0x02);
+                    state = INIT;
+                    break;
+                case 0x84:  // Alt'd PrintScreen
+                    matrix_break(0x6F);
+                    state = INIT;
+                    break;
+                default:
+                    if (code < 0x80) {
+                        matrix_break(code);
+                    } else {
+                        matrix_clear();
+                        xprintf("!CS2_F0!\n");
+                    }
+                    state = INIT;
+            }
+            break;
+        case E0_F0: // Break code of E0-prefixed
+            switch (code) {
+                case 0x12:  // to be ignored
+                case 0x59:  // to be ignored
+                    state = INIT;
+                    break;
+                default:
+                    if (code < 0x80) {
+                        matrix_break(cs2_e0code(code));
+                    } else {
+                        matrix_clear();
+                        xprintf("!CS2_E0_F0!\n");
+                    }
+                    state = INIT;
+            }
+            break;
+        // Pause make: E1 14 77
+        case E1:
+            switch (code) {
+                case 0x14:
+                    state = E1_14;
+                    break;
+                case 0xF0:
+                    state = E1_F0;
+                    break;
+                default:
+                    state = INIT;
+            }
+            break;
+        case E1_14:
+            switch (code) {
+                case 0x77:
+                    matrix_make(0x00);
+                    state = INIT;
+                    break;
+                default:
+                    state = INIT;
+            }
+            break;
+        // Pause break: E1 F0 14 F0 77
+        case E1_F0:
+            switch (code) {
+                case 0x14:
+                    state = E1_F0_14;
+                    break;
+                default:
+                    state = INIT;
+            }
+            break;
+        case E1_F0_14:
+            switch (code) {
+                case 0xF0:
+                    state = E1_F0_14_F0;
+                    break;
+                default:
+                    state = INIT;
+            }
+            break;
+        case E1_F0_14_F0:
+            switch (code) {
+                case 0x77:
+                    matrix_break(0x00);
+                    state = INIT;
+                    break;
+                default:
+                    state = INIT;
+            }
+            break;
+        default:
+            state = INIT;
+    }
+    return 0;
+}
+
+/*
+ * Terminal: Scan Code Set 3
+ *
+ * See [3], [7]
+ *
+ * Scan code 0x83 and 0x84 are handled exceptioanally to fit into 1-byte range index.
+ */
+static int8_t process_cs3(void)
+{
+    static enum {
+        READY,
+        F0,
+    } state = READY;
+
+    uint16_t code = ibmpc_host_recv();
+    if (code == -1) {
+        return 0;
+    }
+
+    switch (state) {
+        case READY:
+            switch (code) {
+                case 0x00:
+                case 0xff:
+                    xprintf("!CS3_%02X!\n", code);
+                    break;
+                case 0xF0:
+                    state = F0;
+                    break;
+                case 0x83:  // F7
+                    matrix_make(0x02);
+                    break;
+                case 0x84:  // keypad -
+                    matrix_make(0x7F);
+                    break;
+                default:    // normal key make
+                    if (code < 0x80) {
+                        matrix_make(code);
+                    } else {
+                        xprintf("!CS3_%02X!\n", code);
+                    }
+                    state = READY;
+            }
+            break;
+        case F0:    // Break code
+            switch (code) {
+                case 0x00:
+                case 0xff:
+                    xprintf("!CS3_F0_%02X!\n", code);
+                    state = READY;
+                    break;
+                case 0x83:  // F7
+                    matrix_break(0x02);
+                    state = READY;
+                    break;
+                case 0x84:  // keypad -
+                    matrix_break(0x7F);
+                    state = READY;
+                    break;
+                default:
+                    if (code < 0x80) {
+                        matrix_break(code);
+                    } else {
+                        xprintf("!CS3_F0_%02X!\n", code);
+                    }
+                    state = READY;
+            }
+            break;
+    }
+    return 0;
+}
+
+/*
+ * IBM PC Keyboard Protocol Resources:
+ *
+ * [a] Microsoft USB HID to PS/2 Translation Table - Scan Code Set 1 and 2
+ * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
+ *
+ * [b] Microsoft Keyboard Scan Code Specification - Special rules of Scan Code Set 1 and 2
+ * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc
+ *
+ * [1] PS/2 Reference Manuals - Collection of IBM Personal System/2 documents.
+ * http://www.mcamafia.de/pdf/pdfref.htm
+ *
+ * [2] Keyboard and Auxiliary Device Controller - Signal Timing and Format
+ * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
+ *
+ * [3] Keyboards(101- and 102-key) - Keyboard Layout, Scan Code Set, POR, and Commands.
+ * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
+ *
+ * [4] IBM PC XT Keyboard Protocol
+ * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
+ *
+ * [5] IBM Keyboard Scan Code by John Elliott - 83-key, 84-key, 102-key and 122-key
+ * https://www.seasip.info/VintagePC/index.html
+ *
+ * [6] IBM 1391406 Keyboard - Scan Code Set 2 of 102-key PS/2 keyboard
+ * https://www.seasip.info/VintagePC/ibm_1391406.html
+ *
+ * [7] The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard
+ * https://www.seasip.info/VintagePC/ibm_6110344.html
+ *
+ * [y] TrackPoint Engineering Specifications for version 3E
+ * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
+ *
+ * [z] [Soarer's XT/AT/PS2/Terminal to USB converter]
+ * https://geekhack.org/index.php?topic=17458.0
+ *
+ */
diff --git a/converter/ibmpc_usb/ibmpc_usb.h b/converter/ibmpc_usb/ibmpc_usb.h
new file mode 100644 (file)
index 0000000..99bce7e
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef IBMPC_USB_H
+#define IBMPC_USB_H
+
+typedef enum { NONE, PC_XT, PC_AT, PC_TERMINAL } keyboard_kind_t;
+
+extern uint16_t keyboard_id;
+extern keyboard_kind_t keyboard_kind;
+
+#endif
diff --git a/converter/ibmpc_usb/unimap_plain.c b/converter/ibmpc_usb/unimap_plain.c
new file mode 100644 (file)
index 0000000..2a38f8c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+Copyright 2016 Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "unimap_trans.h"
+
+
+#define AC_FN0 ACTION_LAYER_MOMENTARY(1)
+
+#ifdef KEYMAP_SECTION_ENABLE
+const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS] __attribute__ ((section (".keymap.keymaps"))) = {
+#else
+const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS] PROGMEM = {
+#endif
+    UNIMAP(
+              F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24,
+    ESC,      F1,  F2,  F3,  F4,  F5,  F6,  F7,  F8,  F9,  F10, F11, F12,           PSCR,SLCK,PAUS,         VOLD,VOLU,MUTE,
+    GRV, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, JYEN,BSPC,     INS, HOME,PGUP,    NLCK,PSLS,PAST,PMNS,
+    TAB, Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,     BSLS,     DEL, END, PGDN,    P7,  P8,  P9,  PPLS,
+    CAPS,A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,     NUHS,ENT,                         P4,  P5,  P6,  PCMM,
+    LSFT,NUBS,Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,     RO,  RSFT,          UP,           P1,  P2,  P3,  PENT,
+    LCTL,LGUI,LALT,MHEN,          SPC,           HENK,KANA,RALT,RGUI,FN0, RCTL,     LEFT,DOWN,RGHT,         P0,  PDOT,PEQL
+    ),
+    UNIMAP(
+              TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,
+    GRV,      TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,          TRNS,TRNS,TRNS,         TRNS,TRNS,TRNS,
+    ESC, F1,  F2,  F3,  F4,  F5,  F6,  F7,  F8,  F9,  F10, F11, F12, INS, DEL,      TRNS,TRNS,TRNS,    TRNS,TRNS,TRNS,TRNS,
+    CAPS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PSCR,SLCK,PAUS,UP,  INS,      TRNS,     TRNS,TRNS,TRNS,    TRNS,TRNS,TRNS,TRNS,
+    TRNS,VOLD,VOLU,MUTE,TRNS,TRNS,TRNS,TRNS,HOME,PGUP,LEFT,RGHT,     TRNS,TRNS,                        TRNS,TRNS,TRNS,TRNS,
+    TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,END, PGDN,DOWN,     TRNS,TRNS,          PGUP,         TRNS,TRNS,TRNS,TRNS,
+    TRNS,TRNS,TRNS,TRNS,          TRNS,          TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,     HOME,PGDN,END,          TRNS,TRNS,TRNS
+    ),
+};
diff --git a/converter/ibmpc_usb/unimap_trans.h b/converter/ibmpc_usb/unimap_trans.h
new file mode 100644 (file)
index 0000000..f78ea75
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+Copyright 2019 Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef UNIMAP_TRANS_H
+#define UNIMAP_TRANS_H
+
+#include <stdint.h>
+#include <avr/pgmspace.h>
+#include "unimap.h"
+#include "action.h"
+#include "ibmpc_usb.h"
+
+
+
+/* Mapping to Universal keyboard layout
+ *
+ * Universal keyboard layout
+ *         ,-----------------------------------------------.
+ *         |F13|F14|F15|F16|F17|F18|F19|F20|F21|F22|F23|F24|
+ * ,---.   |-----------------------------------------------|     ,-----------.     ,-----------.
+ * |Esc|   |F1 |F2 |F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10|F11|F12|     |PrS|ScL|Pau|     |VDn|VUp|Mut|
+ * `---'   `-----------------------------------------------'     `-----------'     `-----------'
+ * ,-----------------------------------------------------------. ,-----------. ,---------------.
+ * |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|JPY|Bsp| |Ins|Hom|PgU| |NmL|  /|  *|  -|
+ * |-----------------------------------------------------------| |-----------| |---------------|
+ * |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|  \  | |Del|End|PgD| |  7|  8|  9|  +|
+ * |-----------------------------------------------------------| `-----------' |---------------|
+ * |CapsL |  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '| ^a|Retn|               |  4|  5|  6|KP,|
+ * |-----------------------------------------------------------|     ,---.     |---------------|
+ * |Shft|  <|  Z|  X|  C|  V|  B|  N|  M|  ,|  .|  /| RO|Shift |     |Up |     |  1|  2|  3|KP=|
+ * |-----------------------------------------------------------| ,-----------. |---------------|
+ * |Ctl|Gui|Alt|MHEN|     Space      |HENK|KANA|Alt|Gui|App|Ctl| |Lef|Dow|Rig| |  #|  0|  .|Ent|
+ * `-----------------------------------------------------------' `-----------' `---------------'
+ */
+
+
+/*
+ * Scan Code Set 1:
+ * ,-------.  ,--------------------------------------------------------------------------.
+ * | F1| F2|  |Esc|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|  BS  |NumLck |ScrLck |
+ * |-------|  |--------------------------------------------------------------------------|
+ * | F3| F4|  | Tab |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ] |   |  7|  8|  9|  -|
+ * |-------|  |------------------------------------------------------|Ent|---------------|
+ * | F5| F6|  | Ctrl |  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|  `|   |  4|  5|  6|   |
+ * |-------|  |----------------------------------------------------------------------|   |
+ * | F7| F8|  |Shif|  \|  Z|  X|  C|  V|  B|  N|  M|  ,|  .|  /|Shift|  *|  1|  2|  3|  +|
+ * |-------|  |----------------------------------------------------------------------|   |
+ * | F9|F10|  |  Alt  |               Space                  |CapsLck|   0   |   .   |   |
+ * `-------'  `--------------------------------------------------------------------------'
+ *
+ * ,-------.  ,--------------------------------------------------------------------------.
+ * | 3B| 3C|  | 01| 02| 03| 04| 05| 06| 07| 08| 09| 0A| 0B| 0C| 0D|  0E  |  45   |  46   |
+ * |-------|  |--------------------------------------------------------------------------|
+ * | 3D| 3E|  | 0F  | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 1A| 1B |   | 47| 48| 49| 4A|
+ * |-------|  |------------------------------------------------------| 1C|---------------|
+ * | 3F| 40|  | 1D   | 1E| 1F| 20| 21| 22| 23| 24| 25| 26| 27| 28| 29|   | 4B| 4C| 4D|   |
+ * |-------|  |----------------------------------------------------------------------|   |
+ * | 41| 42|  | 2A | 2B| 2C| 2D| 2E| 2F| 30| 31| 32| 33| 34| 35|  36 | 37| 4F| 50| 51| 4E|
+ * |-------|  |----------------------------------------------------------------------|   |
+ * | 43| 44|  |  38   |              39                      |  3A   |  52   |  53   |   |
+ * `-------'  `--------------------------------------------------------------------------'
+ * [3], [a]
+ */
+const uint8_t PROGMEM unimap_cs1[MATRIX_ROWS][MATRIX_COLS] = {
+    { UNIMAP_NO,    UNIMAP_ESC,   UNIMAP_1,     UNIMAP_2,     UNIMAP_3,     UNIMAP_4,     UNIMAP_5,     UNIMAP_6     }, /* 00-07 */
+    { UNIMAP_7,     UNIMAP_8,     UNIMAP_9,     UNIMAP_0,     UNIMAP_MINUS, UNIMAP_EQUAL, UNIMAP_BSPACE,UNIMAP_TAB   }, /* 08-0F */
+    { UNIMAP_Q,     UNIMAP_W,     UNIMAP_E,     UNIMAP_R,     UNIMAP_T,     UNIMAP_Y,     UNIMAP_U,     UNIMAP_I     }, /* 10-17 */
+    { UNIMAP_O,     UNIMAP_P,     UNIMAP_LBRC,  UNIMAP_RBRC,  UNIMAP_ENTER, UNIMAP_LCTL,  UNIMAP_A,     UNIMAP_S,    }, /* 18-1F */
+    { UNIMAP_D,     UNIMAP_F,     UNIMAP_G,     UNIMAP_H,     UNIMAP_J,     UNIMAP_K,     UNIMAP_L,     UNIMAP_SCLN  }, /* 20-27 */
+    { UNIMAP_QUOTE, UNIMAP_GRAVE, UNIMAP_LSHIFT,UNIMAP_BSLASH,UNIMAP_Z,     UNIMAP_X,     UNIMAP_C,     UNIMAP_V,    }, /* 28-2F */
+    { UNIMAP_B,     UNIMAP_N,     UNIMAP_M,     UNIMAP_COMMA, UNIMAP_DOT,   UNIMAP_SLASH, UNIMAP_RSHIFT,UNIMAP_PAST  }, /* 30-37 */
+    { UNIMAP_LALT,  UNIMAP_SPACE, UNIMAP_CAPS,  UNIMAP_F1,    UNIMAP_F2,    UNIMAP_F3,    UNIMAP_F4,    UNIMAP_F5    }, /* 38-3F */
+    { UNIMAP_F6,    UNIMAP_F7,    UNIMAP_F8,    UNIMAP_F9,    UNIMAP_F10,   UNIMAP_NLCK,  UNIMAP_SLCK,  UNIMAP_P7    }, /* 40-47 */
+    { UNIMAP_P8,    UNIMAP_P9,    UNIMAP_PMNS,  UNIMAP_P4,    UNIMAP_P5,    UNIMAP_P6,    UNIMAP_PPLS,  UNIMAP_P1    }, /* 48-4F */
+    { UNIMAP_P2,    UNIMAP_P3,    UNIMAP_P0,    UNIMAP_PDOT,  UNIMAP_PSCR,  UNIMAP_PAUSE, UNIMAP_NUHS,  UNIMAP_F11   }, /* 50-57 */
+    { UNIMAP_F12,   UNIMAP_PEQL,  UNIMAP_LGUI,  UNIMAP_RGUI,  UNIMAP_APP,   UNIMAP_MUTE,  UNIMAP_VOLD,  UNIMAP_VOLU  }, /* 58-5F */
+    { UNIMAP_UP,    UNIMAP_LEFT,  UNIMAP_DOWN,  UNIMAP_RIGHT, UNIMAP_F13,   UNIMAP_F14,   UNIMAP_F15,   UNIMAP_F16   }, /* 60-67 */
+    { UNIMAP_F17,   UNIMAP_F18,   UNIMAP_F19,   UNIMAP_F20,   UNIMAP_F21,   UNIMAP_F22,   UNIMAP_F23,   UNIMAP_PENT  }, /* 68-6F */
+    { UNIMAP_KANA,  UNIMAP_INSERT,UNIMAP_DELETE,UNIMAP_RO,    UNIMAP_HOME,  UNIMAP_END,   UNIMAP_F24,   UNIMAP_PGUP  }, /* 70-77 */
+    { UNIMAP_PGDN,  UNIMAP_HENK,  UNIMAP_RCTL,  UNIMAP_MHEN,  UNIMAP_RALT,  UNIMAP_JYEN,  UNIMAP_PCMM,  UNIMAP_PSLS  }, /* 78-7F */
+};
+
+
+/*
+ * Scan Code Set 2:
+ *         ,-----------------------------------------------.
+ *         |F13|F14|F15|F16|F17|F18|F19|F20|F21|F22|F23|F24|
+ * ,---.   |-----------------------------------------------|     ,-----------.     ,-----------.
+ * |Esc|   |F1 |F2 |F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10|F11|F12|     |PrS|ScL|Pau|     |VDn|VUp|Mut|
+ * `---'   `-----------------------------------------------'     `-----------'     `-----------'
+ * ,-----------------------------------------------------------. ,-----------. ,---------------.
+ * |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|JPY|Bsp| |Ins|Hom|PgU| |NmL|  /|  *|  -|
+ * |-----------------------------------------------------------| |-----------| |---------------|
+ * |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|  \  | |Del|End|PgD| |  7|  8|  9|  +|
+ * |-----------------------------------------------------------| `-----------' |---------------|
+ * |CapsL |  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '| ^a|Entr|               |  4|  5|  6|KP,|
+ * |-----------------------------------------------------------|     ,---.     |---------------|
+ * |Shft|  <|  Z|  X|  C|  V|  B|  N|  M|  ,|  .|  /| RO|Shift |     |Up |     |  1|  2|  3|Ent|
+ * |-----------------------------------------------------------| ,-----------. |---------------|
+ * |Ctl|Gui|Alt|MHEN|     Space      |HENK|KANA|Alt|Gui|App|Ctl| |Lef|Dow|Rig| |  #|  0|  .|KP=|
+ * `-----------------------------------------------------------' `-----------' `---------------'
+ *
+ *         ,-----------------------------------------------.
+ *         | 08| 10| 18| 20| 28| 30| 38| 40| 48| 50| 57| 5F|
+ * ,---.   |-----------------------------------------------|     ,-----------.     ,-----------.
+ * | 76|   | 05| 06| 04| 0C| 03| 0B| 83| 0A| 01| 09| 78| 07|     |+7C| 7E|+77|     |*21|*32|*23|
+ * `---'   `-----------------------------------------------'     `-----------'     `-----------'
+ * ,-----------------------------------------------------------. ,-----------. ,---------------.
+ * | 0E| 16| 1E| 26| 25| 2E| 36| 3D| 3E| 46| 45| 4E| 55| 6A| 66| |*70|*6C|*7D| | 77|*4A| 7C| 7B|
+ * |-----------------------------------------------------------| |-----------| |---------------|
+ * | 0D  | 15| 1D| 24| 2D| 2C| 35| 3C| 43| 44| 4D| 54| 5B|  5D | |*71|*69|*7A| | 6C| 75| 7D| 79|
+ * |-----------------------------------------------------------| `-----------' |---------------|
+ * | 58   | 1C| 1B| 23| 2B| 34| 33| 3B| 42| 4B| 4C| 52| ^a| 5A |               | 6B| 73| 74| 6D|
+ * |-----------------------------------------------------------|     ,---.     |---------------|
+ * | 12 | 61| 1A| 22| 21| 2A| 32| 31| 3A| 41| 49| 4A| 51|  59  |     |*75|     | 69| 72| 7A|*5A|
+ * |-----------------------------------------------------------| ,-----------. |---------------|
+ * | 14|*1F| 11| 67 |     29         | 64 | 13 |*11|*27|*2F|*14| |*6B|*72|*74| | 68| 70| 71| 63|
+ * `-----------------------------------------------------------' `-----------' `---------------'
+ * *: E0-prefixed codes
+ * +: Special codes sequence
+ * ^a: ISO hash key uses identical scancode 5D to US backslash.
+ * 51, 63, 68, 6A, 6D: Hidden keys in IBM model M [6]
+ */
+const uint8_t PROGMEM unimap_cs2[MATRIX_ROWS][MATRIX_COLS] = {
+    { UNIMAP_PAUS,  UNIMAP_F9,    UNIMAP_F7,    UNIMAP_F5,    UNIMAP_F3,    UNIMAP_F1,    UNIMAP_F2,    UNIMAP_F12   }, /* 00-07 */
+    { UNIMAP_F13,   UNIMAP_F10,   UNIMAP_F8,    UNIMAP_F6,    UNIMAP_F4,    UNIMAP_TAB,   UNIMAP_GRV,   UNIMAP_RALT  }, /* 08-0F */
+    { UNIMAP_F14,   UNIMAP_LALT,  UNIMAP_LSHIFT,UNIMAP_KANA,  UNIMAP_LCTL,  UNIMAP_Q,     UNIMAP_1,     UNIMAP_RCTL  }, /* 10-17 */
+    { UNIMAP_F15,   UNIMAP_LGUI,  UNIMAP_Z,     UNIMAP_S,     UNIMAP_A,     UNIMAP_W,     UNIMAP_2,     UNIMAP_RGUI  }, /* 18-1F */
+    { UNIMAP_F16,   UNIMAP_C,     UNIMAP_X,     UNIMAP_D,     UNIMAP_E,     UNIMAP_4,     UNIMAP_3,     UNIMAP_END   }, /* 20-27 */
+    { UNIMAP_F17,   UNIMAP_SPACE, UNIMAP_V,     UNIMAP_F,     UNIMAP_T,     UNIMAP_R,     UNIMAP_5,     UNIMAP_HOME  }, /* 28-2F */
+    { UNIMAP_F18,   UNIMAP_N,     UNIMAP_B,     UNIMAP_H,     UNIMAP_G,     UNIMAP_Y,     UNIMAP_6,     UNIMAP_DEL   }, /* 30-37 */
+    { UNIMAP_F19,   UNIMAP_INS,   UNIMAP_M,     UNIMAP_J,     UNIMAP_U,     UNIMAP_7,     UNIMAP_8,     UNIMAP_DOWN  }, /* 38-3F */
+    { UNIMAP_F20,   UNIMAP_COMMA, UNIMAP_K,     UNIMAP_I,     UNIMAP_O,     UNIMAP_0,     UNIMAP_9,     UNIMAP_RIGHT }, /* 40-47 */
+    { UNIMAP_F21,   UNIMAP_DOT,   UNIMAP_SLASH, UNIMAP_L,     UNIMAP_SCOLON,UNIMAP_P,     UNIMAP_MINUS, UNIMAP_UP    }, /* 48-4F */
+    { UNIMAP_F22,   UNIMAP_RO,    UNIMAP_QUOTE, UNIMAP_LEFT,  UNIMAP_LBRC,  UNIMAP_EQUAL, UNIMAP_PGDN,  UNIMAP_F23   }, /* 50-57 */
+    { UNIMAP_CAPS,  UNIMAP_RSHIFT,UNIMAP_ENTER, UNIMAP_RBRC,  UNIMAP_APP,   UNIMAP_BSLASH,UNIMAP_PGUP,  UNIMAP_F24   }, /* 58-5F */
+    { UNIMAP_PSLS,  UNIMAP_NUBS,  UNIMAP_PENT,  UNIMAP_PEQL,  UNIMAP_HENK,  UNIMAP_VOLD,  UNIMAP_BSPACE,UNIMAP_MHEN  }, /* 60-67 */
+    { UNIMAP_NUHS,  UNIMAP_P1,    UNIMAP_JYEN,  UNIMAP_P4,    UNIMAP_P7,    UNIMAP_PCMM,  UNIMAP_VOLU,  UNIMAP_PSCR  }, /* 68-6F */
+    { UNIMAP_P0,    UNIMAP_PDOT,  UNIMAP_P2,    UNIMAP_P5,    UNIMAP_P6,    UNIMAP_P8,    UNIMAP_ESC,   UNIMAP_NLCK  }, /* 70-77 */
+    { UNIMAP_F11,   UNIMAP_PPLS,  UNIMAP_P3,    UNIMAP_PMNS,  UNIMAP_PAST,  UNIMAP_P9,    UNIMAP_SLCK,  UNIMAP_MUTE  }, /* 78-7F */
+};
+
+
+/*
+ * Scan Code Set 3:
+ *               ,-----------------------------------------------.
+ *               |F13|F14|F15|F16|F17|F18|F19|F20|F21|F22|F23|F24|
+ *               |-----------------------------------------------|
+ *               |F1 |F2 |F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10|F11|F12|
+ *               `-----------------------------------------------'
+ * ,-------. ,-----------------------------------------------------------. ,-----------. ,---------------.
+ * |PrS|Esc| |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|Yen| BS| |Ins|Hom|PgU| |NmL|  /|  *|  -|
+ * |-------| |-----------------------------------------------------------| |-----------| |---------------|
+ * |ScL|Hen| |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|    \| |Del|End|PgD| |  7|  8|  9|  +|
+ * |-------| |-----------------------------------------------------------| `-----------' |-----------|---|
+ * |Pau|Muh| |CapsLo|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|  #| Ret|     |Up |     |  4|  5|  6|  ,|
+ * |-------| |-----------------------------------------------------------| ,-----------. |---------------|
+ * |VoD|VoU| |Shif|  \|  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /| RO| Shift| |Lef|App|Rig| |  1|  2|  3|Ent|
+ * |-------| |-----------------------------------------------------------| `-----------' |-----------|---|
+ * |Gui|Gui| |Ctrl|    |Alt |          Space              |Alt |    |Ctrl|     |Dow|     |Kan|  0|  .|  =|
+ * `-------' `----'    `---------------------------------------'    `----'     `---'     `---------------'
+ *
+ *               ,-----------------------------------------------.
+ *               | 08| 10| 18| 20| 28| 30| 38| 40| 48| 50| 57| 5F|
+ *               |-----------------------------------------------|
+ *               | 07| 0F| 17| 1F| 27| 2F| 37| 3F| 47| 4F| 56| 5E|
+ *               `-----------------------------------------------'
+ * ,-------. ,-----------------------------------------------------------. ,-----------. ,---------------.
+ * | 05| 06| | 0E| 16| 1E| 26| 25| 2E| 36| 3D| 3E| 46| 45| 4E| 55| 5D| 66| | 67| 6E| 6F| | 76| 77| 7E|*84|
+ * |-------| |-----------------------------------------------------------| |-----------| |---------------|
+ * | 04| 0C| | 0D  | 15| 1D| 24| 2D| 2C| 35| 3C| 43| 44| 4D| 54| 5B|  5C | | 64| 65| 6D| | 6C| 75| 7D| 7C|
+ * |-------| |-----------------------------------------------------------| `-----------' |---------------|
+ * | 03| 0B| | 58   | 1C| 1B| 23| 2B| 34| 33| 3B| 42| 4B| 4C| 52| 53| 5A |     | 63|     | 6B| 73| 74| 7B|
+ * |-------| |-----------------------------------------------------------| ,-----------. |---------------|
+ * |*83| 0A| | 12 | 13| 1A| 22| 21| 2A| 32| 31| 3A| 41| 49| 4A| 51|  59  | | 61| 62| 6A| | 69| 72| 7A| 79|
+ * |-------| |-----------------------------------------------------------| `-----------' |---------------|
+ * | 01| 09| | 11  |   |19  |        29                   |39  |   | 58  |     | 60|     | 68| 70| 71| 78|
+ * `-------' `-----'   `---------------------------------------'   `-----'     `---'     `---------------'
+ * *: 83=02, 84=7F
+ * 51, 5C, 5D, 68, 78: Hidden keys in IBM 122-key terminal keyboard [7]
+ */
+const uint8_t PROGMEM unimap_cs3[MATRIX_ROWS][MATRIX_COLS] = {
+    { UNIMAP_NO,    UNIMAP_LGUI,  UNIMAP_VOLD,  UNIMAP_PAUSE, UNIMAP_SLCK,  UNIMAP_PSCR,  UNIMAP_ESC,   UNIMAP_F1    }, /* 00-07 */
+    { UNIMAP_F13,   UNIMAP_RGUI,  UNIMAP_VOLU,  UNIMAP_MHEN,  UNIMAP_HENK,  UNIMAP_TAB,   UNIMAP_GRV,   UNIMAP_F2    }, /* 08-0F */
+    { UNIMAP_F14,   UNIMAP_LCTL,  UNIMAP_LSHIFT,UNIMAP_NUBS,  UNIMAP_CAPS,  UNIMAP_Q,     UNIMAP_1,     UNIMAP_F3    }, /* 10-17 */
+    { UNIMAP_F15,   UNIMAP_LALT,  UNIMAP_Z,     UNIMAP_S,     UNIMAP_A,     UNIMAP_W,     UNIMAP_2,     UNIMAP_F4    }, /* 18-1F */
+    { UNIMAP_F16,   UNIMAP_C,     UNIMAP_X,     UNIMAP_D,     UNIMAP_E,     UNIMAP_4,     UNIMAP_3,     UNIMAP_F5    }, /* 20-27 */
+    { UNIMAP_F17,   UNIMAP_SPACE, UNIMAP_V,     UNIMAP_F,     UNIMAP_T,     UNIMAP_R,     UNIMAP_5,     UNIMAP_F6    }, /* 28-2F */
+    { UNIMAP_F18,   UNIMAP_N,     UNIMAP_B,     UNIMAP_H,     UNIMAP_G,     UNIMAP_Y,     UNIMAP_6,     UNIMAP_F7    }, /* 30-37 */
+    { UNIMAP_F19,   UNIMAP_RALT,  UNIMAP_M,     UNIMAP_J,     UNIMAP_U,     UNIMAP_7,     UNIMAP_8,     UNIMAP_F8    }, /* 38-3F */
+    { UNIMAP_F20,   UNIMAP_COMMA, UNIMAP_K,     UNIMAP_I,     UNIMAP_O,     UNIMAP_0,     UNIMAP_9,     UNIMAP_F9    }, /* 40-47 */
+    { UNIMAP_F21,   UNIMAP_DOT,   UNIMAP_SLASH, UNIMAP_L,     UNIMAP_SCOLON,UNIMAP_P,     UNIMAP_MINUS, UNIMAP_F10   }, /* 48-4F */
+    { UNIMAP_F22,   UNIMAP_RO,    UNIMAP_QUOTE, UNIMAP_NUHS,  UNIMAP_LBRC,  UNIMAP_EQUAL, UNIMAP_F11,   UNIMAP_F23   }, /* 50-57 */
+    { UNIMAP_RCTL,  UNIMAP_RSHIFT,UNIMAP_ENTER, UNIMAP_RBRC,  UNIMAP_BSLASH,UNIMAP_JYEN,  UNIMAP_F12,   UNIMAP_F24   }, /* 58-5F */
+    { UNIMAP_DOWN,  UNIMAP_LEFT,  UNIMAP_APP,   UNIMAP_UP,    UNIMAP_DEL,   UNIMAP_END,   UNIMAP_BSPACE,UNIMAP_INS   }, /* 60-67 */
+    { UNIMAP_KANA,  UNIMAP_P1,    UNIMAP_RIGHT, UNIMAP_P4,    UNIMAP_P7,    UNIMAP_PGDN,  UNIMAP_HOME,  UNIMAP_PGUP  }, /* 68-6F */
+    { UNIMAP_P0,    UNIMAP_PDOT,  UNIMAP_P2,    UNIMAP_P5,    UNIMAP_P6,    UNIMAP_P8,    UNIMAP_NLCK,  UNIMAP_PSLS  }, /* 70-77 */
+    { UNIMAP_PEQL,  UNIMAP_PENT,  UNIMAP_P3,    UNIMAP_PCMM,  UNIMAP_PPLS,  UNIMAP_P9,    UNIMAP_PAST,  UNIMAP_PMNS  }, /* 78-7F */
+};
+
+
+extern const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS];
+action_t action_for_key(uint8_t layer, keypos_t key)
+{
+    uint8_t unimap_pos;
+    switch (keyboard_kind) {
+        case PC_XT:
+            unimap_pos = pgm_read_byte(&unimap_cs1[key.row][key.col]);
+            break;
+        case PC_AT:
+            unimap_pos = pgm_read_byte(&unimap_cs2[key.row][key.col]);
+            break;
+        case PC_TERMINAL:
+            unimap_pos = pgm_read_byte(&unimap_cs3[key.row][key.col]);
+            break;
+        default:
+            return (action_t)ACTION_NO;
+    }
+
+    if (unimap_pos == UNIMAP_NO) return (action_t)ACTION_NO;
+
+    return (action_t)pgm_read_word(&actionmaps[(layer)][(unimap_pos & 0x70) >> 4][(unimap_pos & 0x0f)]);
+}
+#endif