src/keyboard.c

Wed, 16 Apr 2014 02:07:24 -0600

author
andrew@localhost
date
Wed, 16 Apr 2014 02:07:24 -0600
changeset 146
fb6a37a266da
parent 136
f7d78dfb45d0
permissions
-rw-r--r--

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