src/musashi/m68k.h

Wed, 16 Apr 2014 02:20:43 -0600

author
andrew@localhost
date
Wed, 16 Apr 2014 02:20:43 -0600
changeset 147
ad888290cdff
parent 19
ea417ac1d83a
permissions
-rw-r--r--

fixed bus error handling for real this time (save registers before every instruction and push the saved registers if a bus error occurs, since the instruction may have changed registers before the bus error, and also stop the instruction immediately with longjmp so it won't change memory after the bus error)

This isn't actually what a real 68k does, but it is a good enough approximation. A real 68k will jump back into the middle of the faulted instruction and resume it from the memory access that faulted as opposed to restarting from the beginning like this CPU emulation does. It would be a lot harder to do that with the way this CPU library is designed. Newer versions of MESS basically do the same thing (they use a newer version of this library).

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 */