Tue, 15 Nov 2011 10:12:37 +0000
[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 | } |