src/keyboard.c

Mon, 14 Jan 2013 09:22:12 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 14 Jan 2013 09:22:12 +0000
changeset 118
feee84e0b3bf
parent 99
2d06b823a015
child 136
f7d78dfb45d0
permissions
-rw-r--r--

More bus error fixes for FreeBee

I have fixed two more bus error handling bugs in FreeBee. First, the CPU core was executing the instruction regardless of whether a bus error occurs when fetching the opcode (which caused it to execute a bogus instruction in such cases). The other one was related to one of my previous fixes - the jump to the bus error vector was at the beginning of the main loop, so it wouldn't be called immediately after the bus error occurred if the timeslot expired, causing the return address to be off.

With these fixes, Unix now runs enough to get into userspace and run the install script (it is also possible to break out and get a shell prompt). However, many commands segfault semi-randomly (or more specifically, it seems that some child processes forked by the shell might be segfaulting before they can exec the command program), so installing the system isn't possible yet. I am not sure exactly what the bug is, but it seems to be related to some function in the shell returning null when the code calling it is assuming that it won't. What the function is, or why it is returning null, I'm not sure (the shell is built without the shared libc and is stripped, making identifying the function harder). I suspect that the function might be in libc, but that is hard to tell.

Author: Andrew Warkentin <andreww591 gmail com>

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
philpem@74 21 // { SDLK_, 1, 0x04 }, // Exit
philpem@74 22 { SDLK_KP1, 0, 0x05 }, // PREV [Keypad 1]
philpem@74 23 // { SDLK_, 1, 0x06 }, // Msg
philpem@74 24 // { SDLK_, 1, 0x07 }, // Cancl
philpem@74 25 { SDLK_BACKSPACE, 0, 0x08 }, // Backspace
philpem@74 26 { SDLK_TAB, 0, 0x09 }, // Tab
philpem@74 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@96 221 size_t last_writep;
philpem@84 222 // Keyboard Data Begins Here (BEGKBD)
philpem@96 223 //ks->buffer[ks->writep] = KEY_BEGIN_KEYBOARD;
philpem@96 224 //ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
philpem@96 225 //if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
philpem@84 226
philpem@80 227 for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) {
philpem@80 228 if (ks->keystate[i]) {
philpem@99 229 LOG_IF(kbc_debug, "KBC KEY DOWN: %d\n", i);
philpem@80 230 ks->buffer[ks->writep] = i;
philpem@96 231 last_writep = ks->writep;
philpem@80 232 ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
philpem@84 233 if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
philpem@84 234 nkeys++;
philpem@80 235 }
philpem@80 236 }
philpem@96 237 if (nkeys) {
philpem@96 238 ks->buffer[ks->writep - 1] |= 0x80;
philpem@96 239 }else{
philpem@96 240 // If no keys down, then send All Keys Up byte
philpem@99 241 LOG_IFS(kbc_debug, "KBC ALL KEYS UP\n");
philpem@91 242 ks->buffer[ks->writep] = KEY_ALL_UP;
philpem@84 243 ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
philpem@84 244 if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
philpem@84 245 }
philpem@84 246
philpem@82 247 // TODO: inject "mouse data follows" chunk header and mouse movement info
philpem@84 248
philpem@74 249 }
philpem@91 250
philpem@91 251 // Clear the update flag
philpem@91 252 ks->update_flag = false;
philpem@74 253 }
philpem@74 254
philpem@80 255 bool keyboard_get_irq(KEYBOARD_STATE *ks)
philpem@80 256 {
philpem@80 257 bool irq_status = false;
philpem@80 258
philpem@80 259 // Conditions which may cause an IRQ :-
philpem@80 260 // Read Data Reg has data and RxIRQ enabled
philpem@80 261 if (ks->rxie)
philpem@80 262 if (ks->buflen > 0) irq_status = true;
philpem@80 263
philpem@80 264 // Transmit Data Reg empty and TxIRQ enabled
philpem@80 265 // if (ks->txie)
philpem@80 266
philpem@80 267 // DCD set and RxIRQ enabled
philpem@80 268 //
philpem@80 269
philpem@80 270 // returns interrupt status -- i.e. is there data in the buffer?
philpem@80 271 return irq_status;
philpem@80 272 }
philpem@80 273
philpem@80 274 uint8_t keyboard_read(KEYBOARD_STATE *ks, uint8_t addr)
philpem@74 275 {
philpem@80 276 if ((addr & 1) == 0) {
philpem@80 277 // Status register -- RS=0, read
philpem@84 278 uint8_t sr = 0;
philpem@84 279 if (ks->buflen > 0) sr |= 1; // SR0: a new character has been received
philpem@84 280 sr |= 2; // SR1: Transmitter Data Register Empty
philpem@84 281 // 0 + // SR2: Data Carrier Detect
philpem@84 282 // 0 + // SR3: Clear To Send
philpem@84 283 // 0 + // SR4: Framing Error
philpem@84 284 // 0 + // SR5: Receiver Overrun
philpem@84 285 // 0 + // SR6: Parity Error
philpem@84 286 if (keyboard_get_irq(ks)) sr |= 0x80; // SR7: IRQ status
philpem@99 287 //LOG_IF(kbc_debug, "KBC DBG: sr=%02X\n", sr);
philpem@84 288 return sr;
philpem@80 289 } else {
philpem@80 290 // return data, pop off the fifo
philpem@80 291 uint8_t x = ks->buffer[ks->readp];
philpem@80 292 ks->readp = (ks->readp + 1) % KEYBOARD_BUFFER_SIZE;
philpem@84 293 if (ks->buflen > 0) ks->buflen--;
philpem@99 294 //LOG_IF(kbc_debug, "\tKBC DBG: rxd=%02X\n", x);
philpem@80 295 return x;
philpem@80 296 }
philpem@74 297 }
philpem@80 298
philpem@80 299 void keyboard_write(KEYBOARD_STATE *ks, uint8_t addr, uint8_t val)
philpem@80 300 {
philpem@80 301 if ((addr & 1) == 0) {
philpem@80 302 // write to control register
philpem@80 303 // transmit intr enabled when CR6,5 = 01
philpem@80 304 // receive intr enabled when CR7 = 1
philpem@80 305
philpem@80 306 // CR0,1 = divider registers. When =11, do a software reset
philpem@80 307 if ((val & 3) == 3) {
philpem@80 308 ks->readp = ks->writep = ks->buflen = 0;
philpem@80 309 }
philpem@80 310
philpem@80 311 // Ignore CR2,3,4 (word length)...
philpem@80 312
philpem@80 313 // CR5,6 = Transmit Mode
philpem@80 314 ks->txie = (val & 0x60)==0x20;
philpem@80 315
philpem@80 316 // CR7 = Receive Interrupt Enable
philpem@80 317 ks->rxie = (val & 0x80)==0x80;
philpem@80 318 } else {
philpem@80 319 // Write command to KBC -- TODO!
philpem@91 320 if (val == KEY_CMD_RESET) {
philpem@99 321 LOG_IFS(kbc_debug, "KBC: KEYBOARD RESET!\n");
philpem@91 322 ks->readp = ks->writep = ks->buflen = 0;
philpem@99 323 } else {
philpem@99 324 LOG("KBC TODO: write keyboard data 0x%02X\n", val);
philpem@91 325 }
philpem@80 326 }
philpem@80 327 }
philpem@80 328