Wed, 16 Apr 2014 02:07:24 -0600
added keyboard mappings for ENTER, CANCL, and EXIT
philpem@80 | 1 | #include <stdbool.h> |
philpem@74 | 2 | #include "SDL.h" |
philpem@99 | 3 | #include "utils.h" |
philpem@86 | 4 | #include "keyboard.h" |
philpem@74 | 5 | |
philpem@99 | 6 | // Enable/disable KBC debugging |
philpem@99 | 7 | #define kbc_debug false |
philpem@99 | 8 | |
philpem@74 | 9 | /** |
philpem@74 | 10 | * Key map -- a mapping from SDLK_xxx constants to scancodes and vice versa. |
philpem@74 | 11 | */ |
philpem@74 | 12 | struct { |
philpem@74 | 13 | SDLKey key; ///< SDLK_xxx key code constant |
philpem@74 | 14 | int extended; ///< 1 if this is an extended keycode |
philpem@74 | 15 | unsigned char scancode; ///< Keyboard scan code |
philpem@74 | 16 | } keymap[] = { |
philpem@74 | 17 | { SDLK_UP, 0, 0x01 }, // ROLL/Up [UpArrow] |
philpem@74 | 18 | { SDLK_KP2, 0, 0x01 }, // ROLL/Up [Keypad 2] |
philpem@74 | 19 | // { SDLK_, 1, 0x02 }, // Clear Line |
philpem@74 | 20 | // { SDLK_, 1, 0x03 }, // Rstrt / Ref |
andrew@146 | 21 | { SDLK_ESCAPE, 1, 0x04 }, // Exit |
philpem@74 | 22 | { SDLK_KP1, 0, 0x05 }, // PREV [Keypad 1] |
philpem@74 | 23 | // { SDLK_, 1, 0x06 }, // Msg |
andrew@146 | 24 | { SDLK_BACKSPACE, 1, 0x07 }, // Cancl |
philpem@74 | 25 | { SDLK_BACKSPACE, 0, 0x08 }, // Backspace |
philpem@74 | 26 | { SDLK_TAB, 0, 0x09 }, // Tab |
andrew@146 | 27 | { SDLK_RETURN, 1, 0x0a }, // ENTER |
philpem@74 | 28 | { SDLK_DOWN, 0, 0x0b }, // ROLL/Down [DownArrow] |
philpem@74 | 29 | { SDLK_KP0, 0, 0x0b }, // ROLL/Down [Keypad 0] |
philpem@74 | 30 | { SDLK_KP3, 0, 0x0c }, // NEXT [Keypad 3] |
philpem@74 | 31 | { SDLK_RETURN, 0, 0x0d }, // RETURN [Return] |
philpem@74 | 32 | { SDLK_LEFT, 0, 0x0e }, // <-- [LeftArrow] |
philpem@74 | 33 | { SDLK_KP_MINUS, 0, 0x0e }, // <-- [Keypad -] |
philpem@74 | 34 | { SDLK_RIGHT, 0, 0x0f }, // --> [RightArrow] |
philpem@74 | 35 | { SDLK_KP_PERIOD, 0, 0x0f }, // --> [Keypad .] |
philpem@74 | 36 | // { SDLK_, 1, 0x10 }, // Creat |
philpem@74 | 37 | // { SDLK_, 1, 0x11 }, // Save |
philpem@74 | 38 | // { SDLK_, 1, 0x12 }, // Move |
philpem@74 | 39 | // { SDLK_, 1, 0x13 }, // Ops |
philpem@74 | 40 | // { SDLK_, 1, 0x14 }, // Copy |
philpem@74 | 41 | { SDLK_F1, 0, 0x15 }, // F1 |
philpem@74 | 42 | { SDLK_F2, 0, 0x16 }, // F2 |
philpem@74 | 43 | { SDLK_F3, 0, 0x17 }, // F3 |
philpem@74 | 44 | { SDLK_F4, 0, 0x18 }, // F4 |
philpem@74 | 45 | { SDLK_F5, 0, 0x19 }, // F5 |
philpem@74 | 46 | { SDLK_F6, 0, 0x1a }, // F6 |
philpem@74 | 47 | { SDLK_ESCAPE, 0, 0x1b }, // ESC/DEL [Escape] |
philpem@74 | 48 | { SDLK_F7, 0, 0x1c }, // F7 |
philpem@74 | 49 | { SDLK_F8, 0, 0x1d }, // F8 |
philpem@74 | 50 | // { SDLK_, 1, 0x1e }, // Suspd |
philpem@74 | 51 | // { SDLK_, 1, 0x1f }, // Rsume |
philpem@74 | 52 | { SDLK_SPACE, 0, 0x20 }, // SPACE [Spacebar] |
philpem@74 | 53 | // { SDLK_, 1, 0x21 }, // Undo |
philpem@74 | 54 | // { SDLK_, 1, 0x22 }, // Redo |
philpem@74 | 55 | // { SDLK_, 1, 0x23 }, // FIND |
philpem@74 | 56 | // { SDLK_, 1, 0x24 }, // RPLAC |
philpem@74 | 57 | { SDLK_BREAK, 0, 0x25 }, // RESET/BREAK [Pause/Break] |
philpem@74 | 58 | // { SDLK_, 1, 0x26 }, // DleteChar |
philpem@74 | 59 | { SDLK_QUOTE, 0, 0x27 }, // ' (single-quote) |
philpem@74 | 60 | // { SDLK_, 1, 0x28 }, // SLCT/MARK |
philpem@74 | 61 | // { SDLK_, 1, 0x29 }, // INPUT/MODE |
philpem@74 | 62 | // { SDLK_, 1, 0x2a }, // HELP |
philpem@74 | 63 | // Keycode 2B not used |
philpem@74 | 64 | { SDLK_COMMA, 0, 0x2c }, // , [Comma] |
philpem@74 | 65 | { SDLK_MINUS, 0, 0x2d }, // - [Dash] |
philpem@74 | 66 | { SDLK_PERIOD, 0, 0x2e }, // . [Period] |
philpem@74 | 67 | { SDLK_SLASH, 0, 0x2f }, // / [Forward-slash] |
philpem@74 | 68 | { SDLK_0, 0, 0x30 }, // 0 |
philpem@74 | 69 | { SDLK_1, 0, 0x31 }, // 1 |
philpem@74 | 70 | { SDLK_2, 0, 0x32 }, // 2 |
philpem@74 | 71 | { SDLK_3, 0, 0x33 }, // 3 |
philpem@74 | 72 | { SDLK_4, 0, 0x34 }, // 4 |
philpem@74 | 73 | { SDLK_5, 0, 0x35 }, // 5 |
philpem@74 | 74 | { SDLK_6, 0, 0x36 }, // 6 |
philpem@74 | 75 | { SDLK_7, 0, 0x37 }, // 7 |
philpem@74 | 76 | { SDLK_8, 0, 0x38 }, // 8 |
philpem@74 | 77 | { SDLK_9, 0, 0x39 }, // 9 |
philpem@74 | 78 | // Keycode 3A not used |
philpem@74 | 79 | { SDLK_SEMICOLON, 0, 0x3b }, // ; [Semicolon] |
philpem@74 | 80 | // Keycode 3C not used |
philpem@74 | 81 | { SDLK_EQUALS, 0, 0x3d }, // = [Equals] |
philpem@74 | 82 | // Keycodes 3E, 3F, 40 not used |
philpem@74 | 83 | // { SDLK_, 1, 0x41 }, // CMD |
philpem@74 | 84 | // { SDLK_, 1, 0x42 }, // CLOSE/OPEN |
philpem@74 | 85 | { SDLK_KP7, 0, 0x43 }, // PRINT |
philpem@74 | 86 | { SDLK_KP8, 0, 0x44 }, // CLEAR/RFRSH |
philpem@74 | 87 | { SDLK_CAPSLOCK, 0, 0x45 }, // Caps Lock |
philpem@74 | 88 | { SDLK_KP9, 0, 0x46 }, // PAGE |
philpem@74 | 89 | { SDLK_KP4, 0, 0x47 }, // BEG |
philpem@74 | 90 | { SDLK_LSHIFT, 0, 0x48 }, // Left Shift |
philpem@74 | 91 | { SDLK_RSHIFT, 0, 0x49 }, // Right Shift |
philpem@74 | 92 | { SDLK_HOME, 0, 0x4a }, // Home |
philpem@74 | 93 | { SDLK_KP5, 0, 0x4a }, // Home [Keypad 5] |
philpem@74 | 94 | { SDLK_END, 0, 0x4b }, // End |
philpem@74 | 95 | { SDLK_KP6, 0, 0x4b }, // End [Keypad 6] |
philpem@74 | 96 | { SDLK_LCTRL, 0, 0x4c }, // Left Ctrl? \___ not sure which is left and which is right... |
philpem@74 | 97 | { SDLK_RCTRL, 0, 0x4d }, // Right Ctrl? / |
philpem@74 | 98 | // Keycodes 4E thru 5A not used |
philpem@74 | 99 | { SDLK_LEFTBRACKET, 0, 0x5b }, // [ |
philpem@74 | 100 | { SDLK_BACKSLASH, 0, 0x5c }, // \ (backslash) |
philpem@74 | 101 | { SDLK_RIGHTBRACKET, 0, 0x5d }, // ] |
philpem@74 | 102 | // Keycodes 5E, 5F not used |
philpem@74 | 103 | { SDLK_BACKQUOTE, 0, 0x60 }, // ` |
philpem@74 | 104 | { SDLK_a, 0, 0x61 }, // A |
philpem@74 | 105 | { SDLK_b, 0, 0x62 }, // B |
philpem@74 | 106 | { SDLK_c, 0, 0x63 }, // C |
philpem@74 | 107 | { SDLK_d, 0, 0x64 }, // D |
philpem@74 | 108 | { SDLK_e, 0, 0x65 }, // E |
philpem@74 | 109 | { SDLK_f, 0, 0x66 }, // F |
philpem@74 | 110 | { SDLK_g, 0, 0x67 }, // G |
philpem@74 | 111 | { SDLK_h, 0, 0x68 }, // H |
philpem@74 | 112 | { SDLK_i, 0, 0x69 }, // I |
philpem@74 | 113 | { SDLK_j, 0, 0x6a }, // J |
philpem@74 | 114 | { SDLK_k, 0, 0x6b }, // K |
philpem@74 | 115 | { SDLK_l, 0, 0x6c }, // L |
philpem@74 | 116 | { SDLK_m, 0, 0x6d }, // M |
philpem@74 | 117 | { SDLK_n, 0, 0x6e }, // N |
philpem@74 | 118 | { SDLK_o, 0, 0x6f }, // O |
philpem@74 | 119 | { SDLK_p, 0, 0x70 }, // P |
philpem@74 | 120 | { SDLK_q, 0, 0x71 }, // Q |
philpem@74 | 121 | { SDLK_r, 0, 0x72 }, // R |
philpem@74 | 122 | { SDLK_s, 0, 0x73 }, // S |
philpem@74 | 123 | { SDLK_t, 0, 0x74 }, // T |
philpem@74 | 124 | { SDLK_u, 0, 0x75 }, // U |
philpem@74 | 125 | { SDLK_v, 0, 0x76 }, // V |
philpem@74 | 126 | { SDLK_w, 0, 0x77 }, // W |
philpem@74 | 127 | { SDLK_x, 0, 0x78 }, // X |
philpem@74 | 128 | { SDLK_y, 0, 0x79 }, // Y |
philpem@74 | 129 | { SDLK_z, 0, 0x7a }, // Z |
philpem@74 | 130 | // Keycodes 7B, 7C, 7D not used |
philpem@83 | 131 | { SDLK_NUMLOCK, 0, 0x7e }, // Numlock |
philpem@83 | 132 | { SDLK_DELETE, 0, 0x7f } // Dlete |
philpem@74 | 133 | }; |
philpem@74 | 134 | |
philpem@74 | 135 | /** |
philpem@86 | 136 | * List of special key codes |
philpem@86 | 137 | */ |
philpem@86 | 138 | enum { |
philpem@86 | 139 | KEY_ALL_UP = 0x40, ///< All keys up |
philpem@86 | 140 | KEY_LIST_END = 0x80, ///< End of key code list |
philpem@86 | 141 | KEY_BEGIN_MOUSE = 0xCF, ///< Mouse data follows |
philpem@86 | 142 | KEY_BEGIN_KEYBOARD = 0xDF, ///< Keyboard data follows |
philpem@86 | 143 | }; |
philpem@86 | 144 | |
philpem@86 | 145 | /** |
philpem@86 | 146 | * List of keyboard commands |
philpem@74 | 147 | */ |
philpem@86 | 148 | enum { |
philpem@86 | 149 | KEY_CMD_RESET = 0x92, ///< Reset keyboard |
philpem@86 | 150 | KEY_CMD_CAPSLED_OFF = 0xB1, ///< Caps Lock LED off--CHECK! |
philpem@86 | 151 | KEY_CMD_CAPSLED_ON = 0xB0, ///< Caps Lock LED on --CHECK! |
philpem@86 | 152 | KEY_CMD_NUMLED_OFF = 0xA1, ///< Num Lock LED off --CHECK! |
philpem@86 | 153 | KEY_CMD_NUMLED_ON = 0xA0, ///< Num Lock LED on --CHECK! |
philpem@86 | 154 | KEY_CMD_MOUSE_ENABLE = 0xD0, ///< Enable mouse |
philpem@86 | 155 | KEY_CMD_MOUSE_DISABLE = 0xD1 ///< Disable mouse |
philpem@86 | 156 | }; |
philpem@74 | 157 | |
philpem@80 | 158 | void keyboard_init(KEYBOARD_STATE *ks) |
philpem@74 | 159 | { |
philpem@74 | 160 | // Set all key states to "not pressed" |
philpem@80 | 161 | for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) { |
philpem@80 | 162 | ks->keystate[i] = 0; |
philpem@74 | 163 | } |
philpem@80 | 164 | |
philpem@80 | 165 | // Reset the R/W pointers and length |
philpem@80 | 166 | ks->readp = ks->writep = ks->buflen = 0; |
philpem@91 | 167 | |
philpem@91 | 168 | // Clear the update flag |
philpem@91 | 169 | ks->update_flag = false; |
philpem@74 | 170 | } |
philpem@74 | 171 | |
philpem@80 | 172 | void keyboard_event(KEYBOARD_STATE *ks, SDL_Event *ev) |
philpem@74 | 173 | { |
philpem@90 | 174 | int v = 0; |
philpem@90 | 175 | switch (ev->type) { |
philpem@90 | 176 | case SDL_KEYDOWN: |
philpem@90 | 177 | // Key down (pressed) |
philpem@90 | 178 | v = 1; |
philpem@90 | 179 | break; |
philpem@90 | 180 | case SDL_KEYUP: |
philpem@90 | 181 | // Key up (released) |
philpem@90 | 182 | v = 0; |
philpem@90 | 183 | break; |
philpem@90 | 184 | default: |
philpem@90 | 185 | // Not a keyboard event |
philpem@90 | 186 | return; |
philpem@86 | 187 | } |
philpem@86 | 188 | |
philpem@82 | 189 | // scan the keymap |
philpem@90 | 190 | for (int i=0; i < sizeof(keymap)/sizeof(keymap[0]); i++) { |
philpem@90 | 191 | if (keymap[i].key == ev->key.keysym.sym) { |
philpem@90 | 192 | // Keycode match. Is this an Extended Map key? |
philpem@86 | 193 | if (keymap[i].extended) { |
philpem@90 | 194 | // Yes -- need ALT set when pressing the key for this to be a match |
philpem@86 | 195 | if (ev->key.keysym.mod & KMOD_ALT) { |
philpem@90 | 196 | ks->keystate[keymap[i].scancode] = v; |
philpem@94 | 197 | ks->update_flag = true; |
philpem@86 | 198 | break; |
philpem@86 | 199 | } |
philpem@86 | 200 | } else { |
philpem@90 | 201 | // Standard Map key. ALT must NOT be pressed for this to be a match |
philpem@86 | 202 | if (!(ev->key.keysym.mod & KMOD_ALT)) { |
philpem@90 | 203 | ks->keystate[keymap[i].scancode] = v; |
philpem@94 | 204 | ks->update_flag = true; |
philpem@86 | 205 | break; |
philpem@86 | 206 | } |
philpem@86 | 207 | } |
philpem@86 | 208 | } |
philpem@86 | 209 | } |
philpem@74 | 210 | } |
philpem@80 | 211 | void keyboard_scan(KEYBOARD_STATE *ks) |
philpem@80 | 212 | { |
philpem@84 | 213 | int nkeys = 0; |
philpem@84 | 214 | |
philpem@91 | 215 | // Skip doing the scan if the keyboard hasn't changed state |
philpem@91 | 216 | if (!ks->update_flag) return; |
philpem@91 | 217 | |
philpem@96 | 218 | |
philpem@80 | 219 | // if buffer empty, do a keyboard scan |
philpem@80 | 220 | if (ks->buflen == 0) { |
philpem@84 | 221 | // Keyboard Data Begins Here (BEGKBD) |
philpem@96 | 222 | //ks->buffer[ks->writep] = KEY_BEGIN_KEYBOARD; |
philpem@96 | 223 | //ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE; |
philpem@96 | 224 | //if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++; |
philpem@84 | 225 | |
philpem@80 | 226 | for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) { |
philpem@80 | 227 | if (ks->keystate[i]) { |
philpem@99 | 228 | LOG_IF(kbc_debug, "KBC KEY DOWN: %d\n", i); |
philpem@80 | 229 | ks->buffer[ks->writep] = i; |
philpem@80 | 230 | ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE; |
philpem@84 | 231 | if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++; |
philpem@84 | 232 | nkeys++; |
philpem@80 | 233 | } |
philpem@80 | 234 | } |
philpem@96 | 235 | if (nkeys) { |
philpem@96 | 236 | ks->buffer[ks->writep - 1] |= 0x80; |
philpem@96 | 237 | }else{ |
philpem@96 | 238 | // If no keys down, then send All Keys Up byte |
philpem@99 | 239 | LOG_IFS(kbc_debug, "KBC ALL KEYS UP\n"); |
philpem@91 | 240 | ks->buffer[ks->writep] = KEY_ALL_UP; |
philpem@84 | 241 | ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE; |
philpem@84 | 242 | if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++; |
philpem@84 | 243 | } |
philpem@84 | 244 | |
philpem@82 | 245 | // TODO: inject "mouse data follows" chunk header and mouse movement info |
philpem@84 | 246 | |
philpem@74 | 247 | } |
philpem@91 | 248 | |
philpem@91 | 249 | // Clear the update flag |
philpem@91 | 250 | ks->update_flag = false; |
philpem@74 | 251 | } |
philpem@74 | 252 | |
philpem@80 | 253 | bool keyboard_get_irq(KEYBOARD_STATE *ks) |
philpem@80 | 254 | { |
philpem@80 | 255 | bool irq_status = false; |
philpem@80 | 256 | |
philpem@80 | 257 | // Conditions which may cause an IRQ :- |
philpem@80 | 258 | // Read Data Reg has data and RxIRQ enabled |
philpem@80 | 259 | if (ks->rxie) |
philpem@80 | 260 | if (ks->buflen > 0) irq_status = true; |
philpem@80 | 261 | |
philpem@80 | 262 | // Transmit Data Reg empty and TxIRQ enabled |
philpem@80 | 263 | // if (ks->txie) |
philpem@80 | 264 | |
philpem@80 | 265 | // DCD set and RxIRQ enabled |
philpem@80 | 266 | // |
philpem@80 | 267 | |
philpem@80 | 268 | // returns interrupt status -- i.e. is there data in the buffer? |
philpem@80 | 269 | return irq_status; |
philpem@80 | 270 | } |
philpem@80 | 271 | |
philpem@80 | 272 | uint8_t keyboard_read(KEYBOARD_STATE *ks, uint8_t addr) |
philpem@74 | 273 | { |
philpem@80 | 274 | if ((addr & 1) == 0) { |
philpem@80 | 275 | // Status register -- RS=0, read |
philpem@84 | 276 | uint8_t sr = 0; |
philpem@84 | 277 | if (ks->buflen > 0) sr |= 1; // SR0: a new character has been received |
philpem@84 | 278 | sr |= 2; // SR1: Transmitter Data Register Empty |
philpem@84 | 279 | // 0 + // SR2: Data Carrier Detect |
philpem@84 | 280 | // 0 + // SR3: Clear To Send |
philpem@84 | 281 | // 0 + // SR4: Framing Error |
philpem@84 | 282 | // 0 + // SR5: Receiver Overrun |
philpem@84 | 283 | // 0 + // SR6: Parity Error |
philpem@84 | 284 | if (keyboard_get_irq(ks)) sr |= 0x80; // SR7: IRQ status |
philpem@99 | 285 | //LOG_IF(kbc_debug, "KBC DBG: sr=%02X\n", sr); |
philpem@84 | 286 | return sr; |
philpem@80 | 287 | } else { |
philpem@80 | 288 | // return data, pop off the fifo |
philpem@80 | 289 | uint8_t x = ks->buffer[ks->readp]; |
philpem@80 | 290 | ks->readp = (ks->readp + 1) % KEYBOARD_BUFFER_SIZE; |
philpem@84 | 291 | if (ks->buflen > 0) ks->buflen--; |
philpem@99 | 292 | //LOG_IF(kbc_debug, "\tKBC DBG: rxd=%02X\n", x); |
philpem@80 | 293 | return x; |
philpem@80 | 294 | } |
philpem@74 | 295 | } |
philpem@80 | 296 | |
philpem@80 | 297 | void keyboard_write(KEYBOARD_STATE *ks, uint8_t addr, uint8_t val) |
philpem@80 | 298 | { |
philpem@80 | 299 | if ((addr & 1) == 0) { |
philpem@80 | 300 | // write to control register |
philpem@80 | 301 | // transmit intr enabled when CR6,5 = 01 |
philpem@80 | 302 | // receive intr enabled when CR7 = 1 |
philpem@80 | 303 | |
philpem@80 | 304 | // CR0,1 = divider registers. When =11, do a software reset |
philpem@80 | 305 | if ((val & 3) == 3) { |
philpem@80 | 306 | ks->readp = ks->writep = ks->buflen = 0; |
philpem@80 | 307 | } |
philpem@80 | 308 | |
philpem@80 | 309 | // Ignore CR2,3,4 (word length)... |
philpem@80 | 310 | |
philpem@80 | 311 | // CR5,6 = Transmit Mode |
philpem@80 | 312 | ks->txie = (val & 0x60)==0x20; |
philpem@80 | 313 | |
philpem@80 | 314 | // CR7 = Receive Interrupt Enable |
philpem@80 | 315 | ks->rxie = (val & 0x80)==0x80; |
philpem@80 | 316 | } else { |
philpem@80 | 317 | // Write command to KBC -- TODO! |
philpem@91 | 318 | if (val == KEY_CMD_RESET) { |
philpem@99 | 319 | LOG_IFS(kbc_debug, "KBC: KEYBOARD RESET!\n"); |
philpem@91 | 320 | ks->readp = ks->writep = ks->buflen = 0; |
philpem@99 | 321 | } else { |
philpem@99 | 322 | LOG("KBC TODO: write keyboard data 0x%02X\n", val); |
philpem@91 | 323 | } |
philpem@80 | 324 | } |
philpem@80 | 325 | } |
philpem@80 | 326 |