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 | #ifndef M68K__HEADER |
philpem@0 | 2 | #define M68K__HEADER |
philpem@0 | 3 | |
philpem@0 | 4 | /* ======================================================================== */ |
philpem@0 | 5 | /* ========================= LICENSING & COPYRIGHT ======================== */ |
philpem@0 | 6 | /* ======================================================================== */ |
philpem@0 | 7 | /* |
philpem@0 | 8 | * MUSASHI |
philpem@0 | 9 | * Version 3.3 |
philpem@0 | 10 | * |
philpem@0 | 11 | * A portable Motorola M680x0 processor emulation engine. |
philpem@0 | 12 | * Copyright 1998-2001 Karl Stenerud. All rights reserved. |
philpem@0 | 13 | * |
philpem@0 | 14 | * This code may be freely used for non-commercial purposes as long as this |
philpem@0 | 15 | * copyright notice remains unaltered in the source code and any binary files |
philpem@0 | 16 | * containing this code in compiled form. |
philpem@0 | 17 | * |
philpem@0 | 18 | * All other lisencing terms must be negotiated with the author |
philpem@0 | 19 | * (Karl Stenerud). |
philpem@0 | 20 | * |
philpem@0 | 21 | * The latest version of this code can be obtained at: |
philpem@0 | 22 | * http://kstenerud.cjb.net |
philpem@0 | 23 | */ |
philpem@0 | 24 | |
philpem@0 | 25 | |
philpem@0 | 26 | |
philpem@0 | 27 | /* ======================================================================== */ |
philpem@0 | 28 | /* ============================ GENERAL DEFINES =========================== */ |
philpem@0 | 29 | |
philpem@0 | 30 | /* ======================================================================== */ |
philpem@0 | 31 | |
philpem@0 | 32 | /* There are 7 levels of interrupt to the 68K. |
philpem@0 | 33 | * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). |
philpem@0 | 34 | */ |
philpem@0 | 35 | #define M68K_IRQ_NONE 0 |
philpem@0 | 36 | #define M68K_IRQ_1 1 |
philpem@0 | 37 | #define M68K_IRQ_2 2 |
philpem@0 | 38 | #define M68K_IRQ_3 3 |
philpem@0 | 39 | #define M68K_IRQ_4 4 |
philpem@0 | 40 | #define M68K_IRQ_5 5 |
philpem@0 | 41 | #define M68K_IRQ_6 6 |
philpem@0 | 42 | #define M68K_IRQ_7 7 |
philpem@0 | 43 | |
philpem@0 | 44 | |
philpem@0 | 45 | /* Special interrupt acknowledge values. |
philpem@0 | 46 | * Use these as special returns from the interrupt acknowledge callback |
philpem@0 | 47 | * (specified later in this header). |
philpem@0 | 48 | */ |
philpem@0 | 49 | |
philpem@0 | 50 | /* Causes an interrupt autovector (0x18 + interrupt level) to be taken. |
philpem@0 | 51 | * This happens in a real 68K if VPA or AVEC is asserted during an interrupt |
philpem@0 | 52 | * acknowledge cycle instead of DTACK. |
philpem@0 | 53 | */ |
philpem@0 | 54 | #define M68K_INT_ACK_AUTOVECTOR 0xffffffff |
philpem@0 | 55 | |
philpem@0 | 56 | /* Causes the spurious interrupt vector (0x18) to be taken |
philpem@0 | 57 | * This happens in a real 68K if BERR is asserted during the interrupt |
philpem@0 | 58 | * acknowledge cycle (i.e. no devices responded to the acknowledge). |
philpem@0 | 59 | */ |
philpem@0 | 60 | #define M68K_INT_ACK_SPURIOUS 0xfffffffe |
philpem@0 | 61 | |
philpem@0 | 62 | |
philpem@0 | 63 | /* CPU types for use in m68k_set_cpu_type() */ |
philpem@0 | 64 | enum |
philpem@0 | 65 | { |
philpem@0 | 66 | M68K_CPU_TYPE_INVALID, |
philpem@0 | 67 | M68K_CPU_TYPE_68000, |
philpem@0 | 68 | M68K_CPU_TYPE_68010, |
philpem@0 | 69 | M68K_CPU_TYPE_68EC020, |
philpem@0 | 70 | M68K_CPU_TYPE_68020, |
philpem@0 | 71 | M68K_CPU_TYPE_68030, /* Supported by disassembler ONLY */ |
philpem@0 | 72 | M68K_CPU_TYPE_68040 /* Supported by disassembler ONLY */ |
philpem@0 | 73 | }; |
philpem@0 | 74 | |
philpem@0 | 75 | /* Registers used by m68k_get_reg() and m68k_set_reg() */ |
philpem@0 | 76 | typedef enum |
philpem@0 | 77 | { |
philpem@0 | 78 | /* Real registers */ |
philpem@0 | 79 | M68K_REG_D0, /* Data registers */ |
philpem@0 | 80 | M68K_REG_D1, |
philpem@0 | 81 | M68K_REG_D2, |
philpem@0 | 82 | M68K_REG_D3, |
philpem@0 | 83 | M68K_REG_D4, |
philpem@0 | 84 | M68K_REG_D5, |
philpem@0 | 85 | M68K_REG_D6, |
philpem@0 | 86 | M68K_REG_D7, |
philpem@0 | 87 | M68K_REG_A0, /* Address registers */ |
philpem@0 | 88 | M68K_REG_A1, |
philpem@0 | 89 | M68K_REG_A2, |
philpem@0 | 90 | M68K_REG_A3, |
philpem@0 | 91 | M68K_REG_A4, |
philpem@0 | 92 | M68K_REG_A5, |
philpem@0 | 93 | M68K_REG_A6, |
philpem@0 | 94 | M68K_REG_A7, |
philpem@0 | 95 | M68K_REG_PC, /* Program Counter */ |
philpem@0 | 96 | M68K_REG_SR, /* Status Register */ |
philpem@0 | 97 | M68K_REG_SP, /* The current Stack Pointer (located in A7) */ |
philpem@0 | 98 | M68K_REG_USP, /* User Stack Pointer */ |
philpem@0 | 99 | M68K_REG_ISP, /* Interrupt Stack Pointer */ |
philpem@0 | 100 | M68K_REG_MSP, /* Master Stack Pointer */ |
philpem@0 | 101 | M68K_REG_SFC, /* Source Function Code */ |
philpem@0 | 102 | M68K_REG_DFC, /* Destination Function Code */ |
philpem@0 | 103 | M68K_REG_VBR, /* Vector Base Register */ |
philpem@0 | 104 | M68K_REG_CACR, /* Cache Control Register */ |
philpem@0 | 105 | M68K_REG_CAAR, /* Cache Address Register */ |
philpem@0 | 106 | |
philpem@0 | 107 | /* Assumed registers */ |
philpem@0 | 108 | /* These are cheat registers which emulate the 1-longword prefetch |
philpem@0 | 109 | * present in the 68000 and 68010. |
philpem@0 | 110 | */ |
philpem@0 | 111 | M68K_REG_PREF_ADDR, /* Last prefetch address */ |
philpem@0 | 112 | M68K_REG_PREF_DATA, /* Last prefetch data */ |
philpem@0 | 113 | |
philpem@0 | 114 | /* Convenience registers */ |
philpem@0 | 115 | M68K_REG_PPC, /* Previous value in the program counter */ |
philpem@0 | 116 | M68K_REG_IR, /* Instruction register */ |
philpem@0 | 117 | M68K_REG_CPU_TYPE /* Type of CPU being run */ |
philpem@0 | 118 | } m68k_register_t; |
philpem@0 | 119 | |
philpem@0 | 120 | /* ======================================================================== */ |
philpem@0 | 121 | /* ====================== FUNCTIONS CALLED BY THE CPU ===================== */ |
philpem@0 | 122 | /* ======================================================================== */ |
philpem@0 | 123 | |
philpem@0 | 124 | /* You will have to implement these functions */ |
philpem@0 | 125 | |
philpem@0 | 126 | /* read/write functions called by the CPU to access memory. |
philpem@0 | 127 | * while values used are 32 bits, only the appropriate number |
philpem@0 | 128 | * of bits are relevant (i.e. in write_memory_8, only the lower 8 bits |
philpem@0 | 129 | * of value should be written to memory). |
philpem@0 | 130 | * |
philpem@0 | 131 | * NOTE: I have separated the immediate and PC-relative memory fetches |
philpem@0 | 132 | * from the other memory fetches because some systems require |
philpem@0 | 133 | * differentiation between PROGRAM and DATA fetches (usually |
philpem@0 | 134 | * for security setups such as encryption). |
philpem@0 | 135 | * This separation can either be achieved by setting |
philpem@0 | 136 | * M68K_SEPARATE_READS in m68kconf.h and defining |
philpem@0 | 137 | * the read functions, or by setting M68K_EMULATE_FC and |
philpem@0 | 138 | * making a function code callback function. |
philpem@0 | 139 | * Using the callback offers better emulation coverage |
philpem@0 | 140 | * because you can also monitor whether the CPU is in SYSTEM or |
philpem@0 | 141 | * USER mode, but it is also slower. |
philpem@0 | 142 | */ |
philpem@0 | 143 | |
philpem@0 | 144 | /* Read from anywhere */ |
philpem@0 | 145 | unsigned int m68k_read_memory_8(unsigned int address); |
philpem@0 | 146 | unsigned int m68k_read_memory_16(unsigned int address); |
philpem@0 | 147 | unsigned int m68k_read_memory_32(unsigned int address); |
philpem@0 | 148 | |
philpem@0 | 149 | /* Read data immediately following the PC */ |
philpem@0 | 150 | unsigned int m68k_read_immediate_16(unsigned int address); |
philpem@0 | 151 | unsigned int m68k_read_immediate_32(unsigned int address); |
philpem@0 | 152 | |
philpem@0 | 153 | /* Read data relative to the PC */ |
philpem@0 | 154 | unsigned int m68k_read_pcrelative_8(unsigned int address); |
philpem@0 | 155 | unsigned int m68k_read_pcrelative_16(unsigned int address); |
philpem@0 | 156 | unsigned int m68k_read_pcrelative_32(unsigned int address); |
philpem@0 | 157 | |
philpem@0 | 158 | /* Memory access for the disassembler */ |
philpem@0 | 159 | unsigned int m68k_read_disassembler_8 (unsigned int address); |
philpem@0 | 160 | unsigned int m68k_read_disassembler_16 (unsigned int address); |
philpem@0 | 161 | unsigned int m68k_read_disassembler_32 (unsigned int address); |
philpem@0 | 162 | |
philpem@0 | 163 | /* Write to anywhere */ |
philpem@0 | 164 | void m68k_write_memory_8(unsigned int address, unsigned int value); |
philpem@0 | 165 | void m68k_write_memory_16(unsigned int address, unsigned int value); |
philpem@0 | 166 | void m68k_write_memory_32(unsigned int address, unsigned int value); |
philpem@0 | 167 | |
philpem@0 | 168 | |
philpem@0 | 169 | |
philpem@0 | 170 | /* ======================================================================== */ |
philpem@0 | 171 | /* ============================== CALLBACKS =============================== */ |
philpem@0 | 172 | /* ======================================================================== */ |
philpem@0 | 173 | |
philpem@0 | 174 | /* These functions allow you to set callbacks to the host when specific events |
philpem@0 | 175 | * occur. Note that you must enable the corresponding value in m68kconf.h |
philpem@0 | 176 | * in order for these to do anything useful. |
philpem@0 | 177 | * Note: I have defined default callbacks which are used if you have enabled |
philpem@0 | 178 | * the corresponding #define in m68kconf.h but either haven't assigned a |
philpem@0 | 179 | * callback or have assigned a callback of NULL. |
philpem@0 | 180 | */ |
philpem@0 | 181 | |
philpem@0 | 182 | /* Set the callback for an interrupt acknowledge. |
philpem@0 | 183 | * You must enable M68K_EMULATE_INT_ACK in m68kconf.h. |
philpem@0 | 184 | * The CPU will call the callback with the interrupt level being acknowledged. |
philpem@0 | 185 | * The host program must return either a vector from 0x02-0xff, or one of the |
philpem@0 | 186 | * special interrupt acknowledge values specified earlier in this header. |
philpem@0 | 187 | * If this is not implemented, the CPU will always assume an autovectored |
philpem@0 | 188 | * interrupt, and will automatically clear the interrupt request when it |
philpem@0 | 189 | * services the interrupt. |
philpem@0 | 190 | * Default behavior: return M68K_INT_ACK_AUTOVECTOR. |
philpem@0 | 191 | */ |
philpem@0 | 192 | void m68k_set_int_ack_callback(int (*callback)(int int_level)); |
philpem@0 | 193 | |
philpem@0 | 194 | |
philpem@0 | 195 | /* Set the callback for a breakpoint acknowledge (68010+). |
philpem@0 | 196 | * You must enable M68K_EMULATE_BKPT_ACK in m68kconf.h. |
philpem@0 | 197 | * The CPU will call the callback with whatever was in the data field of the |
philpem@0 | 198 | * BKPT instruction for 68020+, or 0 for 68010. |
philpem@0 | 199 | * Default behavior: do nothing. |
philpem@0 | 200 | */ |
philpem@0 | 201 | void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data)); |
philpem@0 | 202 | |
philpem@0 | 203 | |
philpem@0 | 204 | /* Set the callback for the RESET instruction. |
philpem@0 | 205 | * You must enable M68K_EMULATE_RESET in m68kconf.h. |
philpem@0 | 206 | * The CPU calls this callback every time it encounters a RESET instruction. |
philpem@0 | 207 | * Default behavior: do nothing. |
philpem@0 | 208 | */ |
philpem@0 | 209 | void m68k_set_reset_instr_callback(void (*callback)(void)); |
philpem@0 | 210 | |
philpem@0 | 211 | |
philpem@0 | 212 | /* Set the callback for informing of a large PC change. |
philpem@0 | 213 | * You must enable M68K_MONITOR_PC in m68kconf.h. |
philpem@0 | 214 | * The CPU calls this callback with the new PC value every time the PC changes |
philpem@0 | 215 | * by a large value (currently set for changes by longwords). |
philpem@0 | 216 | * Default behavior: do nothing. |
philpem@0 | 217 | */ |
philpem@0 | 218 | void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc)); |
philpem@0 | 219 | |
philpem@0 | 220 | |
philpem@0 | 221 | /* Set the callback for CPU function code changes. |
philpem@0 | 222 | * You must enable M68K_EMULATE_FC in m68kconf.h. |
philpem@0 | 223 | * The CPU calls this callback with the function code before every memory |
philpem@0 | 224 | * access to set the CPU's function code according to what kind of memory |
philpem@0 | 225 | * access it is (supervisor/user, program/data and such). |
philpem@0 | 226 | * Default behavior: do nothing. |
philpem@0 | 227 | */ |
philpem@0 | 228 | void m68k_set_fc_callback(void (*callback)(unsigned int new_fc)); |
philpem@0 | 229 | |
philpem@0 | 230 | |
philpem@0 | 231 | /* Set a callback for the instruction cycle of the CPU. |
philpem@0 | 232 | * You must enable M68K_INSTRUCTION_HOOK in m68kconf.h. |
philpem@0 | 233 | * The CPU calls this callback just before fetching the opcode in the |
philpem@0 | 234 | * instruction cycle. |
philpem@0 | 235 | * Default behavior: do nothing. |
philpem@0 | 236 | */ |
philpem@0 | 237 | void m68k_set_instr_hook_callback(void (*callback)(void)); |
philpem@0 | 238 | |
philpem@0 | 239 | |
philpem@0 | 240 | |
philpem@0 | 241 | /* ======================================================================== */ |
philpem@0 | 242 | /* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */ |
philpem@0 | 243 | /* ======================================================================== */ |
philpem@0 | 244 | |
philpem@0 | 245 | /* Use this function to set the CPU type you want to emulate. |
philpem@0 | 246 | * Currently supported types are: M68K_CPU_TYPE_68000, M68K_CPU_TYPE_68010, |
philpem@0 | 247 | * M68K_CPU_TYPE_EC020, and M68K_CPU_TYPE_68020. |
philpem@0 | 248 | */ |
philpem@0 | 249 | void m68k_set_cpu_type(unsigned int cpu_type); |
philpem@0 | 250 | |
philpem@0 | 251 | /* Pulse the RESET pin on the CPU. |
philpem@0 | 252 | * You *MUST* reset the CPU at least once to initialize the emulation |
philpem@0 | 253 | * Note: If you didn't call m68k_set_cpu_type() before resetting |
philpem@0 | 254 | * the CPU for the first time, the CPU will be set to |
philpem@0 | 255 | * M68K_CPU_TYPE_68000. |
philpem@0 | 256 | */ |
philpem@0 | 257 | void m68k_pulse_reset(void); |
philpem@0 | 258 | |
philpem@0 | 259 | /* execute num_cycles worth of instructions. returns number of cycles used */ |
philpem@0 | 260 | int m68k_execute(int num_cycles); |
philpem@0 | 261 | |
philpem@0 | 262 | /* These functions let you read/write/modify the number of cycles left to run |
philpem@0 | 263 | * while m68k_execute() is running. |
philpem@0 | 264 | * These are useful if the 68k accesses a memory-mapped port on another device |
philpem@0 | 265 | * that requires immediate processing by another CPU. |
philpem@0 | 266 | */ |
philpem@0 | 267 | int m68k_cycles_run(void); /* Number of cycles run so far */ |
philpem@0 | 268 | int m68k_cycles_remaining(void); /* Number of cycles left */ |
philpem@0 | 269 | void m68k_modify_timeslice(int cycles); /* Modify cycles left */ |
philpem@0 | 270 | void m68k_end_timeslice(void); /* End timeslice now */ |
philpem@0 | 271 | |
philpem@0 | 272 | /* Set the IPL0-IPL2 pins on the CPU (IRQ). |
philpem@0 | 273 | * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). |
philpem@0 | 274 | * Setting IRQ to 0 will clear an interrupt request. |
philpem@0 | 275 | */ |
philpem@0 | 276 | void m68k_set_irq(unsigned int int_level); |
philpem@0 | 277 | |
philpem@0 | 278 | |
philpem@0 | 279 | /* Halt the CPU as if you pulsed the HALT pin. */ |
philpem@0 | 280 | void m68k_pulse_halt(void); |
philpem@0 | 281 | |
philpem@0 | 282 | |
philpem@19 | 283 | /* Trigger a bus error exception */ |
philpem@19 | 284 | void m68k_pulse_bus_error(void); |
philpem@19 | 285 | |
philpem@19 | 286 | |
philpem@0 | 287 | /* Context switching to allow multiple CPUs */ |
philpem@0 | 288 | |
philpem@0 | 289 | /* Get the size of the cpu context in bytes */ |
philpem@0 | 290 | unsigned int m68k_context_size(void); |
philpem@0 | 291 | |
philpem@0 | 292 | /* Get a cpu context */ |
philpem@0 | 293 | unsigned int m68k_get_context(void* dst); |
philpem@0 | 294 | |
philpem@0 | 295 | /* set the current cpu context */ |
philpem@0 | 296 | void m68k_set_context(void* dst); |
philpem@0 | 297 | |
philpem@0 | 298 | /* Save the current cpu context to disk. |
philpem@0 | 299 | * You must provide a function pointer of the form: |
philpem@0 | 300 | * void save_value(char* identifier, unsigned int value) |
philpem@0 | 301 | */ |
philpem@0 | 302 | void m68k_save_context( void (*save_value)(char* identifier, unsigned int value)); |
philpem@0 | 303 | |
philpem@0 | 304 | /* Load a cpu context from disk. |
philpem@0 | 305 | * You must provide a function pointer of the form: |
philpem@0 | 306 | * unsigned int load_value(char* identifier) |
philpem@0 | 307 | */ |
philpem@0 | 308 | void m68k_load_context(unsigned int (*load_value)(char* identifier)); |
philpem@0 | 309 | |
philpem@0 | 310 | |
philpem@0 | 311 | |
philpem@0 | 312 | /* Peek at the internals of a CPU context. This can either be a context |
philpem@0 | 313 | * retrieved using m68k_get_context() or the currently running context. |
philpem@0 | 314 | * If context is NULL, the currently running CPU context will be used. |
philpem@0 | 315 | */ |
philpem@0 | 316 | unsigned int m68k_get_reg(void* context, m68k_register_t reg); |
philpem@0 | 317 | |
philpem@0 | 318 | /* Poke values into the internals of the currently running CPU context */ |
philpem@0 | 319 | void m68k_set_reg(m68k_register_t reg, unsigned int value); |
philpem@0 | 320 | |
philpem@0 | 321 | /* Check if an instruction is valid for the specified CPU type */ |
philpem@0 | 322 | unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type); |
philpem@0 | 323 | |
philpem@0 | 324 | /* Disassemble 1 instruction using the epecified CPU type at pc. Stores |
philpem@0 | 325 | * disassembly in str_buff and returns the size of the instruction in bytes. |
philpem@0 | 326 | */ |
philpem@0 | 327 | unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type); |
philpem@0 | 328 | |
philpem@0 | 329 | |
philpem@0 | 330 | /* ======================================================================== */ |
philpem@0 | 331 | /* ============================= CONFIGURATION ============================ */ |
philpem@0 | 332 | /* ======================================================================== */ |
philpem@0 | 333 | |
philpem@0 | 334 | /* Import the configuration for this build */ |
philpem@0 | 335 | #include "m68kconf.h" |
philpem@0 | 336 | |
philpem@0 | 337 | |
philpem@0 | 338 | |
philpem@0 | 339 | /* ======================================================================== */ |
philpem@0 | 340 | /* ============================== END OF FILE ============================= */ |
philpem@0 | 341 | /* ======================================================================== */ |
philpem@0 | 342 | |
philpem@0 | 343 | #endif /* M68K__HEADER */ |