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