Sat, 19 Apr 2014 02:19:39 -0600
fixed timing on OSes that set a minimum time for sleeps (previously the main loop code assumed no minimum sleep time; the new version uses longer sleeps less frequently)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <time.h>
5 #include "sim.h"
6 #include "m68k.h"
8 /* Memory-mapped IO ports */
9 #define INPUT_ADDRESS 0x800000
10 #define OUTPUT_ADDRESS 0x400000
12 /* IRQ connections */
13 #define IRQ_NMI_DEVICE 7
14 #define IRQ_INPUT_DEVICE 2
15 #define IRQ_OUTPUT_DEVICE 1
17 /* Time between characters sent to output device (seconds) */
18 #define OUTPUT_DEVICE_PERIOD 1
20 /* ROM and RAM sizes */
21 #define MAX_ROM 0xfff
22 #define MAX_RAM 0xff
25 /* Read/write macros */
26 #define READ_BYTE(BASE, ADDR) (BASE)[ADDR]
27 #define READ_WORD(BASE, ADDR) (((BASE)[ADDR]<<8) | \
28 (BASE)[(ADDR)+1])
29 #define READ_LONG(BASE, ADDR) (((BASE)[ADDR]<<24) | \
30 ((BASE)[(ADDR)+1]<<16) | \
31 ((BASE)[(ADDR)+2]<<8) | \
32 (BASE)[(ADDR)+3])
34 #define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[ADDR] = (VAL)%0xff
35 #define WRITE_WORD(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>8) & 0xff; \
36 (BASE)[(ADDR)+1] = (VAL)&0xff
37 #define WRITE_LONG(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>24) & 0xff; \
38 (BASE)[(ADDR)+1] = ((VAL)>>16)&0xff; \
39 (BASE)[(ADDR)+2] = ((VAL)>>8)&0xff; \
40 (BASE)[(ADDR)+3] = (VAL)&0xff
43 /* Prototypes */
44 void exit_error(char* fmt, ...);
45 int osd_get_char(void);
47 unsigned int m68k_read_memory_8(unsigned int address);
48 unsigned int m68k_read_memory_16(unsigned int address);
49 unsigned int m68k_read_memory_32(unsigned int address);
50 void m68k_write_memory_8(unsigned int address, unsigned int value);
51 void m68k_write_memory_16(unsigned int address, unsigned int value);
52 void m68k_write_memory_32(unsigned int address, unsigned int value);
53 void cpu_pulse_reset(void);
54 void cpu_set_fc(unsigned int fc);
55 int cpu_irq_ack(int level);
57 void nmi_device_reset(void);
58 void nmi_device_update(void);
59 int nmi_device_ack(void);
61 void input_device_reset(void);
62 void input_device_update(void);
63 int input_device_ack(void);
64 unsigned int input_device_read(void);
65 void input_device_write(unsigned int value);
67 void output_device_reset(void);
68 void output_device_update(void);
69 int output_device_ack(void);
70 unsigned int output_device_read(void);
71 void output_device_write(unsigned int value);
73 void int_controller_set(unsigned int value);
74 void int_controller_clear(unsigned int value);
76 void get_user_input(void);
79 /* Data */
80 unsigned int g_quit = 0; /* 1 if we want to quit */
81 unsigned int g_nmi = 0; /* 1 if nmi pending */
83 int g_input_device_value = -1; /* Current value in input device */
85 unsigned int g_output_device_ready = 0; /* 1 if output device is ready */
86 time_t g_output_device_last_output; /* Time of last char output */
88 unsigned int g_int_controller_pending = 0; /* list of pending interrupts */
89 unsigned int g_int_controller_highest_int = 0; /* Highest pending interrupt */
91 unsigned char g_rom[MAX_ROM+1]; /* ROM */
92 unsigned char g_ram[MAX_RAM+1]; /* RAM */
93 unsigned int g_fc; /* Current function code from CPU */
96 /* Exit with an error message. Use printf syntax. */
97 void exit_error(char* fmt, ...)
98 {
99 va_list args;
100 va_start(args, fmt);
101 vfprintf(stderr, fmt, args);
102 va_end(args);
103 fprintf(stderr, "\n");
105 exit(EXIT_FAILURE);
106 }
108 /* OS-dependant code to get a character from the user.
109 * This function must not block, and must either return an ASCII code or -1.
110 */
111 //#include <conio.h>
112 int osd_get_char(void)
113 {
114 int ch = -1;
115 /* if(kbhit())
116 {
117 while(kbhit())
118 ch = getch();
119 }
120 */ return ch;
121 }
124 /* Read data from RAM, ROM, or a device */
125 unsigned int m68k_read_memory_8(unsigned int address)
126 {
127 if(g_fc & 2) /* Program */
128 {
129 if(address > MAX_ROM)
130 exit_error("Attempted to read byte from ROM address %08x", address);
131 return READ_BYTE(g_rom, address);
132 }
134 /* Otherwise it's data space */
135 switch(address)
136 {
137 case INPUT_ADDRESS:
138 return input_device_read();
139 case OUTPUT_ADDRESS:
140 return output_device_read();
141 default:
142 break;
143 }
144 if(address > MAX_RAM)
145 exit_error("Attempted to read byte from RAM address %08x", address);
146 return READ_BYTE(g_ram, address);
147 }
149 unsigned int m68k_read_memory_16(unsigned int address)
150 {
151 if(g_fc & 2) /* Program */
152 {
153 if(address > MAX_ROM)
154 exit_error("Attempted to read word from ROM address %08x", address);
155 return READ_WORD(g_rom, address);
156 }
158 /* Otherwise it's data space */
159 switch(address)
160 {
161 case INPUT_ADDRESS:
162 return input_device_read();
163 case OUTPUT_ADDRESS:
164 return output_device_read();
165 default:
166 break;
167 }
168 if(address > MAX_RAM)
169 exit_error("Attempted to read word from RAM address %08x", address);
170 return READ_WORD(g_ram, address);
171 }
173 unsigned int m68k_read_memory_32(unsigned int address)
174 {
175 if(g_fc & 2) /* Program */
176 {
177 if(address > MAX_ROM)
178 exit_error("Attempted to read long from ROM address %08x", address);
179 return READ_LONG(g_rom, address);
180 }
182 /* Otherwise it's data space */
183 switch(address)
184 {
185 case INPUT_ADDRESS:
186 return input_device_read();
187 case OUTPUT_ADDRESS:
188 return output_device_read();
189 default:
190 break;
191 }
192 if(address > MAX_RAM)
193 exit_error("Attempted to read long from RAM address %08x", address);
194 return READ_LONG(g_ram, address);
195 }
198 /* Write data to RAM or a device */
199 void m68k_write_memory_8(unsigned int address, unsigned int value)
200 {
201 if(g_fc & 2) /* Program */
202 exit_error("Attempted to write %02x to ROM address %08x", value&0xff, address);
204 /* Otherwise it's data space */
205 switch(address)
206 {
207 case INPUT_ADDRESS:
208 input_device_write(value&0xff);
209 return;
210 case OUTPUT_ADDRESS:
211 output_device_write(value&0xff);
212 return;
213 default:
214 break;
215 }
216 if(address > MAX_RAM)
217 exit_error("Attempted to write %02x to RAM address %08x", value&0xff, address);
218 WRITE_BYTE(g_ram, address, value);
219 }
221 void m68k_write_memory_16(unsigned int address, unsigned int value)
222 {
223 if(g_fc & 2) /* Program */
224 exit_error("Attempted to write %04x to ROM address %08x", value&0xffff, address);
226 /* Otherwise it's data space */
227 switch(address)
228 {
229 case INPUT_ADDRESS:
230 input_device_write(value&0xffff);
231 return;
232 case OUTPUT_ADDRESS:
233 output_device_write(value&0xffff);
234 return;
235 default:
236 break;
237 }
238 if(address > MAX_RAM)
239 exit_error("Attempted to write %04x to RAM address %08x", value&0xffff, address);
240 WRITE_WORD(g_ram, address, value);
241 }
243 void m68k_write_memory_32(unsigned int address, unsigned int value)
244 {
245 if(g_fc & 2) /* Program */
246 exit_error("Attempted to write %08x to ROM address %08x", value, address);
248 /* Otherwise it's data space */
249 switch(address)
250 {
251 case INPUT_ADDRESS:
252 input_device_write(value);
253 return;
254 case OUTPUT_ADDRESS:
255 output_device_write(value);
256 return;
257 default:
258 break;
259 }
260 if(address > MAX_RAM)
261 exit_error("Attempted to write %08x to RAM address %08x", value, address);
262 WRITE_LONG(g_ram, address, value);
263 }
265 /* Called when the CPU pulses the RESET line */
266 void cpu_pulse_reset(void)
267 {
268 nmi_device_reset();
269 output_device_reset();
270 input_device_reset();
271 }
273 /* Called when the CPU changes the function code pins */
274 void cpu_set_fc(unsigned int fc)
275 {
276 g_fc = fc;
277 }
279 /* Called when the CPU acknowledges an interrupt */
280 int cpu_irq_ack(int level)
281 {
282 switch(level)
283 {
284 case IRQ_NMI_DEVICE:
285 return nmi_device_ack();
286 case IRQ_INPUT_DEVICE:
287 return input_device_ack();
288 case IRQ_OUTPUT_DEVICE:
289 return output_device_ack();
290 }
291 return M68K_INT_ACK_SPURIOUS;
292 }
297 /* Implementation for the NMI device */
298 void nmi_device_reset(void)
299 {
300 g_nmi = 0;
301 }
303 void nmi_device_update(void)
304 {
305 if(g_nmi)
306 {
307 g_nmi = 0;
308 int_controller_set(IRQ_NMI_DEVICE);
309 }
310 }
312 int nmi_device_ack(void)
313 {
314 printf("\nNMI\n");fflush(stdout);
315 int_controller_clear(IRQ_NMI_DEVICE);
316 return M68K_INT_ACK_AUTOVECTOR;
317 }
320 /* Implementation for the input device */
321 void input_device_reset(void)
322 {
323 g_input_device_value = -1;
324 int_controller_clear(IRQ_INPUT_DEVICE);
325 }
327 void input_device_update(void)
328 {
329 if(g_input_device_value >= 0)
330 int_controller_set(IRQ_INPUT_DEVICE);
331 }
333 int input_device_ack(void)
334 {
335 return M68K_INT_ACK_AUTOVECTOR;
336 }
338 unsigned int input_device_read(void)
339 {
340 int value = g_input_device_value > 0 ? g_input_device_value : 0;
341 int_controller_clear(IRQ_INPUT_DEVICE);
342 g_input_device_value = -1;
343 return value;
344 }
346 void input_device_write(unsigned int value)
347 {
348 }
351 /* Implementation for the output device */
352 void output_device_reset(void)
353 {
354 g_output_device_last_output = time(NULL);
355 g_output_device_ready = 0;
356 int_controller_clear(IRQ_OUTPUT_DEVICE);
357 }
359 void output_device_update(void)
360 {
361 if(!g_output_device_ready)
362 {
363 if((time(NULL) - g_output_device_last_output) >= OUTPUT_DEVICE_PERIOD)
364 {
365 g_output_device_ready = 1;
366 int_controller_set(IRQ_OUTPUT_DEVICE);
367 }
368 }
369 }
371 int output_device_ack(void)
372 {
373 return M68K_INT_ACK_AUTOVECTOR;
374 }
376 unsigned int output_device_read(void)
377 {
378 int_controller_clear(IRQ_OUTPUT_DEVICE);
379 return 0;
380 }
382 void output_device_write(unsigned int value)
383 {
384 char ch;
385 if(g_output_device_ready)
386 {
387 ch = value & 0xff;
388 printf("%c", ch);
389 g_output_device_last_output = time(NULL);
390 g_output_device_ready = 0;
391 int_controller_clear(IRQ_OUTPUT_DEVICE);
392 }
393 }
396 /* Implementation for the interrupt controller */
397 void int_controller_set(unsigned int value)
398 {
399 unsigned int old_pending = g_int_controller_pending;
401 g_int_controller_pending |= (1<<value);
403 if(old_pending != g_int_controller_pending && value > g_int_controller_highest_int)
404 {
405 g_int_controller_highest_int = value;
406 m68k_set_irq(g_int_controller_highest_int);
407 }
408 }
410 void int_controller_clear(unsigned int value)
411 {
412 g_int_controller_pending &= ~(1<<value);
414 for(g_int_controller_highest_int = 7;g_int_controller_highest_int > 0;g_int_controller_highest_int--)
415 if(g_int_controller_pending & (1<<g_int_controller_highest_int))
416 break;
418 m68k_set_irq(g_int_controller_highest_int);
419 }
422 /* Parse user input and update any devices that need user input */
423 void get_user_input(void)
424 {
425 static int last_ch = -1;
426 int ch = osd_get_char();
428 if(ch >= 0)
429 {
430 switch(ch)
431 {
432 case 0x1b:
433 g_quit = 1;
434 break;
435 case '~':
436 if(last_ch != ch)
437 g_nmi = 1;
438 break;
439 default:
440 g_input_device_value = ch;
441 }
442 }
443 last_ch = ch;
444 }
447 /* The main loop */
448 int main(int argc, char* argv[])
449 {
450 FILE* fhandle;
452 if(argc != 2)
453 exit_error("Usage: sim <program file>");
455 if((fhandle = fopen(argv[1], "rb")) == NULL)
456 exit_error("Unable to open %s", argv[1]);
458 if(fread(g_rom, 1, MAX_ROM+1, fhandle) <= 0)
459 exit_error("Error reading %s", argv[1]);
462 m68k_pulse_reset();
463 input_device_reset();
464 output_device_reset();
465 nmi_device_reset();
467 g_quit = 0;
468 while(!g_quit)
469 {
470 get_user_input();
471 /* Note that I am not emulating the correct clock speed! */
472 m68k_execute(1000);
473 output_device_update();
474 input_device_update();
475 nmi_device_update();
476 }
477 return 0;
478 }