src/keyboard.c

Wed, 16 Apr 2014 02:20:43 -0600

author
andrew@localhost
date
Wed, 16 Apr 2014 02:20:43 -0600
changeset 147
ad888290cdff
parent 146
fb6a37a266da
permissions
-rw-r--r--

fixed bus error handling for real this time (save registers before every instruction and push the saved registers if a bus error occurs, since the instruction may have changed registers before the bus error, and also stop the instruction immediately with longjmp so it won't change memory after the bus error)

This isn't actually what a real 68k does, but it is a good enough approximation. A real 68k will jump back into the middle of the faulted instruction and resume it from the memory access that faulted as opposed to restarting from the beginning like this CPU emulation does. It would be a lot harder to do that with the way this CPU library is designed. Newer versions of MESS basically do the same thing (they use a newer version of this library).

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