Wed, 16 Apr 2014 02:20:43 -0600
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).
1 #include <stdbool.h>
2 #include "SDL.h"
3 #include "utils.h"
4 #include "keyboard.h"
6 // Enable/disable KBC debugging
7 #define kbc_debug false
9 /**
10 * Key map -- a mapping from SDLK_xxx constants to scancodes and vice versa.
11 */
12 struct {
13 SDLKey key; ///< SDLK_xxx key code constant
14 int extended; ///< 1 if this is an extended keycode
15 unsigned char scancode; ///< Keyboard scan code
16 } keymap[] = {
17 { SDLK_UP, 0, 0x01 }, // ROLL/Up [UpArrow]
18 { SDLK_KP2, 0, 0x01 }, // ROLL/Up [Keypad 2]
19 // { SDLK_, 1, 0x02 }, // Clear Line
20 // { SDLK_, 1, 0x03 }, // Rstrt / Ref
21 { SDLK_ESCAPE, 1, 0x04 }, // Exit
22 { SDLK_KP1, 0, 0x05 }, // PREV [Keypad 1]
23 // { SDLK_, 1, 0x06 }, // Msg
24 { SDLK_BACKSPACE, 1, 0x07 }, // Cancl
25 { SDLK_BACKSPACE, 0, 0x08 }, // Backspace
26 { SDLK_TAB, 0, 0x09 }, // Tab
27 { SDLK_RETURN, 1, 0x0a }, // ENTER
28 { SDLK_DOWN, 0, 0x0b }, // ROLL/Down [DownArrow]
29 { SDLK_KP0, 0, 0x0b }, // ROLL/Down [Keypad 0]
30 { SDLK_KP3, 0, 0x0c }, // NEXT [Keypad 3]
31 { SDLK_RETURN, 0, 0x0d }, // RETURN [Return]
32 { SDLK_LEFT, 0, 0x0e }, // <-- [LeftArrow]
33 { SDLK_KP_MINUS, 0, 0x0e }, // <-- [Keypad -]
34 { SDLK_RIGHT, 0, 0x0f }, // --> [RightArrow]
35 { SDLK_KP_PERIOD, 0, 0x0f }, // --> [Keypad .]
36 // { SDLK_, 1, 0x10 }, // Creat
37 // { SDLK_, 1, 0x11 }, // Save
38 // { SDLK_, 1, 0x12 }, // Move
39 // { SDLK_, 1, 0x13 }, // Ops
40 // { SDLK_, 1, 0x14 }, // Copy
41 { SDLK_F1, 0, 0x15 }, // F1
42 { SDLK_F2, 0, 0x16 }, // F2
43 { SDLK_F3, 0, 0x17 }, // F3
44 { SDLK_F4, 0, 0x18 }, // F4
45 { SDLK_F5, 0, 0x19 }, // F5
46 { SDLK_F6, 0, 0x1a }, // F6
47 { SDLK_ESCAPE, 0, 0x1b }, // ESC/DEL [Escape]
48 { SDLK_F7, 0, 0x1c }, // F7
49 { SDLK_F8, 0, 0x1d }, // F8
50 // { SDLK_, 1, 0x1e }, // Suspd
51 // { SDLK_, 1, 0x1f }, // Rsume
52 { SDLK_SPACE, 0, 0x20 }, // SPACE [Spacebar]
53 // { SDLK_, 1, 0x21 }, // Undo
54 // { SDLK_, 1, 0x22 }, // Redo
55 // { SDLK_, 1, 0x23 }, // FIND
56 // { SDLK_, 1, 0x24 }, // RPLAC
57 { SDLK_BREAK, 0, 0x25 }, // RESET/BREAK [Pause/Break]
58 // { SDLK_, 1, 0x26 }, // DleteChar
59 { SDLK_QUOTE, 0, 0x27 }, // ' (single-quote)
60 // { SDLK_, 1, 0x28 }, // SLCT/MARK
61 // { SDLK_, 1, 0x29 }, // INPUT/MODE
62 // { SDLK_, 1, 0x2a }, // HELP
63 // Keycode 2B not used
64 { SDLK_COMMA, 0, 0x2c }, // , [Comma]
65 { SDLK_MINUS, 0, 0x2d }, // - [Dash]
66 { SDLK_PERIOD, 0, 0x2e }, // . [Period]
67 { SDLK_SLASH, 0, 0x2f }, // / [Forward-slash]
68 { SDLK_0, 0, 0x30 }, // 0
69 { SDLK_1, 0, 0x31 }, // 1
70 { SDLK_2, 0, 0x32 }, // 2
71 { SDLK_3, 0, 0x33 }, // 3
72 { SDLK_4, 0, 0x34 }, // 4
73 { SDLK_5, 0, 0x35 }, // 5
74 { SDLK_6, 0, 0x36 }, // 6
75 { SDLK_7, 0, 0x37 }, // 7
76 { SDLK_8, 0, 0x38 }, // 8
77 { SDLK_9, 0, 0x39 }, // 9
78 // Keycode 3A not used
79 { SDLK_SEMICOLON, 0, 0x3b }, // ; [Semicolon]
80 // Keycode 3C not used
81 { SDLK_EQUALS, 0, 0x3d }, // = [Equals]
82 // Keycodes 3E, 3F, 40 not used
83 // { SDLK_, 1, 0x41 }, // CMD
84 // { SDLK_, 1, 0x42 }, // CLOSE/OPEN
85 { SDLK_KP7, 0, 0x43 }, // PRINT
86 { SDLK_KP8, 0, 0x44 }, // CLEAR/RFRSH
87 { SDLK_CAPSLOCK, 0, 0x45 }, // Caps Lock
88 { SDLK_KP9, 0, 0x46 }, // PAGE
89 { SDLK_KP4, 0, 0x47 }, // BEG
90 { SDLK_LSHIFT, 0, 0x48 }, // Left Shift
91 { SDLK_RSHIFT, 0, 0x49 }, // Right Shift
92 { SDLK_HOME, 0, 0x4a }, // Home
93 { SDLK_KP5, 0, 0x4a }, // Home [Keypad 5]
94 { SDLK_END, 0, 0x4b }, // End
95 { SDLK_KP6, 0, 0x4b }, // End [Keypad 6]
96 { SDLK_LCTRL, 0, 0x4c }, // Left Ctrl? \___ not sure which is left and which is right...
97 { SDLK_RCTRL, 0, 0x4d }, // Right Ctrl? /
98 // Keycodes 4E thru 5A not used
99 { SDLK_LEFTBRACKET, 0, 0x5b }, // [
100 { SDLK_BACKSLASH, 0, 0x5c }, // \ (backslash)
101 { SDLK_RIGHTBRACKET, 0, 0x5d }, // ]
102 // Keycodes 5E, 5F not used
103 { SDLK_BACKQUOTE, 0, 0x60 }, // `
104 { SDLK_a, 0, 0x61 }, // A
105 { SDLK_b, 0, 0x62 }, // B
106 { SDLK_c, 0, 0x63 }, // C
107 { SDLK_d, 0, 0x64 }, // D
108 { SDLK_e, 0, 0x65 }, // E
109 { SDLK_f, 0, 0x66 }, // F
110 { SDLK_g, 0, 0x67 }, // G
111 { SDLK_h, 0, 0x68 }, // H
112 { SDLK_i, 0, 0x69 }, // I
113 { SDLK_j, 0, 0x6a }, // J
114 { SDLK_k, 0, 0x6b }, // K
115 { SDLK_l, 0, 0x6c }, // L
116 { SDLK_m, 0, 0x6d }, // M
117 { SDLK_n, 0, 0x6e }, // N
118 { SDLK_o, 0, 0x6f }, // O
119 { SDLK_p, 0, 0x70 }, // P
120 { SDLK_q, 0, 0x71 }, // Q
121 { SDLK_r, 0, 0x72 }, // R
122 { SDLK_s, 0, 0x73 }, // S
123 { SDLK_t, 0, 0x74 }, // T
124 { SDLK_u, 0, 0x75 }, // U
125 { SDLK_v, 0, 0x76 }, // V
126 { SDLK_w, 0, 0x77 }, // W
127 { SDLK_x, 0, 0x78 }, // X
128 { SDLK_y, 0, 0x79 }, // Y
129 { SDLK_z, 0, 0x7a }, // Z
130 // Keycodes 7B, 7C, 7D not used
131 { SDLK_NUMLOCK, 0, 0x7e }, // Numlock
132 { SDLK_DELETE, 0, 0x7f } // Dlete
133 };
135 /**
136 * List of special key codes
137 */
138 enum {
139 KEY_ALL_UP = 0x40, ///< All keys up
140 KEY_LIST_END = 0x80, ///< End of key code list
141 KEY_BEGIN_MOUSE = 0xCF, ///< Mouse data follows
142 KEY_BEGIN_KEYBOARD = 0xDF, ///< Keyboard data follows
143 };
145 /**
146 * List of keyboard commands
147 */
148 enum {
149 KEY_CMD_RESET = 0x92, ///< Reset keyboard
150 KEY_CMD_CAPSLED_OFF = 0xB1, ///< Caps Lock LED off--CHECK!
151 KEY_CMD_CAPSLED_ON = 0xB0, ///< Caps Lock LED on --CHECK!
152 KEY_CMD_NUMLED_OFF = 0xA1, ///< Num Lock LED off --CHECK!
153 KEY_CMD_NUMLED_ON = 0xA0, ///< Num Lock LED on --CHECK!
154 KEY_CMD_MOUSE_ENABLE = 0xD0, ///< Enable mouse
155 KEY_CMD_MOUSE_DISABLE = 0xD1 ///< Disable mouse
156 };
158 void keyboard_init(KEYBOARD_STATE *ks)
159 {
160 // Set all key states to "not pressed"
161 for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) {
162 ks->keystate[i] = 0;
163 }
165 // Reset the R/W pointers and length
166 ks->readp = ks->writep = ks->buflen = 0;
168 // Clear the update flag
169 ks->update_flag = false;
170 }
172 void keyboard_event(KEYBOARD_STATE *ks, SDL_Event *ev)
173 {
174 int v = 0;
175 switch (ev->type) {
176 case SDL_KEYDOWN:
177 // Key down (pressed)
178 v = 1;
179 break;
180 case SDL_KEYUP:
181 // Key up (released)
182 v = 0;
183 break;
184 default:
185 // Not a keyboard event
186 return;
187 }
189 // scan the keymap
190 for (int i=0; i < sizeof(keymap)/sizeof(keymap[0]); i++) {
191 if (keymap[i].key == ev->key.keysym.sym) {
192 // Keycode match. Is this an Extended Map key?
193 if (keymap[i].extended) {
194 // Yes -- need ALT set when pressing the key for this to be a match
195 if (ev->key.keysym.mod & KMOD_ALT) {
196 ks->keystate[keymap[i].scancode] = v;
197 ks->update_flag = true;
198 break;
199 }
200 } else {
201 // Standard Map key. ALT must NOT be pressed for this to be a match
202 if (!(ev->key.keysym.mod & KMOD_ALT)) {
203 ks->keystate[keymap[i].scancode] = v;
204 ks->update_flag = true;
205 break;
206 }
207 }
208 }
209 }
210 }
211 void keyboard_scan(KEYBOARD_STATE *ks)
212 {
213 int nkeys = 0;
215 // Skip doing the scan if the keyboard hasn't changed state
216 if (!ks->update_flag) return;
219 // if buffer empty, do a keyboard scan
220 if (ks->buflen == 0) {
221 // Keyboard Data Begins Here (BEGKBD)
222 //ks->buffer[ks->writep] = KEY_BEGIN_KEYBOARD;
223 //ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
224 //if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
226 for (int i=0; i<(sizeof(ks->keystate)/sizeof(ks->keystate[0])); i++) {
227 if (ks->keystate[i]) {
228 LOG_IF(kbc_debug, "KBC KEY DOWN: %d\n", i);
229 ks->buffer[ks->writep] = i;
230 ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
231 if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
232 nkeys++;
233 }
234 }
235 if (nkeys) {
236 ks->buffer[ks->writep - 1] |= 0x80;
237 }else{
238 // If no keys down, then send All Keys Up byte
239 LOG_IFS(kbc_debug, "KBC ALL KEYS UP\n");
240 ks->buffer[ks->writep] = KEY_ALL_UP;
241 ks->writep = (ks->writep + 1) % KEYBOARD_BUFFER_SIZE;
242 if (ks->buflen < KEYBOARD_BUFFER_SIZE) ks->buflen++;
243 }
245 // TODO: inject "mouse data follows" chunk header and mouse movement info
247 }
249 // Clear the update flag
250 ks->update_flag = false;
251 }
253 bool keyboard_get_irq(KEYBOARD_STATE *ks)
254 {
255 bool irq_status = false;
257 // Conditions which may cause an IRQ :-
258 // Read Data Reg has data and RxIRQ enabled
259 if (ks->rxie)
260 if (ks->buflen > 0) irq_status = true;
262 // Transmit Data Reg empty and TxIRQ enabled
263 // if (ks->txie)
265 // DCD set and RxIRQ enabled
266 //
268 // returns interrupt status -- i.e. is there data in the buffer?
269 return irq_status;
270 }
272 uint8_t keyboard_read(KEYBOARD_STATE *ks, uint8_t addr)
273 {
274 if ((addr & 1) == 0) {
275 // Status register -- RS=0, read
276 uint8_t sr = 0;
277 if (ks->buflen > 0) sr |= 1; // SR0: a new character has been received
278 sr |= 2; // SR1: Transmitter Data Register Empty
279 // 0 + // SR2: Data Carrier Detect
280 // 0 + // SR3: Clear To Send
281 // 0 + // SR4: Framing Error
282 // 0 + // SR5: Receiver Overrun
283 // 0 + // SR6: Parity Error
284 if (keyboard_get_irq(ks)) sr |= 0x80; // SR7: IRQ status
285 //LOG_IF(kbc_debug, "KBC DBG: sr=%02X\n", sr);
286 return sr;
287 } else {
288 // return data, pop off the fifo
289 uint8_t x = ks->buffer[ks->readp];
290 ks->readp = (ks->readp + 1) % KEYBOARD_BUFFER_SIZE;
291 if (ks->buflen > 0) ks->buflen--;
292 //LOG_IF(kbc_debug, "\tKBC DBG: rxd=%02X\n", x);
293 return x;
294 }
295 }
297 void keyboard_write(KEYBOARD_STATE *ks, uint8_t addr, uint8_t val)
298 {
299 if ((addr & 1) == 0) {
300 // write to control register
301 // transmit intr enabled when CR6,5 = 01
302 // receive intr enabled when CR7 = 1
304 // CR0,1 = divider registers. When =11, do a software reset
305 if ((val & 3) == 3) {
306 ks->readp = ks->writep = ks->buflen = 0;
307 }
309 // Ignore CR2,3,4 (word length)...
311 // CR5,6 = Transmit Mode
312 ks->txie = (val & 0x60)==0x20;
314 // CR7 = Receive Interrupt Enable
315 ks->rxie = (val & 0x80)==0x80;
316 } else {
317 // Write command to KBC -- TODO!
318 if (val == KEY_CMD_RESET) {
319 LOG_IFS(kbc_debug, "KBC: KEYBOARD RESET!\n");
320 ks->readp = ks->writep = ks->buflen = 0;
321 } else {
322 LOG("KBC TODO: write keyboard data 0x%02X\n", val);
323 }
324 }
325 }