Sun, 05 Dec 2010 16:20:00 +0000
add preliminary WD279x emulation to core
philpem@0 | 1 | MUSASHI |
philpem@0 | 2 | ======= |
philpem@0 | 3 | |
philpem@0 | 4 | Version 3.3 |
philpem@0 | 5 | |
philpem@0 | 6 | A portable Motorola M680x0 processor emulation engine. |
philpem@0 | 7 | Copyright 1998-2001 Karl Stenerud. All rights reserved. |
philpem@0 | 8 | |
philpem@0 | 9 | |
philpem@0 | 10 | |
philpem@0 | 11 | INTRODUCTION: |
philpem@0 | 12 | ------------ |
philpem@0 | 13 | |
philpem@0 | 14 | Musashi is a Motorola 68000, 68010, 68EC020, and 68020 emulator written in C. |
philpem@0 | 15 | This emulator was written with two goals in mind: portability and speed. |
philpem@0 | 16 | |
philpem@0 | 17 | The emulator is written to ANSI C specifications with the exception that I use |
philpem@0 | 18 | inline functions. This is not compliant to the ANSI spec, but will be |
philpem@0 | 19 | compliant to the ANSI C9X spec. |
philpem@0 | 20 | |
philpem@0 | 21 | It has been successfully running in the MAME project (www.mame.net) for over 2 |
philpem@0 | 22 | years and so has had time to mature. |
philpem@0 | 23 | |
philpem@0 | 24 | |
philpem@0 | 25 | |
philpem@0 | 26 | LICENSE AND COPYRIGHT: |
philpem@0 | 27 | --------------------- |
philpem@0 | 28 | |
philpem@0 | 29 | The Musashi M680x0 emulator is copyright 1998-2001 Karl Stenerud. |
philpem@0 | 30 | |
philpem@0 | 31 | The source code included in this archive is provided AS-IS, free for any |
philpem@0 | 32 | non-commercial purpose. |
philpem@0 | 33 | |
philpem@0 | 34 | If you build a program using this core, please give credit to the author. |
philpem@0 | 35 | |
philpem@0 | 36 | If you wish to use this core in a commercial environment, please contact |
philpem@0 | 37 | the author to discuss commercial licensing. |
philpem@0 | 38 | |
philpem@0 | 39 | |
philpem@0 | 40 | |
philpem@0 | 41 | AVAILABILITY: |
philpem@0 | 42 | ------------ |
philpem@0 | 43 | The latest version of this code can be obtained at: |
philpem@0 | 44 | http://kstenerud.cjb.net |
philpem@0 | 45 | |
philpem@0 | 46 | |
philpem@0 | 47 | |
philpem@0 | 48 | CONTACTING THE AUTHOR: |
philpem@0 | 49 | --------------------- |
philpem@0 | 50 | I can be reached at kstenerud@mame.net |
philpem@0 | 51 | |
philpem@0 | 52 | |
philpem@0 | 53 | |
philpem@0 | 54 | BASIC CONFIGURATION: |
philpem@0 | 55 | ------------------- |
philpem@0 | 56 | The basic configuration will give you a standard 68000 that has sufficient |
philpem@0 | 57 | functionality to work in a primitive environment. |
philpem@0 | 58 | |
philpem@0 | 59 | This setup assumes that you only have 1 device interrupting it, that the |
philpem@0 | 60 | device will always request an autovectored interrupt, and it will always clear |
philpem@0 | 61 | the interrupt before the interrupt service routine finishes (but could |
philpem@0 | 62 | possibly re-assert the interrupt). |
philpem@0 | 63 | You will have only one address space, no tracing, and no instruction prefetch. |
philpem@0 | 64 | |
philpem@0 | 65 | To implement the basic configuration: |
philpem@0 | 66 | |
philpem@0 | 67 | - Open m68kconf.h and verify that the settings for INLINE and DECL_SPEC will |
philpem@0 | 68 | work with your compiler. (They are set for gcc) |
philpem@0 | 69 | |
philpem@0 | 70 | - In your host program, implement the following functions: |
philpem@0 | 71 | unsigned int m68k_read_memory_8(unsigned int address); |
philpem@0 | 72 | unsigned int m68k_read_memory_16(unsigned int address); |
philpem@0 | 73 | unsigned int m68k_read_memory_32(unsigned int address); |
philpem@0 | 74 | void m68k_write_memory_8(unsigned int address, unsigned int value); |
philpem@0 | 75 | void m68k_write_memory_16(unsigned int address, unsigned int value); |
philpem@0 | 76 | void m68k_write_memory_32(unsigned int address, unsigned int value); |
philpem@0 | 77 | |
philpem@0 | 78 | - In your host program, be sure to call m68k_pulse_reset() once before calling |
philpem@0 | 79 | any of the other functions as this initializes the core. |
philpem@0 | 80 | |
philpem@0 | 81 | - Use m68k_execute() to execute instructions and m68k_set_irq() to cause an |
philpem@0 | 82 | interrupt. |
philpem@0 | 83 | |
philpem@0 | 84 | |
philpem@0 | 85 | |
philpem@0 | 86 | ADDING PROPER INTERRUPT HANDLING: |
philpem@0 | 87 | -------------------------------- |
philpem@0 | 88 | The interrupt handling in the basic configuration doesn't emulate the |
philpem@0 | 89 | interrupt acknowledge phase of the CPU and automatically clears an interrupt |
philpem@0 | 90 | request during interrupt processing. |
philpem@0 | 91 | While this works for most systems, you may need more accurate interrupt |
philpem@0 | 92 | handling. |
philpem@0 | 93 | |
philpem@0 | 94 | To add proper interrupt handling: |
philpem@0 | 95 | |
philpem@0 | 96 | - In m68kconf.h, set M68K_EMULATE_INT_ACK to OPT_SPECIFY_HANDLER |
philpem@0 | 97 | |
philpem@0 | 98 | - In m68kconf.h, set M68K_INT_ACK_CALLBACK(A) to your interrupt acknowledge |
philpem@0 | 99 | routine |
philpem@0 | 100 | |
philpem@0 | 101 | - Your interrupt acknowledge routine must return an interrupt vector, |
philpem@0 | 102 | M68K_INT_ACK_AUTOVECTOR, or M68K_INT_ACK_SPURIOUS. most m68k |
philpem@0 | 103 | implementations just use autovectored interrupts. |
philpem@0 | 104 | |
philpem@0 | 105 | - When the interrupting device is satisfied, you must call m68k_set_irq(0) to |
philpem@0 | 106 | remove the interrupt request. |
philpem@0 | 107 | |
philpem@0 | 108 | |
philpem@0 | 109 | |
philpem@0 | 110 | MULTIPLE INTERRUPTS: |
philpem@0 | 111 | ------------------- |
philpem@0 | 112 | The above system will work if you have only one device interrupting the CPU, |
philpem@0 | 113 | but if you have more than one device, you must do a bit more. |
philpem@0 | 114 | |
philpem@0 | 115 | To add multiple interrupts: |
philpem@0 | 116 | |
philpem@0 | 117 | - You must make an interrupt arbitration device that will take the highest |
philpem@0 | 118 | priority interrupt and encode it onto the IRQ pins on the CPU. |
philpem@0 | 119 | |
philpem@0 | 120 | - The interrupt arbitration device should use m68k_set_irq() to set the |
philpem@0 | 121 | highest pending interrupt, or 0 for no interrupts pending. |
philpem@0 | 122 | |
philpem@0 | 123 | |
philpem@0 | 124 | |
philpem@0 | 125 | SEPARATE IMMEDIATE AND PC-RELATIVE READS: |
philpem@0 | 126 | ---------------------------------------- |
philpem@0 | 127 | You can write faster memory access functions if you know whether you are |
philpem@0 | 128 | fetching from ROM or RAM. Immediate reads are always from the program space |
philpem@0 | 129 | (Always in ROM unless it is running self-modifying code). |
philpem@0 | 130 | This will also separate the pc-relative reads, since some systems treat |
philpem@0 | 131 | PROGRAM mode reads and DATA mode reads differently (for program encryption, |
philpem@0 | 132 | for instance). See the section below (ADDRESS SPACE) for an explanation of |
philpem@0 | 133 | PROGRAM and DATA mode. |
philpem@0 | 134 | |
philpem@0 | 135 | To enable separate reads: |
philpem@0 | 136 | |
philpem@0 | 137 | - In m68kconf.h, turn on M68K_SEPARATE_READS. |
philpem@0 | 138 | |
philpem@0 | 139 | - In your host program, implement the following functions: |
philpem@0 | 140 | unsigned int m68k_read_immediate_16(unsigned int address); |
philpem@0 | 141 | unsigned int m68k_read_immediate_32(unsigned int address); |
philpem@0 | 142 | |
philpem@0 | 143 | unsigned int m68k_read_pcrelative_8(unsigned int address); |
philpem@0 | 144 | unsigned int m68k_read_pcrelative_16(unsigned int address); |
philpem@0 | 145 | unsigned int m68k_read_pcrelative_32(unsigned int address); |
philpem@0 | 146 | |
philpem@0 | 147 | - If you need to know the current PC (for banking and such), set |
philpem@0 | 148 | M68K_MONITOR_PC to OPT_SPECIFY_HANDLER, and set M68K_SET_PC_CALLBACK(A) to |
philpem@0 | 149 | your routine. |
philpem@0 | 150 | |
philpem@0 | 151 | |
philpem@0 | 152 | |
philpem@0 | 153 | ADDRESS SPACES: |
philpem@0 | 154 | -------------- |
philpem@0 | 155 | Most systems will only implement one address space, placing ROM at the lower |
philpem@0 | 156 | addresses and RAM at the higher. However, there is the possibility that a |
philpem@0 | 157 | system will implement ROM and RAM in the same address range, but in different |
philpem@0 | 158 | address spaces, or will have different mamory types that require different |
philpem@0 | 159 | handling for the program and the data. |
philpem@0 | 160 | |
philpem@0 | 161 | The 68k accomodates this by allowing different program spaces, the most |
philpem@0 | 162 | important to us being PROGRAM and DATA space. Here is a breakdown of |
philpem@0 | 163 | how information is fetched: |
philpem@0 | 164 | |
philpem@0 | 165 | - All immediate reads are fetched from PROGRAM space. |
philpem@0 | 166 | |
philpem@0 | 167 | - All PC-relative reads are fetched from PROGRAM space. |
philpem@0 | 168 | |
philpem@0 | 169 | - The initial stack pointer and program counter are fetched from PROGRAM space. |
philpem@0 | 170 | |
philpem@0 | 171 | - All other reads (except for those from the moves instruction for 68020) |
philpem@0 | 172 | are fetched from DATA space. |
philpem@0 | 173 | |
philpem@0 | 174 | The m68k deals with this by encoding the requested address space on the |
philpem@0 | 175 | function code pins: |
philpem@0 | 176 | |
philpem@0 | 177 | FC |
philpem@0 | 178 | Address Space 210 |
philpem@0 | 179 | ------------------ --- |
philpem@0 | 180 | USER DATA 001 |
philpem@0 | 181 | USER PROGRAM 010 |
philpem@0 | 182 | SUPERVISOR DATA 101 |
philpem@0 | 183 | SUPERVISOR PROGRAM 110 |
philpem@0 | 184 | CPU SPACE 111 <-- not emulated in this core since we emulate |
philpem@0 | 185 | interrupt acknowledge in another way. |
philpem@0 | 186 | |
philpem@0 | 187 | Problems arise here if you need to emulate this distinction (if, for example, |
philpem@0 | 188 | your ROM and RAM are at the same address range, with RAM and ROM enable |
philpem@0 | 189 | wired to the function code pins). |
philpem@0 | 190 | |
philpem@0 | 191 | There are 2 ways to deal with this situation using Musashi: |
philpem@0 | 192 | |
philpem@0 | 193 | 1. If you only need the distinction between PROGRAM and DATA (the most common), |
philpem@0 | 194 | you can just separate the reads (see the preceeding section). This is the |
philpem@0 | 195 | faster solution. |
philpem@0 | 196 | |
philpem@0 | 197 | 2. You can emulate the function code pins entirely. |
philpem@0 | 198 | |
philpem@0 | 199 | To emulate the function code pins: |
philpem@0 | 200 | |
philpem@0 | 201 | - In m68kconf.h, set M68K_EMULATE_FC to OPT_SPECIFY_HANDLER and set |
philpem@0 | 202 | M68K_SET_FC_CALLBACK(A) to your function code handler function. |
philpem@0 | 203 | |
philpem@0 | 204 | - Your function code handler should select the proper address space for |
philpem@0 | 205 | subsequent calls to m68k_read_xx (and m68k_write_xx for 68010+). |
philpem@0 | 206 | |
philpem@0 | 207 | Note: immediate reads are always done from program space, so technically you |
philpem@0 | 208 | don't need to implement the separate immediate reads, although you could |
philpem@0 | 209 | gain more speed improvements leaving them in and doing some clever |
philpem@0 | 210 | programming. |
philpem@0 | 211 | |
philpem@0 | 212 | |
philpem@0 | 213 | |
philpem@0 | 214 | USING DIFFERENT CPU TYPES: |
philpem@0 | 215 | ------------------------- |
philpem@0 | 216 | The default is to enable only the 68000 cpu type. To change this, change the |
philpem@0 | 217 | settings for M68K_EMULATE_010 etc in m68kconf.h. |
philpem@0 | 218 | |
philpem@0 | 219 | To set the CPU type you want to use: |
philpem@0 | 220 | |
philpem@0 | 221 | - Make sure it is enabled in m68kconf.h. Current switches are: |
philpem@0 | 222 | M68K_EMULATE_010 |
philpem@0 | 223 | M68K_EMULATE_EC020 |
philpem@0 | 224 | M68K_EMULATE_020 |
philpem@0 | 225 | |
philpem@0 | 226 | - In your host program, call m68k_set_cpu_type() and then call |
philpem@0 | 227 | m68k_pulse_reset(). Valid CPU types are: |
philpem@0 | 228 | M68K_CPU_TYPE_68000, |
philpem@0 | 229 | M68K_CPU_TYPE_68010, |
philpem@0 | 230 | M68K_CPU_TYPE_68EC020, |
philpem@0 | 231 | M68K_CPU_TYPE_68020 |
philpem@0 | 232 | |
philpem@0 | 233 | |
philpem@0 | 234 | |
philpem@0 | 235 | CLOCK FREQUENCY: |
philpem@0 | 236 | --------------- |
philpem@0 | 237 | In order to emulate the correct clock frequency, you will have to calculate |
philpem@0 | 238 | how long it takes the emulation to execute a certain number of "cycles" and |
philpem@0 | 239 | vary your calls to m68k_execute() accordingly. |
philpem@0 | 240 | As well, it is a good idea to take away the CPU's timeslice when it writes to |
philpem@0 | 241 | a memory-mapped port in order to give the device it wrote to a chance to |
philpem@0 | 242 | react. |
philpem@0 | 243 | |
philpem@0 | 244 | You can use the functions m68k_cycles_run(), m68k_cycles_remaining(), |
philpem@0 | 245 | m68k_modify_timeslice(), and m68k_end_timeslice() to do this. |
philpem@0 | 246 | Try to use large cycle values in your calls to m68k_execute() since it will |
philpem@0 | 247 | increase throughput. You can always take away the timeslice later. |
philpem@0 | 248 | |
philpem@0 | 249 | |
philpem@0 | 250 | |
philpem@0 | 251 | MORE CORRECT EMULATION: |
philpem@0 | 252 | ---------------------- |
philpem@0 | 253 | You may need to enable these in order to properly emulate some of the more |
philpem@0 | 254 | obscure functions of the m68k: |
philpem@0 | 255 | |
philpem@0 | 256 | - M68K_EMULATE_BKPT_ACK causes the CPU to call a breakpoint handler on a BKPT |
philpem@0 | 257 | instruction |
philpem@0 | 258 | |
philpem@0 | 259 | - M68K_EMULATE_TRACE causes the CPU to generate trace exceptions when the |
philpem@0 | 260 | trace bits are set |
philpem@0 | 261 | |
philpem@0 | 262 | - M68K_EMULATE_RESET causes the CPU to call a reset handler on a RESET |
philpem@0 | 263 | instruction. |
philpem@0 | 264 | |
philpem@0 | 265 | - M68K_EMULATE_PREFETCH emulates the 4-word instruction prefetch that is part |
philpem@0 | 266 | of the 68000/68010 (needed for Amiga emulation). |
philpem@0 | 267 | |
philpem@0 | 268 | - call m68k_pulse_halt() to emulate the HALT pin. |
philpem@0 | 269 | |
philpem@0 | 270 | |
philpem@0 | 271 | |
philpem@0 | 272 | CONVENIENCE FUNCTIONS: |
philpem@0 | 273 | --------------------- |
philpem@0 | 274 | These are in here for programmer convenience: |
philpem@0 | 275 | |
philpem@0 | 276 | - M68K_INSTRUCTION_HOOK lets you call a handler before each instruction. |
philpem@0 | 277 | |
philpem@0 | 278 | - M68K_LOG_ENABLE and M68K_LOG_1010_1111 lets you log illegal and A/F-line |
philpem@0 | 279 | instructions. |
philpem@0 | 280 | |
philpem@0 | 281 | |
philpem@0 | 282 | |
philpem@0 | 283 | MULTIPLE CPU EMULATION: |
philpem@0 | 284 | ---------------------- |
philpem@0 | 285 | The default is to use only one CPU. To use more than one CPU in this core, |
philpem@0 | 286 | there are some things to keep in mind: |
philpem@0 | 287 | |
philpem@0 | 288 | - To have different cpus call different functions, use OPT_ON instead of |
philpem@0 | 289 | OPT_SPECIFY_HANDLER, and use the m68k_set_xxx_callback() functions to set |
philpem@0 | 290 | your callback handlers on a per-cpu basis. |
philpem@0 | 291 | |
philpem@0 | 292 | - Be sure to call set_cpu_type() for each CPU you use. |
philpem@0 | 293 | |
philpem@0 | 294 | - Use m68k_set_context() and m68k_get_context() to switch to another CPU. |
philpem@0 | 295 | |
philpem@0 | 296 | |
philpem@0 | 297 | |
philpem@0 | 298 | LOAD AND SAVE CPU CONTEXTS FROM DISK: |
philpem@0 | 299 | ------------------------------------ |
philpem@0 | 300 | You can use them68k_load_context() and m68k_save_context() functions to load |
philpem@0 | 301 | and save the CPU state to disk. |
philpem@0 | 302 | |
philpem@0 | 303 | |
philpem@0 | 304 | |
philpem@0 | 305 | GET/SET INFORMATION FROM THE CPU: |
philpem@0 | 306 | -------------------------------- |
philpem@0 | 307 | You can use m68k_get_reg() and m68k_set_reg() to gain access to the internals |
philpem@0 | 308 | of the CPU. |
philpem@0 | 309 | |
philpem@0 | 310 | |
philpem@0 | 311 | |
philpem@0 | 312 | EXAMPLE: |
philpem@0 | 313 | ------- |
philpem@0 | 314 | |
philpem@0 | 315 | I have included a file example.zip that contains a full example. |