Wed, 09 Feb 2011 15:03:31 +0000
implement some more of the keyboard controller
philpem@80 | 1 | #include <stdbool.h> |
philpem@74 | 2 | #include "SDL.h" |
philpem@80 | 3 | #include "keyboard.h" |
philpem@74 | 4 | |
philpem@74 | 5 | /** |
philpem@74 | 6 | * Key map -- a mapping from SDLK_xxx constants to scancodes and vice versa. |
philpem@74 | 7 | */ |
philpem@74 | 8 | struct { |
philpem@74 | 9 | SDLKey key; ///< SDLK_xxx key code constant |
philpem@74 | 10 | int extended; ///< 1 if this is an extended keycode |
philpem@74 | 11 | unsigned char scancode; ///< Keyboard scan code |
philpem@74 | 12 | } keymap[] = { |
philpem@74 | 13 | { SDLK_UP, 0, 0x01 }, // ROLL/Up [UpArrow] |
philpem@74 | 14 | { SDLK_KP2, 0, 0x01 }, // ROLL/Up [Keypad 2] |
philpem@74 | 15 | // { SDLK_, 1, 0x02 }, // Clear Line |
philpem@74 | 16 | // { SDLK_, 1, 0x03 }, // Rstrt / Ref |
philpem@74 | 17 | // { SDLK_, 1, 0x04 }, // Exit |
philpem@74 | 18 | { SDLK_KP1, 0, 0x05 }, // PREV [Keypad 1] |
philpem@74 | 19 | // { SDLK_, 1, 0x06 }, // Msg |
philpem@74 | 20 | // { SDLK_, 1, 0x07 }, // Cancl |
philpem@74 | 21 | { SDLK_BACKSPACE, 0, 0x08 }, // Backspace |
philpem@74 | 22 | { SDLK_TAB, 0, 0x09 }, // Tab |
philpem@74 | 23 | // { SDLK_RETURN, 1, 0x0a }, // ENTER |
philpem@74 | 24 | { SDLK_DOWN, 0, 0x0b }, // ROLL/Down [DownArrow] |
philpem@74 | 25 | { SDLK_KP0, 0, 0x0b }, // ROLL/Down [Keypad 0] |
philpem@74 | 26 | { SDLK_KP3, 0, 0x0c }, // NEXT [Keypad 3] |
philpem@74 | 27 | { SDLK_RETURN, 0, 0x0d }, // RETURN [Return] |
philpem@74 | 28 | { SDLK_LEFT, 0, 0x0e }, // <-- [LeftArrow] |
philpem@74 | 29 | { SDLK_KP_MINUS, 0, 0x0e }, // <-- [Keypad -] |
philpem@74 | 30 | { SDLK_RIGHT, 0, 0x0f }, // --> [RightArrow] |
philpem@74 | 31 | { SDLK_KP_PERIOD, 0, 0x0f }, // --> [Keypad .] |
philpem@74 | 32 | // { SDLK_, 1, 0x10 }, // Creat |
philpem@74 | 33 | // { SDLK_, 1, 0x11 }, // Save |
philpem@74 | 34 | // { SDLK_, 1, 0x12 }, // Move |
philpem@74 | 35 | // { SDLK_, 1, 0x13 }, // Ops |
philpem@74 | 36 | // { SDLK_, 1, 0x14 }, // Copy |
philpem@74 | 37 | { SDLK_F1, 0, 0x15 }, // F1 |
philpem@74 | 38 | { SDLK_F2, 0, 0x16 }, // F2 |
philpem@74 | 39 | { SDLK_F3, 0, 0x17 }, // F3 |
philpem@74 | 40 | { SDLK_F4, 0, 0x18 }, // F4 |
philpem@74 | 41 | { SDLK_F5, 0, 0x19 }, // F5 |
philpem@74 | 42 | { SDLK_F6, 0, 0x1a }, // F6 |
philpem@74 | 43 | { SDLK_ESCAPE, 0, 0x1b }, // ESC/DEL [Escape] |
philpem@74 | 44 | { SDLK_F7, 0, 0x1c }, // F7 |
philpem@74 | 45 | { SDLK_F8, 0, 0x1d }, // F8 |
philpem@74 | 46 | // { SDLK_, 1, 0x1e }, // Suspd |
philpem@74 | 47 | // { SDLK_, 1, 0x1f }, // Rsume |
philpem@74 | 48 | { SDLK_SPACE, 0, 0x20 }, // SPACE [Spacebar] |
philpem@74 | 49 | // { SDLK_, 1, 0x21 }, // Undo |
philpem@74 | 50 | // { SDLK_, 1, 0x22 }, // Redo |
philpem@74 | 51 | // { SDLK_, 1, 0x23 }, // FIND |
philpem@74 | 52 | // { SDLK_, 1, 0x24 }, // RPLAC |
philpem@74 | 53 | { SDLK_BREAK, 0, 0x25 }, // RESET/BREAK [Pause/Break] |
philpem@74 | 54 | // { SDLK_, 1, 0x26 }, // DleteChar |
philpem@74 | 55 | { SDLK_QUOTE, 0, 0x27 }, // ' (single-quote) |
philpem@74 | 56 | // { SDLK_, 1, 0x28 }, // SLCT/MARK |
philpem@74 | 57 | // { SDLK_, 1, 0x29 }, // INPUT/MODE |
philpem@74 | 58 | // { SDLK_, 1, 0x2a }, // HELP |
philpem@74 | 59 | // Keycode 2B not used |
philpem@74 | 60 | { SDLK_COMMA, 0, 0x2c }, // , [Comma] |
philpem@74 | 61 | { SDLK_MINUS, 0, 0x2d }, // - [Dash] |
philpem@74 | 62 | { SDLK_PERIOD, 0, 0x2e }, // . [Period] |
philpem@74 | 63 | { SDLK_SLASH, 0, 0x2f }, // / [Forward-slash] |
philpem@74 | 64 | { SDLK_0, 0, 0x30 }, // 0 |
philpem@74 | 65 | { SDLK_1, 0, 0x31 }, // 1 |
philpem@74 | 66 | { SDLK_2, 0, 0x32 }, // 2 |
philpem@74 | 67 | { SDLK_3, 0, 0x33 }, // 3 |
philpem@74 | 68 | { SDLK_4, 0, 0x34 }, // 4 |
philpem@74 | 69 | { SDLK_5, 0, 0x35 }, // 5 |
philpem@74 | 70 | { SDLK_6, 0, 0x36 }, // 6 |
philpem@74 | 71 | { SDLK_7, 0, 0x37 }, // 7 |
philpem@74 | 72 | { SDLK_8, 0, 0x38 }, // 8 |
philpem@74 | 73 | { SDLK_9, 0, 0x39 }, // 9 |
philpem@74 | 74 | // Keycode 3A not used |
philpem@74 | 75 | { SDLK_SEMICOLON, 0, 0x3b }, // ; [Semicolon] |
philpem@74 | 76 | // Keycode 3C not used |
philpem@74 | 77 | { SDLK_EQUALS, 0, 0x3d }, // = [Equals] |
philpem@74 | 78 | // Keycodes 3E, 3F, 40 not used |
philpem@74 | 79 | // { SDLK_, 1, 0x41 }, // CMD |
philpem@74 | 80 | // { SDLK_, 1, 0x42 }, // CLOSE/OPEN |
philpem@74 | 81 | { SDLK_KP7, 0, 0x43 }, // PRINT |
philpem@74 | 82 | { SDLK_KP8, 0, 0x44 }, // CLEAR/RFRSH |
philpem@74 | 83 | { SDLK_CAPSLOCK, 0, 0x45 }, // Caps Lock |
philpem@74 | 84 | { SDLK_KP9, 0, 0x46 }, // PAGE |
philpem@74 | 85 | { SDLK_KP4, 0, 0x47 }, // BEG |
philpem@74 | 86 | { SDLK_LSHIFT, 0, 0x48 }, // Left Shift |
philpem@74 | 87 | { SDLK_RSHIFT, 0, 0x49 }, // Right Shift |
philpem@74 | 88 | { SDLK_HOME, 0, 0x4a }, // Home |
philpem@74 | 89 | { SDLK_KP5, 0, 0x4a }, // Home [Keypad 5] |
philpem@74 | 90 | { SDLK_END, 0, 0x4b }, // End |
philpem@74 | 91 | { SDLK_KP6, 0, 0x4b }, // End [Keypad 6] |
philpem@74 | 92 | { SDLK_LCTRL, 0, 0x4c }, // Left Ctrl? \___ not sure which is left and which is right... |
philpem@74 | 93 | { SDLK_RCTRL, 0, 0x4d }, // Right Ctrl? / |
philpem@74 | 94 | // Keycodes 4E thru 5A not used |
philpem@74 | 95 | { SDLK_LEFTBRACKET, 0, 0x5b }, // [ |
philpem@74 | 96 | { SDLK_BACKSLASH, 0, 0x5c }, // \ (backslash) |
philpem@74 | 97 | { SDLK_RIGHTBRACKET, 0, 0x5d }, // ] |
philpem@74 | 98 | // Keycodes 5E, 5F not used |
philpem@74 | 99 | { SDLK_BACKQUOTE, 0, 0x60 }, // ` |
philpem@74 | 100 | { SDLK_a, 0, 0x61 }, // A |
philpem@74 | 101 | { SDLK_b, 0, 0x62 }, // B |
philpem@74 | 102 | { SDLK_c, 0, 0x63 }, // C |
philpem@74 | 103 | { SDLK_d, 0, 0x64 }, // D |
philpem@74 | 104 | { SDLK_e, 0, 0x65 }, // E |
philpem@74 | 105 | { SDLK_f, 0, 0x66 }, // F |
philpem@74 | 106 | { SDLK_g, 0, 0x67 }, // G |
philpem@74 | 107 | { SDLK_h, 0, 0x68 }, // H |
philpem@74 | 108 | { SDLK_i, 0, 0x69 }, // I |
philpem@74 | 109 | { SDLK_j, 0, 0x6a }, // J |
philpem@74 | 110 | { SDLK_k, 0, 0x6b }, // K |
philpem@74 | 111 | { SDLK_l, 0, 0x6c }, // L |
philpem@74 | 112 | { SDLK_m, 0, 0x6d }, // M |
philpem@74 | 113 | { SDLK_n, 0, 0x6e }, // N |
philpem@74 | 114 | { SDLK_o, 0, 0x6f }, // O |
philpem@74 | 115 | { SDLK_p, 0, 0x70 }, // P |
philpem@74 | 116 | { SDLK_q, 0, 0x71 }, // Q |
philpem@74 | 117 | { SDLK_r, 0, 0x72 }, // R |
philpem@74 | 118 | { SDLK_s, 0, 0x73 }, // S |
philpem@74 | 119 | { SDLK_t, 0, 0x74 }, // T |
philpem@74 | 120 | { SDLK_u, 0, 0x75 }, // U |
philpem@74 | 121 | { SDLK_v, 0, 0x76 }, // V |
philpem@74 | 122 | { SDLK_w, 0, 0x77 }, // W |
philpem@74 | 123 | { SDLK_x, 0, 0x78 }, // X |
philpem@74 | 124 | { SDLK_y, 0, 0x79 }, // Y |
philpem@74 | 125 | { SDLK_z, 0, 0x7a }, // Z |
philpem@74 | 126 | // Keycodes 7B, 7C, 7D not used |
philpem@74 | 127 | { SDLK_NUMLOCK, 0, 0x7e } // Numlock |
philpem@74 | 128 | // { SDLK_, 1, 0x7f }, // Dlete |
philpem@74 | 129 | }; |
philpem@74 | 130 | |
philpem@80 | 131 | void keyboard_init(KEYBOARD_STATE *ks) |
philpem@74 | 132 | { |
philpem@74 | 133 | // Set all key states to "not pressed" |
philpem@80 | 134 | for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) { |
philpem@80 | 135 | ks->keystate[i] = 0; |
philpem@80 | 136 | } |
philpem@80 | 137 | |
philpem@80 | 138 | // Reset the R/W pointers and length |
philpem@80 | 139 | ks->readp = ks->writep = ks->buflen = 0; |
philpem@80 | 140 | } |
philpem@80 | 141 | |
philpem@80 | 142 | void keyboard_event(KEYBOARD_STATE *ks, SDL_Event *ev) |
philpem@80 | 143 | { |
philpem@80 | 144 | // event handler -- handles SDL events |
philpem@80 | 145 | } |
philpem@80 | 146 | |
philpem@80 | 147 | void keyboard_scan(KEYBOARD_STATE *ks) |
philpem@80 | 148 | { |
philpem@80 | 149 | // if buffer empty, do a keyboard scan |
philpem@80 | 150 | if (ks->buflen == 0) { |
philpem@80 | 151 | for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) { |
philpem@80 | 152 | if (ks->keystate[i]) { |
philpem@80 | 153 | ks->buffer[ks->writep] = i; |
philpem@80 | 154 | ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE; |
philpem@80 | 155 | } |
philpem@80 | 156 | } |
philpem@74 | 157 | } |
philpem@74 | 158 | } |
philpem@74 | 159 | |
philpem@80 | 160 | bool keyboard_get_irq(KEYBOARD_STATE *ks) |
philpem@80 | 161 | { |
philpem@80 | 162 | bool irq_status = false; |
philpem@80 | 163 | |
philpem@80 | 164 | // Conditions which may cause an IRQ :- |
philpem@80 | 165 | // Read Data Reg has data and RxIRQ enabled |
philpem@80 | 166 | if (ks->rxie) |
philpem@80 | 167 | if (ks->buflen > 0) irq_status = true; |
philpem@80 | 168 | |
philpem@80 | 169 | // Transmit Data Reg empty and TxIRQ enabled |
philpem@80 | 170 | // if (ks->txie) |
philpem@80 | 171 | |
philpem@80 | 172 | // DCD set and RxIRQ enabled |
philpem@80 | 173 | // |
philpem@80 | 174 | |
philpem@80 | 175 | // returns interrupt status -- i.e. is there data in the buffer? |
philpem@80 | 176 | return irq_status; |
philpem@80 | 177 | } |
philpem@80 | 178 | |
philpem@80 | 179 | uint8_t keyboard_read(KEYBOARD_STATE *ks, uint8_t addr) |
philpem@74 | 180 | { |
philpem@80 | 181 | if ((addr & 1) == 0) { |
philpem@80 | 182 | // Status register -- RS=0, read |
philpem@80 | 183 | return |
philpem@80 | 184 | (ks->buflen > 0) ? 1 : 0 + // SR0: a new character has been received |
philpem@80 | 185 | 0 + // SR1: Transmitter Data Register Empty |
philpem@80 | 186 | 0 + // SR2: Data Carrier Detect |
philpem@80 | 187 | 0 + // SR3: Clear To Send |
philpem@80 | 188 | 0 + // SR4: Framing Error |
philpem@80 | 189 | 0 + // SR5: Receiver Overrun |
philpem@80 | 190 | 0 + // SR6: Parity Error |
philpem@80 | 191 | (keyboard_get_irq(ks)) ? 0x80 : 0; // SR7: IRQ status |
philpem@80 | 192 | } else { |
philpem@80 | 193 | // return data, pop off the fifo |
philpem@80 | 194 | uint8_t x = ks->buffer[ks->readp]; |
philpem@80 | 195 | ks->readp = (ks->readp + 1) % KEYBOARD_BUFFER_SIZE; |
philpem@80 | 196 | return x; |
philpem@80 | 197 | } |
philpem@74 | 198 | } |
philpem@80 | 199 | |
philpem@80 | 200 | void keyboard_write(KEYBOARD_STATE *ks, uint8_t addr, uint8_t val) |
philpem@80 | 201 | { |
philpem@80 | 202 | if ((addr & 1) == 0) { |
philpem@80 | 203 | // write to control register |
philpem@80 | 204 | // transmit intr enabled when CR6,5 = 01 |
philpem@80 | 205 | // receive intr enabled when CR7 = 1 |
philpem@80 | 206 | |
philpem@80 | 207 | // CR0,1 = divider registers. When =11, do a software reset |
philpem@80 | 208 | if ((val & 3) == 3) { |
philpem@80 | 209 | ks->readp = ks->writep = ks->buflen = 0; |
philpem@80 | 210 | } |
philpem@80 | 211 | |
philpem@80 | 212 | // Ignore CR2,3,4 (word length)... |
philpem@80 | 213 | |
philpem@80 | 214 | // CR5,6 = Transmit Mode |
philpem@80 | 215 | ks->txie = (val & 0x60)==0x20; |
philpem@80 | 216 | |
philpem@80 | 217 | // CR7 = Receive Interrupt Enable |
philpem@80 | 218 | ks->rxie = (val & 0x80)==0x80; |
philpem@80 | 219 | } else { |
philpem@80 | 220 | // Write command to KBC -- TODO! |
philpem@80 | 221 | } |
philpem@80 | 222 | } |
philpem@80 | 223 |