Mon, 14 Jan 2013 09:50:37 +0000
Max out system memory by default
Set the system memory to 2MiB base, 2MiB ext. This is a fully loaded 3B1
motherboard with a RAM expansion board. 512KiB base/no ext is the minimum
which can be specified (e.g. kernel memory map area only) but does not leave
any room for userspace. The kernel doesn't like that and doesn't handle it
gracefully...!
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 |