diff --git a/docs/PS2 Keyboard.pdf b/docs/PS2 Keyboard.pdf new file mode 100644 index 0000000..f12d03f Binary files /dev/null and b/docs/PS2 Keyboard.pdf differ diff --git a/docs/PS_2 Mouse Interfacing.pdf b/docs/PS_2 Mouse Interfacing.pdf new file mode 100644 index 0000000..17f5bce Binary files /dev/null and b/docs/PS_2 Mouse Interfacing.pdf differ diff --git a/docs/atkeyboard.pdf b/docs/atkeyboard.pdf new file mode 100644 index 0000000..62f0c91 Binary files /dev/null and b/docs/atkeyboard.pdf differ diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 01275c9..cd77187 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -68,7 +68,7 @@ menu "MZ25Key Configuration" GPIO number (IOxx) used to connect the MZ-2500/2800 4bit bidirectional data bus Bit 2 with the ESP32. See schematic for actual used value. May change with revisions. config MZ_KDB3 - int "KDB1 GPIO pin number" + int "KDB3 GPIO pin number" range 0 46 default 27 help @@ -149,6 +149,72 @@ menu "MZ25Key Configuration" endmenu + menu "WiFi" + + config MZ_WIFI_ENABLED + bool "Enable WiFi connectivity" + default false + help + Allow interface to act as an Access Point to allow external connectivity. Once connected the WiFi is intended to be used for making + key mapping changes. + This is an experimental feature and under development. + + config MZ_WIFI_EN_KEY + int "WiFi Enable GPIO pin number" + range 0 46 + default 34 + depends on MZ_WIFI_ENABLED + help + GPIO number (IOxx) used by the WiFi En switch to enable wifi connectivity. + + config MZ_SSID + string "Default SSID in Access Point Mode" + default "mz25key" + depends on MZ_WIFI_ENABLED + help + The SSID broadcast whilst the mz25key module advertises wireless connectivity. + + config MZ_DEFAULT_SSID_PWD + string "Default password for initial connection to Access Point Mode" + default "mz25key" + depends on MZ_WIFI_ENABLED + help + The initial password needed to connect and logon to access point. + + config MZ_WIFI_MAX_RETRIES + int "Maximum number of connection retries." + range 0 100 + default 10 + depends on MZ_WIFI_ENABLED + help + Number of retries allowed for making a wireless connection with a client. + + config MZ_WIFI_AP_CHANNEL + int "Channel of the Access Point." + range 0 13 + default 7 + depends on MZ_WIFI_ENABLED + help + Channel use by the Access Point, default is 7. + + config MZ_WIFI_SSID_HIDDEN + int "Broadcast SSID?" + range 0 1 + default 0 + depends on MZ_WIFI_ENABLED + help + Broadcast the SSID (0) or hide it (1). + + config MZ_WIFI_MAX_CONNECTIONS + int "Maximum sinultaneous connections." + range 0 20 + default 5 + depends on MZ_WIFI_ENABLED + help + Maximum number of simultaneous open connections supported. + + endmenu + menu "Debug Options" menu "OLED" diff --git a/main/MZKeyTable.h b/main/MZKeyTable.h index d34e6a5..f4838f6 100644 --- a/main/MZKeyTable.h +++ b/main/MZKeyTable.h @@ -12,6 +12,13 @@ // Copyright: (c) 2019-2022 Philip Smart // // History: Jan 2022 - Initial write. +// Feb 2022 - Added parameterisation to the mapping table such that keymaps +// are applied according to machine selected to cater for differences +// between the MZ2500/2000/80B. Also added release parameters which are +// needed when say SHIFT+Key on PS/2 is required whereas on the MZ +// only the mapped key. As SHIFT is an override and will be presented to +// the MZ, it needs to be deactivated before applying a mapped key then +// reactivated after completion. // // Notes: See Makefile to enable/disable conditional components // @@ -73,27 +80,39 @@ // // +#define MZ_ALL 0 +#define MZ_80B 1 +#define MZ_2000 2 +#define MZ_2500 3 #define PSMZTBL_KEYPOS 0 -#define PSMZTBL_SHIFTPOS 1 -#define PSMZTBL_FUNCPOS 2 -#define PSMZTBL_CTRLPOS 3 -#define PSMZTBL_ALTPOS 4 -#define PSMZTBL_ALTGRPOS 5 -#define PSMZTBL_GUIPOS 6 -#define PSMZTBL_MXROW1 7 -#define PSMZTBL_MXKEY1 8 -#define PSMZTBL_MXROW2 9 -#define PSMZTBL_MXKEY2 10 -#define PSMZTBL_MXROW3 11 -#define PSMZTBL_MXKEY3 12 -#define PSMZTBL_MAXROWS 13 +#define PSMZTBL_MACHINE 1 +#define PSMZTBL_SHIFTPOS 2 +#define PSMZTBL_FUNCPOS 3 +#define PSMZTBL_CTRLPOS 4 +#define PSMZTBL_ALTPOS 5 +#define PSMZTBL_ALTGRPOS 6 +#define PSMZTBL_GUIPOS 7 +#define PSMZTBL_MK_ROW1 8 +#define PSMZTBL_MK_KEY1 9 +#define PSMZTBL_MK_ROW2 10 +#define PSMZTBL_MK_KEY2 11 +#define PSMZTBL_MK_ROW3 12 +#define PSMZTBL_MK_KEY3 13 +#define PSMZTBL_BRK_ROW1 14 +#define PSMZTBL_BRK_KEY1 15 +#define PSMZTBL_BRK_ROW2 16 +#define PSMZTBL_BRK_KEY2 17 +#define PSMZTBL_MAXROWS 18 -// Lookup table to matrix row/column co-ordinates. If a PS2 key is matched, then the Matrix is updated -// using ROW to point into the array with a column value, equivalent of strobe line and the KEY defines the bits to be set. -// Upto 3 matrix bits can be set (3 key presses on the MZ-2500 keyboard) per PS/2 key. -// A set bit = 1, reset bits = 0 but is inverted in the actual matrix (1 = inactive, 0 = active). -// The table is scanned for a match from top to bottom. The first match is used so order is important. Japanese characters still -// need to be added. +// Lookup table to matrix row/column co-ordinates. +// +// Given that the MZ-2500 can emulate 3 machines and each machine has it's own mapping, differences are tagged by machine name, ie. ALL, MZ80B, MZ2000, MZ2500 +// +// If a PS2 key is matched, then the Matrix is updated using MK_ROW to point into the array with MK_KEY being the column value, equivalent of strobe line and +// the required KEY bits to be set. Upto 3 matrix bits can be set (3 key presses on the MZ-2500 keyboard) per PS/2 key. Upto 2 matrix releases can be set per +// PS/2 key. A key release is used when a modifier may already have been pressed, ie. SHIFT and it needs to be released to set the required key into the matrix. +// A set bit = 1, reset bits = 0 but is inverted in the actual matrix (1 = inactive, 0 = active), this applies for releases two, if bit = 1 then that key will be released. +// The table is scanned for a match from top to bottom. The first match is used so order is important. Japanese characters are being added as I gleam more information. // #if defined(CONFIG_KEYMAP_WYSE_KB3926) || defined(CONFIG_KEYMAP_STANDARD) // @@ -101,167 +120,179 @@ // const unsigned char PS2toMZ[][PSMZTBL_MAXROWS] = { - // PS2 Code Shift Function Ctrl ALT ALT-Gr GUI MXROW1 MXKEY1 MXROW2 MXKEY2 MXROW3 MXKEY3 - PS2_KEY_F1, 0, 0, 0, 0, 0, 0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // F1 - PS2_KEY_F2, 0, 0, 0, 0, 0, 0, 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // F2 - PS2_KEY_F3, 0, 0, 0, 0, 0, 0, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // F3 - PS2_KEY_F4, 0, 0, 0, 0, 0, 0, 0x00, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // F4 - PS2_KEY_F5, 0, 0, 0, 0, 0, 0, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // F5 - PS2_KEY_F6, 0, 0, 0, 0, 0, 0, 0x00, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // F6 - PS2_KEY_F7, 0, 0, 0, 0, 0, 0, 0x00, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // F7 - PS2_KEY_F8, 0, 0, 0, 0, 0, 0, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // F8 - PS2_KEY_F9, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // F9 - PS2_KEY_F10, 0, 0, 0, 0, 0, 0, 0x01, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // F10 - PS2_KEY_F11, 0, 0, 0, 0, 0, 0, 0x0D, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // HELP - PS2_KEY_F12, 0, 0, 0, 0, 0, 0, 0x0A, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // COPY - PS2_KEY_TAB, 0, 0, 0, 0, 0, 0, 0x03, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // TAB - PS2_KEY_0, 1, 0, 0, 0, 0, 0, 0x08, 0x01, 0x0B, 0x04, 0xFF, 0xFF, // Close Right Bracket ) - PS2_KEY_0, 0, 0, 0, 0, 0, 0, 0x08, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // 0 - PS2_KEY_1, 1, 0, 0, 0, 0, 0, 0x08, 0x02, 0x0B, 0x04, 0xFF, 0xFF, // Exclamation - PS2_KEY_1, 0, 0, 0, 0, 0, 0, 0x08, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // 1 - PS2_KEY_2, 1, 0, 0, 0, 0, 0, 0x08, 0x04, 0x0B, 0x04, 0xFF, 0xFF, // Double quote. - PS2_KEY_2, 0, 0, 0, 0, 0, 0, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // 2 - PS2_KEY_3, 1, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Pound Sign - PS2_KEY_3, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // 3 - PS2_KEY_4, 1, 0, 0, 0, 0, 0, 0x08, 0x10, 0x0B, 0x04, 0xFF, 0xFF, // Dollar - PS2_KEY_4, 0, 0, 0, 0, 0, 0, 0x08, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // 4 - PS2_KEY_5, 1, 0, 0, 0, 0, 0, 0x08, 0x20, 0x0B, 0x04, 0xFF, 0xFF, // Percent - PS2_KEY_5, 0, 0, 0, 0, 0, 0, 0x08, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // 5 - PS2_KEY_6, 1, 0, 0, 0, 0, 0, 0x07, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // Kappa - PS2_KEY_6, 0, 0, 0, 0, 0, 0, 0x08, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // 6 - PS2_KEY_7, 1, 0, 0, 0, 0, 0, 0x08, 0x40, 0x0B, 0x04, 0xFF, 0xFF, // Ampersand - PS2_KEY_7, 0, 0, 0, 0, 0, 0, 0x08, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // 7 - PS2_KEY_8, 1, 0, 0, 0, 0, 0, 0x09, 0x04, 0x0B, 0x04, 0xFF, 0xFF, // Star - PS2_KEY_8, 0, 0, 0, 0, 0, 0, 0x09, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // 8 - PS2_KEY_9, 1, 0, 0, 0, 0, 0, 0x09, 0x02, 0x0B, 0x04, 0xFF, 0xFF, // Open Left Bracket ( - PS2_KEY_9, 0, 0, 0, 0, 0, 0, 0x09, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // 9 - PS2_KEY_A, 1, 0, 0, 0, 0, 0, 0x04, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // a - PS2_KEY_A, 0, 0, 0, 0, 0, 0, 0x04, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // A - PS2_KEY_B, 1, 0, 0, 0, 0, 0, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // b - PS2_KEY_B, 0, 0, 0, 0, 0, 0, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // B - PS2_KEY_C, 1, 0, 0, 0, 0, 0, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // c - PS2_KEY_C, 0, 0, 0, 0, 0, 0, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // C - PS2_KEY_D, 1, 0, 0, 0, 0, 0, 0x04, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // d - PS2_KEY_D, 0, 0, 0, 0, 0, 0, 0x04, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // D - PS2_KEY_E, 1, 0, 0, 0, 0, 0, 0x04, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // e - PS2_KEY_E, 0, 0, 0, 0, 0, 0, 0x04, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // E - PS2_KEY_F, 1, 0, 0, 0, 0, 0, 0x04, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // f - PS2_KEY_F, 0, 0, 0, 0, 0, 0, 0x04, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // F - PS2_KEY_G, 1, 0, 0, 0, 0, 0, 0x04, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // g - PS2_KEY_G, 0, 0, 0, 0, 0, 0, 0x04, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // G - PS2_KEY_H, 1, 0, 0, 0, 0, 0, 0x05, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // h - PS2_KEY_H, 0, 0, 0, 0, 0, 0, 0x05, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // H - PS2_KEY_I, 1, 0, 0, 0, 0, 0, 0x05, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // i - PS2_KEY_I, 0, 0, 0, 0, 0, 0, 0x05, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // I - PS2_KEY_J, 1, 0, 0, 0, 0, 0, 0x05, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // j - PS2_KEY_J, 0, 0, 0, 0, 0, 0, 0x05, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // J - PS2_KEY_K, 1, 0, 0, 0, 0, 0, 0x05, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // k - PS2_KEY_K, 0, 0, 0, 0, 0, 0, 0x05, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // K - PS2_KEY_L, 1, 0, 0, 0, 0, 0, 0x05, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // l - PS2_KEY_L, 0, 0, 0, 0, 0, 0, 0x05, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // L - PS2_KEY_M, 1, 0, 0, 0, 0, 0, 0x05, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // m - PS2_KEY_M, 0, 0, 0, 0, 0, 0, 0x05, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // M - PS2_KEY_N, 1, 0, 0, 0, 0, 0, 0x05, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // n - PS2_KEY_N, 0, 0, 0, 0, 0, 0, 0x05, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // N - PS2_KEY_O, 1, 0, 0, 0, 0, 0, 0x05, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // o - PS2_KEY_O, 0, 0, 0, 0, 0, 0, 0x05, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // O - PS2_KEY_P, 1, 0, 0, 0, 0, 0, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // p - PS2_KEY_P, 0, 0, 0, 0, 0, 0, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // P - PS2_KEY_Q, 1, 0, 0, 0, 0, 0, 0x06, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // q - PS2_KEY_Q, 0, 0, 0, 0, 0, 0, 0x06, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // Q - PS2_KEY_R, 1, 0, 0, 0, 0, 0, 0x06, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // r - PS2_KEY_R, 0, 0, 0, 0, 0, 0, 0x06, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // R - PS2_KEY_S, 1, 0, 0, 0, 0, 0, 0x06, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // s - PS2_KEY_S, 0, 0, 0, 0, 0, 0, 0x06, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // S - PS2_KEY_T, 1, 0, 0, 0, 0, 0, 0x06, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // t - PS2_KEY_T, 0, 0, 0, 0, 0, 0, 0x06, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // T - PS2_KEY_U, 1, 0, 0, 0, 0, 0, 0x06, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // u - PS2_KEY_U, 0, 0, 0, 0, 0, 0, 0x06, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // U - PS2_KEY_V, 1, 0, 0, 0, 0, 0, 0x06, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // v - PS2_KEY_V, 0, 0, 0, 0, 0, 0, 0x06, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // V - PS2_KEY_W, 1, 0, 0, 0, 0, 0, 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // w - PS2_KEY_W, 0, 0, 0, 0, 0, 0, 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // W - PS2_KEY_X, 1, 0, 0, 0, 0, 0, 0x07, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // x - PS2_KEY_X, 0, 0, 0, 0, 0, 0, 0x07, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // X - PS2_KEY_Y, 1, 0, 0, 0, 0, 0, 0x07, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // y - PS2_KEY_Y, 0, 0, 0, 0, 0, 0, 0x07, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // Y - PS2_KEY_Z, 1, 0, 0, 0, 0, 0, 0x07, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // z - PS2_KEY_Z, 0, 0, 0, 0, 0, 0, 0x07, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // Z - // PS2 Code Shift Function Ctrl ALT ALT-Gr GUI MXROW1 MXKEY1 MXROW2 MXKEY2 MXROW3 MXKEY3 - PS2_KEY_SPACE, 0, 0, 0, 0, 0, 0, 0x03, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // Space - PS2_KEY_COMMA, 1, 0, 0, 0, 0, 0, 0x07, 0x80, 0x0B, 0x04, 0xFF, 0xFF, // Less Than < - PS2_KEY_COMMA, 0, 0, 0, 0, 0, 0, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // Comma , - PS2_KEY_SEMI, 1, 0, 0, 0, 0, 0, 0x09, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // Colon : - PS2_KEY_SEMI, 0, 0, 0, 0, 0, 0, 0x09, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // Semi-Colon ; - PS2_KEY_DOT, 1, 0, 0, 0, 0, 0, 0x07, 0x40, 0x0B, 0x04, 0xFF, 0xFF, // Greater Than > - PS2_KEY_DOT, 0, 0, 0, 0, 0, 0, 0x07, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // Full stop . - PS2_KEY_DIV, 1, 0, 0, 0, 0, 0, 0x04, 0x01, 0x0B, 0x04, 0xFF, 0xFF, // Question ? - PS2_KEY_DIV, 0, 0, 0, 0, 0, 0, 0x04, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // Divide / - PS2_KEY_MINUS, 1, 0, 0, 0, 0, 0, 0x07, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // Underscore - PS2_KEY_MINUS, 0, 0, 0, 0, 0, 0, 0x09, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, - PS2_KEY_APOS, 1, 0, 0, 0, 0, 0, 0x09, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // At @ - PS2_KEY_APOS, 0, 0, 0, 0, 0, 0, 0x08, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // Single quote ' - PS2_KEY_OPEN_SQ, 1, 0, 0, 0, 0, 0, 0x09, 0x40, 0x0B, 0x04, 0xFF, 0xFF, // Open Left Brace { - PS2_KEY_OPEN_SQ, 0, 0, 0, 0, 0, 0, 0x09, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // Open Left Square Bracket [ - PS2_KEY_EQUAL, 1, 0, 0, 0, 0, 0, 0x09, 0x08, 0x0B, 0x04, 0xFF, 0xFF, // Plus + - PS2_KEY_EQUAL, 0, 0, 0, 0, 0, 0, 0x09, 0x10, 0x0B, 0x04, 0xFF, 0xFF, // Equal = - PS2_KEY_CAPS, 0, 0, 0, 0, 0, 0, 0x0B, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // LOCK - PS2_KEY_ENTER, 0, 0, 0, 0, 0, 0, 0x03, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // ENTER/RETURN - PS2_KEY_CLOSE_SQ, 0, 0, 0, 0, 0, 0, 0x0A, 0x01, 0x0B, 0x04, 0xFF, 0xFF, // Close Right Brace } - PS2_KEY_CLOSE_SQ, 0, 0, 0, 0, 0, 0, 0x0A, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // Close Right Square Bracket ] - PS2_KEY_BACK, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // - PS2_KEY_BTICK, 0, 0, 0, 0, 0, 0, 0x09, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // Back tick ` - PS2_KEY_HASH, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // Hash - PS2_KEY_BS, 0, 0, 0, 0, 0, 0, 0x0A, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // Backspace - PS2_KEY_ESC, 0, 0, 0, 0, 0, 0, 0x0A, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // ESCape - PS2_KEY_SCROLL, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. - PS2_KEY_INSERT, 0, 0, 0, 0, 0, 0, 0x0A, 0x08, 0x0B, 0x04, 0xFF, 0xFF, // INSERT - PS2_KEY_HOME, 1, 0, 0, 0, 0, 0, 0x0A, 0x04, 0x0B, 0x04, 0xFF, 0xFF, // CLR - PS2_KEY_HOME, 0, 0, 0, 0, 0, 0, 0x0A, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // HOME - PS2_KEY_PGUP, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. - PS2_KEY_DELETE, 0, 0, 0, 0, 0, 0, 0x0A, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // DELETE - PS2_KEY_END, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. - PS2_KEY_PGDN, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - PS2_KEY_UP_ARROW, 0, 0, 0, 0, 0, 0, 0x03, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // Up Arrow - PS2_KEY_L_ARROW, 0, 0, 0, 0, 0, 0, 0x03, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // Left Arrow - PS2_KEY_DN_ARROW, 0, 0, 0, 0, 0, 0, 0x03, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // Down Arrow - PS2_KEY_R_ARROW, 0, 0, 0, 0, 0, 0, 0x03, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // Right Arrow - PS2_KEY_NUM, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. - - // Keypad. - PS2_KEY_KP0, 0, 0, 0, 0, 0, 0, 0x02, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 0 - PS2_KEY_KP1, 0, 0, 0, 0, 0, 0, 0x02, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 1 - PS2_KEY_KP2, 0, 0, 0, 0, 0, 0, 0x02, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 2 - PS2_KEY_KP3, 0, 0, 0, 0, 0, 0, 0x02, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 3 - PS2_KEY_KP4, 0, 0, 0, 0, 0, 0, 0x02, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 4 - PS2_KEY_KP5, 0, 0, 0, 0, 0, 0, 0x02, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 5 - PS2_KEY_KP6, 0, 0, 0, 0, 0, 0, 0x02, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 6 - PS2_KEY_KP7, 0, 0, 0, 0, 0, 0, 0x02, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 7 - PS2_KEY_KP8, 0, 0, 0, 0, 0, 0, 0x01, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 8 - PS2_KEY_KP9, 0, 0, 0, 0, 0, 0, 0x01, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 9 - PS2_KEY_KP_COMMA, 0, 0, 0, 0, 0, 0, 0x01, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Comma , - PS2_KEY_KP_DOT, 0, 0, 0, 0, 0, 0, 0x01, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Full stop . - PS2_KEY_KP_PLUS, 0, 0, 0, 0, 0, 0, 0x01, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Plus + - PS2_KEY_KP_MINUS, 0, 0, 0, 0, 0, 0, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Minus - - PS2_KEY_KP_TIMES, 0, 0, 0, 0, 0, 0, 0x0A, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Times * - PS2_KEY_KP_DIV, 0, 0, 0, 0, 0, 0, 0x0A, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Divide / - - // PS2 Code Shift Function Ctrl ALT ALT-Gr GUI MXROW1 MXKEY1 MXROW2 MXKEY2 MXROW3 MXKEY3 - - // Special keys. - PS2_KEY_PRTSCR, 0, 1, 0, 0, 0, 0, 0x0D, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // ARGO KEY - PS2_KEY_BREAK, 0, 0, 0, 0, 0, 0, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, // BREAK KEY - PS2_KEY_L_GUI, 0, 1, 0, 0, 0, 1, 0x0B, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // GRAPH KEY - PS2_KEY_L_ALT, 0, 1, 0, 1, 0, 0, 0x0C, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // KJ1 Sentence - PS2_KEY_R_ALT, 0, 1, 0, 0, 1, 0, 0x0C, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, // KJ2 Transform - PS2_KEY_R_GUI, 0, 1, 0, 0, 0, 1, 0x0B, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, // KANA KEY - PS2_KEY_MENU, 0, 1, 0, 0, 0, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. - // Modifiers are last, only being selected if an earlier match isnt made. - PS2_KEY_L_SHIFT, 0, 0, 0, 0, 0, 0, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, - PS2_KEY_R_SHIFT, 0, 0, 0, 0, 0, 0, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, - PS2_KEY_L_CTRL, 0, 0, 0, 0, 0, 0, 0x0B, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, - PS2_KEY_R_CTRL, 0, 0, 0, 0, 0, 0, 0x0B, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, - 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + // < Keys to be applied on match > < Keys to be reset on match > + // PS2 Code Machine Shift Function Ctrl ALT ALT-Gr GUI MK_ROW1 MK_KEY1 MK_ROW2 MK_KEY2 MK_ROW3 MK_KEY3 BRK_ROW1 BRK_KEY1 BRK_ROW2 BRK_KEY2 + PS2_KEY_F1, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F1 + PS2_KEY_F2, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F2 + PS2_KEY_F3, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F3 + PS2_KEY_F4, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F4 + PS2_KEY_F5, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F5 + PS2_KEY_F6, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F6 + PS2_KEY_F7, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F7 + PS2_KEY_F8, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F8 + PS2_KEY_F9, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F9 + PS2_KEY_F10, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F10 + PS2_KEY_F11, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0D, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // HELP + PS2_KEY_F12, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // COPY + PS2_KEY_TAB, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // TAB + PS2_KEY_0, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x09, 0x02, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Close Right Bracket ) + PS2_KEY_0, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0 + PS2_KEY_1, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x08, 0x02, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Exclamation + PS2_KEY_1, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 1 + PS2_KEY_2, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x08, 0x04, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Double quote. + PS2_KEY_2, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 2 + PS2_KEY_3, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x08, 0x08, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Pound Sign -> Hash + PS2_KEY_3, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 3 + PS2_KEY_4, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x08, 0x10, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Dollar + PS2_KEY_4, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 4 + PS2_KEY_5, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x08, 0x20, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Percent + PS2_KEY_5, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 5 + PS2_KEY_6, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x04, 0xFF, 0xFF, // Kappa + PS2_KEY_6, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 6 + PS2_KEY_7, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x08, 0x40, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Ampersand + PS2_KEY_7, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 7 + PS2_KEY_8, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x09, 0x04, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Star + PS2_KEY_8, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x09, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 8 + PS2_KEY_9, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x09, 0x01, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Open Left Bracket ( + PS2_KEY_9, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x09, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 9 + PS2_KEY_A, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // a + PS2_KEY_A, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A + PS2_KEY_B, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // b + PS2_KEY_B, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // B + PS2_KEY_C, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // c + PS2_KEY_C, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // C + PS2_KEY_D, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // d + PS2_KEY_D, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // D + PS2_KEY_E, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // e + PS2_KEY_E, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E + PS2_KEY_F, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // f + PS2_KEY_F, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F + PS2_KEY_G, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // g + PS2_KEY_G, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // G + PS2_KEY_H, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // h + PS2_KEY_H, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // H + PS2_KEY_I, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // i + PS2_KEY_I, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // I + PS2_KEY_J, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // j + PS2_KEY_J, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // J + PS2_KEY_K, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // k + PS2_KEY_K, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // K + PS2_KEY_L, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // l + PS2_KEY_L, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // L + PS2_KEY_M, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // m + PS2_KEY_M, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // M + PS2_KEY_N, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // n + PS2_KEY_N, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // N + PS2_KEY_O, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x05, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // o + PS2_KEY_O, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x05, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // O + PS2_KEY_P, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // p + PS2_KEY_P, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // P + PS2_KEY_Q, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // q + PS2_KEY_Q, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Q + PS2_KEY_R, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // r + PS2_KEY_R, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // R + PS2_KEY_S, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // s + PS2_KEY_S, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // S + PS2_KEY_T, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // t + PS2_KEY_T, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // T + PS2_KEY_U, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // u + PS2_KEY_U, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // U + PS2_KEY_V, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // v + PS2_KEY_V, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // V + PS2_KEY_W, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // w + PS2_KEY_W, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // W + PS2_KEY_X, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // x + PS2_KEY_X, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x07, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // X + PS2_KEY_Y, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // y + PS2_KEY_Y, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x07, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Y + PS2_KEY_Z, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // z + PS2_KEY_Z, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x07, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Z + // PS2 Code Machine Shift Function Ctrl ALT ALT-Gr GUI MK_ROW1 MK_KEY1 MK_ROW2 MK_KEY2 MK_ROW3 MK_KEY3 BRK_ROW1 BRK_KEY1 BRK_ROW2 BRK_KEY2 + PS2_KEY_SPACE, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Space + PS2_KEY_COMMA, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x80, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Less Than < + PS2_KEY_COMMA, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Comma , + PS2_KEY_SEMI, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x09, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x04, 0xFF, 0xFF, // Colon : + PS2_KEY_SEMI, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x09, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Semi-Colon ; + PS2_KEY_DOT, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x40, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Greater Than > + PS2_KEY_DOT, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x07, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Full stop . + PS2_KEY_DIV, MZ_2000, 1, 0, 0, 0, 0, 0, 0x07, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x04, 0xFF, 0xFF, // Question ? + PS2_KEY_DIV, MZ_80B, 1, 0, 0, 0, 0, 0, 0x07, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x04, 0xFF, 0xFF, // Question ? + PS2_KEY_DIV, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x04, 0x01, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Question ? + PS2_KEY_DIV, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x04, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Divide / + PS2_KEY_MINUS, MZ_2000, 1, 0, 0, 0, 0, 0, 0x08, 0x01, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Upper bar + PS2_KEY_MINUS, MZ_80B, 1, 0, 0, 0, 0, 0, 0x08, 0x01, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Upper bar + PS2_KEY_MINUS, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x04, 0xFF, 0xFF, // Underscore + PS2_KEY_MINUS, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x09, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + PS2_KEY_APOS, MZ_80B, 1, 0, 0, 0, 0, 0, 0x09, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x04, 0xFF, 0xFF, // At @ + PS2_KEY_APOS, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x09, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x04, 0xFF, 0xFF, // At @ + PS2_KEY_APOS, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x80, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Single quote ' + PS2_KEY_OPEN_SQ, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x09, 0x40, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Open Left Brace { + PS2_KEY_OPEN_SQ, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x09, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Open Left Square Bracket [ + PS2_KEY_EQUAL, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x09, 0x08, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Plus + + PS2_KEY_EQUAL, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x09, 0x10, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Equal = + PS2_KEY_CAPS, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0B, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // LOCK + PS2_KEY_ENTER, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // ENTER/RETURN + PS2_KEY_CLOSE_SQ, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x0A, 0x01, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Close Right Brace } + PS2_KEY_CLOSE_SQ, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Close Right Square Bracket ] + PS2_KEY_BACK, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x10, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // + PS2_KEY_BACK, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x07, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Back slash maps to Yen + PS2_KEY_BTICK, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x07, 0x10, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Pipe + PS2_KEY_BTICK, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x09, 0x20, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Back tick ` + PS2_KEY_HASH, MZ_2000, 1, 0, 0, 0, 0, 0, 0x07, 0x08, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Tilde + PS2_KEY_HASH, MZ_80B, 1, 0, 0, 0, 0, 0, 0x07, 0x08, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Tilde + PS2_KEY_HASH, MZ_ALL, 1, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Tilde has no mapping. + PS2_KEY_HASH, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Hash + PS2_KEY_BS, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Backspace + PS2_KEY_ESC, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // ESCape + PS2_KEY_SCROLL, MZ_ALL, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. + PS2_KEY_INSERT, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x08, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // INSERT + PS2_KEY_HOME, MZ_ALL, 1, 0, 0, 0, 0, 0, 0x0A, 0x04, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // CLR + PS2_KEY_HOME, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // HOME + PS2_KEY_PGUP, MZ_ALL, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. + PS2_KEY_DELETE, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // DELETE + PS2_KEY_END, MZ_ALL, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. + PS2_KEY_PGDN, MZ_ALL, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + PS2_KEY_UP_ARROW, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Up Arrow + PS2_KEY_L_ARROW, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Left Arrow + PS2_KEY_DN_ARROW, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Down Arrow + PS2_KEY_R_ARROW, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Right Arrow + PS2_KEY_NUM, MZ_ALL, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. + + // Keypad. + PS2_KEY_KP0, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 0 + PS2_KEY_KP1, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 1 + PS2_KEY_KP2, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 2 + PS2_KEY_KP3, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 3 + PS2_KEY_KP4, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 4 + PS2_KEY_KP5, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 5 + PS2_KEY_KP6, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 6 + PS2_KEY_KP7, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x02, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 7 + PS2_KEY_KP8, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 8 + PS2_KEY_KP9, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad 9 + PS2_KEY_KP_COMMA, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Comma , + PS2_KEY_KP_DOT, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Full stop . + PS2_KEY_KP_PLUS, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Plus + + PS2_KEY_KP_MINUS, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Minus - + PS2_KEY_KP_TIMES, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Times * + PS2_KEY_KP_DIV, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0A, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Divide / + PS2_KEY_KP_ENTER, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Keypad Ebter / + + // PS2 Code Machine Shift Function Ctrl ALT ALT-Gr GUI MK_ROW1 MK_KEY1 MK_ROW2 MK_KEY2 MK_ROW3 MK_KEY3 BRK_ROW1 BRK_KEY1 BRK_ROW2 BRK_KEY2 + + // Special keys. + PS2_KEY_PRTSCR, MZ_ALL, 0, 1, 0, 0, 0, 0, 0x0D, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // ARGO KEY + PS2_KEY_PAUSE, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x03, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // BREAK KEY + PS2_KEY_L_GUI, MZ_ALL, 0, 1, 0, 0, 0, 1, 0x0B, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // GRAPH KEY + PS2_KEY_L_ALT, MZ_ALL, 0, 1, 0, 1, 0, 0, 0x0C, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // KJ1 Sentence + PS2_KEY_R_ALT, MZ_ALL, 0, 1, 0, 0, 1, 0, 0x0C, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // KJ2 Transform + PS2_KEY_R_GUI, MZ_ALL, 0, 1, 0, 0, 0, 1, 0x0B, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // KANA KEY + PS2_KEY_MENU, MZ_ALL, 0, 1, 0, 0, 0, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Not assigned. + // Modifiers are last, only being selected if an earlier match isnt made. + PS2_KEY_L_SHIFT, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + PS2_KEY_R_SHIFT, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0B, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + PS2_KEY_L_CTRL, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0B, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + PS2_KEY_R_CTRL, MZ_ALL, 0, 0, 0, 0, 0, 0, 0x0B, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, MZ_ALL, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; #endif diff --git a/main/PS2KeyAdvanced.cpp b/main/PS2KeyAdvanced.cpp index 54fab9b..6f7784a 100644 --- a/main/PS2KeyAdvanced.cpp +++ b/main/PS2KeyAdvanced.cpp @@ -642,7 +642,7 @@ _ps2mode = 0; */ uint16_t translate( void ) { - uint8_t index, length, data; + uint8_t index, length, data, status; uint16_t retdata; // get next character @@ -655,7 +655,17 @@ uint16_t translate( void ) index++; if( index >= _RX_BUFFER_SIZE ) index = 0; - _tail = index; + + // Special handling for PAUSE/BREAK, PAUSE key doesnt send a BREAK code yet MZ machines need SHIFT (hold) -> BREAK to recognise a BREAK, CTRL+BREAK will not work. + // In this case we inject a BREAK code by setting the flag on the received code. + status = (( _rx_buffer[ index ] & 0xFF00 ) >> 8); + if( (status & _E1_MODE) && (status & _BREAK)) + { + _rx_buffer[index] &= ~PS2_BREAK; + } else + { + _tail = index; + } // Get the flags byte break modes etc in this order data = _rx_buffer[ index ] & 0xFF; @@ -664,9 +674,9 @@ uint16_t translate( void ) // Catch special case of PAUSE key if( index & _E1_MODE ) { - return PS2_KEY_PAUSE + _FUNCTION; + return ( (uint16_t)PS2_keystatus << 8 ) | PS2_KEY_PAUSE | PS2_FUNCTION | (status & _BREAK ? 0 : PS2_BREAK); } - + // Ignore anything not actual keycode but command/response // Return untranslated as valid if( ( data >= PS2_KC_BAT && data != PS2_KC_LANG1 && data != PS2_KC_LANG2 ) || ( index & _WAIT_RESPONSE ) ) @@ -736,18 +746,26 @@ uint16_t translate( void ) if( PS2_keystatus & _BREAK ) { PS2_lockstate[ retdata ] = 0; // Set received a break so next make toggles LOCK status - retdata = PS2_KEY_IGNORE; // ignore key + // MZ-2500 uses one make-break cycle to enable and one make-break to disable, so the BREAK is needed. + if(retdata != PS2_KEY_CAPS) + { + retdata = PS2_KEY_IGNORE; // ignore key + } } else { if( PS2_lockstate[ retdata ] == 1 ) { +printf("PLEASE REMOVE ME translate: %04x, %d\n", retdata, PS2_lockstate[ retdata ]); retdata = PS2_KEY_IGNORE; // ignore key if make and not received break + // As per above, MZ-2500 needs both events, so this code changed from original authors. } else { PS2_lockstate[ retdata ] = 1; switch( retdata ) { - case PS2_KEY_CAPS: index = PS2_LOCK_CAPS; + case PS2_KEY_CAPS: + index = PS2_LOCK_CAPS; + // Set CAPS lock if not set before if( PS2_keystatus & _CAPS ) { @@ -757,16 +775,21 @@ uint16_t translate( void ) PS2_keystatus |= _CAPS; } break; - case PS2_KEY_SCROLL: index = PS2_LOCK_SCROLL; + case PS2_KEY_SCROLL: + index = PS2_LOCK_SCROLL; break; - case PS2_KEY_NUM: index = PS2_LOCK_NUM; + case PS2_KEY_NUM: + index = PS2_LOCK_NUM; break; } // Now update PS2_led_lock status to match if( PS2_led_lock & index ) { PS2_led_lock &= ~index; - PS2_keystatus |= _BREAK; // send as break + if(index != PS2_LOCK_CAPS) + { + PS2_keystatus |= _BREAK; // send as break + } } else { PS2_led_lock |= index; @@ -1002,6 +1025,7 @@ while( i < ( _KEY_BUFF_SIZE - 1 ) ) // process if not full if( keyAvailable( ) ) // not check for more keys to process { data = translate( ); // get next translated key + if( data == 0 ) // unless in buffer is empty break; if( (data & 0xFF) != PS2_KEY_IGNORE && (data & 0xFF) > 0) diff --git a/main/main.cpp b/main/main.cpp index 2abd2fe..aa8de25 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -29,6 +29,12 @@ // Copyright: (c) 2022 Philip Smart // // History: Jan 2022 - Initial write. +// Feb 2022 - Updates and fixes. Added logic to detect PS/2 disconnect and reconnect, +// added 3 alternative maps selected by ALT+F1 (MZ2500), ALT+F2(MZ2000) +// ALT+F3(MZ80B) due to slight differences in the key layout. +// Added framework for wifi so that the interface can enter AP mode to +// acquire local net parameters then connect to local net. Needs the web +// interface writing. // // Notes: See Makefile to enable/disable conditional components // @@ -53,6 +59,16 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" +#if defined(CONFIG_MZ_WIFI_ENABLED) + #include "freertos/event_groups.h" + #include "esp_system.h" + #include "esp_wifi.h" + #include "esp_event.h" + #include "nvs_flash.h" +#include "lwip/err.h" +#include "lwip/sys.h" + +#endif #include "Arduino.h" #include "driver/gpio.h" #include "soc/timer_group_struct.h" @@ -63,45 +79,12 @@ #include "font8x8_basic.h" #include "sdkconfig.h" -// ESP Logging tag. -#define tag "mz25key" - ////////////////////////////////////////////////////////////////////////// // Important: // // All configuration is performed via the 'idf.py menuconfig' command. -// Optionally override via the definitions below. +// The file 'sdkconfig' contains the configured parameter defines. ////////////////////////////////////////////////////////////////////////// -//#define CONFIG_DEBUG_OLED !CONFIG_OLED_DISABLED -//#define CONFIG_PWRLED 25 -//#define CONFIG_PS2_HW_DATAPIN 14 -//#define CONFIG_PS2_HW_CLKPIN 13 -//#define CONFIG_KEYMAP_WYSE_KB3926 -//#define CONFIG_KEYMAP_STANDARD -//#define CONFIG_MZ_KDB0 23 -//#define CONFIG_MZ_KDB1 25 -//#define CONFIG_MZ_KDB2 26 -//#define CONFIG_MZ_KDB3 27 -//#define CONFIG_MZ_KDO0 14 -//#define CONFIG_MZ_KDO1 15 -//#define CONFIG_MZ_KDO2 16 -//#define CONFIG_MZ_KDO3 17 -//#define CONFIG_MZ_KDO4 18 -//#define CONFIG_MZ_KDO5 19 -//#define CONFIG_MZ_KDO6 21 -//#define CONFIG_MZ_KDO7 21 -//#define CONFIG_MZ_RTSNI 35 -//#define CONFIG_MZ_KDI4 13 -//#CONFIG_OLED_DISABLED -//#CONFIG_I2C_INTERFACE -//#CONFIG_SPI_INTERFACE -//#CONFIG_SSD1306_128x32 -//#CONFIG_SSD1306_128x64 -//#CONFIG_OFFSETX 0 -//#CONFIG_FLIP -//#CONFIG_SCL_GPIO 5 -//#CONFIG_SDA_GPIO 4 -//#CONFIG_RESET_GPIO 16 // Macros. // @@ -109,54 +92,92 @@ // Structure to manage the translated key matrix. This is updated by the ps2Interface thread and read by the mzInterface thead. typedef struct { - uint8_t strobeAll; - uint32_t strobeAllAsGPIO; - uint8_t keyMatrix[16]; - uint32_t keyMatrixAsGPIO[16]; + uint8_t strobeAll; + uint32_t strobeAllAsGPIO; + uint8_t keyMatrix[16]; + uint32_t keyMatrixAsGPIO[16]; + uint8_t activeKeyMap; } t_mzControl; -volatile t_mzControl mzControl = { 0xFF, 0x00000000, - { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } - }; +volatile t_mzControl mzControl = { 0xFF, 0x00000000, + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + MZ_ALL + }; // Instantiate base classes. First, production required objects. -PS2KeyAdvanced Keyboard; +PS2KeyAdvanced Keyboard; // Debug required objects. -SSD1306_t SSD1306; +SSD1306_t SSD1306; -// Handle to interact with the mz-2500 interface thread. -TaskHandle_t TaskMZ25IF = NULL; -TaskHandle_t TaskPS2IF = NULL; +// Handle to interact with the mz-2500 interface, ps/2 interface and wifi threads. +TaskHandle_t TaskMZ25IF = NULL; +TaskHandle_t TaskPS2IF = NULL; +#if defined(CONFIG_MZ_WIFI_ENABLED) + TaskHandle_t TaskWIFI = NULL; +#endif // Spin lock mutex to hold a core tied to an uninterruptable method. This only works on dual core ESP32's. -static portMUX_TYPE mzMutex = portMUX_INITIALIZER_UNLOCKED; +static portMUX_TYPE mzMutex = portMUX_INITIALIZER_UNLOCKED; + +// Tag for ESP main application logging. +#define MAINTAG "mz25key" + +#if defined(CONFIG_MZ_WIFI_ENABLED) + // The event group allows multiple bits for each event, but we only care about two events: + // - we are connected to the AP with an IP + // - we failed to connect after the maximum amount of retries + #define WIFI_CONNECTED_BIT BIT0 + #define WIFI_FAIL_BIT BIT1 + + // Tag for ESP WiFi logging. + #define WIFITAG "wifi" + + // Menu selection types. + enum WIFIMODES { + WIFI_OFF = 0x00, // WiFi is disabled. + WIFI_ON = 0x01, // WiFi is enabled. + WIFI_CONFIG_AP = 0x02 // WiFi is set to enable Access Point to configure WiFi settings. + }; + + // Flag to indicate WiFi is active. Whilst active the MZ25 interface cannot run as it has to + // free the core. Need to find a way around this to make wifi only work on one core! + static bool wifiActivated = 0; + + // FreeRTOS event group to signal when we are connected + static EventGroupHandle_t s_wifi_event_group; + + + static int clientRetryCnt = 0; +#endif #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) -// Printf to debug console terminal. -void dbgprintf(const char * format, ...) -{ - // Locals. - va_list ap; - - // Commence variable argument processing. - va_start(ap, format); - // Use vararg printf to expand and return the buffer size needed. - int size = vsnprintf(nullptr, 0, format, ap) + 1; - if (size > 0) + // Printf to debug console terminal. + void dbgprintf(const char * format, ...) { - va_end(ap); - - // Repeat and this time output the expanded string to a buffer for printing. + // Locals. + va_list ap; + + // Commence variable argument processing. va_start(ap, format); - char buf[size + 1]; - vsnprintf(buf, size, format, ap); - - // Output to LED or console - // tbc + // Use vararg printf to expand and return the buffer size needed. + int size = vsnprintf(nullptr, 0, format, ap) + 1; + if (size > 0) + { + va_end(ap); + + // Repeat and this time output the expanded string to a buffer for printing. + va_start(ap, format); + char buf[size + 1]; + vsnprintf(buf, size, format, ap); + + // Output to LED or console, currently via printf! + printf(buf); + } + va_end(ap); } - va_end(ap); -} +#else + #define dbgprintf(a, ...) {}; #endif // Method to connect and interact with the MZ-2500/MZ-2800 keyboard controller. @@ -181,17 +202,28 @@ void dbgprintf(const char * format, ...) // so ~16.8uS for one full key matrix, The MZ machine if stuck in a tight loop will take // approx 100uS to scan the matrix so the Gate Arrays are over sampling 6 times. // +// WARNING: The GPIO's are configurable via menuconfig BUT it is assumed all except RTSNi +// are in the first GPIO bank and RTSNi is in the second GPIO bank. Modify the code +// if RTSNi is set in the first bank or KDB[3:0], KDI4 are in the second bank. +// IRAM_ATTR void mz25Interface( void * pvParameters ) { // Locals. volatile uint32_t gpioIN; - volatile uint8_t strobeRow = 0; + volatile uint8_t strobeRow = 1; + + // Mask values declared as variables, let the optimiser decide wether they are constants or placed in-memory. uint32_t rowBitMask = (1 << CONFIG_MZ_KDB3) | (1 << CONFIG_MZ_KDB2) | (1 << CONFIG_MZ_KDB1) | (1 << CONFIG_MZ_KDB0); uint32_t colBitMask = (1 << CONFIG_MZ_KDO7) | (1 << CONFIG_MZ_KDO6) | (1 << CONFIG_MZ_KDO5) | (1 << CONFIG_MZ_KDO4) | (1 << CONFIG_MZ_KDO3) | (1 << CONFIG_MZ_KDO2) | (1 << CONFIG_MZ_KDO1) | (1 << CONFIG_MZ_KDO0); - uint32_t pwrLEDMask = (1 << CONFIG_PWRLED); + uint32_t KDB3_MASK = (1 << CONFIG_MZ_KDB3); + uint32_t KDB2_MASK = (1 << CONFIG_MZ_KDB2); + uint32_t KDB1_MASK = (1 << CONFIG_MZ_KDB1); + uint32_t KDB0_MASK = (1 << CONFIG_MZ_KDB0); + uint32_t KDI4_MASK = (1 << CONFIG_MZ_KDI4); + uint32_t RTSNI_MASK = (1 << (CONFIG_MZ_RTSNI - 32)); - ESP_LOGI(tag, "Starting mz25Interface thread, colBitMask=%08x, rowBitMask=%08x.", colBitMask, rowBitMask); + ESP_LOGI(MAINTAG, "Starting mz25Interface thread, colBitMask=%08x, rowBitMask=%08x.", colBitMask, rowBitMask); // Create, initialise and hold a spinlock so the current core is bound to this one method. portENTER_CRITICAL(&mzMutex); @@ -200,23 +232,30 @@ IRAM_ATTR void mz25Interface( void * pvParameters ) // Timings with Power LED = LED Off to On = 108ns, LED On to Off = 392ns for(;;) { - // Turn on Power LED. - GPIO.out_w1ts = pwrLEDMask; - - // Read the GPIO ports to get latest RTSNi and KDI4 states. - gpioIN = REG_READ(GPIO_IN_REG); + #if defined(CONFIG_MZ_WIFI_ENABLED) + // Whilst Wifi is active, suspend processing as we need to free up the core. + if(wifiActivated) + { + portEXIT_CRITICAL(&mzMutex); + while(wifiActivated); + portENTER_CRITICAL(&mzMutex); + } + #endif // Detect RTSN going high, the MZ will send the required row during this cycle. - if(gpioIN & CONFIG_MZ_RTSNI) + if(REG_READ(GPIO_IN1_REG) & RTSNI_MASK) { + // Read the GPIO ports to get latest Row and KDI4 states. + gpioIN = REG_READ(GPIO_IN_REG); + // Assemble the required matrix row from the configured bits. - strobeRow = (gpioIN >> (CONFIG_MZ_KDB3-3)) | (gpioIN >> (CONFIG_MZ_KDB2-2)) | (gpioIN >> (CONFIG_MZ_KDB1-1)) | (gpioIN >> CONFIG_MZ_KDB0); + strobeRow = ((gpioIN&KDB3_MASK) >> (CONFIG_MZ_KDB3-3)) | ((gpioIN&KDB2_MASK) >> (CONFIG_MZ_KDB2-2)) | ((gpioIN&KDB1_MASK) >> (CONFIG_MZ_KDB1-1)) | ((gpioIN&KDB0_MASK) >> CONFIG_MZ_KDB0); // Clear all KDO bits - clear state = '1' - GPIO.out_w1ts = colBitMask; // Reset all scan data bits to '1', inactive. + GPIO.out_w1ts = colBitMask; // Reset all scan data bits to '1', inactive. // KDI4 indicates if row data is needed or a single byte ANDing all the keys together, ie. to detect a key press without strobing all rows. - if(gpioIN & CONFIG_MZ_KDI4) + if(gpioIN & KDI4_MASK) { // Set all required KDO bits according to keyMatrix, set state = '0'. GPIO.out_w1tc = mzControl.keyMatrixAsGPIO[strobeRow]; // Set to '0' active bits. @@ -226,13 +265,10 @@ IRAM_ATTR void mz25Interface( void * pvParameters ) GPIO.out_w1tc = mzControl.strobeAllAsGPIO; // Set to '0' active bits. } - // Wait for RTSN to go low. - while(REG_READ(GPIO_IN_REG) & CONFIG_MZ_RTSNI); + // Wait for RTSN to go low. No lockup guarding as timing is critical also the watchdog is disabled, if RTSN never goes low then the user has probably unplugged the interface! + while(REG_READ(GPIO_IN1_REG) & RTSNI_MASK); } - // Turn off Power LED. - GPIO.out_w1tc = pwrLEDMask; - // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable //TIMERG0.wdt_feed=1; // feed dog @@ -243,13 +279,55 @@ IRAM_ATTR void mz25Interface( void * pvParameters ) } } +// Method to refresh the transposed matrix used in the MZ interface. The normal key matrix is transposed to save valuable time +// because even though a core is dedicated to the MZ interface the timing is critical and the ESP-32 doesnt have spare horse power! +// +IRAM_ATTR void updateMirrorMatrix(void) +{ + // Locals. + + // To save time in the MZ Interface, a mirror keyMatrix is built up, 32bit (GPIO Bank 0) wide, with the keyMatrix 8 bit data + // mapped onto the configured pins in the 32bit register. This saves previous time in order to meet the tight 1.2uS cycle. + // + for(int idx=0; idx < 15; idx++) + { + mzControl.keyMatrixAsGPIO[idx] = (((mzControl.keyMatrix[idx] >> 7) & 0x01) ^ 0x01) << CONFIG_MZ_KDO7 | + (((mzControl.keyMatrix[idx] >> 6) & 0x01) ^ 0x01) << CONFIG_MZ_KDO6 | + (((mzControl.keyMatrix[idx] >> 5) & 0x01) ^ 0x01) << CONFIG_MZ_KDO5 | + (((mzControl.keyMatrix[idx] >> 4) & 0x01) ^ 0x01) << CONFIG_MZ_KDO4 | + (((mzControl.keyMatrix[idx] >> 3) & 0x01) ^ 0x01) << CONFIG_MZ_KDO3 | + (((mzControl.keyMatrix[idx] >> 2) & 0x01) ^ 0x01) << CONFIG_MZ_KDO2 | + (((mzControl.keyMatrix[idx] >> 1) & 0x01) ^ 0x01) << CONFIG_MZ_KDO1 | + (((mzControl.keyMatrix[idx] ) & 0x01) ^ 0x01) << CONFIG_MZ_KDO0 ; + } + + // Re-calculate the Strobe All (KD4 = 1) signal, this indicates if any bit (key) in the matrix is active. + mzControl.strobeAll = 0xFF; + mzControl.strobeAllAsGPIO = 0x00000000; + for(int idx2=0; idx2 < 15; idx2++) + { + mzControl.strobeAll &= mzControl.keyMatrix[idx2]; + } + + // To speed up the mzInterface logic, pre-calculate the strobeAll value as a 32bit GPIO output value. + mzControl.strobeAllAsGPIO |= (((mzControl.strobeAll >> 7) & 0x01) ^ 0x01) << CONFIG_MZ_KDO7 | + (((mzControl.strobeAll >> 6) & 0x01) ^ 0x01) << CONFIG_MZ_KDO6 | + (((mzControl.strobeAll >> 5) & 0x01) ^ 0x01) << CONFIG_MZ_KDO5 | + (((mzControl.strobeAll >> 4) & 0x01) ^ 0x01) << CONFIG_MZ_KDO4 | + (((mzControl.strobeAll >> 3) & 0x01) ^ 0x01) << CONFIG_MZ_KDO3 | + (((mzControl.strobeAll >> 2) & 0x01) ^ 0x01) << CONFIG_MZ_KDO2 | + (((mzControl.strobeAll >> 1) & 0x01) ^ 0x01) << CONFIG_MZ_KDO1 | + (((mzControl.strobeAll ) & 0x01) ^ 0x01) << CONFIG_MZ_KDO0 ; + + return; +} + // Method to convert the PS2 scan code into a key matrix representation which the MZ-2500/2800 is expecting. // IRAM_ATTR unsigned char updateMatrix(uint16_t data) { // Locals. uint8_t idx; - uint8_t idx2; uint8_t changed = 0; uint8_t matchExact = 0; @@ -259,7 +337,7 @@ IRAM_ATTR unsigned char updateMatrix(uint16_t data) for(idx=0, changed=0, matchExact=0; idx < NUMELEM(PS2toMZ) && (changed == 0 || (changed == 1 && matchExact == 0)); idx++) { // Match key code? - if(PS2toMZ[idx][PSMZTBL_KEYPOS] == (uint8_t)(data&0xFF)) + if(PS2toMZ[idx][PSMZTBL_KEYPOS] == (uint8_t)(data&0xFF) && ((PS2toMZ[idx][PSMZTBL_MACHINE] == MZ_ALL) || (PS2toMZ[idx][PSMZTBL_MACHINE] == mzControl.activeKeyMap))) { // Match Raw, Shift, Function, Control, ALT or ALT-Gr? if( (PS2toMZ[idx][PSMZTBL_SHIFTPOS] == 0 && PS2toMZ[idx][PSMZTBL_FUNCPOS] == 0 && PS2toMZ[idx][PSMZTBL_CTRLPOS] == 0 && PS2toMZ[idx][PSMZTBL_ALTPOS] == 0 && PS2toMZ[idx][PSMZTBL_ALTGRPOS] == 0) || @@ -278,85 +356,86 @@ IRAM_ATTR unsigned char updateMatrix(uint16_t data) ((data & PS2_ALT) && PS2toMZ[idx][PSMZTBL_ALTPOS] == 1) || ((data & PS2_GUI) && PS2toMZ[idx][PSMZTBL_ALTPOS] == 1) || ((data & PS2_FUNCTION) && PS2toMZ[idx][PSMZTBL_FUNCPOS] == 1) ? 1 : 0; - + // RELEASE (PS2_BREAK == 1) or PRESS? if((data & PS2_BREAK)) { - // Reset the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix. - if(PS2toMZ[idx][PSMZTBL_MXROW1] != 0xFF) + // Special case for the PAUSE / BREAK key. The underlying logic has been modified to send a BREAK key event immediately + // after a PAUSE make, this is necessary as the Sharp MZ machines require SHIFT (pause) BREAK so the PS/2 CTRL+BREAK wont + // work (unless logic is added to insert a SHIFT, pause, add BREAK). The solution was to generate a BREAK event + // and add a slight delay for the key matrix to register it. + if((data&0x00FF) == PS2_KEY_PAUSE) { - mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW1]] |= PS2toMZ[idx][PSMZTBL_MXKEY1]; - changed = 1; + vTaskDelay(100); } - if(PS2toMZ[idx][PSMZTBL_MXROW2] != 0xFF) + + // Loop through all the row/column combinations and if valid, apply to the matrix. + for(int row=PSMZTBL_MK_ROW1; row < PSMZTBL_MK_ROW3+1; row+=2) { - mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW2]] |= PS2toMZ[idx][PSMZTBL_MXKEY2]; - changed = 1; + // Reset the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix. + if(PS2toMZ[idx][row] != 0xFF) + { + mzControl.keyMatrix[PS2toMZ[idx][row]] |= PS2toMZ[idx][row+1]; + changed = 1; + } } - if(PS2toMZ[idx][PSMZTBL_MXROW3] != 0xFF) + + // Loop through all the key releases associated with this key and reset the relevant matrix bit which was cleared on + // initial keydown. + // + for(int row=PSMZTBL_BRK_ROW1; row < PSMZTBL_BRK_ROW2+1; row+=2) { - mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW3]] |= PS2toMZ[idx][PSMZTBL_MXKEY3]; - changed = 1; + if(PS2toMZ[idx][row] != 0xFF) + { + mzControl.keyMatrix[PS2toMZ[idx][row]] &= ~PS2toMZ[idx][row+1]; + changed = 1; + } } } else { - // Set the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix. - if(PS2toMZ[idx][PSMZTBL_MXROW1] != 0xFF) + // Loop through all the key releases associated with this key and clear the relevant matrix bit. + // This is done first so as to avoid false key detection in the MZ logic. + // + for(int row=PSMZTBL_BRK_ROW1; row < PSMZTBL_BRK_ROW2+1; row+=2) { - mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW1]] &= ~PS2toMZ[idx][PSMZTBL_MXKEY1]; - changed = 1; + if(PS2toMZ[idx][row] != 0xFF) + { + mzControl.keyMatrix[PS2toMZ[idx][row]] |= PS2toMZ[idx][row+1]; + changed = 1; + } } - if(PS2toMZ[idx][PSMZTBL_MXROW2] != 0xFF) + + // If a release key has been actioned, update the matrix and insert a slight pause to avoid + // the MZ logic seeing the released keys in combination with the newly pressed keys. + if(changed) { - mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW2]] &= ~PS2toMZ[idx][PSMZTBL_MXKEY2]; - changed = 1; + updateMirrorMatrix(); + changed = 0; + vTaskDelay(10); } - if(PS2toMZ[idx][PSMZTBL_MXROW3] != 0xFF) + + // Loop through all the row/column combinations and if valid, apply to the matrix. + for(int row=PSMZTBL_MK_ROW1; row < PSMZTBL_MK_ROW3+1; row+=2) { - mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW3]] &= ~PS2toMZ[idx][PSMZTBL_MXKEY3]; - changed = 1; + // Set the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix. + if(PS2toMZ[idx][row] != 0xFF) + { + mzControl.keyMatrix[PS2toMZ[idx][row]] &= ~PS2toMZ[idx][row+1]; + changed = 1; + } } } - } - - // Only spend time updating signals if an actual change occurred. Some keys arent valid so no change will be effected. - if(changed) - { - // To save time in the MZ Interface, a mirror keyMatrix is built up, 32bit (GPIO Bank 0) wide, with the keyMatrix 8 bit data - // mapped onto the configured pins in the 32bit register. This saves previous time in order to meet the tight 1.2uS cycle. - // - for(int idx=0; idx < 15; idx++) - { - mzControl.keyMatrixAsGPIO[idx] = (((mzControl.keyMatrix[idx] >> 7) & 0x01) ^ 0x01) << CONFIG_MZ_KDO7 | - (((mzControl.keyMatrix[idx] >> 6) & 0x01) ^ 0x01) << CONFIG_MZ_KDO6 | - (((mzControl.keyMatrix[idx] >> 5) & 0x01) ^ 0x01) << CONFIG_MZ_KDO5 | - (((mzControl.keyMatrix[idx] >> 4) & 0x01) ^ 0x01) << CONFIG_MZ_KDO4 | - (((mzControl.keyMatrix[idx] >> 3) & 0x01) ^ 0x01) << CONFIG_MZ_KDO3 | - (((mzControl.keyMatrix[idx] >> 2) & 0x01) ^ 0x01) << CONFIG_MZ_KDO2 | - (((mzControl.keyMatrix[idx] >> 1) & 0x01) ^ 0x01) << CONFIG_MZ_KDO1 | - (((mzControl.keyMatrix[idx] ) & 0x01) ^ 0x01) << CONFIG_MZ_KDO0 ; - } - // Re-calculate the Strobe All (KD4 = 1) signal, this indicates if any bit (key) in the matrix is active. - mzControl.strobeAll = 0xFF; - mzControl.strobeAllAsGPIO = 0x00000000; - for(idx2=0; idx2 < 15; idx2++) + // Only spend time updating signals if an actual change occurred. Some keys arent valid so no change will be effected. + if(changed) { - mzControl.strobeAll &= mzControl.keyMatrix[idx2]; + updateMirrorMatrix(); } + } // match key or a special function + } // match key code + } // for loop - // To speed up the mzInterface logic, pre-calculate the strobeAll value as a 32bit GPIO output value. - mzControl.strobeAllAsGPIO |= (((mzControl.strobeAll >> 7) & 0x01) ^ 0x01) << CONFIG_MZ_KDO7 | - (((mzControl.strobeAll >> 6) & 0x01) ^ 0x01) << CONFIG_MZ_KDO6 | - (((mzControl.strobeAll >> 5) & 0x01) ^ 0x01) << CONFIG_MZ_KDO5 | - (((mzControl.strobeAll >> 4) & 0x01) ^ 0x01) << CONFIG_MZ_KDO4 | - (((mzControl.strobeAll >> 3) & 0x01) ^ 0x01) << CONFIG_MZ_KDO3 | - (((mzControl.strobeAll >> 2) & 0x01) ^ 0x01) << CONFIG_MZ_KDO2 | - (((mzControl.strobeAll >> 1) & 0x01) ^ 0x01) << CONFIG_MZ_KDO1 | - (((mzControl.strobeAll ) & 0x01) ^ 0x01) << CONFIG_MZ_KDO0 ; - } - } - } + // Return flag to indicate if a match occurred and the matrix updated. return(changed); } @@ -367,28 +446,109 @@ IRAM_ATTR unsigned char updateMatrix(uint16_t data) IRAM_ATTR void ps2Interface( void * pvParameters ) { // Locals. - uint16_t scanCode = 0x0000; + uint16_t scanCode = 0x0000; + bool activityLED = 0; + TickType_t ps2CheckTimer = 0; + TickType_t ps2LedTimer = 0; + bool ps2Active = 0; // Flag to indicate the PS/2 keyboard is connected and online. #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) - uint8_t dataChange = 0; - static int scanPrtCol = 0; - static uint32_t rfshTimer = 0; + uint8_t dataChange = 0; + static int scanPrtCol = 0; + static uint32_t rfshTimer = 0; #endif + // Thread never exits, just polls the keyboard and updates the matrix. while(1) { - // Check for PS/2 keyboard scan codes. - while((scanCode = Keyboard.read()) != 0) + // Check the keyboard is online, this is done at startup and periodically to cater for user disconnect. + if((xTaskGetTickCount() - ps2CheckTimer) > 1000 && (Keyboard.keyAvailable() == 0 || ps2Active == 0)) { - printf("%04x\n", scanCode); - #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) - // Output the scan code for verification. - dbgprintf("%04x,", scanCode); - if(scanPrtCol++ >= 3) scanPrtCol = 0; - #endif + // Check to see if the keyboard is still available, no keyboard = no point!! + // Firstly, ping keyboard to see if it is there. + Keyboard.echo(); + vTaskDelay(6); + scanCode = Keyboard.read(); + + // If the keyboard doesnt answer back, then it has been disconnected. + if( (scanCode & 0xFF) != PS2_KEY_ECHO && (scanCode & 0xFF) != PS2_KEY_BAT) + { + // Re-initialise the subsystem, if the keyboard is plugged in then it will be detected on next loop. + Keyboard.begin(CONFIG_PS2_HW_DATAPIN, CONFIG_PS2_HW_CLKPIN); - // Update the virtual matrix with the new key value. - dataChange |= updateMatrix(scanCode); - } + // First entry print out message that the keyboard has disconnected. + if(ps2Active == 1 || ps2CheckTimer == 0) + { + ESP_LOGE(MAINTAG, "No PS2 keyboard detected, please connect.\n"); + #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) + ssd1306_display_text(&SSD1306, 0, (char *)"No PS2 Keyboard", 15, false); + #endif + } + ps2Active = 0; + + // Turn on LED when keyboard is detached. + gpio_set_level((gpio_num_t)CONFIG_PWRLED, 1); + } else + { + // First entry after keyboard starts responding, print out message. + if(ps2Active == 0) + { + ESP_LOGI(MAINTAG, "PS2 keyboard detected and online.\n"); + ps2Active = 1; + + // Flash LED to indicate Keyboard recognised. + gpio_set_level((gpio_num_t)CONFIG_PWRLED, 1); + ps2LedTimer = xTaskGetTickCount() - 400; + } + } + ps2CheckTimer = xTaskGetTickCount(); // Check every second. + } else + { + // Check for PS/2 keyboard scan codes. + while((scanCode = Keyboard.read()) != 0) + { + #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) + // Output the scan code for verification. + dbgprintf("%04x,", scanCode); + if(scanPrtCol++ >= 3) scanPrtCol = 0; + #else + dbgprintf("%04x\n", scanCode); + #endif + + // Filter out ALT+F1..3 keys as these select the active keymap. + switch(scanCode) + { + case 0x0961: + mzControl.activeKeyMap = MZ_2500; + break; + case 0x0962: + mzControl.activeKeyMap = MZ_2000; + break; + case 0x0963: + mzControl.activeKeyMap = MZ_80B; + break; + + default: + // Update the virtual matrix with the new key value. + dataChange |= updateMatrix(scanCode); + break; + } + + // Toggle LED to indicate data flow. + gpio_set_level((gpio_num_t)CONFIG_PWRLED, activityLED); + activityLED = !activityLED; + + // Reset the check keyboard timer, no need to check as activity is seen. + ps2CheckTimer = xTaskGetTickCount(); // Check every second. + ps2LedTimer = xTaskGetTickCount(); + } + + // If no activity has been seen for 100ms then switch off the LED. + if(ps2LedTimer > 0 && (xTaskGetTickCount() - ps2LedTimer) > 100) + { + activityLED = 0; + ps2LedTimer = 0; + gpio_set_level((gpio_num_t)CONFIG_PWRLED, activityLED); + } #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) if(dataChange || (rfshTimer > 0 && --rfshTimer == 0)) @@ -414,19 +574,394 @@ IRAM_ATTR void ps2Interface( void * pvParameters ) dataChange = 0; } #endif + } // Let other tasks run. - vTaskDelay(0); + vTaskDelay(10); } } +#if defined(CONFIG_MZ_WIFI_ENABLED) +// Event handler for Client mode Wifi event callback. +// +IRAM_ATTR void wifiClientHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + // Locals. + // + + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) + { + esp_wifi_connect(); + } + else if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) + { + if(clientRetryCnt < CONFIG_MZ_WIFI_MAX_RETRIES) + { + esp_wifi_connect(); + clientRetryCnt++; + ESP_LOGI(WIFITAG, "retry to connect to the AP"); + } else + { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(WIFITAG,"connect to the AP fail"); + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) + { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(WIFITAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + clientRetryCnt = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } + return; +} + +// Event handler for Access Point mode Wifi event callback. +// +IRAM_ATTR void wifiAPHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + // Locals. + // + + + if (event_id == WIFI_EVENT_AP_STACONNECTED) + { + wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; + ESP_LOGI(WIFITAG, "station " MACSTR " join, AID=%d", MAC2STR(event->mac), event->aid); + } + else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) + { + wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; + ESP_LOGI(WIFITAG, "station " MACSTR " leave, AID=%d", MAC2STR(event->mac), event->aid); + } + return; +} + +// Method to initialise the interface as a client to a known network, the SSID +// and password have already been setup. +// +uint8_t setupWifiClient(void) +{ + // Locals. + // + wifi_init_config_t wifiInitConfig; + esp_event_handler_instance_t instID; + esp_event_handler_instance_t instIP; + EventBits_t bits; + wifi_config_t wifiConfig = { .sta = { + /* ssid */ CONFIG_MZ_SSID, + /* password */ CONFIG_MZ_DEFAULT_SSID_PWD, + /* scan_method */ {}, + /* bssid_set */ {}, + /* bssid */ {}, + /* channel */ {}, + /* listen_interval */ {}, + /* sort_method */ {}, + /* threshold */ { + /* rssi */ {}, + /* authmode */ WIFI_AUTH_WPA2_PSK + }, + /* pmf_cfg */ { + /* capable */ true, + /* required */ false + }, + /* rm_enabled */ {}, + /* btm_enabled */ {}, + /* mbo_enabled */ {}, // For IDF 4.4 and higher + /* reserved */ {} + } + }; + + // Create an event handler group to manage callbacks. + s_wifi_event_group = xEventGroupCreate(); + + // Setup the network interface. + if(esp_netif_init()) + { + ESP_LOGI(WIFITAG, "Couldnt initialise netif, disabling WiFi."); + return(1); + } + + // Setup the event loop. + if(esp_event_loop_create_default()) + { + ESP_LOGI(WIFITAG, "Couldnt initialise event loop, disabling WiFi."); + return(1); + } + + // Setup the wifi client (station). + esp_netif_create_default_wifi_sta(); + + // Setup the config for wifi. + wifiInitConfig = WIFI_INIT_CONFIG_DEFAULT(); + if(esp_wifi_init(&wifiInitConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt initialise wifi with default parameters, disabling WiFi."); + return(1); + } + + // Register event handlers. + if(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifiClientHandler, NULL, &instID)) + { + ESP_LOGI(WIFITAG, "Couldnt register event handler for ID, disabling WiFi."); + return(1); + } + if(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifiClientHandler, NULL, &instIP)) + { + ESP_LOGI(WIFITAG, "Couldnt register event handler for IP, disabling WiFi."); + return(1); + } + if(esp_wifi_set_mode(WIFI_MODE_STA)) + { + ESP_LOGI(WIFITAG, "Couldnt set Wifi mode to Client, disabling WiFi."); + return(1); + } + if(esp_wifi_set_config(WIFI_IF_STA, &wifiConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt configure client mode, disabling WiFi."); + return(1); + } + if(esp_wifi_start()) + { + ESP_LOGI(WIFITAG, "Couldnt start Client session, disabling WiFi."); + return(1); + } + + // Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + // number of re-tries (WIFI_FAIL_BIT). The bits are set by wifiClientHandler() (see above) + bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + // xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + // happened. + if(bits & WIFI_CONNECTED_BIT) + { + ESP_LOGI(WIFITAG, "Connected: SSID:%s password:%s", CONFIG_MZ_SSID, CONFIG_MZ_DEFAULT_SSID_PWD); + } + else if (bits & WIFI_FAIL_BIT) + { + ESP_LOGI(WIFITAG, "Connection Fail: SSID:%s, password:%s", CONFIG_MZ_SSID, CONFIG_MZ_DEFAULT_SSID_PWD); + } + else + { + ESP_LOGE(WIFITAG, "Unknown evemt, bits:%d", bits); + } + + // Close connection, not yet ready with application. + if(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instIP)) + { + ESP_LOGI(WIFITAG, "Couldnt unregister IP assignment halder, disabling WiFi."); + return(1); + } + if(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instID)) + { + ESP_LOGI(WIFITAG, "Couldnt unregister ID assignment halder, disabling WiFi."); + return(1); + } + vEventGroupDelete(s_wifi_event_group); + + // No errors. + return(0); +} + +// Method to initialise the interface as a Soft Access point with a given SSID +// and password. +// The Access Point mode is basically to bootstrap a Client connection where the +// client connecting provides the credentials in order to connect as a client to +// another AP to join a local network. +// +uint8_t setupWifiAP(void) +{ + // Locals. + // + esp_err_t retcode; + wifi_init_config_t wifiInitConfig; + wifi_config_t wifiConfig = { .ap = { + /* ssid */ CONFIG_MZ_SSID, + /* password */ CONFIG_MZ_DEFAULT_SSID_PWD, + /* ssid_len */ strlen(CONFIG_MZ_SSID), + /* channel */ CONFIG_MZ_WIFI_AP_CHANNEL, + /* authmode */ WIFI_AUTH_WPA_WPA2_PSK, + /* hidden */ CONFIG_MZ_WIFI_SSID_HIDDEN, + /* nax_connection */ CONFIG_MZ_WIFI_MAX_CONNECTIONS, + /* beacon_interval */ 100, + /* pairwise_cipher */ WIFI_CIPHER_TYPE_TKIP, + /* ftm_responder */ 0, + // /* pmf_cfg */ { + // /* capable */ true, + // /* required */ false + // } + } + }; + + // Intialise the network interface. + if(esp_netif_init()) + { + ESP_LOGI(WIFITAG, "Couldnt initialise network interface, disabling WiFi."); + return(1); + } + if((retcode = esp_event_loop_create_default())) + { + ESP_LOGI(WIFITAG, "Couldnt create default loop(%d), disabling WiFi.", retcode); + return(1); + } + + // Create the default Access Point. + // + esp_netif_create_default_wifi_ap(); + + // Initialise AP with default parameters. + wifiInitConfig = WIFI_INIT_CONFIG_DEFAULT(); + if(esp_wifi_init(&wifiInitConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt setup AP with default parameters, disabling WiFi."); + return(1); + } + + // Setup callback handlers for wifi events. + if(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifiAPHandler, NULL, NULL)) + { + ESP_LOGI(WIFITAG, "Couldnt setup event handlers, disabling WiFi."); + return(1); + } + + // If there is no password for the access point set authentication to open. + if (strlen(CONFIG_MZ_DEFAULT_SSID_PWD) == 0) + { + wifiConfig.ap.authmode = WIFI_AUTH_OPEN; + } + + // Setup as an Access Point. + if(esp_wifi_set_mode(WIFI_MODE_AP)) + { + ESP_LOGI(WIFITAG, "Couldnt set mode to Access Point, disabling WiFi."); + return(1); + } + // Configure the Access Point + if(esp_wifi_set_config(WIFI_IF_AP, &wifiConfig)) + { + ESP_LOGI(WIFITAG, "Couldnt configure Access Point, disabling WiFi."); + return(1); + } + // Start the Access Point. + if(esp_wifi_start()) + { + ESP_LOGI(WIFITAG, "Couldnt start Access Point session, disabling WiFi."); + return(1); + } + + // No errors. + return(0); +} +#endif + +// Work in progress. Intention is to add a WiFi access point to garner local net details, +// connect to local net then offer a browser interface to change keys. +#if defined(CONFIG_MZ_WIFI_ENABLED) +IRAM_ATTR void wifiInterface( void * pvParameters ) +{ + // Locals. + uint32_t keyDebCtr = 0; + uint32_t WIFIEN_MASK = (1 << (CONFIG_MZ_WIFI_EN_KEY - 32)); + esp_err_t nvsStatus; + enum WIFIMODES wifiMode = WIFI_OFF; + + + // Loop forever, detecting the Wifi activation/de-activation and subsequent processing. + while(1) + { + // Has wifi been activated? If the Wifi switch has been pressed, initialise Wifi according to desired mode. + // + if(wifiMode != WIFI_OFF) + { + if(!wifiActivated) + { + // Initialise the NVS storage, needed for WiFi parameters. + nvsStatus = nvs_flash_init(); + if (nvsStatus == ESP_ERR_NVS_NO_FREE_PAGES || nvsStatus == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_ERROR_CHECK(nvs_flash_erase()); + nvsStatus = nvs_flash_init(); + } + + if(nvsStatus) + { + ESP_LOGI(WIFITAG, "Couldnt initialise NVS, disabling WiFi."); + wifiActivated = 0; + wifiMode = WIFI_OFF; + } else + { + if(wifiMode == WIFI_ON) + { + if(setupWifiClient()) + { + wifiActivated = 0; + wifiMode = WIFI_OFF; + } else + { + dbgprintf("Wifi Client %s\n", wifiActivated ? "activated" : "de-activated"); + wifiActivated = 1; + } + } + else if(wifiMode == WIFI_CONFIG_AP) + { + if(setupWifiAP()) + { + wifiActivated = 0; + wifiMode = WIFI_OFF; + } else + { + dbgprintf("Wifi AP %s\n", wifiActivated ? "activated" : "de-activated"); + wifiActivated = 1; + } + } + } + + // Re-init switch variables for next activation. + keyDebCtr = 0; + } + + } + + // Check the switch, has it gone to zero, ie. pressed? + // + if((REG_READ(GPIO_IN1_REG) & WIFIEN_MASK) == 0) + { + // On first press, wait 1 second to see if user is selecting WiFi on or WiFi Config. + if(keyDebCtr == 0) + { + wifiMode = WIFI_OFF; + keyDebCtr = 10; + } + // If counter gets to 1 then assume WiFi on. + else if(keyDebCtr == 1) + { + wifiMode = WIFI_ON; + // Reset counter for 10 seconds, 10 being required to enter WiFi Config AP mode. + keyDebCtr = 100; + } + // 9 seconds later, mode is Wifi Config AP. + else if(keyDebCtr == 11) + { + wifiMode = WIFI_CONFIG_AP; + } + else if(keyDebCtr > 0) + { + keyDebCtr--; + } + } + + // Let other tasks run. NB. This value affects the debounce counter, update as necessary. + vTaskDelay(100); + } +} +#endif // Setup method to configure ports, devices and threads prior to application run. // Configuration: // PS/2 Keyboard over 2 wire interface // Power/Status LED // Optional OLED debug output screen -// 4 bit input - MZ-2500/2800 Row Number +// 4 bit input - MZ-2500/2800 Row Number // 8 bit output - MZ-2500/2800 Scan data // 1 bit input - RTSN strobe line, low indicating a new Row Number available. // 1 bit input - KD4, High = Key scan data required, Low = AND of all key matrix rows required. @@ -437,7 +972,7 @@ void setup() gpio_config_t io_conf; // Setup power LED first to show life. - ESP_LOGI(tag, "Configuring Power LED."); + ESP_LOGI(MAINTAG, "Configuring Power LED."); io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = (1ULL<