/* * PS/2 keyboard handling for Babelfish talking to Gigatron * Complete rewrite of PS2Keyboard for game controller mapping * and keymap compression to reduce footprint * * References: * * https://www.avrfreaks.net/sites/default/files/PS2%20Keyboard.pdf * The PS/2 Mouse/Keyboard Protocol * * http://www.quadibloc.com/comp/scan.htm * Scan Codes Demytified * * https://retired.beyondlogic.org/keyboard/keybrd.htm * Interfacing the AT keyboard * * https://web.archive.org/web/20180101224739/http://www.computer-engineering.org/ps2keyboard/scancodes2.html * Keyboard Scan Codes: Set 2 * * https://github.com/PaulStoffregen/PS2Keyboard * PS/2 Keyboard Library for Arduino * * https://github.com/techpaul/PS2KeyAdvanced * Arduino PS2 Keyboard FULL keyboard protocol support and full keys to integer coding * * https://github.com/techpaul/PS2KeyMap/ * Arduino PS2 keyboard International Keyboard mapping from PS2KeyAdvanced and return as UTF-8 * * https://www.terena.org/activities/multiling/ml-mua/test/kbd-all.html * This page provides information about layouts of different national * keyboard their mapping to used Character Sets and UCS * * https://github.com/ilpianista/itlinux-win-keyboard * An Italian keyboard layout for Windows, customized with shortcuts from Linux systems */ // TODO // [Basic features] // XXX Test keymap for DE // XXX Test keymap for FR // XXX Test keymap for GB // XXX Test keymap for ES // XXX Issue: Sometimes keyboard starts injecting break codes // when pressing [buttonA] + arrow keys, making it impossible // to set the time in Mandelbrot?? // XXX (Left)Alt for buttonB? // // [Advanced features, for consideration] // XXX Caps-Lock and setting LEDs // XXX Do something with NumLock? // XXX Key combination to reset keyboard? // XXX Allow Gigatron to control the keyboard lights // XXX Let keyboard lights run in a pattern (bad idea :-) #define PS2_ENTER '\n' #define PS2_TAB 9 #define PS2_BACKSPACE 127 #define PS2_ESC 27 #define PS2_F1 (0xc0 + 1) #define PS2_F2 (0xc0 + 2) #define PS2_F3 (0xc0 + 3) #define PS2_F4 (0xc0 + 4) #define PS2_F5 (0xc0 + 5) #define PS2_F6 (0xc0 + 6) #define PS2_F7 (0xc0 + 7) #define PS2_F8 (0xc0 + 8) #define PS2_F9 (0xc0 + 9) #define PS2_F10 (0xc0 + 10) #define PS2_F11 (0xc0 + 11) #define PS2_F12 (0xc0 + 12) #define CTRLALTDEL (255 ^ buttonStart) /* * Keyboard layout mapping */ const char keymapNames[][3] = { "US", "GB", "DE", "FR", "IT", "ES", }; const byte nrKeymaps = arrayLen(keymapNames); int getKeymapIndex(void) { byte index = EEPROM.read(offsetof(struct EEPROMlayout, keymapIndex)); return (index >= nrKeymaps) ? 0 : index; // Also handle invalid values } const char *getKeymapName() { return getKeymapName(getKeymapIndex()); } const char *getKeymapName(byte index) { return (index > arrayLen(keymapNames)) ? 0 : keymapNames[index]; } enum { US = 1 << 0, GB = 1 << 1, DE = 1 << 2, FR = 1 << 3, IT = 1 << 4, ES = 1 << 5, NOMOD = 0 << nrKeymaps, SHIFT = 1 << nrKeymaps, ALTGR = 2 << nrKeymaps, EVERY = 3 << nrKeymaps, }; typedef struct { byte flags; // Change from byte to word if we add more keymaps byte code; byte ascii; // XXX Remove this if we add flags. Value can be inferred by lookup() } keyTuple_t; // Keymaps courtesy of // Arduino PS2Keyboard library (US,DE,FR) // https://playground.arduino.cc/Main/PS2Keyboard // and // Teensy PS2Keyboard library (GB,IT,ES) // http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html // // 229 * 3 bytes = 687 bytes for holding 6 keyboard layouts // To save space this table excludes codes that aren't in US-ASCII // because these aren't in the Gigatron font either (yet). // So accented letters and such are all absent. This is needed // to make it all fit in the ATtiny85 configuration. const PROGMEM keyTuple_t keymaps[] = { { +US+GB+DE+FR+IT+ES +EVERY, 0x0d, 9 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x5a, 10 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x76, 27 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x29, 32 }, { +US+GB+DE +IT+ES +SHIFT, 0x16, 33 }, // '!' { +FR +NOMOD, 0x4a, 33 }, { +GB+DE +IT+ES +SHIFT, 0x1e, 34 }, // '"' { +FR +NOMOD, 0x26, 34 }, { +US +SHIFT, 0x52, 34 }, { +US +SHIFT, 0x26, 35 }, // '#' { +FR +ES +ALTGR, 0x26, 35 }, { +IT +ALTGR, 0x52, 35 }, { +GB+DE +NOMOD, 0x5d, 35 }, { +DE+FR +ALTGR, 0x5d, 35 }, { +US+GB+DE +IT+ES +SHIFT, 0x25, 36 }, // '$' { +FR +NOMOD, 0x5b, 36 }, { +US+GB+DE +IT+ES +SHIFT, 0x2e, 37 }, // '%' { +FR +SHIFT, 0x52, 37 }, { +FR +NOMOD, 0x16, 38 }, // '&' { +DE +IT+ES +SHIFT, 0x36, 38 }, { +US+GB +SHIFT, 0x3d, 38 }, { +FR +NOMOD, 0x25, 39 }, // ''' { +IT+ES +NOMOD, 0x4e, 39 }, { +US+GB +NOMOD, 0x52, 39 }, { +DE +NOMOD, 0x55, 39 }, { +DE +SHIFT, 0x5d, 39 }, { +FR +NOMOD, 0x2e, 40 }, // '(' { +DE +IT+ES +SHIFT, 0x3e, 40 }, { +US+GB +SHIFT, 0x46, 40 }, { +US+GB +SHIFT, 0x45, 41 }, // ')' { +DE +IT+ES +SHIFT, 0x46, 41 }, { +FR +NOMOD, 0x4e, 41 }, { +US+GB +SHIFT, 0x3e, 42 }, // '*' { +DE +IT+ES +SHIFT, 0x5b, 42 }, { +FR +NOMOD, 0x5d, 42 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x7c, 42 }, { +US+GB +FR +SHIFT, 0x55, 43 }, // '+' { +DE +IT+ES +NOMOD, 0x5b, 43 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x79, 43 }, { +FR +NOMOD, 0x3a, 44 }, // ',' { +US+GB+DE +IT+ES +NOMOD, 0x41, 44 }, { +FR +NOMOD, 0x36, 45 }, // '-' { +DE +IT+ES +NOMOD, 0x4a, 45 }, { +US+GB +NOMOD, 0x4e, 45 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x7b, 45 }, { +FR +SHIFT, 0x41, 46 }, // '.' { +US+GB+DE +IT+ES +NOMOD, 0x49, 46 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x71, 46 }, { +DE +IT+ES +SHIFT, 0x3d, 47 }, // '/' { +FR +SHIFT, 0x49, 47 }, { +US+GB +NOMOD, 0x4a, 47 }, { +US+GB+DE +IT+ES +NOMOD, 0x45, 48 }, // '0' { +FR +SHIFT, 0x45, 48 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x70, 48 }, { +US+GB+DE +IT+ES +NOMOD, 0x16, 49 }, // '1' { +FR +SHIFT, 0x16, 49 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x69, 49 }, { +US+GB+DE +IT+ES +NOMOD, 0x1e, 50 }, // '2' { +FR +SHIFT, 0x1e, 50 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x72, 50 }, { +US+GB+DE +IT+ES +NOMOD, 0x26, 51 }, // '3' { +FR +SHIFT, 0x26, 51 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x7a, 51 }, { +US+GB+DE +IT+ES +NOMOD, 0x25, 52 }, // '4' { +FR +SHIFT, 0x25, 52 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x6b, 52 }, { +US+GB+DE +IT+ES +NOMOD, 0x2e, 53 }, // '5' { +FR +SHIFT, 0x2e, 53 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x73, 53 }, { +US+GB+DE +IT+ES +NOMOD, 0x36, 54 }, // '6' { +FR +SHIFT, 0x36, 54 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x74, 54 }, { +US+GB+DE +IT+ES +NOMOD, 0x3d, 55 }, // '7' { +FR +SHIFT, 0x3d, 55 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x6c, 55 }, { +US+GB+DE +IT+ES +NOMOD, 0x3e, 56 }, // '8' { +FR +SHIFT, 0x3e, 56 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x75, 56 }, { +US+GB+DE +IT+ES +NOMOD, 0x46, 57 }, // '9' { +FR +SHIFT, 0x46, 57 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x7d, 57 }, { +FR +NOMOD, 0x49, 58 }, // ':' { +DE +IT+ES +SHIFT, 0x49, 58 }, { +US+GB +SHIFT, 0x4c, 58 }, { +FR +NOMOD, 0x41, 59 }, // ';' { +DE +IT+ES +SHIFT, 0x41, 59 }, { +US+GB +NOMOD, 0x4c, 59 }, { +US+GB +SHIFT, 0x41, 60 }, // '<' { +DE+FR+IT+ES +NOMOD, 0x61, 60 }, { +DE +IT+ES +SHIFT, 0x45, 61 }, // '=' { +US+GB +FR +NOMOD, 0x55, 61 }, { +US+GB +SHIFT, 0x49, 62 }, // '>' { +DE+FR+IT+ES +SHIFT, 0x61, 62 }, { +FR +SHIFT, 0x3a, 63 }, // '?' { +US+GB +SHIFT, 0x4a, 63 }, { +DE +IT+ES +SHIFT, 0x4e, 63 }, { +DE+FR +ALTGR, 0x15, 64 }, // '@' { +US +SHIFT, 0x1e, 64 }, { +ES +ALTGR, 0x1e, 64 }, { +FR +ALTGR, 0x45, 64 }, { +IT +ALTGR, 0x4c, 64 }, { +GB +SHIFT, 0x52, 64 }, { +FR +SHIFT, 0x15, 65 }, // 'A' { +US+GB+DE +IT+ES +SHIFT, 0x1c, 65 }, { +US+GB+DE+FR+IT+ES +SHIFT, 0x32, 66 }, // 'B' { +US+GB+DE+FR+IT+ES +SHIFT, 0x21, 67 }, // 'C' { +US+GB+DE+FR+IT+ES +SHIFT, 0x23, 68 }, // 'D' { +US+GB+DE+FR+IT+ES +SHIFT, 0x24, 69 }, // 'E' { +US+GB+DE+FR+IT+ES +SHIFT, 0x2b, 70 }, // 'F' { +US+GB+DE+FR+IT+ES +SHIFT, 0x34, 71 }, // 'G' { +US+GB+DE+FR+IT+ES +SHIFT, 0x33, 72 }, // 'H' { +US+GB+DE+FR+IT+ES +SHIFT, 0x43, 73 }, // 'I' { +US+GB+DE+FR+IT+ES +SHIFT, 0x3b, 74 }, // 'J' { +US+GB+DE+FR+IT+ES +SHIFT, 0x42, 75 }, // 'K' { +US+GB+DE+FR+IT+ES +SHIFT, 0x4b, 76 }, // 'L' { +US+GB+DE +IT+ES +SHIFT, 0x3a, 77 }, // 'M' { +FR +SHIFT, 0x4c, 77 }, { +US+GB+DE+FR+IT+ES +SHIFT, 0x31, 78 }, // 'N' { +US+GB+DE+FR+IT+ES +SHIFT, 0x44, 79 }, // 'O' { +US+GB+DE+FR+IT+ES +SHIFT, 0x4d, 80 }, // 'P' { +US+GB+DE +IT+ES +SHIFT, 0x15, 81 }, // 'Q' { +FR +SHIFT, 0x1c, 81 }, { +US+GB+DE+FR+IT+ES +SHIFT, 0x2d, 82 }, // 'R' { +US+GB+DE+FR+IT+ES +SHIFT, 0x1b, 83 }, // 'S' { +US+GB+DE+FR+IT+ES +SHIFT, 0x2c, 84 }, // 'T' { +US+GB+DE+FR+IT+ES +SHIFT, 0x3c, 85 }, // 'U' { +US+GB+DE+FR+IT+ES +SHIFT, 0x2a, 86 }, // 'V' { +FR +SHIFT, 0x1a, 87 }, // 'W' { +US+GB+DE +IT+ES +SHIFT, 0x1d, 87 }, { +US+GB+DE+FR+IT+ES +SHIFT, 0x22, 88 }, // 'X' { +DE +SHIFT, 0x1a, 89 }, // 'Y' { +US+GB +FR+IT+ES +SHIFT, 0x35, 89 }, { +US+GB +IT+ES +SHIFT, 0x1a, 90 }, // 'Z' { +FR +SHIFT, 0x1d, 90 }, { +DE +SHIFT, 0x35, 90 }, { +FR +ALTGR, 0x2e, 91 }, // '[' { +DE +IT +ALTGR, 0x3e, 91 }, { +US+GB +NOMOD, 0x54, 91 }, { +IT+ES +ALTGR, 0x54, 91 }, { +IT +NOMOD, 0x0e, 92 }, // '\' { +ES +ALTGR, 0x0e, 92 }, { +FR +ALTGR, 0x3e, 92 }, { +DE +ALTGR, 0x4e, 92 }, { +US +NOMOD, 0x5d, 92 }, { +GB +NOMOD, 0x61, 92 }, { +DE +IT +ALTGR, 0x46, 93 }, // ']' { +FR +ALTGR, 0x4e, 93 }, { +US+GB +NOMOD, 0x5b, 93 }, { +IT+ES +ALTGR, 0x5b, 93 }, { +DE +NOMOD, 0x0e, 94 }, // '^' { +US+GB +SHIFT, 0x36, 94 }, { +FR +ALTGR, 0x46, 94 }, { +FR +NOMOD, 0x54, 94 }, { +ES +SHIFT, 0x54, 94 }, { +IT +SHIFT, 0x55, 94 }, { +FR +NOMOD, 0x3e, 95 }, // '_' { +DE +IT+ES +SHIFT, 0x4a, 95 }, { +US+GB +SHIFT, 0x4e, 95 }, { +US+GB +NOMOD, 0x0e, 96 }, // '`' { +FR +ALTGR, 0x3d, 96 }, { +IT +ALTGR, 0x4e, 96 }, { +ES +NOMOD, 0x54, 96 }, { +DE +SHIFT, 0x55, 96 }, { +FR +NOMOD, 0x15, 97 }, // 'a' { +US+GB+DE +IT+ES +NOMOD, 0x1c, 97 }, { +US+GB+DE+FR+IT+ES +NOMOD, 0x32, 98 }, // 'b' { +US+GB+DE+FR+IT+ES +NOMOD, 0x21, 99 }, // 'c' { +US+GB+DE+FR+IT+ES +NOMOD, 0x23, 100 }, // 'd' { +US+GB+DE+FR+IT+ES +NOMOD, 0x24, 101 }, // 'e' { +US+GB+DE+FR+IT+ES +NOMOD, 0x2b, 102 }, // 'f' { +US+GB+DE+FR+IT+ES +NOMOD, 0x34, 103 }, // 'g' { +US+GB+DE+FR+IT+ES +NOMOD, 0x33, 104 }, // 'h' { +US+GB+DE+FR+IT+ES +NOMOD, 0x43, 105 }, // 'i' { +US+GB+DE+FR+IT+ES +NOMOD, 0x3b, 106 }, // 'j' { +US+GB+DE+FR+IT+ES +NOMOD, 0x42, 107 }, // 'k' { +US+GB+DE+FR+IT+ES +NOMOD, 0x4b, 108 }, // 'l' { +US+GB+DE +IT+ES +NOMOD, 0x3a, 109 }, // 'm' { +FR +NOMOD, 0x4c, 109 }, { +US+GB+DE+FR+IT+ES +NOMOD, 0x31, 110 }, // 'n' { +US+GB+DE+FR+IT+ES +NOMOD, 0x44, 111 }, // 'o' { +US+GB+DE+FR+IT+ES +NOMOD, 0x4d, 112 }, // 'p' { +US+GB+DE +IT+ES +NOMOD, 0x15, 113 }, // 'q' { +FR +NOMOD, 0x1c, 113 }, { +US+GB+DE+FR+IT+ES +NOMOD, 0x2d, 114 }, // 'r' { +US+GB+DE+FR+IT+ES +NOMOD, 0x1b, 115 }, // 's' { +US+GB+DE+FR+IT+ES +NOMOD, 0x2c, 116 }, // 't' { +US+GB+DE+FR+IT+ES +NOMOD, 0x3c, 117 }, // 'u' { +US+GB+DE+FR+IT+ES +NOMOD, 0x2a, 118 }, // 'v' { +FR +NOMOD, 0x1a, 119 }, // 'w' { +US+GB+DE +IT+ES +NOMOD, 0x1d, 119 }, { +US+GB+DE+FR+IT+ES +NOMOD, 0x22, 120 }, // 'x' { +DE +NOMOD, 0x1a, 121 }, // 'y' { +US+GB +FR+IT+ES +NOMOD, 0x35, 121 }, { +US+GB +IT+ES +NOMOD, 0x1a, 122 }, // 'z' { +FR +NOMOD, 0x1d, 122 }, { +DE +NOMOD, 0x35, 122 }, { +FR +ALTGR, 0x25, 123 }, // '{' { +DE +IT +ALTGR, 0x3d, 123 }, { +ES +ALTGR, 0x52, 123 }, { +US+GB +SHIFT, 0x54, 123 }, { +IT +SHIFT, 0x0e, 124 }, // '|' { +ES +ALTGR, 0x16, 124 }, { +FR +ALTGR, 0x36, 124 }, { +US +SHIFT, 0x5d, 124 }, { +GB +SHIFT, 0x61, 124 }, { +DE+FR +ES +ALTGR, 0x61, 124 }, { +DE +IT +ALTGR, 0x45, 125 }, // '}' { +FR +ALTGR, 0x56, 125 }, { +US+GB +SHIFT, 0x5b, 125 }, { +ES +ALTGR, 0x5d, 125 }, { +US +SHIFT, 0x0e, 126 }, // '~' { +FR +ALTGR, 0x1e, 126 }, { +ES +ALTGR, 0x25, 126 }, { +IT +ALTGR, 0x55, 126 }, { +DE +ALTGR, 0x5b, 126 }, { +GB +SHIFT, 0x5d, 126 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x66, 127 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x05, 193 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x06, 194 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x04, 195 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x0c, 196 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x03, 197 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x0b, 198 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x83, 199 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x0a, 200 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x01, 201 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x09, 202 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x78, 203 }, { +US+GB+DE+FR+IT+ES +EVERY, 0x07, 204 }, }; /* * Find ASCII code corresponding to key code and active modifiers * for the current keymap */ static inline byte lookup(byte mods, word code) { byte keymapFlag = 1 << getKeymapIndex(); for (int i=0; iflags); if (~f & keymapFlag) continue; // Different keymap if ((f & EVERY) != EVERY && (f & EVERY) != mods) continue; // Different modifiers byte c = pgm_read_byte(&t->code); if (c != code) continue; // Different scan code return pgm_read_byte(&t->ascii); } return 0; } /* * Output state */ enum { // bit 0:7 Equivalent game controller buttons (positive) // buttonRight .. buttonA // bit 8:13 State of modifier keys leftShiftFlag = 1<<8, rightShiftFlag = 1<<9, leftCtrlFlag = 1<<10, rightCtrlFlag = 1<<11, altFlag = 1<<12, altGrFlag = 1<<13, shiftFlags = leftShiftFlag | rightShiftFlag, ctrlFlags = leftCtrlFlag | rightCtrlFlag, altFlags = altFlag | altGrFlag, // bit 14:15 Protocol state breakFlag = 1<<14, // Release of physical key extendedFlag = 1<<15, // Extended PS/2 keyboard codes }; #define nextPs2()\ flags &= ~(breakFlag | extendedFlag) static word flags = 0; // Modifier keys and button state static byte ascii = 0; // Current ASCII key press static word lastChange, hold; // Babelfish controls the hold time for keys // XXX Put variables in a class for better scoping /* * PS/2 sequences */ static int bitBuffer; static byte _n; static word lastClock; /* * Input buffering */ static volatile byte ps2Buffer[15+1]; static volatile byte head = 0, tail = 0; #if defined(ARDUINO_attiny) // attachInterrupt() doesn't work on the ATtiny85 static byte keyboardClockBit; ISR(PCINT0_vect) { if (~PINB & keyboardClockBit) // FALLING edge of PS/2 clock ps2interrupt(); } #endif void keyboard_setup() { // Initialize the pins pinMode(keyboardDataPin, INPUT_PULLUP); allowPs2(); #if defined(ARDUINO_attiny) keyboardClockBit = digitalPinToBitMask(keyboardClockPin); GIMSK |= 1< 5 || _n >= 11) { bitBuffer = 1; // Clear bits and setup for odd parity _n = 0; } lastClock = now; bitBuffer |= nextBit << _n; bitBuffer ^= nextBit; // Track parity in bit 0 if (_n == 10) { ps2Buffer[head] = (bitBuffer >> 1) & 255; byte nextHead = (head + 1) % sizeof ps2Buffer; if (nextHead != tail // Buffer not (almost) full && (bitBuffer & 1)) // Parity check, with bonus check of start/stop bits head = nextHead; } _n++; } // Ready to receive void allowPs2() { pinMode(keyboardClockPin, INPUT_PULLUP); _n = 11; // Consider previous bits lost } // Pull clock line LOW to signal that we're not ready to receive void forbidPs2() { digitalWrite(keyboardClockPin, LOW); pinMode(keyboardClockPin, OUTPUT); } // Read next byte from incoming PS/2 buffer, or 0 when empty static byte readPs2Buffer() { byte value = 0; if (head != tail) { value = ps2Buffer[tail]; tail = (tail + 1) % sizeof ps2Buffer; } return value; } byte keyboard_getState() { word now = (word) millis(); // Note: without interrupts millis() stops counting for (;;) { word value = readPs2Buffer(); if (value == 0) { // Buffer is empty if (now - lastChange > hold) // Auto-release keys after `hold' milliseconds ascii = 0; if (now - lastChange > 1000) // Hold `buttons' a bit longer than the repeat delay. This flags &= ~255; // is merely a safeguard against missing the break event // and then ending up with a stuck virtual button press. break; } switch (value) { // The BREAK or EXTENDED bytes are part of a longer sequence // Here we don't mind the order they appear in case 0xe0: flags |= extendedFlag; continue; case 0xf0: flags |= breakFlag; continue; case 0xaa: // BAT ok #define oddParity(x) ((1 ^ x ^ (x>>1) ^ (x>>2) ^ (x>>3) ^ (x>>4) ^ (x>>5) ^ (x>>6) ^ (x>>7)) & 1) #define code(x) ((1 << 10) | (oddParity(x) << 9) | ((x) << 1)) keyboard_send(code(0xf4)); // Enable (fixes Walter's PERIBOARD-409) continue; } if (flags & extendedFlag) value |= 0xe0 << 8; // Simplify our switch statements // Recognize modifier keys word modifier = 0; switch (value) { case 0x11: modifier = altFlag; break; case 0x12: modifier = leftShiftFlag; break; case 0x14: modifier = leftCtrlFlag; break; case 0x59: modifier = rightShiftFlag; break; case 0xe011: modifier = altGrFlag; break; case 0xe014: modifier = rightCtrlFlag; break; } // Case 1: Handle modifier keys if (modifier) { if (flags & breakFlag) flags &= ~modifier; // Modifier key released else flags |= modifier; // Modifier key pressed nextPs2(); // Reset PS/2 sequence continue; // Back to reading from buffer } // Recognize character and control keys and map them to ASCII // or game controller buttons // Always return an updated state to the caller from this point on, // even if there are still bytes waiting in the buffer. byte button = 0; byte newAscii = 0; switch (value) { // Extended codes in "Set 2" case 0xe075: button = buttonUp; break; // [UpArrow] case 0xe06b: button = buttonLeft; break; // [LeftArrow] case 0xe072: button = buttonDown; break; // [DownArrow] case 0xe074: button = buttonRight; break; // [RightArrow] case 0x66: // [BackSpace] case 0xe071: if ((flags & ctrlFlags) // [Delete] && (flags & altFlags)) newAscii = CTRLALTDEL; // [Ctrl-Alt-Del] becomes [Start] else button = buttonA; break; // ASCII DEL is 127 is ~buttonA case 0xe069: button = buttonA; break; // [End] case 0xe070: // [Insert] case 0xe06c: button = buttonB; break; // [Home] case 0xe07a: button = buttonSelect; break; // [PageDown] case 0xe07d: button = buttonStart; break; // [PageUp] case 0xe04a: newAscii = '/'; break; // [/] on numeric island case 0xe05a: newAscii = '\n'; break; // [Enter] on numeric island default: // Apply keyboard mapping to ASCII if ((flags & altGrFlag) || ((flags & ctrlFlags) && (flags & altFlag))) // Ctrl+Alt is AltGr newAscii = lookup(ALTGR, value); else if (flags & shiftFlags) newAscii = lookup(SHIFT, value); else newAscii = lookup(NOMOD, value); // Handle control key combinations if (flags & ctrlFlags) switch (newAscii) { case '?': newAscii = 127; break; // Traditional mapping of Ctrl-? to DEL case ' ': button = 255; break; // Make it send a 0 byte default: byte f = fnKey(newAscii); if (f) { if (flags & altFlags) // Ctrl-Alt-Fxx changes the keymap EEPROM.write(offsetof(struct EEPROMlayout, keymapIndex), f-1); else // Ctrl+Fxx are BabelFish commands newAscii ^= 64; } else // Make control codes (what the key is for...) newAscii &= 31; } } // Consolidate the `flags' and `ascii' states if (button) { // Case 2: Simulated game controller buttons lastChange = now; if (flags & breakFlag) flags &= ~button; // Button released else flags |= button; // Button pressed ascii = 0; } else { // Case 3: ASCII keys if (newAscii != ascii) lastChange = now; if (~flags & breakFlag) // Ignore ASCII release events (use 'hold' instead) ascii = newAscii; // ASCII pressed hold = 35; // Hold for 3 frames (less than the repeat rate) if (ascii == CTRLALTDEL) // At least 2 seconds for the [Start] signal hold = max(500 + now - lastChange, 2500); // No accidental Easter Egg trigger (8b guy) flags &= ~255; } nextPs2(); // Reset PS/2 sequence break; // Always return, leave anything in buffer behind } // Return the newly determined state to caller return ascii ? ascii : (255 & ~flags); } // Return 0 if not a function key, or its ordinal otherwise byte fnKey(byte key) { return (PS2_F1 <= key && key <= PS2_F12) ? (key & 15) : 0; } // 'code' must include start, data, parity and stop bits void keyboard_send(word code) { // 1. Bring the Clock line low for at least 100 microseconds forbidPs2(); delayMicroseconds(100); // 2. Bring the data line low digitalWrite(keyboardDataPin, 0); pinMode(keyboardDataPin, OUTPUT); // 3. Release the Clock line allowPs2(); // Pull-up resistor will bring clock line up first do { // 6. Wait for the device to bring Clock high while (digitalRead(keyboardClockPin) == 0) ; // 4. 7. Wait for the device to bring the Clock line low // MvK: Falling edge transfers the data while (digitalRead(keyboardClockPin) != 0) ; // 5. Set/reset the Data line to send the next data bit code >>= 1; if (code & 1) pinMode(keyboardDataPin, INPUT_PULLUP); // To send 1 else { digitalWrite(keyboardDataPin, 0); pinMode(keyboardDataPin, OUTPUT); // To send 0 } // 8. Repeat steps 5-7 for the other seven data bits and the parity bit // 9. Release the Data line } while (code > 1); pinMode(keyboardDataPin, INPUT_PULLUP); // 10. Wait for the device to bring Data low while (digitalRead(keyboardDataPin) != 0) ; // 11. Wait for the device to bring Clock low while (digitalRead(keyboardClockPin) != 0) ; // 12. Wait for the device to release Data and Clock while (digitalRead(keyboardClockPin) == 0 || digitalRead(keyboardDataPin) == 0) ; }