src/keyboard.c

Thu, 10 Feb 2011 01:09:04 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Thu, 10 Feb 2011 01:09:04 +0000
changeset 94
80872ecf64fa
parent 92
3d5680edc1f0
child 96
45ae4c97155b
permissions
-rw-r--r--

fix keyboard update-flag handler to only set flag when a mapped key has been pressed

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@91 163
philpem@91 164 // Clear the update flag
philpem@91 165 ks->update_flag = false;
philpem@74 166 }
philpem@74 167
philpem@80 168 void keyboard_event(KEYBOARD_STATE *ks, SDL_Event *ev)
philpem@74 169 {
philpem@90 170 int v = 0;
philpem@90 171 switch (ev->type) {
philpem@90 172 case SDL_KEYDOWN:
philpem@90 173 // Key down (pressed)
philpem@90 174 v = 1;
philpem@90 175 break;
philpem@90 176 case SDL_KEYUP:
philpem@90 177 // Key up (released)
philpem@90 178 v = 0;
philpem@90 179 break;
philpem@90 180 default:
philpem@90 181 // Not a keyboard event
philpem@90 182 return;
philpem@86 183 }
philpem@86 184
philpem@82 185 // scan the keymap
philpem@90 186 for (int i=0; i < sizeof(keymap)/sizeof(keymap[0]); i++) {
philpem@90 187 if (keymap[i].key == ev->key.keysym.sym) {
philpem@90 188 // Keycode match. Is this an Extended Map key?
philpem@86 189 if (keymap[i].extended) {
philpem@90 190 // Yes -- need ALT set when pressing the key for this to be a match
philpem@86 191 if (ev->key.keysym.mod & KMOD_ALT) {
philpem@90 192 ks->keystate[keymap[i].scancode] = v;
philpem@94 193 ks->update_flag = true;
philpem@86 194 break;
philpem@86 195 }
philpem@86 196 } else {
philpem@90 197 // Standard Map key. ALT must NOT be pressed for this to be a match
philpem@86 198 if (!(ev->key.keysym.mod & KMOD_ALT)) {
philpem@90 199 ks->keystate[keymap[i].scancode] = v;
philpem@94 200 ks->update_flag = true;
philpem@86 201 break;
philpem@86 202 }
philpem@86 203 }
philpem@86 204 }
philpem@86 205 }
philpem@74 206 }
philpem@86 207
philpem@80 208 void keyboard_scan(KEYBOARD_STATE *ks)
philpem@80 209 {
philpem@84 210 int nkeys = 0;
philpem@84 211
philpem@91 212 // Skip doing the scan if the keyboard hasn't changed state
philpem@91 213 if (!ks->update_flag) return;
philpem@91 214
philpem@80 215 // if buffer empty, do a keyboard scan
philpem@80 216 if (ks->buflen == 0) {
philpem@84 217 // Keyboard Data Begins Here (BEGKBD)
philpem@91 218 ks->buffer[ks->writep] = KEY_BEGIN_KEYBOARD;
philpem@84 219 ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
philpem@84 220 if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
philpem@84 221
philpem@80 222 for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) {
philpem@80 223 if (ks->keystate[i]) {
philpem@84 224 printf("\tKBC KEY DOWN: %d\n", i);
philpem@80 225 ks->buffer[ks->writep] = i;
philpem@80 226 ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
philpem@84 227 if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
philpem@84 228 nkeys++;
philpem@80 229 }
philpem@80 230 }
philpem@84 231
philpem@84 232 // If no keys down, then send All Keys Up byte
philpem@84 233 if (nkeys == 0) {
philpem@92 234 printf("\tKBC ALL KEYS UP\n");
philpem@91 235 ks->buffer[ks->writep] = KEY_ALL_UP;
philpem@84 236 ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
philpem@84 237 if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
philpem@84 238 }
philpem@84 239
philpem@82 240 // TODO: inject "mouse data follows" chunk header and mouse movement info
philpem@84 241
philpem@84 242 // Last Entry In List
philpem@92 243 // ks->buffer[ks->writep] = KEY_LIST_END;
philpem@91 244 // ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
philpem@91 245 // if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
philpem@74 246 }
philpem@91 247
philpem@91 248 // Clear the update flag
philpem@91 249 ks->update_flag = false;
philpem@74 250 }
philpem@74 251
philpem@80 252 bool keyboard_get_irq(KEYBOARD_STATE *ks)
philpem@80 253 {
philpem@80 254 bool irq_status = false;
philpem@80 255
philpem@80 256 // Conditions which may cause an IRQ :-
philpem@80 257 // Read Data Reg has data and RxIRQ enabled
philpem@80 258 if (ks->rxie)
philpem@80 259 if (ks->buflen > 0) irq_status = true;
philpem@80 260
philpem@80 261 // Transmit Data Reg empty and TxIRQ enabled
philpem@80 262 // if (ks->txie)
philpem@80 263
philpem@80 264 // DCD set and RxIRQ enabled
philpem@80 265 //
philpem@80 266
philpem@80 267 // returns interrupt status -- i.e. is there data in the buffer?
philpem@80 268 return irq_status;
philpem@80 269 }
philpem@80 270
philpem@80 271 uint8_t keyboard_read(KEYBOARD_STATE *ks, uint8_t addr)
philpem@74 272 {
philpem@80 273 if ((addr & 1) == 0) {
philpem@80 274 // Status register -- RS=0, read
philpem@84 275 uint8_t sr = 0;
philpem@84 276 if (ks->buflen > 0) sr |= 1; // SR0: a new character has been received
philpem@84 277 sr |= 2; // SR1: Transmitter Data Register Empty
philpem@84 278 // 0 + // SR2: Data Carrier Detect
philpem@84 279 // 0 + // SR3: Clear To Send
philpem@84 280 // 0 + // SR4: Framing Error
philpem@84 281 // 0 + // SR5: Receiver Overrun
philpem@84 282 // 0 + // SR6: Parity Error
philpem@84 283 if (keyboard_get_irq(ks)) sr |= 0x80; // SR7: IRQ status
philpem@85 284 printf("\tKBC DBG: sr=%02X\n", sr);
philpem@84 285 return sr;
philpem@80 286 } else {
philpem@80 287 // return data, pop off the fifo
philpem@80 288 uint8_t x = ks->buffer[ks->readp];
philpem@80 289 ks->readp = (ks->readp + 1) % KEYBOARD_BUFFER_SIZE;
philpem@84 290 if (ks->buflen > 0) ks->buflen--;
philpem@80 291 return x;
philpem@80 292 }
philpem@74 293 }
philpem@80 294
philpem@80 295 void keyboard_write(KEYBOARD_STATE *ks, uint8_t addr, uint8_t val)
philpem@80 296 {
philpem@80 297 if ((addr & 1) == 0) {
philpem@80 298 // write to control register
philpem@80 299 // transmit intr enabled when CR6,5 = 01
philpem@80 300 // receive intr enabled when CR7 = 1
philpem@80 301
philpem@80 302 // CR0,1 = divider registers. When =11, do a software reset
philpem@80 303 if ((val & 3) == 3) {
philpem@80 304 ks->readp = ks->writep = ks->buflen = 0;
philpem@80 305 }
philpem@80 306
philpem@80 307 // Ignore CR2,3,4 (word length)...
philpem@80 308
philpem@80 309 // CR5,6 = Transmit Mode
philpem@80 310 ks->txie = (val & 0x60)==0x20;
philpem@80 311
philpem@80 312 // CR7 = Receive Interrupt Enable
philpem@80 313 ks->rxie = (val & 0x80)==0x80;
philpem@80 314 } else {
philpem@80 315 // Write command to KBC -- TODO!
philpem@84 316 printf("KBC TODO: write keyboard data 0x%02X\n", val);
philpem@91 317 if (val == KEY_CMD_RESET) {
philpem@91 318 printf("\tKBC: KEYBOARD RESET!\n");
philpem@91 319 ks->readp = ks->writep = ks->buflen = 0;
philpem@91 320 }
philpem@80 321 }
philpem@80 322 }
philpem@80 323