src/keyboard.c

Sat, 17 Nov 2012 19:18:29 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Sat, 17 Nov 2012 19:18:29 +0000
changeset 112
a392eb8f9806
parent 99
2d06b823a015
child 136
f7d78dfb45d0
permissions
-rw-r--r--

add HDD support + fixes

Patch-Author: Andrew Warkentin <andreww591!gmail>
Patch-Message-ID: <50A772FC.8020009@gmail.com>

I have added floppy write support, full hard disk emulation, and proper handling of DMA page faults to FreeBee. I also fixed the floppy step commands, changed the "force interrupt" floppy command to generate a type 1 status, and changed the DMA address counter to reset to 3fff when a transfer completes (which is what Unix seems to expect - without it, the kernel says that the floppy isn't ready). The floppy, hard disk, and DMA page fault tests all pass. Initializing hard disks and floppies also works (the geometry for both is still fixed by the size of the image, though, and format commands only appear to succeed, but they don't modify the image). Unix still doesn't run, though (it hangs after reading some sectors from the floppy).

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