src/musashi/example/sim.c

Sat, 27 Nov 2010 01:13:12 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Sat, 27 Nov 2010 01:13:12 +0000
changeset 0
8bf1bf91a36d
permissions
-rw-r--r--

initial commit

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