src/musashi/example/sim.c

Tue, 15 Nov 2011 10:12:37 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Tue, 15 Nov 2011 10:12:37 +0000
changeset 109
2f8afb9e5baa
parent 0
8bf1bf91a36d
permissions
-rw-r--r--

[musashi] Fix handling of bus errors

Patch-Author: Andrew Warkentin <andreww591!gmail>
Patch-MessageID: <4EC200CE.2020304@gmail.com>

I have fixed the first page fault test failure in FreeBee (the page fault test now hangs rather than errors out, because it is trying to read from the hard drive to test DMA page faults).

There were actually two bugs (the first bug was masking the second one).

First, the ancient version of Musashi that you used is unable to properly resume from bus errors that happen in the middle of certain instructions (some instructions are fetched in stages, with the PC being advanced to each part of the instruction, so basically what happens is the CPU core attempts to read the memory location referenced by the first operand, the bus error occurs, causing the PC to jump to the exception vector, but the faulting instruction is still in the middle of being fetched, so the PC is then advanced past the beginning of the exception handler). I fixed this by delaying the jump to the bus error vector until after the faulting instruction finishes.

The second bug is simpler - you had the UDS and LDS bits in BSR0 inverted (they are supposed to be active low).

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 }