From d9d77995ec88700f438b3638df179a014bf4f6b3 Mon Sep 17 00:00:00 2001 From: notaz Date: Sun, 13 Feb 2011 17:49:17 +0000 Subject: [PATCH] Commit my changes, version set to 0.099 Compile-tested under Linux, VisualStudio project might be not up-to-date. --- Cyclone/Cyclone.h | 92 ++- Cyclone/Cyclone.txt | 522 ++++++++++++----- Cyclone/Ea.cpp | 361 +++++++++--- Cyclone/Main.cpp | 1275 +++++++++++++++++++++++++++++++++++++----- Cyclone/Makefile | 7 +- Cyclone/OpAny.cpp | 210 +++++-- Cyclone/OpArith.cpp | 662 ++++++++++++++++------ Cyclone/OpBranch.cpp | 441 ++++++++++----- Cyclone/OpLogic.cpp | 489 +++++++++++----- Cyclone/OpMove.cpp | 600 ++++++++++++++------ Cyclone/app.h | 70 ++- Cyclone/config.h | 182 ++++++ 12 files changed, 3789 insertions(+), 1122 deletions(-) create mode 100644 Cyclone/config.h diff --git a/Cyclone/Cyclone.h b/Cyclone/Cyclone.h index 77ea268..c2c27d8 100644 --- a/Cyclone/Cyclone.h +++ b/Cyclone/Cyclone.h @@ -1,13 +1,18 @@ // Cyclone 68000 Emulator - Header File -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + +#ifndef __CYCLONE_H__ +#define __CYCLONE_H__ + #ifdef __cplusplus extern "C" { #endif @@ -16,32 +21,79 @@ extern int CycloneVer; // Version number of library struct Cyclone { - unsigned int d[8]; // [r7,#0x00] - unsigned int a[8]; // [r7,#0x20] - unsigned int pc; // [r7,#0x40] Memory Base+PC - unsigned char srh; // [r7,#0x44] Status Register high (T_S__III) - unsigned char xc; // [r7,#0x45] Extend flag (____??X?) - unsigned char flags; // [r7,#0x46] Flags (ARM order: ____NZCV) - unsigned char irq; // [r7,#0x47] IRQ level - unsigned int osp; // [r7,#0x48] Other Stack Pointer (USP/SSP) - unsigned int vector; // [r7,#0x50] IRQ vector (temporary) - int pad1[3]; - int cycles; // [r7,#0x5c] - int membase; // [r7,#0x60] Memory Base (ARM address minus 68000 address) - unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] - Called to recalc Memory Base+pc - unsigned char (*read8 )(unsigned int a); // [r7,#0x68] - unsigned short (*read16 )(unsigned int a); // [r7,#0x6c] - unsigned int (*read32 )(unsigned int a); // [r7,#0x70] + unsigned int d[8]; // [r7,#0x00] + unsigned int a[8]; // [r7,#0x20] + unsigned int pc; // [r7,#0x40] Memory Base (.membase) + 68k PC + unsigned char srh; // [r7,#0x44] Status Register high (T_S__III) + unsigned char unused; // [r7,#0x45] Unused + unsigned char flags; // [r7,#0x46] Flags (ARM order: ____NZCV) [68k order is XNZVC] + unsigned char irq; // [r7,#0x47] IRQ level + unsigned int osp; // [r7,#0x48] Other Stack Pointer (USP/SSP) + unsigned int xc; // [r7,#0x4c] Extend flag (bit29: ??X? _) + unsigned int prev_pc; // [r7,#0x50] Set to start address of currently executed opcode + 2 (if enabled in config.h) + unsigned int jumptab; // [r7,#0x54] Jump table pointer + int state_flags; // [r7,#0x58] bit: 0: stopped state, 1: trace state, 2: activity bit, 3: addr error, 4: fatal halt + int cycles; // [r7,#0x5c] Number of cycles to execute - 1. Updates to cycles left after CycloneRun() + int membase; // [r7,#0x60] Memory Base (ARM address minus 68000 address) + unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] called to recalc Memory Base+pc + unsigned int (*read8 )(unsigned int a); // [r7,#0x68] + unsigned int (*read16 )(unsigned int a); // [r7,#0x6c] + unsigned int (*read32 )(unsigned int a); // [r7,#0x70] void (*write8 )(unsigned int a,unsigned char d); // [r7,#0x74] void (*write16)(unsigned int a,unsigned short d); // [r7,#0x78] void (*write32)(unsigned int a,unsigned int d); // [r7,#0x7c] - unsigned char (*fetch8 )(unsigned int a); // [r7,#0x80] - unsigned short (*fetch16)(unsigned int a); // [r7,#0x84] - unsigned int (*fetch32)(unsigned int a); // [r7,#0x88] + unsigned int (*fetch8 )(unsigned int a); // [r7,#0x80] + unsigned int (*fetch16)(unsigned int a); // [r7,#0x84] + unsigned int (*fetch32)(unsigned int a); // [r7,#0x88] + int (*IrqCallback)(int int_level); // [r7,#0x8c] optional irq callback function, see config.h + void (*ResetCallback)(void); // [r7,#0x90] if enabled in config.h, calls this whenever RESET opcode is encountered. + int (*UnrecognizedCallback)(void); // [r7,#0x94] if enabled in config.h, calls this whenever unrecognized opcode is encountered. + unsigned int internal[6]; // [r7,#0x98] reserved for internal use, do not change. }; +// Initialize. Used only if Cyclone was compiled with compressed jumptable, see config.h +void CycloneInit(void); + +// Reset +void CycloneReset(struct Cyclone *pcy); + +// Run cyclone. Cycles should be specified in context (pcy->cycles) void CycloneRun(struct Cyclone *pcy); +// Utility functions to get and set SR +void CycloneSetSr(struct Cyclone *pcy, unsigned int sr); +unsigned int CycloneGetSr(const struct Cyclone *pcy); + +// Generates irq exception if needed (if pcy->irq > mask). +// Returns cycles used for exception if it was generated, 0 otherwise. +int CycloneFlushIrq(struct Cyclone *pcy); + +// Functions for saving and restoring state. +// CycloneUnpack() uses checkpc(), so it must be initialized. +// save_buffer must point to buffer of 128 (0x80) bytes of size. +void CyclonePack(const struct Cyclone *pcy, void *save_buffer); +void CycloneUnpack(struct Cyclone *pcy, const void *save_buffer); + +// genesis: if 1, switch to normal TAS handlers +void CycloneSetRealTAS(int use_real); + + +// These values are special return values for IrqCallback. + +// Causes an interrupt autovector (0x18 + interrupt level) to be taken. +// This happens in a real 68K if VPA or AVEC is asserted during an interrupt +// acknowledge cycle instead of DTACK (the most common situation). +#define CYCLONE_INT_ACK_AUTOVECTOR -1 + +// Causes the spurious interrupt vector (0x18) to be taken +// This happens in a real 68K if BERR is asserted during the interrupt +// acknowledge cycle (i.e. no devices responded to the acknowledge). +#define CYCLONE_INT_ACK_SPURIOUS -2 + + #ifdef __cplusplus } // End of extern "C" #endif + +#endif // __CYCLONE_H__ + diff --git a/Cyclone/Cyclone.txt b/Cyclone/Cyclone.txt index 0b0d062..c305f27 100644 --- a/Cyclone/Cyclone.txt +++ b/Cyclone/Cyclone.txt @@ -11,141 +11,82 @@ ___________________________________________________________________________ + Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) + Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) -This code is licensed under the GNU General Public License version 2.0 and the MAME License. -You can choose the license that has the most advantages for you. + This code is licensed under the GNU General Public License version 2.0 and the MAME License. + You can choose the license that has the most advantages for you. + + Homepage: http://code.google.com/p/cyclone68000/ ___________________________________________________________________________ -What is it? ------------ +About +----- Cyclone 68000 is an emulator for the 68000 microprocessor, written in ARM 32-bit assembly. It is aimed at chips such as ARM7 and ARM9 cores, StrongARM and XScale, to interpret 68000 -code as fast as possible. - -Flags are mapped onto ARM flags whenever possible, which speeds up the processing of opcode. - -Developers: ------------ - -Dave / FinalDave: emudave(atsymbol)gmail.com - - -What's New ----------- -v0.069 - + Added SBCD and the flags for ABCD/SBCD. Score and time now works in games such as - Rolling Thunder 2, Ghouls 'N Ghosts - + Fixed a problem with addx and subx with 8-bit and 16-bit values. - Ghouls 'N' Ghosts now works! - -v0.068 - + Added ABCD opcode (Streets of Rage works now!) - -v0.067 - + Added dbCC (After Burner) - + Added asr EA (Sonic 1 Boss/Labyrinth Zone) - + Added andi/ori/eori ccr (Altered Beast) - + Added trap (After Burner) - + Added special case for move.b (a7)+ and -(a7), stepping by 2 - After Burner is playable! Eternal Champions shows more - + Fixed lsr.b/w zero flag (Ghostbusters) - Rolling Thunder 2 now works! - + Fixed N flag for .b and .w arithmetic. Golden Axe works! - -v0.066 - + Fixed a stupid typo for exg (orr r10,r10, not orr r10,r8), which caused alignment - crashes on Strider - -v0.065 - + Fixed a problem with immediate values - they weren't being shifted up correctly for some - opcodes. Spiderman works, After Burner shows a bit of graphics. - + Fixed a problem with EA:"110nnn" extension word. 32-bit offsets were being decoded as 8-bit - offsets by mistake. Castlevania Bloodlines seems fine now. - + Added exg opcode - + Fixed asr opcode (Sonic jumping left is fixed) - + Fixed a problem with the carry bit in rol.b (Marble Madness) - -v0.064 - + Added rtr - + Fixed addq/subq.l (all An opcodes are 32-bit) (Road Rash) - + Fixed various little timings - -v0.063 - + Added link/unlk opcodes - + Fixed various little timings - + Fixed a problem with dbCC opcode being emitted at set opcodes - + Improved long register access, the EA fetch now does ldr r0,[r7,r0,lsl #2] whenever - possible, saving 1 or 2 cycles on many opcodes, which should give a nice speed up. - + May have fixed N flag on ext opcode? - + Added dasm for link opcode. +code as fast as possible. It can emulate all 68000 instructions quite accurately, instruction +timing was synchronized with MAME's Musashi. Most 68k features are emulated (trace mode, +address errors), but prefetch is not emulated. -v0.062 - * I was a bit too keen with the Arithmetic opcodes! Some of them should have been abcd, - exg and addx. Removed the incorrect opcodes, pending re-adding them as abcd, exg and addx. - + Changed unknown opcodes to act as nops. - Not very technical, but fun - a few more games show more graphics ;) -v0.060 - + Fixed divu (EA intro) - + Added sf (set false) opcode - SOR2 - * Todo: pea/link/unlk opcodes +How to Compile +-------------- -v0.059: Added remainder to divide opcodes. +Like Starscream and A68K, Cyclone uses a 'Core Creator' program which calculates and outputs +all possible 68000 Opcodes and a jump table into file called Cyclone.s or Cyclone.asm. +Only Cyclone.h and the mentioned .s or .asm file will be needed for your project, other files +are here to produce or test it. +First unzip "Cyclone.zip" into a "Cyclone" directory. The next thing to do is to edit config.h +file to tune Cyclone for your project. There are lots of options in config.h, but all of them +are documented and have defaults. You should set a define value to 1 to enable option, and +to 0 to disable. -ARM Register Usage ------------------- +After you are done with config.h, save it and compile Cyclone. If you are using Linux, Cygwin, +mingw or similar, you can simply cd to Cyclone/proj and type "make". If you are under Windows +and have Visual Studio installed, you can import cyclone.dsp in the proj/ directory and compile +it from there (this will produce cyclone.exe which you will have to run to get .s or .asm). +You can also use Microsoft command line compile tools by entering Cyclone/proj directory and +typing "nmake -f Makefile.win". Note that this step is done only to produce .s or .asm, and it +is done using native tools on your PC (not using cross-compiler or similar). -See source code for up to date of register usage, however a summary is here: +The .s file is meant to be compiled with GNU assembler, and .asm with ARMASM.EXE +(the Microsoft ARM assembler). Once you have the file, you can add it to your +Makefile/project/whatever. - r0-3: Temporary registers - r4 : Current PC + Memory Base (i.e. pointer to next opcode) - r5 : Cycles remaining - r6 : Pointer to Opcode Jump table - r7 : Pointer to Cpu Context - r8 : Current Opcode - r9 : Flags (NZCV) in highest four bits - (r10 : Temporary source value or Memory Base) - (r11 : Temporary register) +Adding to your project +---------------------- -How to Compile --------------- - -Like Starscream and A68K, Cyclone uses a 'Core Creator' program which calculates and outputs -all possible 68000 Opcodes and a jump table into files called Cyclone.s and .asm -It then assembles these files into Cyclone.o and .obj - -Cyclone.o is the GCC assembled version and Cyclone.obj is the Microsoft assembled version. +Compiling the .s or .asm (from previous step) for your target platform may require custom +build rules in your Makefile/project. -First unzip "Cyclone.zip" into a "Cyclone" directory. -If you are compiling for Windows CE, find ARMASM.EXE (the Microsoft ARM assembler) and -put it in the directory as well or put it on your path. +If you use some gcc-based toolchain, you will need to add Cyclone.o to an object list in +the Makefile. GNU make will use "as" to build Cyclone.o from Cyclone.s by default, so +you may need to define correct cross-assembler by setting AS variable like this: -Open up Cyclone.dsw in Visual Studio 6.0, compile and run the project. -Cyclone.obj and Cyclone.o will be created. +AS = arm-linux-as -Compiling without Visual C++ ----------------------------- -If you aren't using Visual C++, it still shouldn't be too hard to compile, just get a C compiler, -compile all the CPPs and C file, link them into an EXE, and run the exe. +This might be different in your case, basically it should be same prefix as for gcc. +You may also need to specify floating point type in your assembler flags for Cyclone.o +to link properly. This is done like this: - e.g. gcc Main.cpp OpAny.cpp OpArith.cpp OpBranch.cpp OpLogic.cpp OpMove.cpp Disa.c - Main.exe +ASFLAGS = -mfloat-abi=soft +Note that Cyclone does not use floating points, this is just to make the linker happy. -Adding to your project ----------------------- -To add Cyclone to you project, add Cyclone.o or obj, and include Cyclone.h -There is one structure: 'struct Cyclone', and one function: CycloneRun +If you are using Visual Studio, you may need to add "custom build step", which creates +Cyclone.obj from Cyclone.asm (asmasm.exe Cyclone.asm). Alternatively you can create +Cyclone.obj by using armasm once and then just add it to you project. Don't worry if this seem very minimal - its all you need to run as many 68000s as you want. It works with both C and C++. + Byteswapped Memory ------------------ @@ -153,7 +94,7 @@ If you have used Starscream, A68K or Turbo68K or similar emulators you'll be fam Any memory which the 68000 can access directly must be have every two bytes swapped around. This is to speed up 16-bit memory accesses, because the 68000 has Big-Endian memory -and ARM has Little-Endian memory. +and ARM has Little-Endian memory (in most cases). Now you may think you only technically have to byteswap ROM, not RAM, because 16-bit RAM reads go through a memory handler and you could just return (mem[a]<<8) | mem[a+1]. @@ -166,8 +107,8 @@ It's also faster for the memory handlers, because you can do this: return *(unsigned short *)(mem+a) -Declaring a Memory handlers ---------------------------- +Declaring Memory handlers +------------------------- Before you can reset or execute 68000 opcodes you must first set up a set of memory handlers. There are 7 functions you have to set up per CPU, like this: @@ -195,16 +136,17 @@ For example you might set MyRead8 like this: The other 5 read/write functions are similar. I'll describe the CheckPc function later on. + Declaring a CPU Context ----------------------- -To declare a CPU simple declare a struct Cyclone in your code. For example to declare -two 68000s: +To declare a CPU simple declare a struct Cyclone in your code (don't forget to include Cyclone.h). +For example to declare two 68000s: struct Cyclone MyCpu; struct Cyclone MyCpu2; -It's probably a good idea to initialise the memory to zero: +It's probably a good idea to initialize the memory to zero: memset(&MyCpu, 0,sizeof(MyCpu)); memset(&MyCpu2,0,sizeof(MyCpu2)); @@ -231,7 +173,9 @@ point them at the read handlers: If this is the case, you can set up different functions for fetch reads. Generally though you don't need to. ) -Now you are nearly ready to reset the 68000, except you need one more function: checkpc(). +Now you are nearly ready to reset the 68000, except a few more functions, +one of them is: checkpc(). + The checkpc() function ---------------------- @@ -242,7 +186,7 @@ For example if your Rom image was at 0x3000000 and the program counter was $206, Cyclone's program counter would be 0x3000206. The difference between an ARM address and a 68000 address is also stored in a variable called -'membase'. In the above example it's 0x3000000. To retrieve the real PC, Cyclone just +'membase'. In the above example it's 0x3000000. To retrieve the real 68k PC, Cyclone just subtracts 'membase'. When a long jump happens, Cyclone calls checkpc(). If the PC is in a different bank, @@ -261,20 +205,32 @@ static int MyCheckPc(unsigned int pc) Notice that the membase is always ARM address minus 68000 address. The above example doesn't consider mirrored ram, but for an example of what to do see -PicoDrive (in Memory.cpp). +PicoDrive (in Memory.c). + +The exact cases when checkpc() is called can be configured in config.h. + + +Initialization +-------------- + +Add a call to CycloneInit(). This is really only needed to be called once at startup +if you enabled COMPRESS_JUMPTABLE in config.h, but you can add this in any case, +it won't hurt. Almost there - Reset the 68000! ------------------------------- -Next we need to Reset the 68000 to get the initial Program Counter and Stack Pointer. This -is obtained from addresses 000000 and 000004. +Cyclone doesn't provide a reset function, so next we need to Reset the 68000 to get +the initial Program Counter and Stack Pointer. This is obtained from addresses +000000 and 000004. Here is code which resets the 68000 (using your memory handlers): + MyCpu.state_flags=0; // Go to default state (not stopped, halted, etc.) MyCpu.srh=0x27; // Set supervisor mode MyCpu.a[7]=MyCpu.read32(0); // Get Stack Pointer - MyCpu.membase=0; + MyCpu.membase=0; // Will be set by checkpc() MyCpu.pc=MyCpu.checkpc(MyCpu.read32(4)); // Get Program Counter And that's ready to go. @@ -291,7 +247,7 @@ e.g.: MyCpu.cycles=1000; CycloneRun(&MyCpu); For each opcode, the number of cycles it took is subtracted and the function returns when -it reaches 0. +it reaches negative number. The result is stored back to MyCpu.cycles. e.g. // Execute one instruction on the 68000: @@ -325,42 +281,48 @@ e.g: Note that the interrupt is not actually processed until the next call to CycloneRun, and the interrupt may not be taken until the 68000 interrupt mask is changed to allow it. -( The IRQ isn't checked on exiting from a memory handler: I don't think this will cause - me any trouble because I've never needed to trigger an interrupt from a memory handler, - but if someone needs to, let me know...) +If you need to force interrupt processing, you can use CycloneFlushIrq() function. +It is the same as doing -Accessing Cycle Counter ------------------------ +MyCpu.cycles=0; CycloneRun(&MyCpu); + +but is better optimized and doesn't update .cycles (returns them instead). +This function can't be used from memory handlers and has no effect if interrupt is masked. -The cycle counter in the Cyclone structure is not, by default, updated before -calling a memory handler, only at the end of an execution. +The IRQ isn't checked on exiting from a memory handler. If you need to cause interrupt +check immediately, you should change cycle counter to 0 to cause a return from CycloneRun(), +and then call CycloneRun() again or just call CycloneFlushIrq(). Note that you need to +enable MEMHANDLERS_CHANGE_CYCLES in config.h for this to work. -If you do need to read the cycle counter inside memory handlers, there is a -bitfield called 'Debug' in Cyclone/Main.cpp. -You can try setting Debug to 1 and then making the Cyclone library. -This will add extra instructions so Cyclone writes register r5 back into the structure. +If you need to do something during the interrupt acknowledge (the moment when interrupt +is taken), you can set USE_INT_ACK_CALLBACK in config.h and specify IrqCallback function. +This function should update the IRQ level (.irq variable in context) and return the +interrupt vector number. But for most cases it should return special constant +CYCLONE_INT_ACK_AUTOVECTOR so that Cyclone uses autovectors, which is what most real +systems were doing. Another less commonly used option is to return CYCLONE_INT_ACK_SPURIOUS +for spurious interrupt. -If you need to *modify* cycles in a memory handler, set Debug to 3, this will read back -the cycle counter as well. Accessing Program Counter and registers --------------------------------------- -You can read Cyclone's registers directly from the structure at any time (as far as I know). +You can read most Cyclone's registers directly from the structure at any time. +However, the PC value, CCR and cycle counter are cached in ARM registers and can't +be accessed from memory handlers by default. They are written back and can be +accessed after execution. + +But if you need to access the mentioned registers during execution, you can set +MEMHANDLERS_NEED_* and MEMHANDLERS_CHANGE_* options in config.h The Program Counter, should you need to read or write it, is stored with membase added on. So use this formula to calculate the real 68000 program counter: pc = MyCpu.pc - MyCpu.membase; -The program counter is stored in r4 during execution, and isn't written back to the -structure until the end of execution, which means you can't read normally real it from -a memory handler. -However you can try setting Debug to 4 and then making the Cyclone library, this will -write back r4 to the structure. - -You can't access the flags from a handler either. I can't imagine why anyone would particularly -need to do this, but if you do e-mail me and I'll add another bit to 'Debug' ;) +For performance reasons Cyclone keeps the status register split into .srh +(status register "high" supervisor byte), .xc for the X flag, and .flags for remaining +CCR flags (in ARM order). To easily read/write the status register as normal 68k +16bit SR register, use CycloneGetSr() and CycloneSetSr() utility functions. Emulating more than one CPU @@ -379,12 +341,286 @@ e.g. MyCpu2.cycles=1000; CycloneRun(&MyCpu2); +Quick API reference +------------------- + +void CycloneInit(void); + Initializes Cyclone. Must be called if the jumptable is compressed, + doesn't matter otherwise. + +void CycloneRun(struct Cyclone *pcy); + Runs cyclone for pcy->cycles. Writes amount of cycles left back to + pcy->cycles (always negative). + +unsigned int CycloneGetSr(const struct Cyclone *pcy); + Reads status register in internal form from pcy, converts to standard 68k SR and returns it. + +void CycloneSetSr(struct Cyclone *pcy, unsigned int sr); + Takes standard 68k status register (sr), and updates Cyclone context with it. + +int CycloneFlushIrq(struct Cyclone *pcy); + If .irq is greater than IRQ mask in SR, or it is equal to 7 (NMI), processes interrupt + exception and returns number of cycles used. Otherwise, does nothing and returns 0. + +void CyclonePack(const struct Cyclone *pcy, void *save_buffer); + Writes Cyclone state to save_buffer. This allows to avoid all the trouble figuring what + actually needs to be saved from the Cyclone structure, as saving whole struct Cyclone + to a file will also save various pointers, which may become invalid after your program + is restarted, so simply reloading the structure will cause a crash. save_buffer size + should be 128 bytes (now it is really using less, but this allows future expansion). + +void CycloneUnpack(struct Cyclone *pcy, const void *save_buffer); + Reloads Cyclone state from save_buffer, which was previously saved by CyclonePack(). + This function uses checkpc() callback to rebase the PC, so .checkpc must be initialized + before calling it. + +Callbacks: + +.checkpc +unsigned int (*checkpc)(unsigned int pc); + This function is called when PC changes are performed in 68k code or because of exceptions. + It is passed ARM pointer and should return ARM pointer casted to int. It must also update + .membase if needed. See "The checkpc() function" section above. + +unsigned int (*read8 )(unsigned int a); +unsigned int (*read16 )(unsigned int a); +unsigned int (*read32 )(unsigned int a); + These are the read memory handler callbacks. They are called when 68k code reads from memory. + The parameter is a 68k address in data space, return value is a data value read. Data value + doesn't have to be masked to 8 or 16 bits for read8 or read16, Cyclone will do that itself + if needed. + +unsigned int (*fetch8 )(unsigned int a); +unsigned int (*fetch16)(unsigned int a); +unsigned int (*fetch32)(unsigned int a); + Same as above, but these are reads from program space (PC relative reads mostly). + +void (*write8 )(unsigned int a,unsigned char d); +void (*write16)(unsigned int a,unsigned short d); +void (*write32)(unsigned int a,unsigned int d); + These are called when 68k code writes to data space. d is the data value. + +int (*IrqCallback)(int int_level); + This function is called when Cyclone acknowledges an interrupt. The parameter is the IRQ + level being acknowledged, and return value is exception vector to use, or one of these special + values: CYCLONE_INT_ACK_AUTOVECTOR or CYCLONE_INT_ACK_SPURIOUS. Can be disabled in config.h. + See "Interrupts" section for more information. + +void (*ResetCallback)(void); + Cyclone will call this function if it encounters RESET 68k instruction. + Can be disabled in config.h. + +int (*UnrecognizedCallback)(void); + Cyclone will call this function if it encounters illegal instructions (including A-line and + F-line ones). Can be tuned / disabled in config.h. + + +Function codes +-------------- + +Cyclone doesn't pass function codes to it's memory handlers, but they can be calculated: +FC2: just use supervisor state bit from status register (eg. (MyCpu.srh & 0x20) >> 5) +FC1: if we are in fetch* function, then 1, else 0. +FC0: if we are in read* or write*, then 1, else 0. +CPU state (all FC bits set) is active in IrqCallback function. + + +References +---------- + +These documents were used while writing Cyclone and should be useful for those who want to +understand deeper how the 68000 works. + +MOTOROLA M68000 FAMILY Programmer's Reference Manual +common name: 68kPM.pdf + +M68000 8-/16-/32-Bit Microprocessors User's Manual +common name: MC68000UM.pdf + +68000 Undocumented Behavior Notes by Bart Trzynadlowski +http://www.trzy.org/files/68knotes.txt + +Instruction prefetch on the Motorola 68000 processor by Jorge Cwik +http://pasti.fxatari.com/68kdocs/68kPrefetch.html + + +ARM Register Usage +------------------ + +See source code for up to date of register usage, however a summary is here: + + r0-3: Temporary registers + r4 : Current PC + Memory Base (i.e. pointer to next opcode) + r5 : Cycles remaining + r6 : Pointer to Opcode Jump table + r7 : Pointer to Cpu Context + r8 : Current Opcode + r10 : Flags (NZCV) in highest four bits + (r11 : Temporary register) + +Flags are mapped onto ARM flags whenever possible, which speeds up the processing of opcode. +r9 is not used intentionally, because AAPCS defines it as "platform register", so it's +reserved in some systems. + + Thanks to... ------------ * All the previous code-generating assembler cpu core guys! Who are iirc... Neill Corlett, Neil Bradley, Mike Coates, Darren Olafson - and Bart Trzynadlowski + Karl Stenerud and Bart Trzynadlowski * Charles Macdonald, for researching just about every console ever * MameDev+FBA, for keeping on going and going and going + + +What's New +---------- +v0.0099 + * Cyclone no longer uses r9, because AAPCS defines it as "platform register", + so it's reserved in some systems. + * Made SPLIT_MOVEL_PD to affect MOVEM too. + +v0.0088 + - Reduced amount of code in opcode handlers by ~23% by doing the following: + - Removed duplicate opcode handlers + - Optimized code to use less ARM instructions + - Merged some duplicate handler endings + + Cyclone now does better job avoiding pipeline interlocks. + + Replaced incorrect handler of DBT with proper one. + + Changed "MOVEA (An)+ An" behavior. + + Fixed flag behavior of ROXR, ASL, LSR and NBCD in certain situations. + Hopefully got them right now. + + Cyclone no longer sets most significant bits while pushing PC to stack. + Amiga Kickstart depends on this. + + Added optional trace mode emulation. + + Added optional address error emulation. + + Additional functionality added for MAME and other ports (see config.h). + + Added return value for IrqCallback to make it suitable for emulating devices which + pass the vector number during interrupt acknowledge cycle. For usual autovector + processing this function must return CYCLONE_INT_ACK_AUTOVECTOR, so those who are + upgrading must add "return CYCLONE_INT_ACK_AUTOVECTOR;" to their IrqCallback functions. + * Updated documentation. + +v0.0086 + + Cyclone now can be customized to better suit your project, see config.h . + + Added an option to compress the jumptable at compile-time. Must call CycloneInit() + at runtime to decompress it if enabled (see config.h). + + Added missing CHK opcode handler (used by SeaQuest DSV). + + Added missing TAS opcode handler (Gargoyles,Bubba N Stix,...). As in real genesis, + memory write-back phase is ignored (but can be enabled in config.h if needed). + + Added missing NBCD and TRAPV opcode handlers. + + Added missing addressing mode for CMP/EOR. + + Added some minor optimizations. + - Removed 216 handlers for 2927 opcodes which were generated for invalid addressing modes. + + Fixed flags for ASL, NEG, NEGX, DIVU, ADDX, SUBX, ROXR. + + Bugs fixed in MOVEP, LINK, ADDQ, DIVS handlers. + * Undocumented flags for CHK, ABCD, SBCD and NBCD are now emulated the same way as in Musashi. + + Added Uninitialized Interrupt emulation. + + Altered timing for about half of opcodes to match Musashi's. + +v0.0082 + + Change cyclone to clear cycles before returning when halted + + Added Irq call back function. This allows emulators to be notified + when cyclone has taken an interrupt allowing them to set internal flags + which can help fix timing problems. + +v0.0081 + + .asm version was broken and did not compile with armasm. Fixed. + + Finished implementing Stop opcode. Now it really stops the processor. + +v0.0080 + + Added real cmpm opcode, it was using eor handler before this. + Fixes Dune and Sensible Soccer. + +v0.0078 + note: these bugs were actually found Reesy, I reimplemented these by + using his changelog as a guide. + + Fixed a problem with divu which was using long divisor instead of word. + Fixes gear switching in Top Gear 2. + + Fixed btst opcode, The bit to test should shifted a max of 31 or 7 + depending on if a register or memory location is being tested. + + Fixed abcd,sbcd. They did bad decimal correction on invalid BCD numbers + Score counters in Streets of Rage level end work now. + + Changed flag handling of abcd,sbcd,addx,subx,asl,lsl,... + Some ops did not have flag handling at all. + Some ops must not change Z flag when result is zero, but they did. + Shift ops must not change X if shift count is zero, but they did. + There are probably still some flag problems left. + + Patially implemented Stop and Reset opcodes - Fixes Thunderforce IV + +v0.0075 + + Added missing displacement addressing mode for movem (Fantastic Dizzy) + + Added OSP <-> A7 swapping code in opcodes, which change privilege mode + + Implemented privilege violation, line emulator and divide by zero exceptions + + Added negx opcode (Shining Force works!) + + Added overflow detection for divs/divu + +v0.0072 + note: I could only get v0.0069 cyclone, so I had to implement these myself using Dave's + changelog as a guide. + + Fixed a problem with divs - remainder should be negative when divident is negative + + Added movep opcode (Sonic 3 works) + + Fixed a problem with DBcc incorrectly decrementing if the condition is true (Shadow of the Beast) + +v0.0069 + + Added SBCD and the flags for ABCD/SBCD. Score and time now works in games such as + Rolling Thunder 2, Ghouls 'N Ghosts + + Fixed a problem with addx and subx with 8-bit and 16-bit values. + Ghouls 'N' Ghosts now works! + +v0.0068 + + Added ABCD opcode (Streets of Rage works now!) + +v0.0067 + + Added dbCC (After Burner) + + Added asr EA (Sonic 1 Boss/Labyrinth Zone) + + Added andi/ori/eori ccr (Altered Beast) + + Added trap (After Burner) + + Added special case for move.b (a7)+ and -(a7), stepping by 2 + After Burner is playable! Eternal Champions shows more + + Fixed lsr.b/w zero flag (Ghostbusters) + Rolling Thunder 2 now works! + + Fixed N flag for .b and .w arithmetic. Golden Axe works! + +v0.0066 + + Fixed a stupid typo for exg (orr r10,r10, not orr r10,r8), which caused alignment + crashes on Strider + +v0.0065 + + Fixed a problem with immediate values - they weren't being shifted up correctly for some + opcodes. Spiderman works, After Burner shows a bit of graphics. + + Fixed a problem with EA:"110nnn" extension word. 32-bit offsets were being decoded as 8-bit + offsets by mistake. Castlevania Bloodlines seems fine now. + + Added exg opcode + + Fixed asr opcode (Sonic jumping left is fixed) + + Fixed a problem with the carry bit in rol.b (Marble Madness) + +v0.0064 + + Added rtr + + Fixed addq/subq.l (all An opcodes are 32-bit) (Road Rash) + + Fixed various little timings + +v0.0063 + + Added link/unlk opcodes + + Fixed various little timings + + Fixed a problem with dbCC opcode being emitted at set opcodes + + Improved long register access, the EA fetch now does ldr r0,[r7,r0,lsl #2] whenever + possible, saving 1 or 2 cycles on many opcodes, which should give a nice speed up. + + May have fixed N flag on ext opcode? + + Added dasm for link opcode. + +v0.0062 + * I was a bit too keen with the Arithmetic opcodes! Some of them should have been abcd, + exg and addx. Removed the incorrect opcodes, pending re-adding them as abcd, exg and addx. + + Changed unknown opcodes to act as nops. + Not very technical, but fun - a few more games show more graphics ;) + +v0.0060 + + Fixed divu (EA intro) + + Added sf (set false) opcode - SOR2 + * Todo: pea/link/unlk opcodes + +v0.0059: Added remainder to divide opcodes. + + diff --git a/Cyclone/Ea.cpp b/Cyclone/Ea.cpp index 3b39e8e..77c282b 100644 --- a/Cyclone/Ea.cpp +++ b/Cyclone/Ea.cpp @@ -1,20 +1,109 @@ // This file is part of the Cyclone 68000 Emulator -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + #include "app.h" +int earead_check_addrerr = 1, eawrite_check_addrerr = 0; + +// some ops use non-standard cycle counts for EAs, so are listed here. +// all constants borrowed from the MUSASHI core by Karl Stenerud. + +/* Extra cycles for JMP instruction (000, 010) */ +int g_jmp_cycle_table[8] = +{ + 4, /* EA_MODE_AI */ + 6, /* EA_MODE_DI */ + 10, /* EA_MODE_IX */ + 6, /* EA_MODE_AW */ + 8, /* EA_MODE_AL */ + 6, /* EA_MODE_PCDI */ + 10, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for JSR instruction (000, 010) */ +int g_jsr_cycle_table[8] = +{ + 4, /* EA_MODE_AI */ + 6, /* EA_MODE_DI */ + 10, /* EA_MODE_IX */ + 6, /* EA_MODE_AW */ + 8, /* EA_MODE_AL */ + 6, /* EA_MODE_PCDI */ + 10, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for LEA instruction (000, 010) */ +int g_lea_cycle_table[8] = +{ + 4, /* EA_MODE_AI */ + 8, /* EA_MODE_DI */ + 12, /* EA_MODE_IX */ + 8, /* EA_MODE_AW */ + 12, /* EA_MODE_AL */ + 8, /* EA_MODE_PCDI */ + 12, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for PEA instruction (000, 010) */ +int g_pea_cycle_table[8] = +{ + 6, /* EA_MODE_AI */ + 10, /* EA_MODE_DI */ + 14, /* EA_MODE_IX */ + 10, /* EA_MODE_AW */ + 14, /* EA_MODE_AL */ + 10, /* EA_MODE_PCDI */ + 14, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +/* Extra cycles for MOVEM instruction (000, 010) */ +int g_movem_cycle_table[8] = +{ + 0, /* EA_MODE_AI */ + 4, /* EA_MODE_DI */ + 6, /* EA_MODE_IX */ + 4, /* EA_MODE_AW */ + 8, /* EA_MODE_AL */ + 0, /* EA_MODE_PCDI */ + 0, /* EA_MODE_PCIX */ + 0, /* EA_MODE_I */ +}; + +// add nonstandard EA +int Ea_add_ns(int *tab, int ea) +{ + if(ea<0x10) return 0; + if((ea&0x38)==0x10) return tab[0]; // (An) (ai) + if(ea<0x28) return 0; + if(ea<0x30) return tab[1]; // ($nn,An) (di) + if(ea<0x38) return tab[2]; // ($nn,An,Rn) (ix) + if(ea==0x38) return tab[3]; // (aw) + if(ea==0x39) return tab[4]; // (al) + if(ea==0x3a) return tab[5]; // ($nn,PC) (pcdi) + if(ea==0x3b) return tab[6]; // ($nn,pc,Rn) (pcix) + if(ea==0x3c) return tab[7]; // #$nnnn (i) + return 0; +} + + // --------------------------------------------------------------------------- // Gets the offset of a register for an ea, and puts it in 'r' // Shifted left by 'shift' // Doesn't trash anything -static int EaCalcReg(int r,int ea,int mask,int forceor,int shift) +static int EaCalcReg(int r,int ea,int mask,int forceor,int shift,int noshift=0) { int i=0,low=0,needor=0; int lsl=0; @@ -22,31 +111,34 @@ static int EaCalcReg(int r,int ea,int mask,int forceor,int shift) for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is mask&=0xf<=8) needor=1; // Need to OR to access A0-7 - - if ((mask>>low)&8) if (ea&8) needor=0; // Ah - no we don't actually need to or, since the bit is high in r8 - - if (forceor) needor=1; // Special case for 0x30-0x38 EAs ;) + if (ea>=8) + { + needor=1; // Need to OR to access A0-7 + if ((g_op>>low)&8) { needor=0; mask|=8<0) ot("lsl #%d\n", lsl); else ot("lsr #%d\n",-lsl); } - if (needor) ot(" orr r%d,r%d,#0x%x ;@ A0-7\n",r,r,8<=0x10, trashes r0,r2 and r3, else nothing +// size values 0, 1, 2 ~ byte, word, long +// mask shows usable bits in r8 +int EaCalc(int a,int mask,int ea,int size,int top,int sign_extend) { char text[32]=""; int func=0; @@ -56,36 +148,58 @@ int EaCalc(int a,int mask,int ea,int size) if (ea<0x10) { - int lsl=2; - if (size>=2) lsl=0; // Saves one opcode + int noshift=0; + if (size>=2||(size==0&&(top||!sign_extend))) noshift=1; // Saves one opcode ot(";@ EaCalc : Get register index into r%d:\n",a); - EaCalcReg(a,ea,mask,0,lsl); + EaCalcReg(a,ea,mask,0,2,noshift); return 0; } - + ot(";@ EaCalc : Get '%s' into r%d:\n",text,a); - // (An), (An)+, -(An): + // (An), (An)+, -(An) if (ea<0x28) { - int step=1<>=1) low++; // Find out how high up the EA mask is + lsl=2-low; // Having a lsl #x here saves one opcode + if (lsl>=0) ot(" ldr r%d,[r7,r2,lsl #%i]\n",a,lsl); + else if (lsl<0) ot(" ldr r%d,[r7,r2,lsr #%i]\n",a,-lsl); + } - if ((ea&0x38)==0x18) + if ((ea&0x38)==0x18) // (An)+ { ot(" add r3,r%d,#%d ;@ Post-increment An\n",a,step); - ot(" str r3,[r7,r2]\n"); + strr=3; } - if ((ea&0x38)==0x20) - { + if ((ea&0x38)==0x20) // -(An) ot(" sub r%d,r%d,#%d ;@ Pre-decrement An\n",a,a,step); - ot(" str r%d,[r7,r2]\n",a); + + if ((ea&0x38)==0x18||(ea&0x38)==0x20) + { + if (ea==0x1f||ea==0x27) + { + ot(" str r%d,[r7,#0x3c] ;@ A7\n",strr); + } + else + { + if (lsl>=0) ot(" str r%d,[r7,r2,lsl #%i]\n",strr,lsl); + else if (lsl<0) ot(" str r%d,[r7,r2,lsr #%i]\n",strr,-lsl); + } } if ((ea&0x38)==0x20) Cycles+=size<2 ? 6:10; // -(An) Extra cycles @@ -93,92 +207,93 @@ int EaCalc(int a,int mask,int ea,int size) return 0; } - if (ea<0x30) // ($nn,An) + if (ea<0x30) // ($nn,An) (di) { - EaCalcReg(2,8,mask,0,2); - ot(" ldr r2,[r7,r2]\n"); - ot(" ldrsh r0,[r4],#2 ;@ Fetch offset\n"); + ot(" ldrsh r0,[r4],#2 ;@ Fetch offset\n"); pc_dirty=1; + EaCalcReg(2,8,mask,0,0); + ot(" ldr r2,[r7,r2,lsl #2]\n"); ot(" add r%d,r0,r2 ;@ Add on offset\n",a); Cycles+=size<2 ? 8:12; // Extra cycles return 0; } - if (ea<0x38) // ($nn,An,Rn) + if (ea<0x38) // ($nn,An,Rn) (ix) { ot(";@ Get extension word into r3:\n"); - ot(" ldrh r3,[r4],#2 ;@ ($Disp,PC,Rn)\n"); + ot(" ldrh r3,[r4],#2 ;@ ($Disp,PC,Rn)\n"); pc_dirty=1; ot(" mov r2,r3,lsr #10\n"); ot(" tst r3,#0x0800 ;@ Is Rn Word or Long\n"); ot(" and r2,r2,#0x3c ;@ r2=Index of Rn\n"); - ot(" mov r0,r3,asl #24 ;@ r0=Get 8-bit signed Disp\n"); ot(" ldreqsh r2,[r7,r2] ;@ r2=Rn.w\n"); ot(" ldrne r2,[r7,r2] ;@ r2=Rn.l\n"); + ot(" mov r0,r3,asl #24 ;@ r0=Get 8-bit signed Disp\n"); ot(" add r3,r2,r0,asr #24 ;@ r3=Disp+Rn\n"); - EaCalcReg(2,8,mask,1,2); - ot(" ldr r2,[r7,r2]\n"); + EaCalcReg(2,8,mask,1,0); + ot(" ldr r2,[r7,r2,lsl #2]\n"); ot(" add r%d,r2,r3 ;@ r%d=Disp+An+Rn\n",a,a); Cycles+=size<2 ? 10:14; // Extra cycles return 0; } - if (ea==0x38) + if (ea==0x38) // (aw) { - ot(" ldrsh r%d,[r4],#2 ;@ Fetch Absolute Short address\n",a); + ot(" ldrsh r%d,[r4],#2 ;@ Fetch Absolute Short address\n",a); pc_dirty=1; Cycles+=size<2 ? 8:12; // Extra cycles return 0; } - if (ea==0x39) + if (ea==0x39) // (al) { ot(" ldrh r2,[r4],#2 ;@ Fetch Absolute Long address\n"); - ot(" ldrh r0,[r4],#2\n"); + ot(" ldrh r0,[r4],#2\n"); pc_dirty=1; ot(" orr r%d,r0,r2,lsl #16\n",a); Cycles+=size<2 ? 12:16; // Extra cycles return 0; } - if (ea==0x3a) + if (ea==0x3a) // ($nn,PC) (pcdi) { ot(" ldr r0,[r7,#0x60] ;@ Get Memory base\n"); ot(" sub r0,r4,r0 ;@ Real PC\n"); - ot(" ldrsh r2,[r4],#2 ;@ Fetch extension\n"); - ot(" add r%d,r0,r2 ;@ ($nn,PC)\n",a); + ot(" ldrsh r2,[r4],#2 ;@ Fetch extension\n"); pc_dirty=1; + ot(" mov r0,r0,lsl #8\n"); + ot(" add r%d,r2,r0,asr #8 ;@ ($nn,PC)\n",a); Cycles+=size<2 ? 8:12; // Extra cycles return 0; } - if (ea==0x3b) // ($nn,pc,Rn) + if (ea==0x3b) // ($nn,pc,Rn) (pcix) { - ot(";@ Get extension word into r3:\n"); - ot(" ldrh r3,[r4]\n"); + ot(" ldr r0,[r7,#0x60] ;@ Get Memory base\n"); + ot(" ldrh r3,[r4] ;@ Get extension word\n"); + ot(" sub r0,r4,r0 ;@ r0=PC\n"); + ot(" add r4,r4,#2\n"); pc_dirty=1; + ot(" mov r0,r0,asl #8 ;@ use only 24bits of PC\n"); ot(" mov r2,r3,lsr #10\n"); ot(" tst r3,#0x0800 ;@ Is Rn Word or Long\n"); ot(" and r2,r2,#0x3c ;@ r2=Index of Rn\n"); - ot(" mov r0,r3,asl #24 ;@ r0=Get 8-bit signed Disp\n"); ot(" ldreqsh r2,[r7,r2] ;@ r2=Rn.w\n"); ot(" ldrne r2,[r7,r2] ;@ r2=Rn.l\n"); - ot(" add r2,r2,r0,asr #24 ;@ r2=Disp+Rn\n"); - ot(" ldr r0,[r7,#0x60] ;@ Get Memory base\n"); - ot(" add r2,r2,r4 ;@ r2=Disp+Rn + Base+PC\n"); - ot(" add r4,r4,#2 ;@ Increase PC\n"); - ot(" sub r%d,r2,r0 ;@ r%d=Disp+PC+Rn\n",a,a); + ot(" mov r3,r3,asl #24 ;@ r3=Get 8-bit signed Disp\n"); + ot(" add r2,r2,r3,asr #24 ;@ r2=Disp+Rn\n"); + ot(" add r%d,r2,r0,asr #8 ;@ r%d=Disp+PC+Rn\n",a,a); Cycles+=size<2 ? 10:14; // Extra cycles return 0; } - if (ea==0x3c) + if (ea==0x3c) // #$nnnn (i) { if (size<2) { - ot(" ldr%s r%d,[r4],#2 ;@ Fetch immediate value\n",Sarm[size&3],a); + ot(" ldr%s r%d,[r4],#2 ;@ Fetch immediate value\n",Sarm[size&3],a); pc_dirty=1; Cycles+=4; // Extra cycles return 0; } ot(" ldrh r2,[r4],#2 ;@ Fetch immediate value\n"); - ot(" ldrh r0,[r4],#2\n"); - ot(" orr r%d,r0,r2,lsl #16\n",a); + ot(" ldrh r3,[r4],#2\n"); pc_dirty=1; + ot(" orr r%d,r3,r2,lsl #16\n",a); Cycles+=8; // Extra cycles return 0; } @@ -190,29 +305,37 @@ int EaCalc(int a,int mask,int ea,int size) // Read effective address in (ARM Register 'a') to ARM register 'v' // 'a' and 'v' can be anything but 0 is generally best (for both) // If (ea<0x10) nothing is trashed, else r0-r3 is trashed -// If 'top' is 1, the ARM register v shifted to the top, e.g. 0xc000 -> 0xc0000000 -// Otherwise the ARM register v is sign extended, e.g. 0xc000 -> 0xffffc000 +// If 'top' is given, the ARM register v shifted to the top, e.g. 0xc000 -> 0xc0000000 +// If top is 0 and sign_extend is not, then ARM register v is sign extended, +// e.g. 0xc000 -> 0xffffc000 (else it may or may not be sign extended) -int EaRead(int a,int v,int ea,int size,int top) +int EaRead(int a,int v,int ea,int size,int mask,int top,int sign_extend) { char text[32]=""; int shift=0; - + shift=32-(8<=2) lsl=0; // Having a lsl #2 here saves one opcode + int lsl=0,low=0,nsarm=size&3,i; + if (size>=2||(size==0&&(top||!sign_extend))) { + if(mask) + for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is + lsl=2-low; // Having a lsl #2 here saves one opcode + } + + if (top||!sign_extend) nsarm=3; ot(";@ EaRead : Read register[r%d] into r%d:\n",a,v); - if (lsl==0) ot(" ldr r%d,[r7,r%d,lsl #2]\n",v,a); - else ot(" ldr%s r%d,[r7,r%d]\n",Sarm[size&3],v,a); + if (lsl>0) ot(" ldr%s r%d,[r7,r%d,lsl #%i]\n",Narm[nsarm],v,a,lsl); + else if (lsl<0) ot(" ldr%s r%d,[r7,r%d,lsr #%i]\n",Narm[nsarm],v,a,-lsl); + else ot(" ldr%s r%d,[r7,r%d]\n",Sarm[nsarm],v,a); - if (top && shift) ot(" mov r%d,r%d,asl #%d\n",v,v,shift); + if (top&&shift) ot(" mov r%d,r%d,asl #%d\n",v,v,shift); ot("\n"); return 0; } @@ -225,21 +348,79 @@ int EaRead(int a,int v,int ea,int size,int top) if (top) asl=shift; - if (v!=a || asl) ot(" mov r%d,r%d,asl #%d\n",v,a,asl); + if (asl) ot(" mov r%d,r%d,asl #%d\n",v,a,asl); + else if (v!=a) ot(" mov r%d,r%d\n",v,a); ot("\n"); return 0; } - if (a!=0) ot(" mov r0,r%d\n",a); + if (ea>=0x3a && ea<=0x3b) MemHandler(2,size,a,earead_check_addrerr); // Fetch + else MemHandler(0,size,a,earead_check_addrerr); // Read - if (ea>=0x3a && ea<=0x3b) MemHandler(2,size); // Fetch - else MemHandler(0,size); // Read + // defaults to 1, as most things begins with a read + earead_check_addrerr=1; - if (v!=0 || shift) ot(" mov r%d,r0,asl #%d\n",v,shift); - if (top==0 && shift) ot(" mov r%d,r%d,asr #%d\n",v,v,shift); + if (sign_extend) + { + int d_reg=0; + if (shift) { + ot(" mov r%d,r%d,asl #%d\n",v,d_reg,shift); + d_reg=v; + } + if (!top && shift) { + ot(" mov r%d,r%d,asr #%d\n",v,d_reg,shift); + d_reg=v; + } + if (d_reg != v) + ot(" mov r%d,r%d\n",v,d_reg); + } + else + { + if (top && shift) + ot(" mov r%d,r0,asl #%d\n",v,shift); + else if (v!=0) + ot(" mov r%d,r0\n",v); + } ot("\n"); return 0; } +// calculate EA and read +// if (ea < 0x10) nothing is trashed +// if (ea == 0x3c) r2 and r3 are trashed +// else r0-r3 are trashed +// size values 0, 1, 2 ~ byte, word, long +// r_ea is reg to store ea in (-1 means ea is not needed), r is dst reg +// if sign_extend is 0, non-32bit values will have MS bits undefined +int EaCalcRead(int r_ea,int r,int ea,int size,int mask,int sign_extend) +{ + if (ea<0x10) + { + if (r_ea==-1) + { + r_ea=r; + if (!sign_extend) size=2; + } + } + else if (ea==0x3c) // #imm + { + r_ea=r; + } + else + { + if (r_ea==-1) r_ea=0; + } + + EaCalc (r_ea,mask,ea,size,0,sign_extend); + EaRead (r_ea, r,ea,size,mask,0,sign_extend); + + return 0; +} + +int EaCalcReadNoSE(int r_ea,int r,int ea,int size,int mask) +{ + return EaCalcRead(r_ea,r,ea,size,mask,0); +} + // Return 1 if we can read this ea int EaCanRead(int ea,int size) { @@ -247,7 +428,7 @@ int EaCanRead(int ea,int size) { // LEA: // These don't make sense?: - if (ea<0x10) return 0; // Register + if (ea< 0x10) return 0; // Register if (ea==0x3c) return 0; // Immediate if (ea>=0x18 && ea<0x28) return 0; // Pre/Post inc/dec An } @@ -258,27 +439,34 @@ int EaCanRead(int ea,int size) // --------------------------------------------------------------------------- // Write effective address (ARM Register 'a') with ARM register 'v' -// Trashes r0-r3, 'a' can be 0 or 2+, 'v' can be 1 or higher +// Trashes r0-r3,r12,lr; 'a' can be 0 or 2+, 'v' can be 1 or higher // If a==0 and v==1 it's faster though. -int EaWrite(int a,int v,int ea,int size,int top) +int EaWrite(int a,int v,int ea,int size,int mask,int top,int sign_extend_ea) { char text[32]=""; int shift=0; + if(a == 1) { printf("Error! EaWrite a==1 !\n"); return 1; } + if (top) shift=32-(8<=2) lsl=0; // Having a lsl #2 here saves one opcode + int lsl=0,low=0,i; + if (size>=2||(size==0&&(top||!sign_extend_ea))) { + if(mask) + for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is + lsl=2-low; // Having a lsl #x here saves one opcode + } ot(";@ EaWrite: r%d into register[r%d]:\n",v,a); - if (shift) ot(" mov r%d,r%d,asr #%d\n",v,v,shift); + if (shift) ot(" mov r%d,r%d,asr #%d\n",v,v,shift); - if (lsl==0) ot(" str r%d,[r7,r%d,lsl #2]\n",v,a); - else ot(" str%s r%d,[r7,r%d]\n",Narm[size&3],v,a); + if (lsl>0) ot(" str%s r%d,[r7,r%d,lsl #%i]\n",Narm[size&3],v,a,lsl); + else if (lsl<0) ot(" str%s r%d,[r7,r%d,lsr #%i]\n",Narm[size&3],v,a,-lsl); + else ot(" str%s r%d,[r7,r%d]\n",Narm[size&3],v,a); ot("\n"); return 0; } @@ -287,11 +475,14 @@ int EaWrite(int a,int v,int ea,int size,int top) if (ea==0x3c) { ot("Error! Write EA=0x%x\n\n",ea); return 1; } - if (a!=0 && v!=0) ot(" mov r0,r%d\n",a); - if (v!=1 || shift) ot(" mov r1,r%d,asr #%d\n",v,shift); - if (a!=0 && v==0) ot(" mov r0,r%d\n",a); + if (shift) ot(" mov r1,r%d,asr #%d\n",v,shift); + else if (v!=1) ot(" mov r1,r%d\n",v); - MemHandler(1,size); // Call write handler + MemHandler(1,size,a,eawrite_check_addrerr); // Call write handler + + // not check by default, because most cases are rmw and + // address was already checked before reading + eawrite_check_addrerr = 0; ot("\n"); return 0; } @@ -299,7 +490,15 @@ int EaWrite(int a,int v,int ea,int size,int top) // Return 1 if we can write this ea int EaCanWrite(int ea) { - if (ea<=0x3b) return 1; + if (ea<=0x39) return 1; // 3b? return 0; } // --------------------------------------------------------------------------- + +// Return 1 if EA is An reg +int EaAn(int ea) +{ + if((ea&0x38)==8) return 1; + return 0; +} + diff --git a/Cyclone/Main.cpp b/Cyclone/Main.cpp index 0e03169..1f532a0 100644 --- a/Cyclone/Main.cpp +++ b/Cyclone/Main.cpp @@ -1,30 +1,104 @@ // This file is part of the Cyclone 68000 Emulator -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + #include "app.h" static FILE *AsmFile=NULL; -static int CycloneVer=0x0069; // Version number of library +static int CycloneVer=0x0099; // Version number of library int *CyJump=NULL; // Jump table -int ms=0; // If non-zero, output in Microsoft ARMASM format -char *Narm[4]={ "b", "h","",""}; // Normal ARM Extensions for operand sizes 0,1,2 -char *Sarm[4]={"sb","sh","",""}; // Sign-extend ARM Extensions for operand sizes 0,1,2 -int Cycles=0; // Current cycles for opcode -int Amatch=1; // If one, try to match A68K timing -int Accu=-1; // Accuracy -int Debug=0; // Debug info +int ms=USE_MS_SYNTAX; // If non-zero, output in Microsoft ARMASM format +const char * const Narm[4]={ "b", "h","",""}; // Normal ARM Extensions for operand sizes 0,1,2 +const char * const Sarm[4]={"sb","sh","",""}; // Sign-extend ARM Extensions for operand sizes 0,1,2 +int Cycles; // Current cycles for opcode +int pc_dirty; // something changed PC during processing +int arm_op_count; + +// opcodes often used by games +static const unsigned short hot_opcodes[] = { + 0x6701, // beq $3 + 0x6601, // bne $3 + 0x51c8, // dbra Dn, $2 + 0x4a38, // tst.b $0.w + 0xd040, // add.w Dn, Dn + 0x4a79, // tst.w $0.l + 0x0240, // andi.w #$0, D0 + 0x2038, // move.l $0.w, D0 + 0xb0b8, // cmp.l $0.w, D0 + 0x6001, // bra $3 + 0x30c0, // move.w D0, (A0)+ + 0x3028, // move.w ($0,A0), D0 + 0x0c40, // cmpi.w #$0, D0 + 0x0c79, // cmpi.w #$0, $0.l + 0x4e75, // rts + 0x4e71, // nop + 0x3000, // move.w D0, D0 + 0x0839, // btst #$0, $0.l + 0x7000, // moveq #$0, D0 + 0x3040, // movea.w D0, A0 + 0x0838, // btst #$0, $0.w + 0x4a39, // tst.b $0.l + 0x33d8, // move.w (A0)+, $0.l + 0x6700, // beq $2 + 0xb038, // cmp.b $0.w, D0 + 0x3039, // move.w $0.l, D0 + 0x4840, // swap D0 + 0x6101, // bsr $3 + 0x6100, // bsr $2 + 0x5e40, // addq.w #7, D0 + 0x1039, // move.b $0.l, D0 + 0x20c0, // move.l D0, (A0)+ + 0x1018, // move.b (A0)+, D0 + 0x30d0, // move.w (A0), (A0)+ + 0x3080, // move.w D0, (A0) + 0x3018, // move.w (A0)+, D0 + 0xc040, // and.w D0, D0 + 0x3180, // move.w D0, (A0,D0.w) + 0x1198, // move.b (A0)+, (A0,D0.w) + 0x6501, // bcs $3 + 0x6500, // bcs $2 + 0x6401, // bcc $3 + 0x6a01, // bpl $3 + 0x41f0, // lea (A0,D0.w), A0 + 0x4a28, // tst.b ($0,A0) + 0x0828, // btst #$0, ($0,A0) + 0x0640, // addi.w #$0, D0 + 0x10c0, // move.b D0, (A0)+ + 0x10d8, // move.b (A0)+, (A0)+ +}; +#define hot_opcode_count (int)(sizeof(hot_opcodes) / sizeof(hot_opcodes[0])) + +static int is_op_hot(int op) +{ + int i; + for (i = 0; i < hot_opcode_count; i++) + if (op == hot_opcodes[i]) + return 1; + return 0; +} void ot(const char *format, ...) { va_list valist; + int i, len; + + // notaz: stop me from leaving newlines in the middle of format string + // and generating bad code + for(i=0, len=strlen(format); i < len && format[i] != '\n'; i++); + if(i < len-1 && format[len-1] != '\n') printf("\nWARNING: possible improper newline placement:\n%s\n", format); + + if (format[0] == ' ' && format[1] == ' ' && format[2] != ' ' && format[2] != '.') + arm_op_count++; + va_start(valist,format); if (AsmFile) vfprintf(AsmFile,format,valist); va_end(valist); @@ -36,164 +110,990 @@ void ltorg() else ot(" .ltorg\n"); } -static void PrintException() +#if (CYCLONE_FOR_GENESIS == 2) +// r12=ptr to tas in table, trashes r0,r1 +static void ChangeTAS(int norm) { - ot(" ;@ Cause an Exception - Vector in [r7,#0x50]\n"); - ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); - ot(" sub r1,r4,r10 ;@ r1 = Old PC\n"); - OpPush32(); - OpPushSr(1); - ot(" ldr r0,[r7,#0x50] ;@ Get Vector\n"); - ot(";@ Read IRQ Vector:\n"); - MemHandler(0,2); - ot(" add r0,r0,r10 ;@ r0 = Memory Base + New PC\n"); - ot(" mov lr,pc\n"); - ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); - ot(" mov r4,r0\n"); - ot("\n"); - - // todo - make Interrupt code use this function as well + ot(" ldr r0,=Op4ad0%s\n",norm?"_":""); + ot(" mov r1,#8\n"); + ot("setrtas_loop%i0%s ;@ 4ad0-4ad7\n",norm,ms?"":":"); + ot(" subs r1,r1,#1\n"); + ot(" str r0,[r12],#4\n"); + ot(" bne setrtas_loop%i0\n",norm); + ot(" ldr r0,=Op4ad8%s\n",norm?"_":""); + ot(" mov r1,#7\n"); + ot("setrtas_loop%i1%s ;@ 4ad8-4ade\n",norm,ms?"":":"); + ot(" subs r1,r1,#1\n"); + ot(" str r0,[r12],#4\n"); + ot(" bne setrtas_loop%i1\n",norm); + ot(" ldr r0,=Op4adf%s\n",norm?"_":""); + ot(" str r0,[r12],#4\n"); + ot(" ldr r0,=Op4ae0%s\n",norm?"_":""); + ot(" mov r1,#7\n"); + ot("setrtas_loop%i2%s ;@ 4ae0-4ae6\n",norm,ms?"":":"); + ot(" subs r1,r1,#1\n"); + ot(" str r0,[r12],#4\n"); + ot(" bne setrtas_loop%i2\n",norm); + ot(" ldr r0,=Op4ae7%s\n",norm?"_":""); + ot(" str r0,[r12],#4\n"); + ot(" ldr r0,=Op4ae8%s\n",norm?"_":""); + ot(" mov r1,#8\n"); + ot("setrtas_loop%i3%s ;@ 4ae8-4aef\n",norm,ms?"":":"); + ot(" subs r1,r1,#1\n"); + ot(" str r0,[r12],#4\n"); + ot(" bne setrtas_loop%i3\n",norm); + ot(" ldr r0,=Op4af0%s\n",norm?"_":""); + ot(" mov r1,#8\n"); + ot("setrtas_loop%i4%s ;@ 4af0-4af7\n",norm,ms?"":":"); + ot(" subs r1,r1,#1\n"); + ot(" str r0,[r12],#4\n"); + ot(" bne setrtas_loop%i4\n",norm); + ot(" ldr r0,=Op4af8%s\n",norm?"_":""); + ot(" str r0,[r12],#4\n"); + ot(" ldr r0,=Op4af9%s\n",norm?"_":""); + ot(" str r0,[r12],#4\n"); } +#endif -// Trashes r0 -void CheckInterrupt() +#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO +static void AddressErrorWrapper(char rw, const char *dataprg, int iw) { - ot(";@ CheckInterrupt:\n"); - ot(" ldrb r0,[r7,#0x47] ;@ Get IRQ level\n"); - ot(" tst r0,r0\n"); - ot(" blne DoInterrupt\n"); + ot("ExceptionAddressError_%c_%s%s\n", rw, dataprg, ms?"":":"); + ot(" ldr r1,[r7,#0x44]\n"); + ot(" mov r6,#0x%02x\n", iw); + ot(" mov r11,r0\n"); + ot(" tst r1,#0x20\n"); + ot(" orrne r6,r6,#4\n"); + ot(" b ExceptionAddressError\n"); ot("\n"); } +#endif + +void FlushPC(void) +{ +#if MEMHANDLERS_NEED_PC + if (pc_dirty) + ot(" str r4,[r7,#0x40] ;@ Save PC\n"); +#endif + pc_dirty = 0; +} static void PrintFramework() { + int state_flags_to_check = 1; // stopped +#if EMULATE_TRACE + state_flags_to_check |= 2; // tracing +#endif +#if EMULATE_HALT + state_flags_to_check |= 0x10; // halted +#endif + ot(";@ --------------------------- Framework --------------------------\n"); if (ms) ot("CycloneRun\n"); else ot("CycloneRun:\n"); - ot(" stmdb sp!,{r4-r11,lr}\n"); + ot(" stmdb sp!,{r4-r8,r10,r11,lr}\n"); ot(" mov r7,r0 ;@ r7 = Pointer to Cpu Context\n"); ot(" ;@ r0-3 = Temporary registers\n"); - ot(" ldrb r9,[r7,#0x46] ;@ r9 = Flags (NZCV)\n"); - ot(" ldr r6,=JumpTab ;@ r6 = Opcode Jump table\n"); + ot(" ldrb r10,[r7,#0x46] ;@ r10 = Flags (NZCV)\n"); + ot(" ldr r6,=CycloneJumpTab ;@ r6 = Opcode Jump table\n"); ot(" ldr r5,[r7,#0x5c] ;@ r5 = Cycles\n"); ot(" ldr r4,[r7,#0x40] ;@ r4 = Current PC + Memory Base\n"); ot(" ;@ r8 = Current Opcode\n"); - ot(" mov r9,r9,lsl #28 ;@ r9 = Flags 0xf0000000, cpsr format\n"); - ot(" ;@ r10 = Source value / Memory Base\n"); + ot(" ldr r1,[r7,#0x44] ;@ Get SR high T_S__III and irq level\n"); + ot(" mov r10,r10,lsl #28;@ r10 = Flags 0xf0000000, cpsr format\n"); + ot(" ;@ r11 = Source value / Memory Base\n"); + ot(" str r6,[r7,#0x54] ;@ make a copy to avoid literal pools\n"); + ot("\n"); +#if (CYCLONE_FOR_GENESIS == 2) || EMULATE_TRACE + ot(" mov r2,#0\n"); + ot(" str r2,[r7,#0x98] ;@ clear custom CycloneEnd\n"); +#endif + ot(";@ CheckInterrupt:\n"); + ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47] + ot(" beq NoInts0\n"); + ot(" cmp r0,#6 ;@ irq>6 ?\n"); + ot(" andle r1,r1,#7 ;@ Get interrupt mask\n"); + ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n"); + ot(" bgt CycloneDoInterrupt\n"); + ot("NoInts0%s\n", ms?"":":"); + ot("\n"); + ot(";@ Check if our processor is in special state\n"); + ot(";@ and jump to opcode handler if not\n"); + ot(" ldr r0,[r7,#0x58] ;@ state_flags\n"); + ot(" ldrh r8,[r4],#2 ;@ Fetch first opcode\n"); + ot(" tst r0,#0x%02x ;@ special state?\n", state_flags_to_check); + ot(" ldreq pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); + ot("\n"); + ot("CycloneSpecial%s\n", ms?"":":"); +#if EMULATE_TRACE + ot(" tst r0,#2 ;@ tracing?\n"); + ot(" bne CycloneDoTrace\n"); +#endif + ot(";@ stopped or halted\n"); + ot(" mov r5,#0\n"); + ot(" str r5,[r7,#0x5C] ;@ eat all cycles\n"); + ot(" ldmia sp!,{r4-r8,r10,r11,pc} ;@ we are stopped, do nothing!\n"); ot("\n"); - CheckInterrupt(); - ot(";@ Check if interrupt used up all the cycles:\n"); - ot(" subs r5,r5,#0\n"); - ot(" blt CycloneEndNoBack\n"); - - OpFirst(); - ltorg(); ot("\n"); ot(";@ We come back here after execution\n"); ot("CycloneEnd%s\n", ms?"":":"); ot(" sub r4,r4,#2\n"); ot("CycloneEndNoBack%s\n", ms?"":":"); - ot(" mov r9,r9,lsr #28\n"); +#if (CYCLONE_FOR_GENESIS == 2) || EMULATE_TRACE + ot(" ldr r1,[r7,#0x98]\n"); + ot(" mov r10,r10,lsr #28\n"); + ot(" tst r1,r1\n"); + ot(" bxne r1 ;@ jump to alternative CycloneEnd\n"); +#else + ot(" mov r10,r10,lsr #28\n"); +#endif ot(" str r4,[r7,#0x40] ;@ Save Current PC + Memory Base\n"); ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n"); - ot(" strb r9,[r7,#0x46] ;@ Save Flags (NZCV)\n"); - ot(" ldmia sp!,{r4-r11,pc}\n"); + ot(" strb r10,[r7,#0x46] ;@ Save Flags (NZCV)\n"); + ot(" ldmia sp!,{r4-r8,r10,r11,pc}\n"); + ltorg(); ot("\n"); - - ot(";@ DoInterrupt - r0=IRQ number\n"); - ot("DoInterrupt%s\n", ms?"":":"); ot("\n"); - ot(" ldrb r1,[r7,#0x44] ;@ Get SR high: T_S__III\n"); - ot(" and r1,r1,#7 ;@ Get interrupt mask\n"); - ot(" cmp r0,#6 ;@ irq>6 ?\n"); - ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n"); - ot(" movle pc,lr ;@ irq<=6 and mask, not allowed\n"); + + ot("CycloneInit%s\n", ms?"":":"); +#if COMPRESS_JUMPTABLE + ot(";@ decompress jump table\n"); + ot(" ldr r12,=CycloneJumpTab\n"); + ot(" add r0,r12,#0xe000*4 ;@ ctrl code pointer\n"); + ot(" ldr r1,[r0,#-4]\n"); + ot(" tst r1,r1\n"); + ot(" movne pc,lr ;@ already uncompressed\n"); + ot(" add r3,r12,#0xa000*4 ;@ handler table pointer, r12=dest\n"); + ot("unc_loop%s\n", ms?"":":"); + ot(" ldrh r1,[r0],#2\n"); + ot(" and r2,r1,#0xf\n"); + ot(" bic r1,r1,#0xf\n"); + ot(" ldr r1,[r3,r1,lsr #2] ;@ r1=handler\n"); + ot(" cmp r2,#0xf\n"); + ot(" addeq r2,r2,#1 ;@ 0xf is really 0x10\n"); + ot(" tst r2,r2\n"); + ot(" ldreqh r2,[r0],#2 ;@ counter is in next word\n"); + ot(" tst r2,r2\n"); + ot(" beq unc_finish ;@ done decompressing\n"); + ot(" tst r1,r1\n"); + ot(" addeq r12,r12,r2,lsl #2 ;@ 0 handler means we should skip those bytes\n"); + ot(" beq unc_loop\n"); + ot("unc_loop_in%s\n", ms?"":":"); + ot(" subs r2,r2,#1\n"); + ot(" str r1,[r12],#4\n"); + ot(" bgt unc_loop_in\n"); + ot(" b unc_loop\n"); + ot("unc_finish%s\n", ms?"":":"); + ot(" ldr r12,=CycloneJumpTab\n"); + ot(" ;@ set a-line and f-line handlers\n"); + ot(" add r0,r12,#0xa000*4\n"); + ot(" ldr r1,[r0,#4] ;@ a-line handler\n"); + ot(" ldr r3,[r0,#8] ;@ f-line handler\n"); + ot(" mov r2,#0x1000\n"); + ot("unc_fill3%s\n", ms?"":":"); + ot(" subs r2,r2,#1\n"); + ot(" str r1,[r0],#4\n"); + ot(" bgt unc_fill3\n"); + ot(" add r0,r12,#0xf000*4\n"); + ot(" mov r2,#0x1000\n"); + ot("unc_fill4%s\n", ms?"":":"); + ot(" subs r2,r2,#1\n"); + ot(" str r3,[r0],#4\n"); + ot(" bgt unc_fill4\n"); + ot(" bx lr\n"); + ltorg(); +#else + ot(";@ do nothing\n"); + ot(" bx lr\n"); +#endif ot("\n"); - ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); - ot(" mov r11,lr ;@ Preserve ARM return address\n"); - ot(" sub r1,r4,r10 ;@ r1 = Old PC\n"); - OpPush32(); - OpPushSr(1); - ot(";@ Get IRQ Vector address:\n"); - ot(" ldrb r1,[r7,#0x47] ;@ IRQ\n"); - ot(" mov r0,r1,asl #2\n"); - ot(" add r0,r0,#0x60\n"); - ot(";@ Read IRQ Vector:\n"); + + // -------------- + ot("CycloneReset%s\n", ms?"":":"); + ot(" stmfd sp!,{r7,lr}\n"); + ot(" mov r7,r0\n"); + ot(" mov r0,#0\n"); + ot(" str r0,[r7,#0x58] ;@ state_flags\n"); + ot(" str r0,[r7,#0x48] ;@ OSP\n"); + ot(" mov r1,#0x27 ;@ Supervisor mode\n"); + ot(" strb r1,[r7,#0x44] ;@ set SR high\n"); + ot(" strb r0,[r7,#0x47] ;@ IRQ\n"); MemHandler(0,2); - ot(" add r0,r0,r10 ;@ r0 = Memory Base + New PC\n"); + ot(" str r0,[r7,#0x3c] ;@ Stack pointer\n"); + ot(" mov r0,#0\n"); + ot(" str r0,[r7,#0x60] ;@ Membase\n"); + ot(" mov r0,#4\n"); + MemHandler(0,2); +#ifdef MEMHANDLERS_DIRECT_PREFIX + ot(" bl %scheckpc ;@ Call checkpc()\n", MEMHANDLERS_DIRECT_PREFIX); +#else ot(" mov lr,pc\n"); ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); +#endif + ot(" str r0,[r7,#0x40] ;@ PC + base\n"); + ot(" ldmfd sp!,{r7,pc}\n"); + ot("\n"); + + // -------------- + // 68k: XNZVC, ARM: NZCV + ot("CycloneSetSr%s\n", ms?"":":"); + ot(" mov r2,r1,lsr #8\n"); +// ot(" ldrb r3,[r0,#0x44] ;@ get SR high\n"); +// ot(" eor r3,r3,r2\n"); +// ot(" tst r3,#0x20\n"); +#if EMULATE_TRACE + ot(" and r2,r2,#0xa7 ;@ only defined bits\n"); +#else + ot(" and r2,r2,#0x27 ;@ only defined bits\n"); +#endif + ot(" strb r2,[r0,#0x44] ;@ set SR high\n"); + ot(" mov r2,r1,lsl #25\n"); + ot(" str r2,[r0,#0x4c] ;@ the X flag\n"); + ot(" bic r2,r1,#0xf3\n"); + ot(" tst r1,#1\n"); + ot(" orrne r2,r2,#2\n"); + ot(" tst r1,#2\n"); + ot(" orrne r2,r2,#1\n"); + ot(" strb r2,[r0,#0x46] ;@ flags\n"); + ot(" bx lr\n"); + ot("\n"); + + // -------------- + ot("CycloneGetSr%s\n", ms?"":":"); + ot(" ldrb r1,[r0,#0x46] ;@ flags\n"); + ot(" bic r2,r1,#0xf3\n"); + ot(" tst r1,#1\n"); + ot(" orrne r2,r2,#2\n"); + ot(" tst r1,#2\n"); + ot(" orrne r2,r2,#1\n"); + ot(" ldr r1,[r0,#0x4c] ;@ the X flag\n"); + ot(" tst r1,#0x20000000\n"); + ot(" orrne r2,r2,#0x10\n"); + ot(" ldrb r1,[r0,#0x44] ;@ the SR high\n"); + ot(" orr r0,r2,r1,lsl #8\n"); + ot(" bx lr\n"); + ot("\n"); + + // -------------- + ot("CyclonePack%s\n", ms?"":":"); + ot(" stmfd sp!,{r4,r5,lr}\n"); ot(" mov r4,r0\n"); + ot(" mov r5,r1\n"); + ot(" mov r3,#16\n"); + ot(";@ 0x00-0x3f: DA registers\n"); + ot("c_pack_loop%s\n",ms?"":":"); + ot(" ldr r1,[r0],#4\n"); + ot(" subs r3,r3,#1\n"); + ot(" str r1,[r5],#4\n"); + ot(" bne c_pack_loop\n"); + ot(";@ 0x40: PC\n"); + ot(" ldr r0,[r4,#0x40] ;@ PC + Memory Base\n"); + ot(" ldr r1,[r4,#0x60] ;@ Memory base\n"); + ot(" sub r0,r0,r1\n"); + ot(" str r0,[r5],#4\n"); + ot(";@ 0x44: SR\n"); + ot(" mov r0,r4\n"); + ot(" bl CycloneGetSr\n"); + ot(" strh r0,[r5],#2\n"); + ot(";@ 0x46: IRQ level\n"); + ot(" ldrb r0,[r4,#0x47]\n"); + ot(" strb r0,[r5],#2\n"); + ot(";@ 0x48: other SP\n"); + ot(" ldr r0,[r4,#0x48]\n"); + ot(" str r0,[r5],#4\n"); + ot(";@ 0x4c: CPU state flags\n"); + ot(" ldr r0,[r4,#0x58]\n"); + ot(" str r0,[r5],#4\n"); + ot(" ldmfd sp!,{r4,r5,pc}\n"); + ot("\n"); + + // -------------- + ot("CycloneUnpack%s\n", ms?"":":"); + ot(" stmfd sp!,{r5,r7,lr}\n"); + ot(" mov r7,r0\n"); + ot(" movs r5,r1\n"); + ot(" beq c_unpack_do_pc\n"); + ot(" mov r3,#16\n"); + ot(";@ 0x00-0x3f: DA registers\n"); + ot("c_unpack_loop%s\n",ms?"":":"); + ot(" ldr r1,[r5],#4\n"); + ot(" subs r3,r3,#1\n"); + ot(" str r1,[r0],#4\n"); + ot(" bne c_unpack_loop\n"); + ot(";@ 0x40: PC\n"); + ot(" ldr r0,[r5],#4 ;@ PC\n"); + ot(" str r0,[r7,#0x40] ;@ handle later\n"); + ot(";@ 0x44: SR\n"); + ot(" ldrh r1,[r5],#2\n"); + ot(" mov r0,r7\n"); + ot(" bl CycloneSetSr\n"); + ot(";@ 0x46: IRQ level\n"); + ot(" ldrb r0,[r5],#2\n"); + ot(" strb r0,[r7,#0x47]\n"); + ot(";@ 0x48: other SP\n"); + ot(" ldr r0,[r5],#4\n"); + ot(" str r0,[r7,#0x48]\n"); + ot(";@ 0x4c: CPU state flags\n"); + ot(" ldr r0,[r5],#4\n"); + ot(" str r0,[r7,#0x58]\n"); + ot("c_unpack_do_pc%s\n",ms?"":":"); + ot(" ldr r0,[r7,#0x40] ;@ unbased PC\n"); +#if USE_CHECKPC_CALLBACK + ot(" mov r1,#0\n"); + ot(" str r1,[r7,#0x60] ;@ Memory base\n"); + #ifdef MEMHANDLERS_DIRECT_PREFIX + ot(" bl %scheckpc ;@ Call checkpc()\n", MEMHANDLERS_DIRECT_PREFIX); + #else + ot(" mov lr,pc\n"); + ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); + #endif +#else + ot(" ldr r1,[r7,#0x60] ;@ Memory base\n"); + ot(" add r0,r0,r1 ;@ r0 = Memory Base + New PC\n"); +#endif + ot(" str r0,[r7,#0x40] ;@ PC + Memory Base\n"); + ot(" ldmfd sp!,{r5,r7,pc}\n"); + ot("\n"); + + // -------------- + ot("CycloneFlushIrq%s\n", ms?"":":"); + ot(" ldr r1,[r0,#0x44] ;@ Get SR high T_S__III and irq level\n"); + ot(" mov r2,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47] + ot(" cmp r2,#6 ;@ irq>6 ?\n"); + ot(" andle r1,r1,#7 ;@ Get interrupt mask\n"); + ot(" cmple r2,r1 ;@ irq<=6: Is irq<=mask ?\n"); + ot(" movle r0,#0\n"); + ot(" bxle lr ;@ no ints\n"); + ot("\n"); + ot(" stmdb sp!,{r4,r5,r7,r8,r10,r11,lr}\n"); + ot(" mov r7,r0\n"); + ot(" mov r0,r2\n"); + ot(" ldrb r10,[r7,#0x46] ;@ r10 = Flags (NZCV)\n"); + ot(" mov r5,#0\n"); + ot(" ldr r4,[r7,#0x40] ;@ r4 = Current PC + Memory Base\n"); + ot(" mov r10,r10,lsl #28 ;@ r10 = Flags 0xf0000000, cpsr format\n"); + ot(" adr r2,CycloneFlushIrqEnd\n"); + ot(" str r2,[r7,#0x98] ;@ set custom CycloneEnd\n"); + ot(" b CycloneDoInterrupt\n"); + ot("\n"); + ot("CycloneFlushIrqEnd%s\n", ms?"":":"); + ot(" rsb r0,r5,#0\n"); + ot(" str r4,[r7,#0x40] ;@ Save Current PC + Memory Base\n"); + ot(" strb r10,[r7,#0x46] ;@ Save Flags (NZCV)\n"); + ot(" ldmia sp!,{r4,r5,r7,r8,r10,r11,lr}\n"); + ot(" bx lr\n"); + ot("\n"); + ot("\n"); + + // -------------- + ot("CycloneSetRealTAS%s\n", ms?"":":"); +#if (CYCLONE_FOR_GENESIS == 2) + ot(" ldr r12,=CycloneJumpTab\n"); + ot(" tst r0,r0\n"); + ot(" add r12,r12,#0x4a00*4\n"); + ot(" add r12,r12,#0x00d0*4\n"); + ot(" beq setrtas_off\n"); + ChangeTAS(1); + ot(" bx lr\n"); + ot("setrtas_off%s\n",ms?"":":"); + ChangeTAS(0); + ot(" bx lr\n"); + ltorg(); +#else + ot(" bx lr\n"); +#endif + ot("\n"); + + // -------------- + ot(";@ DoInterrupt - r0=IRQ level\n"); + ot("CycloneDoInterruptGoBack%s\n", ms?"":":"); + ot(" sub r4,r4,#2\n"); + ot("CycloneDoInterrupt%s\n", ms?"":":"); + ot(" bic r8,r8,#0xff000000\n"); + ot(" orr r8,r8,r0,lsl #29 ;@ abuse r8\n"); + + // Steps are from "M68000 8-/16-/32-BIT MICROPROCESSORS USER'S MANUAL", p. 6-4 + // but their order is based on http://pasti.fxatari.com/68kdocs/68kPrefetch.html + // 1. Make a temporary copy of the status register and set the status register for exception processing. + ot(" ldr r2,[r7,#0x58] ;@ state flags\n"); + ot(" and r0,r0,#7\n"); + ot(" orr r3,r0,#0x20 ;@ Supervisor mode + IRQ level\n"); + ot(" bic r2,r2,#3 ;@ clear stopped and trace states\n"); +#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO + ot(" orr r2,r2,#4 ;@ set activity bit: 'not processing instruction'\n"); +#endif + ot(" str r2,[r7,#0x58]\n"); + ot(" ldrb r6,[r7,#0x44] ;@ Get old SR high, abuse r6\n"); + ot(" strb r3,[r7,#0x44] ;@ Put new SR high\n"); ot("\n"); - ot(";@ todo - swap OSP and A7 if not in Supervisor mode\n"); - ot(" ldrb r0,[r7,#0x47] ;@ IRQ\n"); - ot(" orr r0,r0,#0x20 ;@ Supervisor mode + IRQ number\n"); - ot(" strb r0,[r7,#0x44] ;@ Put SR high\n"); + + // 3. Save the current processor context. + ot(" ldr r1,[r7,#0x60] ;@ Get Memory base\n"); + ot(" ldr r11,[r7,#0x3c] ;@ Get A7\n"); + ot(" tst r6,#0x20\n"); + ot(";@ get our SP:\n"); + ot(" ldreq r2,[r7,#0x48] ;@ ...or OSP as our stack pointer\n"); + ot(" streq r11,[r7,#0x48]\n"); + ot(" moveq r11,r2\n"); + ot(";@ Push old PC onto stack\n"); + ot(" sub r0,r11,#4 ;@ Predecremented A7\n"); + ot(" sub r1,r4,r1 ;@ r1 = Old PC\n"); + MemHandler(1,2); + ot(";@ Push old SR:\n"); + ot(" ldr r0,[r7,#0x4c] ;@ X bit\n"); + ot(" mov r1,r10,lsr #28 ;@ ____NZCV\n"); + ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n"); + ot(" tst r2,#1 ;@ 1 if C!=V\n"); + ot(" eorne r1,r1,#3 ;@ ____NZVC\n"); + ot(" and r0,r0,#0x20000000\n"); + ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n"); + ot(" orr r1,r1,r6,lsl #8 ;@ Include old SR high\n"); + ot(" sub r0,r11,#6 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,1,0,0); // already checked for address error by prev MemHandler ot("\n"); + + // 2. Obtain the exception vector. + ot(" mov r11,r8,lsr #29\n"); + ot(" mov r0,r11\n"); +#if USE_INT_ACK_CALLBACK + ot(";@ call IrqCallback if it is defined\n"); +#if INT_ACK_NEEDS_STUFF + ot(" str r4,[r7,#0x40] ;@ Save PC\n"); + ot(" mov r1,r10,lsr #28\n"); + ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n"); + ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n"); +#endif + ot(" ldr r3,[r7,#0x8c] ;@ IrqCallback\n"); + ot(" add lr,pc,#4*3\n"); + ot(" tst r3,r3\n"); + ot(" streqb r3,[r7,#0x47] ;@ just clear IRQ if there is no callback\n"); + ot(" mvneq r0,#0 ;@ and simulate -1 return\n"); + ot(" bxne r3\n"); +#if INT_ACK_CHANGES_CYCLES + ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n"); +#endif + ot(";@ get IRQ vector address:\n"); + ot(" cmn r0,#1 ;@ returned -1?\n"); + ot(" addeq r0,r11,#0x18 ;@ use autovector then\n"); + ot(" cmn r0,#2 ;@ returned -2?\n"); // should be safe as above add should never result in -2 + ot(" moveq r0,#0x18 ;@ use spurious interrupt then\n"); +#else // !USE_INT_ACK_CALLBACK ot(";@ Clear irq:\n"); - ot(" mov r0,#0\n"); - ot(" strb r0,[r7,#0x47]\n"); - ot(" subs r5,r5,#%d ;@ Subtract cycles\n",46); - ot(" mov pc,r11 ;@ Return\n"); + ot(" mov r2,#0\n"); + ot(" strb r2,[r7,#0x47]\n"); + ot(" add r0,r0,#0x18 ;@ use autovector\n"); +#endif + ot(" mov r0,r0,lsl #2 ;@ get vector address\n"); + ot("\n"); + ot(" ldr r11,[r7,#0x60] ;@ Get Memory base\n"); + ot(";@ Read IRQ Vector:\n"); + MemHandler(0,2,0,0); + ot(" tst r0,r0 ;@ uninitialized int vector?\n"); + ot(" moveq r0,#0x3c\n"); + #ifdef MEMHANDLERS_DIRECT_PREFIX + ot(" bleq %sread32 ;@ Call read32(r0) handler\n", MEMHANDLERS_DIRECT_PREFIX); + #else + ot(" moveq lr,pc\n"); + ot(" ldreq pc,[r7,#0x70] ;@ Call read32(r0) handler\n"); + #endif +#if USE_CHECKPC_CALLBACK + ot(" add lr,pc,#4\n"); + ot(" add r0,r0,r11 ;@ r0 = Memory Base + New PC\n"); + #ifdef MEMHANDLERS_DIRECT_PREFIX + ot(" bl %scheckpc ;@ Call checkpc()\n", MEMHANDLERS_DIRECT_PREFIX); + #else + ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); + #endif + #if EMULATE_ADDRESS_ERRORS_JUMP + ot(" mov r4,r0\n"); + #else + ot(" bic r4,r0,#1\n"); + #endif +#else + ot(" add r4,r0,r11 ;@ r4 = Memory Base + New PC\n"); + #if EMULATE_ADDRESS_ERRORS_JUMP + ot(" bic r4,r4,#1\n"); + #endif +#endif ot("\n"); - ot("Exception%s\n", ms?"":":"); + // 4. Obtain a new context and resume instruction processing. + // note: the obtain part was already done in previous steps +#if EMULATE_ADDRESS_ERRORS_JUMP + ot(" tst r4,#1\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#endif + ot(" ldr r6,[r7,#0x54]\n"); + ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n"); + ot(" subs r5,r5,#44 ;@ Subtract cycles\n"); + ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); + ot(" b CycloneEnd\n"); ot("\n"); + + // -------------- + // trashes all temp regs + ot("Exception%s\n", ms?"":":"); + ot(" ;@ Cause an Exception - Vector number in r0\n"); ot(" mov r11,lr ;@ Preserve ARM return address\n"); - PrintException(); - ot(" mov pc,r11 ;@ Return\n"); + ot(" bic r8,r8,#0xff000000\n"); + ot(" orr r8,r8,r0,lsl #24 ;@ abuse r8\n"); + + // 1. Make a temporary copy of the status register and set the status register for exception processing. + ot(" ldr r6,[r7,#0x44] ;@ Get old SR high, abuse r6\n"); + ot(" ldr r2,[r7,#0x58] ;@ state flags\n"); + ot(" and r3,r6,#0x27 ;@ clear trace and unused flags\n"); + ot(" orr r3,r3,#0x20 ;@ set supervisor mode\n"); + ot(" bic r2,r2,#3 ;@ clear stopped and trace states\n"); + ot(" str r2,[r7,#0x58]\n"); + ot(" strb r3,[r7,#0x44] ;@ Put new SR high\n"); + ot("\n"); + + // 3. Save the current processor context. + ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n"); + ot(" tst r6,#0x20\n"); + ot(";@ get our SP:\n"); + ot(" ldreq r2,[r7,#0x48] ;@ ...or OSP as our stack pointer\n"); + ot(" streq r0,[r7,#0x48]\n"); + ot(" moveq r0,r2\n"); + ot(";@ Push old PC onto stack\n"); + ot(" ldr r1,[r7,#0x60] ;@ Get Memory base\n"); + ot(" sub r0,r0,#4 ;@ Predecremented A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + ot(" sub r1,r4,r1 ;@ r1 = Old PC\n"); + MemHandler(1,2); + ot(";@ Push old SR:\n"); + ot(" ldr r0,[r7,#0x4c] ;@ X bit\n"); + ot(" mov r1,r10,lsr #28 ;@ ____NZCV\n"); + ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n"); + ot(" tst r2,#1 ;@ 1 if C!=V\n"); + ot(" eorne r1,r1,#3 ;@ ____NZVC\n"); + ot(" and r0,r0,#0x20000000\n"); + ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n"); + ot(" ldr r0,[r7,#0x3c] ;@ A7\n"); + ot(" orr r1,r1,r6,lsl #8 ;@ Include SR high\n"); + ot(" sub r0,r0,#2 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,1,0,0); + ot("\n"); + + // 2. Obtain the exception vector + ot(";@ Read Exception Vector:\n"); + ot(" mov r0,r8,lsr #24\n"); + ot(" mov r0,r0,lsl #2\n"); + MemHandler(0,2,0,0); + ot(" ldr r3,[r7,#0x60] ;@ Get Memory base\n"); +#if USE_CHECKPC_CALLBACK + ot(" add lr,pc,#4\n"); + ot(" add r0,r0,r3 ;@ r0 = Memory Base + New PC\n"); + #ifdef MEMHANDLERS_DIRECT_PREFIX + ot(" bl %scheckpc ;@ Call checkpc()\n", MEMHANDLERS_DIRECT_PREFIX); + #else + ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); + #endif + #if EMULATE_ADDRESS_ERRORS_JUMP + ot(" mov r4,r0\n"); + #else + ot(" bic r4,r0,#1\n"); + #endif +#else + ot(" add r4,r0,r3 ;@ r4 = Memory Base + New PC\n"); + #if EMULATE_ADDRESS_ERRORS_JUMP + ot(" bic r4,r4,#1\n"); + #endif +#endif ot("\n"); + + // 4. Resume execution. +#if EMULATE_ADDRESS_ERRORS_JUMP + ot(" tst r4,#1\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#endif + ot(" ldr r6,[r7,#0x54]\n"); + ot(" bx r11 ;@ Return\n"); + ot("\n"); + + // -------------- +#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO + // first some wrappers: I see no point inlining this code, + // as it will be executed in really rare cases. + AddressErrorWrapper('r', "data", 0x11); + AddressErrorWrapper('r', "prg", 0x12); + AddressErrorWrapper('w', "data", 0x01); + // there are no program writes + // cpu space is only for bus errors? + ot("ExceptionAddressError_r_prg_r4%s\n", ms?"":":"); + ot(" ldr r1,[r7,#0x44]\n"); + ot(" ldr r3,[r7,#0x60] ;@ Get Memory base\n"); + ot(" mov r6,#0x12\n"); + ot(" sub r11,r4,r3\n"); + ot(" tst r1,#0x20\n"); + ot(" orrne r6,r6,#4\n"); + ot("\n"); + + ot("ExceptionAddressError%s\n", ms?"":":"); + ot(";@ r6 - info word (without instruction/not bit), r11 - faulting address\n"); + + // 1. Make a temporary copy of the status register and set the status register for exception processing. + ot(" ldrb r0,[r7,#0x44] ;@ Get old SR high\n"); + ot(" ldr r2,[r7,#0x58] ;@ state flags\n"); + ot(" and r3,r0,#0x27 ;@ clear trace and unused flags\n"); + ot(" orr r3,r3,#0x20 ;@ set supervisor mode\n"); + ot(" strb r3,[r7,#0x44] ;@ Put new SR high\n"); + ot(" bic r2,r2,#3 ;@ clear stopped and trace states\n"); + ot(" tst r2,#4\n"); + ot(" orrne r6,r6,#8 ;@ complete info word\n"); + ot(" orr r2,r2,#4 ;@ set activity bit: 'not processing instruction'\n"); +#if EMULATE_HALT + ot(" tst r2,#8\n"); + ot(" orrne r2,r2,#0x10 ;@ HALT\n"); + ot(" orr r2,r2,#8 ;@ processing address error\n"); + ot(" str r2,[r7,#0x58]\n"); + ot(" movne r5,#0\n"); + ot(" bne CycloneEndNoBack ;@ bye bye\n"); +#else + ot(" str r2,[r7,#0x58]\n"); +#endif + ot(" and r10,r10,#0xf0000000\n"); + ot(" orr r10,r10,r0,lsl #4 ;@ some preparations for SR push\n"); + ot("\n"); + + // 3. Save the current processor context + additional information. + ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n"); + ot(" tst r10,#0x200\n"); + ot(";@ get our SP:\n"); + ot(" ldreq r2,[r7,#0x48] ;@ ...or OSP as our stack pointer\n"); + ot(" streq r0,[r7,#0x48]\n"); + ot(" moveq r0,r2\n"); + // PC + ot(";@ Push old PC onto stack\n"); + ot(" ldr r1,[r7,#0x60] ;@ Get Memory base\n"); + ot(" sub r0,r0,#4 ;@ Predecremented A7\n"); + ot(" sub r1,r4,r1 ;@ r1 = Old PC\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,2,0,EMULATE_HALT); + // SR + ot(";@ Push old SR:\n"); + ot(" ldr r0,[r7,#0x4c] ;@ X bit\n"); + ot(" mov r1,r10,ror #28 ;@ ____NZCV\n"); + ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n"); + ot(" tst r2,#1 ;@ 1 if C!=V\n"); + ot(" eorne r1,r1,#3 ;@ ____NZVC\n"); + ot(" and r0,r0,#0x20000000\n"); + ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n"); + ot(" ldr r0,[r7,#0x3c] ;@ A7\n"); + ot(" and r10,r10,#0xf0000000\n"); + ot(" sub r0,r0,#2 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,1,0,0); + // IR (instruction register) + ot(";@ Push IR:\n"); + ot(" ldr r0,[r7,#0x3c] ;@ A7\n"); + ot(" mov r1,r8\n"); + ot(" sub r0,r0,#2 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,1,0,0); + // access address + ot(";@ Push address:\n"); + ot(" ldr r0,[r7,#0x3c] ;@ A7\n"); + ot(" mov r1,r11\n"); + ot(" sub r0,r0,#4 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,2,0,0); + // information word + ot(";@ Push info word:\n"); + ot(" ldr r0,[r7,#0x3c] ;@ A7\n"); + ot(" mov r1,r6\n"); + ot(" sub r0,r0,#2 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,1,0,0); + ot("\n"); + + // 2. Obtain the exception vector + ot(";@ Read Exception Vector:\n"); + ot(" mov r0,#0x0c\n"); + MemHandler(0,2,0,0); + ot(" ldr r3,[r7,#0x60] ;@ Get Memory base\n"); +#if USE_CHECKPC_CALLBACK + ot(" add lr,pc,#4\n"); + ot(" add r0,r0,r3 ;@ r0 = Memory Base + New PC\n"); + #ifdef MEMHANDLERS_DIRECT_PREFIX + ot(" bl %scheckpc ;@ Call checkpc()\n", MEMHANDLERS_DIRECT_PREFIX); + #else + ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); + #endif + ot(" mov r4,r0\n"); +#else + ot(" add r4,r0,r3 ;@ r4 = Memory Base + New PC\n"); +#endif + ot("\n"); + +#if EMULATE_ADDRESS_ERRORS_JUMP && EMULATE_HALT + ot(" tst r4,#1\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#else + ot(" bic r4,r4,#1\n"); +#endif + + // 4. Resume execution. + ot(" ldr r6,[r7,#0x54]\n"); + ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n"); + ot(" subs r5,r5,#50 ;@ Subtract cycles\n"); + ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); + ot(" b CycloneEnd\n"); + ot("\n"); +#endif + + // -------------- +#if EMULATE_TRACE + // expects srh and irq level in r1, next opcode already fetched to r8 + ot("CycloneDoTraceWithChecks%s\n", ms?"":":"); + ot(" ldr r0,[r7,#0x58]\n"); + ot(" cmp r5,#0\n"); + ot(" orr r0,r0,#2 ;@ go to trace mode\n"); + ot(" str r0,[r7,#0x58]\n"); + ot(" blt CycloneEnd\n"); // should take care of situation where we come here when already tracing + ot(";@ CheckInterrupt:\n"); + ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n"); + ot(" beq CycloneDoTrace\n"); + ot(" cmp r0,#6 ;@ irq>6 ?\n"); + ot(" andle r1,r1,#7 ;@ Get interrupt mask\n"); + ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n"); + ot(" bgt CycloneDoInterruptGoBack\n"); + ot("\n"); + + // expects next opcode to be already fetched to r8 + ot("CycloneDoTrace%s\n", ms?"":":"); + ot(" str r5,[r7,#0x9c] ;@ save cycles\n"); + ot(" ldr r1,[r7,#0x98]\n"); + ot(" mov r5,#0\n"); + ot(" str r1,[r7,#0xa0]\n"); + ot(" adr r0,TraceEnd\n"); + ot(" str r0,[r7,#0x98] ;@ store TraceEnd as CycloneEnd hadler\n"); + ot(" ldr pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); + ot("\n"); + + ot("TraceEnd%s\n", ms?"":":"); + ot(" ldr r2,[r7,#0x58]\n"); + ot(" ldr r0,[r7,#0x9c] ;@ restore cycles\n"); + ot(" ldr r1,[r7,#0xa0] ;@ old CycloneEnd handler\n"); + ot(" mov r10,r10,lsl #28\n"); + ot(" add r5,r0,r5\n"); + ot(" str r1,[r7,#0x98]\n"); + ot(";@ still tracing?\n"); // exception might have happend + ot(" tst r2,#2\n"); + ot(" beq TraceDisabled\n"); + ot(";@ trace exception\n"); +#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO + ot(" ldr r1,[r7,#0x58]\n"); + ot(" mov r0,#9\n"); + ot(" orr r1,r1,#4 ;@ set activity bit: 'not processing instruction'\n"); + ot(" str r1,[r7,#0x58]\n"); +#else + ot(" mov r0,#9\n"); +#endif + ot(" bl Exception\n"); + ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n"); + ot(" subs r5,r5,#34 ;@ Subtract cycles\n"); + ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); + ot(" b CycloneEnd\n"); + ot("\n"); + ot("TraceDisabled%s\n", ms?"":":"); + ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n"); + ot(" cmp r5,#0\n"); + ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); + ot(" b CycloneEnd\n"); + ot("\n"); +#endif } // --------------------------------------------------------------------------- // Call Read(r0), Write(r0,r1) or Fetch(r0) -// Trashes r0-r3 -int MemHandler(int type,int size) +// Trashes r0-r3,r12,lr +int MemHandler(int type,int size,int addrreg,int need_addrerr_check) { - int func=0; - func=0x68+type*0xc+(size<<2); // Find correct offset + int func=0x68+type*0xc+(size<<2); // Find correct offset + char what[32]; - if (Debug&4) ot(" str r4,[r7,#0x40] ;@ Save PC\n"); - if (Debug&3) ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n"); +#if MEMHANDLERS_NEED_FLAGS + ot(" mov r3,r10,lsr #28\n"); + ot(" strb r3,[r7,#0x46] ;@ Save Flags (NZCV)\n"); +#endif + FlushPC(); - ot(" mov lr,pc\n"); +#if (MEMHANDLERS_ADDR_MASK & 0xff000000) + ot(" bic r0,r%i,#0x%08x\n", addrreg, MEMHANDLERS_ADDR_MASK & 0xff000000); + addrreg=0; +#endif +#if (MEMHANDLERS_ADDR_MASK & 0x00ff0000) + ot(" bic r0,r%i,#0x%08x\n", addrreg, MEMHANDLERS_ADDR_MASK & 0x00ff0000); + addrreg=0; +#endif +#if (MEMHANDLERS_ADDR_MASK & 0x0000ff00) + ot(" bic r0,r%i,#0x%08x\n", addrreg, MEMHANDLERS_ADDR_MASK & 0x0000ff00); + addrreg=0; +#endif +#if (MEMHANDLERS_ADDR_MASK & 0x000000ff) + ot(" bic r0,r%i,#0x%08x\n", addrreg, MEMHANDLERS_ADDR_MASK & 0x000000ff); + addrreg=0; +#endif + +#if EMULATE_ADDRESS_ERRORS_IO + if (size > 0 && need_addrerr_check) + { + ot(" add lr,pc,#4*%i\n", addrreg==0?2:3); // helps to prevent interlocks + if (addrreg != 0) ot(" mov r0,r%i\n", addrreg); + ot(" tst r0,#1 ;@ address error?\n"); + switch (type) { + case 0: ot(" bne ExceptionAddressError_r_data\n"); break; + case 1: ot(" bne ExceptionAddressError_w_data\n"); break; + case 2: ot(" bne ExceptionAddressError_r_prg\n"); break; + } + } + else +#endif + + sprintf(what, "%s%d", type==0 ? "read" : (type==1 ? "write" : "fetch"), 8<>12); fflush(stdout); } // Update progress - OpAny(op); + if (!is_op_hot(op)) + OpAny(op); } ot("\n"); @@ -201,41 +1101,146 @@ static void PrintOpcodes() printf("]\n"); } +// helper +static void ott(const char *str, int par, const char *nl, int nlp, int counter, int size) +{ + switch(size) { + case 0: if((counter&7)==0) ot(ms?" dcb ":" .byte "); break; + case 1: if((counter&7)==0) ot(ms?" dcw ":" .hword "); break; + case 2: if((counter&7)==0) ot(ms?" dcd ":" .long "); break; + } + ot(str, par); + if((counter&7)==7) ot(nl,nlp); else ot(","); +} + static void PrintJumpTable() { int i=0,op=0,len=0; ot(";@ -------------------------- Jump Table --------------------------\n"); - ot("JumpTab%s\n", ms?"":":"); - len=0xfffe; // Hmmm, armasm 2.50.8684 messes up with a 0x10000 long jump table - for (i=0;i=0; u--) if(op == CyJump[u]) break; // already done with this op? + if(u==-1 && op >= 0) { + ott("Op%.4x",op," ;@ %.4x\n",i,handlers,2); + indexes[op] = handlers; + handlers++; + } + } + if(handlers&7) { + fseek(AsmFile, -1, SEEK_CUR); // remove last comma + for(i = 8-(handlers&7); i > 0; i--) + ot(",000000"); + ot("\n"); + } + if(ms) { + for(i = (0x4000-handlers)/8; i > 0; i--) + ot(" dcd 0,0,0,0,0,0,0,0\n"); + } else { + ot(ms?"":" .rept 0x%x\n .long 0,0,0,0,0,0,0,0\n .endr\n", (0x4000-handlers)/8); + } + printf("total distinct hanlers: %i\n",handlers); + // output data + for(i=0,ip=0; i < 0xf000; i++, ip++) { + op=CyJump[i]; + if(op == -2) { + // it must skip a-line area, because we keep our data there + ott("0x%.4x", handlers<<4, "\n",0,ip++,1); + ott("0x%.4x", 0x1000, "\n",0,ip,1); + i+=0xfff; + continue; + } + for(reps=1; i < 0xf000; i++, reps++) if(op != CyJump[i+1]) break; + if(op>=0) out=indexes[op]<<4; else out=0; // unrecognised + if(reps <= 0xe || reps==0x10) { + if(reps!=0x10) out|=reps; else out|=0xf; // 0xf means 0x10 (0xf appeared to be unused anyway) + ott("0x%.4x", out, "\n",0,ip,1); + } else { + ott("0x%.4x", out, "\n",0,ip++,1); + ott("0x%.4x", reps,"\n",0,ip,1); + } + } + if(ip&1) ott("0x%.4x", 0, "\n",0,ip++,1); + if(ip&7) fseek(AsmFile, -1, SEEK_CUR); // remove last comma + if(ip&7) { + for(i = 8-(ip&7); i > 0; i--) + ot(",0x0000"); + } + ot("\n"); + if(ms) { + for(i = (0x2000-ip/2)/8+1; i > 0; i--) + ot(" dcd 0,0,0,0,0,0,0,0\n"); + } else { + ot(" .rept 0x%x\n .long 0,0,0,0,0,0,0,0\n .endr\n", (0x2000-ip/2)/8+1); + } + ot("\n"); + free(indexes); +#else + ot("CycloneJumpTab%s\n", ms?"":":"); + len=0xfffe; // Hmmm, armasm 2.50.8684 messes up with a 0x10000 long jump table + // notaz: same thing with GNU as 2.9-psion-98r2 (reloc overflow) + // this is due to COFF objects using only 2 bytes for reloc count + + for (i=0;i=0) ott("Op%.4x",op," ;@ %.4x\n",i-7,i,2); + else if(op==-2) ott("Op__al",0, " ;@ %.4x\n",i-7,i,2); + else if(op==-3) ott("Op__fl",0, " ;@ %.4x\n",i-7,i,2); + else ott("Op____",0, " ;@ %.4x\n",i-7,i,2); + } + if(i&7) fseek(AsmFile, -1, SEEK_CUR); // remove last comma + + ot("\n"); + ot(";@ notaz: we don't want to crash if we run into those 2 missing opcodes\n"); + ot(";@ so we leave this pattern to patch it later\n"); + ot("%s 0x78563412\n", ms?" dcd":" .long"); + ot("%s 0x56341290\n", ms?" dcd":" .long"); +#endif } static int CycloneMake() { - char *name="Cyclone.s"; - + int i; + const char *name="Cyclone.s"; + const char *globl=ms?"export":".global"; + // Open the assembly file if (ms) name="Cyclone.asm"; AsmFile=fopen(name,"wt"); if (AsmFile==NULL) return 1; - + printf("Making %s...\n",name); ot("\n;@ Cyclone 68000 Emulator v%x.%.3x - Assembler Output\n\n",CycloneVer>>12,CycloneVer&0xfff); - ot(";@ Copyright (c) 2011 FinalDave (emudave (at) gmail.com)\n\n"); + ot(";@ Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com)\n"); + ot(";@ Copyright (c) 2005-2011 Gražvydas \"notaz\" Ignotas (notasas (at) gmail.com)\n\n"); ot(";@ This code is licensed under the GNU General Public License version 2.0 and the MAME License.\n"); ot(";@ You can choose the license that has the most advantages for you.\n\n"); @@ -243,36 +1248,53 @@ static int CycloneMake() CyJump=(int *)malloc(0x40000); if (CyJump==NULL) return 1; memset(CyJump,0xff,0x40000); // Init to -1 + for(i=0xa000; i<0xb000; i++) CyJump[i] = -2; // a-line emulation + for(i=0xf000; i<0x10000; i++) CyJump[i] = -3; // f-line emulation - if (ms) - { - ot(" area |.text|, code\n"); - ot(" export CycloneRun\n"); - ot(" export CycloneVer\n"); - ot("\n"); - ot("CycloneVer dcd 0x%.4x\n",CycloneVer); - } - else - { - ot(" .global CycloneRun\n"); - ot(" .global CycloneVer\n"); - ot("CycloneVer: .long 0x%.4x\n",CycloneVer); - } + ot(ms?" area |.text|, code\n":" .text\n .align 4\n\n"); + ot(" %s CycloneInit\n",globl); + ot(" %s CycloneReset\n",globl); + ot(" %s CycloneRun\n",globl); + ot(" %s CycloneSetSr\n",globl); + ot(" %s CycloneGetSr\n",globl); + ot(" %s CycloneFlushIrq\n",globl); + ot(" %s CyclonePack\n",globl); + ot(" %s CycloneUnpack\n",globl); + ot(" %s CycloneVer\n",globl); +#if (CYCLONE_FOR_GENESIS == 2) + ot(" %s CycloneSetRealTAS\n",globl); + ot(" %s CycloneDoInterrupt\n",globl); + ot(" %s CycloneDoTrace\n",globl); + ot(" %s CycloneJumpTab\n",globl); + ot(" %s Op____\n",globl); + ot(" %s Op6001\n",globl); + ot(" %s Op6601\n",globl); + ot(" %s Op6701\n",globl); +#endif + ot("\n"); + ot(ms?"CycloneVer dcd 0x":"CycloneVer: .long 0x"); + ot("%.4x\n",CycloneVer); ot("\n"); PrintFramework(); + arm_op_count = 0; PrintOpcodes(); + printf("~%i ARM instructions used for opcode handlers\n", arm_op_count); PrintJumpTable(); if (ms) ot(" END\n"); + ot("\n\n;@ vim:filetype=armasm\n"); + fclose(AsmFile); AsmFile=NULL; +#if 0 printf("Assembling...\n"); // Assemble the file if (ms) system("armasm Cyclone.asm"); else system("as -o Cyclone.o Cyclone.s"); printf("Done!\n\n"); +#endif free(CyJump); return 0; @@ -282,7 +1304,8 @@ int main() { printf("\n Cyclone 68000 Emulator v%x.%.3x - Core Creator\n\n",CycloneVer>>12,CycloneVer&0xfff); - // Make GAS and ARMASM versions - for (ms=0;ms<2;ms++) CycloneMake(); + // Make GAS or ARMASM version + CycloneMake(); return 0; } + diff --git a/Cyclone/Makefile b/Cyclone/Makefile index 3202a98..4849d98 100644 --- a/Cyclone/Makefile +++ b/Cyclone/Makefile @@ -1,14 +1,15 @@ CFLAGS += -Wall -ggdb +CXXFLAGS += $(CFLAGS) OBJS = Main.o Ea.o OpAny.o OpArith.o OpBranch.o OpLogic.o OpMove.o Disa/Disa.o -all: cyclone.s +all: Cyclone.s -cyclone.s: cyclone_gen +Cyclone.s: cyclone_gen ./$< cyclone_gen: $(OBJS) $(CXX) -o $@ $^ $(LDFLAGS) clean: - $(RM) $(OBJS) cyclone_gen cyclone.s + $(RM) $(OBJS) cyclone_gen Cyclone.s diff --git a/Cyclone/OpAny.cpp b/Cyclone/OpAny.cpp index 92e695e..9d45a01 100644 --- a/Cyclone/OpAny.cpp +++ b/Cyclone/OpAny.cpp @@ -1,18 +1,22 @@ // This file is part of the Cyclone 68000 Emulator -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + #include "app.h" +int opend_op_changes_cycles, opend_check_interrupt, opend_check_trace; + static unsigned char OpData[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; -static unsigned short CPU_CALL OpRead16(unsigned int a) +static unsigned short OpRead16(unsigned int a) { return (unsigned short)( (OpData[a&15]<<8) | OpData[(a+1)&15] ); } @@ -21,9 +25,7 @@ static unsigned short CPU_CALL OpRead16(unsigned int a) void OpUse(int op,int use) { char text[64]=""; - - if (op>=0) - CyJump[op]=use; + CyJump[op]=use; if (op!=use) return; @@ -36,93 +38,181 @@ void OpUse(int op,int use) ot(";@ ---------- [%.4x] %s uses Op%.4x ----------\n",op,text,use); } -void OpFirst() +void OpStart(int op, int sea, int tea, int op_changes_cycles, int supervisor_check) { - ot(" ldrh r8,[r4],#2 ;@ Fetch first opcode\n"); - ot(" ldr pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); -} + int last_op_count=arm_op_count; -void OpStart(int op) -{ Cycles=0; OpUse(op,op); // This opcode obviously uses this handler ot("Op%.4x%s\n", op, ms?"":":"); + + if (supervisor_check) + { + // checks for supervisor bit, if not set, jumps to SuperEnd() + // also sets r11 to SR high value, SuperChange() uses this + ot(" ldr r11,[r7,#0x44] ;@ Get SR high\n"); + } + if ((sea >= 0x10 && sea != 0x3c) || (tea >= 0x10 && tea != 0x3c)) + { +#if MEMHANDLERS_NEED_PREV_PC + ot(" str r4,[r7,#0x50] ;@ Save prev PC + 2\n"); +#endif +#if MEMHANDLERS_NEED_CYCLES + ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n"); +#endif + } + if (supervisor_check) + { + ot(" tst r11,#0x20 ;@ Check we are in supervisor mode\n"); + ot(" beq WrongPrivilegeMode ;@ No\n"); + } + if ((sea >= 0x10 && sea != 0x3c) || (tea >= 0x10 && tea != 0x3c)) { +#if MEMHANDLERS_CHANGE_CYCLES + if (op_changes_cycles) + ot(" mov r5,#0\n"); +#endif + } + if (last_op_count!=arm_op_count) + ot("\n"); + pc_dirty = 1; + opend_op_changes_cycles = opend_check_interrupt = opend_check_trace = 0; } -void OpEnd() +void OpEnd(int sea, int tea) { - ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n"); + int did_fetch=0; + opend_check_trace = opend_check_trace && EMULATE_TRACE; +#if MEMHANDLERS_CHANGE_CYCLES + if ((sea >= 0x10 && sea != 0x3c) || (tea >= 0x10 && tea != 0x3c)) + { + if (opend_op_changes_cycles) + { + ot(" ldr r0,[r7,#0x5c] ;@ Load Cycles\n"); + ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n"); + ot(" add r5,r0,r5\n"); + did_fetch=1; + } + else + { + ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n"); + } + } +#endif + if (!did_fetch) + ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n"); + if (opend_check_trace) + ot(" ldr r1,[r7,#0x44]\n"); ot(" subs r5,r5,#%d ;@ Subtract cycles\n",Cycles); - ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); - ot(" b CycloneEnd\n"); - ltorg(); + if (opend_check_trace) + { + ot(";@ CheckTrace:\n"); + ot(" tst r1,#0x80\n"); + ot(" bne CycloneDoTraceWithChecks\n"); + ot(" cmp r5,#0\n"); + } + if (opend_check_interrupt) + { + ot(" blt CycloneEnd\n"); + ot(";@ CheckInterrupt:\n"); + if (!opend_check_trace) + ot(" ldr r1,[r7,#0x44]\n"); + ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47] + ot(" ldreq pc,[r6,r8,asl #2] ;@ Jump to next opcode handler\n"); + ot(" cmp r0,#6 ;@ irq>6 ?\n"); + ot(" andle r1,r1,#7 ;@ Get interrupt mask\n"); + ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n"); + ot(" ldrle pc,[r6,r8,asl #2] ;@ Jump to next opcode handler\n"); + ot(" b CycloneDoInterruptGoBack\n"); + } + else + { + ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); + ot(" b CycloneEnd\n"); + } ot("\n"); } -int OpBase(int op) +int OpBase(int op,int size,int sepa) { int ea=op&0x3f; // Get Effective Address - if (ea<0x10) return op&~0xf; // Use 1 handler for d0-d7 and a0-a7 - if (ea>=0x18 && ea<0x28 && (ea&7)==7) return op; // Specific handler for (a7)+ and -(a7) + if (ea<0x10) return sepa?(op&~0x7):(op&~0xf); // Use 1 handler for d0-d7 and a0-a7 + if (size==0&&(ea==0x1f || ea==0x27)) return op; // Specific handler for (a7)+ and -(a7) if (ea<0x38) return op&~7; // Use 1 handler for (a0)-(a7), etc... return op; } // Get flags, trashes r2 -int OpGetFlags(int subtract,int xbit) +int OpGetFlags(int subtract,int xbit,int specialz) { - ot(" mrs r9,cpsr ;@ r9=flags\n"); + if (specialz) ot(" orr r2,r10,#0xb0000000 ;@ for old Z\n"); + + ot(" mrs r10,cpsr ;@ r10=flags\n"); - if (subtract) ot(" eor r9,r9,#0x20000000 ;@ Invert carry\n"); + if (specialz) ot(" andeq r10,r10,r2 ;@ fix Z\n"); - if (Accu&1) if (xbit) + if (subtract) ot(" eor r10,r10,#0x20000000 ;@ Invert carry\n"); + + if (xbit) { - ot(" mov r2,r9,lsr #28\n"); - ot(" strb r2,[r7,#0x45] ;@ Save X bit\n"); + ot(" str r10,[r7,#0x4c] ;@ Save X bit\n"); } return 0; } // ----------------------------------------------------------------- +int g_op; + void OpAny(int op) { memset(OpData,0x33,sizeof(OpData)); OpData[0]=(unsigned char)(op>>8); OpData[1]=(unsigned char)op; - - if ((op&0xf100)==0x0000) OpArith(op); - if ((op&0xc000)==0x0000) OpMove(op); - if ((op&0xf5bf)==0x003c) OpArithSr(op); // Ori/Andi/Eori $nnnn,sr - if ((op&0xf100)==0x0100) OpBtstReg(op); - if ((op&0xff00)==0x0800) OpBtstImm(op); - if ((op&0xf900)==0x4000) OpNeg(op); - if ((op&0xf1c0)==0x41c0) OpLea(op); - if ((op&0xf9c0)==0x40c0) OpMoveSr(op); - if ((op&0xfff8)==0x4840) OpSwap(op); - if ((op&0xffc0)==0x4840) OpPea(op); - if ((op&0xffb8)==0x4880) OpExt(op); - if ((op&0xfb80)==0x4880) OpMovem(op); - if ((op&0xff00)==0x4a00) OpTst(op); - if ((op&0xfff0)==0x4e40) OpTrap(op); - if ((op&0xfff8)==0x4e50) OpLink(op); - if ((op&0xfff8)==0x4e58) OpUnlk(op); - if ((op&0xfff0)==0x4e60) OpMoveUsp(op); - if ((op&0xfff8)==0x4e70) Op4E70(op); // Reset/Rts etc - if ((op&0xff80)==0x4e80) OpJsr(op); - if ((op&0xf000)==0x5000) OpAddq(op); - if ((op&0xf0c0)==0x50c0) OpSet(op); - if ((op&0xf0f8)==0x50c8) OpDbra(op); - if ((op&0xf000)==0x6000) OpBranch(op); - if ((op&0xf100)==0x7000) OpMoveq(op); - if ((op&0xa000)==0x8000) OpArithReg(op); // Or/Sub/And/Add - if ((op&0xb1f0)==0x8100) OpAbcd(op); - if ((op&0xb0c0)==0x80c0) OpMul(op); - if ((op&0x90c0)==0x90c0) OpAritha(op); - if ((op&0xb138)==0x9100) OpAddx(op); - if ((op&0xf000)==0xb000) OpCmpEor(op); - if ((op&0xf130)==0xc100) OpExg(op); - if ((op&0xf000)==0xe000) OpAsr(op); // Asr/l/Ror/l etc - if ((op&0xf8c0)==0xe0c0) OpAsrEa(op); - + g_op=op; + + if ((op&0xf100)==0x0000) OpArith(op); // + + if ((op&0xc000)==0x0000) OpMove(op); // + + if ((op&0xf5bf)==0x003c) OpArithSr(op); // + Ori/Andi/Eori $nnnn,sr + if ((op&0xf100)==0x0100) OpBtstReg(op); // + + if ((op&0xf138)==0x0108) OpMovep(op); // + + if ((op&0xff00)==0x0800) OpBtstImm(op); // + + if ((op&0xf900)==0x4000) OpNeg(op); // + + if ((op&0xf140)==0x4100) OpChk(op); // + + if ((op&0xf1c0)==0x41c0) OpLea(op); // + + if ((op&0xf9c0)==0x40c0) OpMoveSr(op); // + + if ((op&0xffc0)==0x4800) OpNbcd(op); // + + if ((op&0xfff8)==0x4840) OpSwap(op); // + + if ((op&0xffc0)==0x4840) OpPea(op); // + + if ((op&0xffb8)==0x4880) OpExt(op); // + + if ((op&0xfb80)==0x4880) OpMovem(op); // + + if ((op&0xff00)==0x4a00) OpTst(op); // + + if ((op&0xffc0)==0x4ac0) OpTas(op); // + + if ((op&0xfff0)==0x4e40) OpTrap(op); // + + if ((op&0xfff8)==0x4e50) OpLink(op); // + + if ((op&0xfff8)==0x4e58) OpUnlk(op); // + + if ((op&0xfff0)==0x4e60) OpMoveUsp(op); // + + if ((op&0xfff8)==0x4e70) Op4E70(op); // + Reset/Rts etc + if ((op&0xfffd)==0x4e70) OpStopReset(op);// + + if ((op&0xff80)==0x4e80) OpJsr(op); // + + if ((op&0xf000)==0x5000) OpAddq(op); // + + if ((op&0xf0c0)==0x50c0) OpSet(op); // + + if ((op&0xf0f8)==0x50c8) OpDbra(op); // + + if ((op&0xf000)==0x6000) OpBranch(op); // + + if ((op&0xf100)==0x7000) OpMoveq(op); // + + if ((op&0xa000)==0x8000) OpArithReg(op); // + Or/Sub/And/Add + if ((op&0xb1f0)==0x8100) OpAbcd(op); // + + if ((op&0xb0c0)==0x80c0) OpMul(op); // + + if ((op&0x90c0)==0x90c0) OpAritha(op); // + + if ((op&0xb130)==0x9100) OpAddx(op); // + + if ((op&0xf000)==0xb000) OpCmpEor(op); // + + if ((op&0xf138)==0xb108) OpCmpm(op); // + + if ((op&0xf130)==0xc100) OpExg(op); // + + if ((op&0xf000)==0xe000) OpAsr(op); // + Asr/l/Ror/l etc + if ((op&0xf8c0)==0xe0c0) OpAsrEa(op); // + + + if (op==0xffff) + { + SuperEnd(); + } } + diff --git a/Cyclone/OpArith.cpp b/Cyclone/OpArith.cpp index c8144f4..d762049 100644 --- a/Cyclone/OpArith.cpp +++ b/Cyclone/OpArith.cpp @@ -1,22 +1,25 @@ // This file is part of the Cyclone 68000 Emulator -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + #include "app.h" // --------------------- Opcodes 0x0000+ --------------------- -// Emit an Ori/And/Sub/Add/Eor/Cmp Immediate opcode, 0000ttt0 00aaaaaa +// Emit an Ori/And/Sub/Add/Eor/Cmp Immediate opcode, 0000ttt0 ssaaaaaa int OpArith(int op) { int type=0,size=0; int sea=0,tea=0; int use=0; + const char *shiftstr=""; // Get source and target EA type=(op>>9)&7; if (type==4 || type>=7) return 1; @@ -26,27 +29,28 @@ int OpArith(int op) // See if we can do this opcode: if (EaCanRead(tea,size)==0) return 1; - if (type!=6 && EaCanWrite(tea)==0) return 1; + if (EaCanWrite(tea)==0 || EaAn(tea)) return 1; - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; + OpStart(op, sea, tea); Cycles=4; - EaCalc(10,0x0000, sea,size); - EaRead(10, 10, sea,size,1); + // imm must be read first + EaCalcReadNoSE(-1,10,sea,size,0); + EaCalcReadNoSE((type!=6)?11:-1,0,tea,size,0x003f); - EaCalc(11,0x003f, tea,size); - EaRead(11, 0, tea,size,1); + if (size<2) shiftstr=(char *)(size?",asl #16":",asl #24"); + if (size<2) ot(" mov r10,r10,asl #%i\n",size?16:24); ot(";@ Do arithmetic:\n"); - if (type==0) ot(" orr r1,r0,r10\n"); - if (type==1) ot(" and r1,r0,r10\n"); - if (type==2) ot(" subs r1,r0,r10 ;@ Defines NZCV\n"); - if (type==3) ot(" adds r1,r0,r10 ;@ Defines NZCV\n"); - if (type==5) ot(" eor r1,r0,r10\n"); - if (type==6) ot(" cmp r0,r10 ;@ Defines NZCV\n"); + if (type==0) ot(" orr r1,r10,r0%s\n",shiftstr); + if (type==1) ot(" and r1,r10,r0%s\n",shiftstr); + if (type==2||type==6) + ot(" rsbs r1,r10,r0%s ;@ Defines NZCV\n",shiftstr); + if (type==3) ot(" adds r1,r10,r0%s ;@ Defines NZCV\n",shiftstr); + if (type==5) ot(" eor r1,r10,r0%s\n",shiftstr); if (type<2 || type==5) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); // 0,1,5 @@ -59,7 +63,7 @@ int OpArith(int op) if (type!=6) { - EaWrite(11, 1, tea,size,1); + EaWrite(11, 1, tea,size,0x003f,1); } // Correct cycles: @@ -70,11 +74,11 @@ int OpArith(int op) else { if (size>=2) Cycles+=4; - if (tea>=0x10) Cycles+=4; - if (Amatch && type==1 && size>=2 && tea<0x10) Cycles-=2; + if (tea>=8) Cycles+=4; + if (type==1 && size>=2 && tea<8) Cycles-=2; } - OpEnd(); + OpEnd(sea,tea); return 0; } @@ -95,20 +99,22 @@ int OpAddq(int op) // See if we can do this opcode: if (EaCanRead (ea,size)==0) return 1; - if (EaCanWrite(ea )==0) return 1; + if (EaCanWrite(ea) ==0) return 1; + if (size == 0 && EaAn(ea) ) return 1; - use=op; if (ea<0x38) use&=~7; - if ((ea&0x38)==0x08) { size=2; use&=~0xc0; } // Every addq #n,An is 32-bit + use=OpBase(op,size,1); if (num!=8) use|=0x0e00; // If num is not 8, use same handler if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); + OpStart(op,ea); Cycles=ea<8?4:8; - if (size>=2 && ea!=8) Cycles+=4; + if(type==0&&size==1) Cycles=ea<0x10?4:8; + if(size>=2) Cycles=ea<0x10?8:12; + + if (size>0 && (ea&0x38)==0x08) size=2; // addq.w #n,An is also 32-bit - EaCalc(10,0x003f, ea,size); - EaRead(10, 0, ea,size,1); + EaCalcReadNoSE(11,0,ea,size,0x003f); shift=32-(8<=0) ot(" mov r2,r8,lsr #%d ;@ Get quick value\n", lsr); - else ot(" mov r2,r8,lsl #%d ;@ Get quick value\n",-lsr); + ot(" and r2,r8,#0x0e00 ;@ Get quick value\n"); + + if (lsr>=0) sprintf(count,"r2,lsr #%d", lsr); + else sprintf(count,"r2,lsl #%d", -lsr); - ot(" and r2,r2,#0x%.4x\n",7<>12)&5; rea =(op>> 9)&7; - dir =(op>> 8)&1; + dir =(op>> 8)&1; // er,re size=(op>> 6)&3; if (size>=3) return 1; ea = op&0x3f; if (dir && ea<0x10) return 1; // addx/subx opcode // See if we can do this opcode: - if (dir==0 && EaCanWrite(rea)==0) return 1; - if (dir && EaCanWrite( ea)==0) return 1; + if (dir==0 && EaCanRead (ea,size)==0) return 1; + if (dir && EaCanWrite(ea)==0) return 1; + if ((size==0||!(type&1))&&EaAn(ea)) return 1; - use=OpBase(op); + use=OpBase(op,size); use&=~0x0e00; // Use same opcode for Dn if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; + OpStart(op,ea); Cycles=4; - ot(";@ Get r10=EA r11=EA value\n"); - EaCalc(10,0x003f, ea,size); - EaRead(10, 11, ea,size,1); - ot(";@ Get r0=Register r1=Register value\n"); - EaCalc( 0,0x0e00,rea,size); - EaRead( 0, 1,rea,size,1); + EaCalcReadNoSE(dir?11:-1,0,ea,size,0x003f); + + EaCalcReadNoSE(dir?-1:11,1,rea,size,0x0e00); ot(";@ Do arithmetic:\n"); - if (type==0) ot(" orr "); - if (type==1) ot(" subs "); - if (type==4) ot(" and "); - if (type==5) ot(" adds "); - if (dir) ot("r1,r11,r1\n"); - else ot("r1,r1,r11\n"); + if (type==0) strop = "orr"; + if (type==1) strop = (char *) (dir ? "subs" : "rsbs"); + if (type==4) strop = "and"; + if (type==5) strop = "adds"; + + if (size==0) asl=",asl #24"; + if (size==1) asl=",asl #16"; + + if (size<2) ot(" mov r0,r0%s\n",asl); + ot(" %s r1,r0,r1%s\n",strop,asl); if ((type&1)==0) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); @@ -185,13 +199,23 @@ int OpArithReg(int op) ot("\n"); ot(";@ Save result:\n"); - if (dir) EaWrite(10, 1, ea,size,1); - else EaWrite( 0, 1,rea,size,1); - - if (size==1 && ea>=0x10) Cycles+=4; - if (size>=2) { if (ea<0x10) Cycles+=4; else Cycles+=2; } + if (size<2) ot(" mov r1,r1,asr #%d\n",size?16:24); + if (dir) EaWrite(11, 1, ea,size,0x003f,0,0); + else EaWrite(11, 1,rea,size,0x0e00,0,0); + + if(rea==ea) { + if(ea<8) Cycles=(size>=2)?8:4; else Cycles+=(size>=2)?26:14; + } else if(dir) { + Cycles+=4; + if(size>=2) Cycles+=4; + } else { + if(size>=2) { + Cycles+=2; + if(ea<0x10||ea==0x3c) Cycles+=2; + } + } - OpEnd(); + OpEnd(ea); return 0; } @@ -209,41 +233,55 @@ int OpMul(int op) ea = op&0x3f; // See if we can do this opcode: - if (EaCanRead(ea,1)==0) return 1; + if (EaCanRead(ea,1)==0||EaAn(ea)) return 1; - use=OpBase(op); + use=OpBase(op,1); use&=~0x0e00; // Use same for all registers if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=type?70:133; + OpStart(op,ea); + if(type) Cycles=54; + else Cycles=sign?158:140; - EaCalc(10,0x003f, ea, 1); - EaRead(10, 10, ea, 1); + EaCalcReadNoSE(-1,0,ea,1,0x003f); - EaCalc (0,0x0e00,rea, 2); - EaRead (0, 2,rea, 2); + EaCalc(11,0x0e00,rea, 2); + EaRead(11, 2,rea, 2,0x0e00); - if (type==0) + ot(" movs r1,r0,asl #16\n"); + + if (type==0) // div { - ot(" cmp r10,#0\n"); - ot(" moveq r10,#1 ;@ Divide by zero\n"); + // the manual says C is always cleared, but neither Musashi nor FAME do that + //ot(" bic r10,r10,#0x20000000 ;@ always clear C\n"); + ot(" beq divzero%.4x ;@ division by zero\n",op); ot("\n"); if (sign) { - ot(" mov r11,#0 ;@ r11 = 1 if the result is negative\n"); - ot(" eorlt r11,r11,#1\n"); - ot(" rsblt r10,r10,#0 ;@ Make r10 positive\n"); + ot(" mov r12,#0 ;@ r12 = 1 or 2 if the result is negative\n"); + ot(" tst r2,r2\n"); + ot(" orrmi r12,r12,#2\n"); + ot(" rsbmi r2,r2,#0 ;@ Make r2 positive\n"); ot("\n"); - ot(" cmp r2,#0\n"); - ot(" eorlt r11,r11,#1\n"); - ot(" rsblt r2,r2,#0 ;@ Make r2 positive\n"); + ot(" movs r0,r1,asr #16\n"); + ot(" orrmi r12,r12,#1\n"); + ot(" rsbmi r0,r0,#0 ;@ Make r0 positive\n"); ot("\n"); + ot(";@ detect the nasty 0x80000000 / -1 situation\n"); + ot(" mov r3,r2,asr #31\n"); + ot(" eors r3,r3,r1,asr #16\n"); + ot(" beq wrendofop%.4x\n",op); + } + else + { + ot(" mov r0,r1,lsr #16 ;@ use only 16 bits of divisor\n"); } - ot(";@ Divide r2 by r10\n"); + ot("\n"); + ot(";@ Divide r2 by r0\n"); ot(" mov r3,#0\n"); - ot(" mov r1,r10\n"); + ot(" mov r1,r0\n"); ot("\n"); ot(";@ Shift up divisor till it's just less than numerator\n"); ot("Shift%.4x%s\n",op,ms?"":":"); @@ -256,62 +294,85 @@ int OpMul(int op) ot(" cmp r2,r1\n"); ot(" adc r3,r3,r3 ;@ Double r3 and add 1 if carry set\n"); ot(" subcs r2,r2,r1\n"); - ot(" teq r1,r10\n"); + ot(" teq r1,r0\n"); ot(" movne r1,r1,lsr #1\n"); ot(" bne Divide%.4x\n",op); ot("\n"); + ot(";@r3==quotient,r2==remainder\n"); if (sign) { - ot(" tst r11,r11\n"); - ot(" rsbne r3,r3,#0 ;@ Negate if result is negative\n"); - } + // sign correction + ot(" and r1,r12,#1\n"); + ot(" teq r1,r12,lsr #1\n"); + ot(" rsbne r3,r3,#0 ;@ negate if quotient is negative\n"); + ot(" tst r12,#2\n"); + ot(" rsbne r2,r2,#0 ;@ negate the remainder if divident was negative\n"); + ot("\n"); - ot(" mov r11,r2 ;@ Remainder\n"); + // signed overflow check + ot(" mov r1,r3,asl #16\n"); + ot(" cmp r3,r1,asr #16 ;@ signed overflow?\n"); + ot(" orrne r10,r10,#0x10000000 ;@ set overflow flag\n"); + ot(" bne endofop%.4x ;@ overflow!\n",op); + ot("\n"); + ot("wrendofop%.4x%s\n",op,ms?"":":"); + } + else + { + // overflow check + ot(" movs r1,r3,lsr #16 ;@ check for overflow condition\n"); + ot(" orrne r10,r10,#0x10000000 ;@ set overflow flag\n"); + ot(" bne endofop%.4x ;@ overflow!\n",op); + ot("\n"); + } - ot(" adds r1,r3,#0 ;@ Defines NZ, clears CV\n"); + ot(" mov r1,r3,lsl #16 ;@ Clip to 16-bits\n"); + ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); OpGetFlags(0,0); - ot(" mov r1,r1,lsl #16 ;@ Clip to 16-bits\n"); ot(" mov r1,r1,lsr #16\n"); - ot(" orr r1,r1,r11,lsl #16 ;@ Insert remainder\n"); + ot(" orr r1,r1,r2,lsl #16 ;@ Insert remainder\n"); } if (type==1) { - char *shift="asr"; - ot(";@ Get 16-bit signs right:\n"); - if (sign==0) { ot(" mov r10,r10,lsl #16\n"); shift="lsr"; } + ot(" mov r0,r1,%s #16\n",sign?"asr":"lsr"); ot(" mov r2,r2,lsl #16\n"); - - if (sign==0) ot(" mov r10,r10,lsr #16\n"); - ot(" mov r2,r2,%s #16\n",shift); + ot(" mov r2,r2,%s #16\n",sign?"asr":"lsr"); ot("\n"); - ot(" mul r1,r2,r10\n"); + ot(" mul r1,r2,r0\n"); ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); OpGetFlags(0,0); - - if (Amatch && ea==0x3c) Cycles-=4; } ot("\n"); - EaWrite(0, 1,rea, 2); + EaWrite(11, 1,rea, 2,0x0e00,1); + if (type==0) ot("endofop%.4x%s\n",op,ms?"":":"); + OpEnd(ea); - OpEnd(); + if (type==0) // div + { + ot("divzero%.4x%s\n",op,ms?"":":"); + ot(" mov r0,#5 ;@ Divide by zero\n"); + ot(" bl Exception\n"); + Cycles+=38; + OpEnd(ea); + ot("\n"); + } return 0; } // Get X Bit into carry - trashes r2 -static int GetXBit(int subtract) +int GetXBit(int subtract) { ot(";@ Get X bit:\n"); - ot(" ldrb r2,[r7,#0x45]\n"); - if (subtract) ot(" mvn r2,r2,lsl #28 ;@ Invert it\n"); - else ot(" mov r2,r2,lsl #28\n"); + ot(" ldr r2,[r7,#0x4c]\n"); + if (subtract) ot(" mvn r2,r2 ;@ Invert it\n"); ot(" msr cpsr_flg,r2 ;@ Get into Carry\n"); ot("\n"); return 0; @@ -322,59 +383,158 @@ static int GetXBit(int subtract) int OpAbcd(int op) { int use=0; - int type=0,sea=0,addr=0,dea=0; + int type=0,sea=0,mem=0,dea=0; - type=(op>>14)&1; + type=(op>>14)&1; // sbcd/abcd dea =(op>> 9)&7; - addr=(op>> 3)&1; + mem =(op>> 3)&1; sea = op &7; - if (addr) { sea|=0x20; dea|=0x20; } + if (mem) { sea|=0x20; dea|=0x20; } - use=op&~0x0e07; // Use same opcode for all registers + use=op&~0x0e07; // Use same opcode for all registers.. + if (sea==0x27) use|=0x0007; // ___x.b -(a7) + if (dea==0x27) use|=0x0e00; // ___x.b -(a7) if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=6; + OpStart(op,sea,dea); Cycles=6; - EaCalc( 0,0x0007, sea,0); - EaRead( 0, 10, sea,0,1); - EaCalc(11,0x0e00, dea,0); - EaRead(11, 1, dea,0,1); + if (mem) + { + ot(";@ Get src/dest EA vals\n"); + EaCalc (0,0x000f, sea,0,1); + EaRead (0, 6, sea,0,0x000f,1); + EaCalcReadNoSE(11,0,dea,0,0x0e00); + } + else + { + ot(";@ Get src/dest reg vals\n"); + EaCalcReadNoSE(-1,6,sea,0,0x0007); + EaCalcReadNoSE(11,0,dea,0,0x0e00); + ot(" mov r6,r6,asl #24\n"); + } + ot(" mov r1,r0,asl #24\n\n"); - ot(" ldrb r2,[r7,#0x45] ;@ Get X bit\n"); - ot(" tst r2,#2\n"); - ot(" addne r10,r10,#0x01000000 ;@ Add carry bit\n"); + ot(" bic r10,r10,#0xb1000000 ;@ clear all flags except old Z\n"); if (type) { - ot(";@ Add units into r2:\n"); - ot(" and r2,r1, #0x0f000000\n"); - ot(" and r0,r10,#0x0f000000\n"); - ot(" add r2,r2,r0\n"); - ot(" cmp r2,#0x0a000000\n"); - ot(" addpl r1,r1,#0x06000000 ;@ Decimal adjust units\n"); - ot(" add r1,r1,r10 ;@ Add BCD\n"); - ot(" mov r0,r1,lsr #24\n"); - ot(" cmp r0,#0xa0\n"); - ot(" addpl r1,r1,#0x60000000 ;@ Decimal adjust tens\n"); - OpGetFlags(0,1); + ot(" ldr r0,[r7,#0x4c] ;@ Get X bit\n"); + ot(" mov r3,#0x00f00000\n"); + ot(" and r2,r3,r1,lsr #4\n"); + ot(" tst r0,#0x20000000\n"); + ot(" and r0,r3,r6,lsr #4\n"); + ot(" add r0,r0,r2\n"); + ot(" addne r0,r0,#0x00100000\n"); +// ot(" tst r0,#0x00800000\n"); +// ot(" orreq r10,r10,#0x01000000 ;@ Undefined V behavior\n"); + ot(" cmp r0,#0x00900000\n"); + ot(" addhi r0,r0,#0x00600000 ;@ Decimal adjust units\n"); + + ot(" mov r2,r1,lsr #28\n"); + ot(" add r0,r0,r2,lsl #24\n"); + ot(" mov r2,r6,lsr #28\n"); + ot(" add r0,r0,r2,lsl #24\n"); + ot(" cmp r0,#0x09900000\n"); + ot(" orrhi r10,r10,#0x20000000 ;@ C\n"); + ot(" subhi r0,r0,#0x0a000000\n"); +// ot(" and r3,r10,r0,lsr #3 ;@ Undefined V behavior part II\n"); +// ot(" orr r10,r10,r3,lsl #4 ;@ V\n"); + ot(" movs r0,r0,lsl #4\n"); + ot(" orrmi r10,r10,#0x90000000 ;@ Undefined N+V behavior\n"); // this is what Musashi really does + ot(" bicne r10,r10,#0x40000000 ;@ Z flag\n"); } else { - ot(";@ Sub units into r2:\n"); - ot(" and r2,r1, #0x0f000000\n"); - ot(" and r0,r10,#0x0f000000\n"); - ot(" subs r2,r2,r0\n"); - ot(" submi r1,r1,#0x06000000 ;@ Decimal adjust units\n"); - ot(" subs r1,r1,r10 ;@ Subtract BCD\n"); - ot(" submis r1,r1,#0x60000000 ;@ Decimal adjust tens\n"); - OpGetFlags(1,1); + ot(" ldr r0,[r7,#0x4c] ;@ Get X bit\n"); + ot(" mov r3,#0x00f00000\n"); + ot(" and r2,r3,r6,lsr #4\n"); + ot(" tst r0,#0x20000000\n"); + ot(" and r0,r3,r1,lsr #4\n"); + ot(" sub r0,r0,r2\n"); + ot(" subne r0,r0,#0x00100000\n"); +// ot(" tst r0,#0x00800000\n"); +// ot(" orreq r10,r10,#0x01000000 ;@ Undefined V behavior\n"); + ot(" cmp r0,#0x00900000\n"); + ot(" subhi r0,r0,#0x00600000 ;@ Decimal adjust units\n"); + + ot(" mov r2,r1,lsr #28\n"); + ot(" add r0,r0,r2,lsl #24\n"); + ot(" mov r2,r6,lsr #28\n"); + ot(" sub r0,r0,r2,lsl #24\n"); + ot(" cmp r0,#0x09900000\n"); + ot(" orrhi r10,r10,#0xa0000000 ;@ N and C\n"); + ot(" addhi r0,r0,#0x0a000000\n"); +// ot(" and r3,r10,r0,lsr #3 ;@ Undefined V behavior part II\n"); +// ot(" orr r10,r10,r3,lsl #4 ;@ V\n"); + ot(" movs r0,r0,lsl #4\n"); +// ot(" orrmi r10,r10,#0x80000000 ;@ Undefined N behavior\n"); + ot(" bicne r10,r10,#0x40000000 ;@ Z flag\n"); } + + ot(" str r10,[r7,#0x4c] ;@ Save X bit\n"); + ot("\n"); + + EaWrite(11, 0, dea,0,0x0e00,1); + + ot(" ldr r6,[r7,#0x54]\n"); + OpEnd(sea,dea); + + return 0; +} + +// 01001000 00eeeeee - nbcd +int OpNbcd(int op) +{ + int use=0; + int ea=0; + + ea=op&0x3f; + + if(EaCanWrite(ea)==0||EaAn(ea)) return 1; + + use=OpBase(op,0); + if(op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op,ea); Cycles=6; + if(ea >= 8) Cycles+=2; + + EaCalcReadNoSE(6,0,ea,0,0x003f); + + // this is rewrite of Musashi's code + ot(" ldr r2,[r7,#0x4c]\n"); + ot(" bic r10,r10,#0xb0000000 ;@ clear all flags, except Z\n"); + ot(" mov r0,r0,asl #24\n"); + ot(" and r2,r2,#0x20000000\n"); + ot(" add r2,r0,r2,lsr #5 ;@ add X\n"); + ot(" rsb r11,r2,#0x9a000000 ;@ do arithmetic\n"); + + ot(" cmp r11,#0x9a000000\n"); + ot(" beq finish%.4x\n",op); ot("\n"); - EaWrite(11, 1, dea,0,1); + ot(" mvn r3,r11,lsr #31 ;@ Undefined V behavior\n",op); + ot(" and r2,r11,#0x0f000000\n"); + ot(" cmp r2,#0x0a000000\n"); + ot(" andeq r11,r11,#0xf0000000\n"); + ot(" addeq r11,r11,#0x10000000\n"); + ot(" and r3,r3,r11,lsr #31 ;@ Undefined V behavior part II\n",op); + ot(" movs r1,r11,asr #24\n"); + ot(" bicne r10,r10,#0x40000000 ;@ Z\n"); + ot(" orr r10,r10,r3,lsl #28 ;@ save V\n",op); + ot(" orr r10,r10,#0x20000000 ;@ C\n"); + ot("\n"); + + EaWrite(6, 1, ea,0,0x3f,0,0); + + ot("finish%.4x%s\n",op,ms?"":":"); + ot(" tst r11,r11\n"); + ot(" orrmi r10,r10,#0x80000000 ;@ N\n"); + ot(" str r10,[r7,#0x4c] ;@ Save X\n"); + ot("\n"); - OpEnd(); + ot(" ldr r6,[r7,#0x54]\n"); + OpEnd(ea); return 0; } @@ -385,6 +545,7 @@ int OpAritha(int op) { int use=0; int type=0,size=0,sea=0,dea=0; + const char *asr=""; // Suba/Cmpa/Adda/(invalid): type=(op>>13)&3; if (type>=3) return 1; @@ -396,83 +557,118 @@ int OpAritha(int op) // See if we can do this opcode: if (EaCanRead(sea,size)==0) return 1; - use=OpBase(op); + use=OpBase(op,size); use&=~0x0e00; // Use same opcode for An if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; - EaCalc ( 0,0x003f, sea,size); - EaRead ( 0, 10, sea,size); + OpStart(op,sea); Cycles=(size==2)?6:8; + if(size==2&&(sea<0x10||sea==0x3c)) Cycles+=2; + if(type==1) Cycles=6; + + // EA calculation order defines how situations like suba.w (A0)+, A0 get handled. + // different emus act differently in this situation, I couldn't fugure which is right behaviour. + //if (type == 1) + { + EaCalcReadNoSE(-1,0,sea,size,0x003f); + EaCalcReadNoSE(type!=1?11:-1,1,dea,2,0x0e00); + } +#if 0 + else + { + EaCalcReadNoSE(type!=1?11:-1,1,dea,2,0x0e00); + EaCalcReadNoSE(-1,0,sea,size,0x003f); + } +#endif - EaCalc ( 0,0x0e00, dea,2); - EaRead ( 0, 1, dea,2); + if (size<2) ot(" mov r0,r0,asl #%d\n\n",size?16:24); + if (size<2) asr=(char *)(size?",asr #16":",asr #24"); - if (type==0) ot(" sub r1,r1,r10\n"); - if (type==1) ot(" cmp r1,r10 ;@ Defines NZCV\n"); + if (type==0) ot(" sub r1,r1,r0%s\n",asr); + if (type==1) ot(" cmp r1,r0%s ;@ Defines NZCV\n",asr); if (type==1) OpGetFlags(1,0); // Get Cmp flags - if (type==2) ot(" add r1,r1,r10\n"); + if (type==2) ot(" add r1,r1,r0%s\n",asr); ot("\n"); - - EaWrite( 0, 1, dea,2); - if (Amatch && sea==0x3c) Cycles-=size<2?4:8; // Correct? - if (size>=2) { if (sea<0x10) Cycles+=4; else Cycles+=2; } + if (type!=1) EaWrite(11, 1, dea,2,0x0e00); - OpEnd(); + OpEnd(sea); return 0; } // --------------------- Opcodes 0x9100+ --------------------- -// Emit a Subx/Addx opcode, 1t01ddd1 zz000sss addx.z Ds,Dd +// Emit a Subx/Addx opcode, 1t01ddd1 zz00rsss addx.z Ds,Dd int OpAddx(int op) { int use=0; - int type=0,size=0,dea=0,sea=0; + int type=0,size=0,dea=0,sea=0,mem=0; + const char *asl=""; - type=(op>>12)&5; + type=(op>>14)&1; dea =(op>> 9)&7; size=(op>> 6)&3; if (size>=3) return 1; - sea = op&0x3f; + sea = op&7; + mem =(op>> 3)&1; // See if we can do this opcode: if (EaCanRead(sea,size)==0) return 1; if (EaCanWrite(dea)==0) return 1; - use=OpBase(op); - use&=~0x0e00; // Use same opcode for Dn + if (mem) { sea+=0x20; dea+=0x20; } + + use=op&~0x0e07; // Use same opcode for Dn + if (size==0&&sea==0x27) use|=0x0007; // ___x.b -(a7) + if (size==0&&dea==0x27) use|=0x0e00; // ___x.b -(a7) if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=8; + OpStart(op,sea,dea); Cycles=4; + if(size>=2) Cycles+=4; + if(sea>=0x10) Cycles+=2; - ot(";@ Get r10=EA r11=EA value\n"); - EaCalc( 0,0x003f,sea,size); - EaRead( 0, 11,sea,size,1); - ot(";@ Get r0=Register r1=Register value\n"); - EaCalc( 0,0x0e00,dea,size); - EaRead( 0, 1,dea,size,1); + if (mem) + { + ot(";@ Get src/dest EA vals\n"); + EaCalc (0,0x000f, sea,size,1); + EaRead (0, 6, sea,size,0x000f,1); + EaCalcReadNoSE(11,0,dea,size,0x0e00); + } + else + { + ot(";@ Get src/dest reg vals\n"); + EaCalcReadNoSE(-1,6,sea,size,0x0007); + EaCalcReadNoSE(11,0,dea,size,0x0e00); + if (size<2) ot(" mov r6,r6,asl #%d\n\n",size?16:24); + } + + if (size<2) asl=(char *)(size?",asl #16":",asl #24"); ot(";@ Do arithmetic:\n"); - GetXBit(type==1); + GetXBit(type==0); - if (type==5 && size<2) + if (type==1 && size<2) { ot(";@ Make sure the carry bit will tip the balance:\n"); - if (size==0) ot(" ldr r2,=0x00ffffff\n"); - else ot(" ldr r2,=0x0000ffff\n"); - ot(" orr r11,r11,r2\n"); + ot(" mvn r2,#0\n"); + ot(" orr r6,r6,r2,lsr #%i\n",(size==0)?8:16); ot("\n"); } - if (type==1) ot(" sbcs r1,r1,r11\n"); - if (type==5) ot(" adcs r1,r1,r11\n"); - OpGetFlags(type==1,1); // subtract + if (type==0) ot(" rscs r1,r6,r0%s\n",asl); + if (type==1) ot(" adcs r1,r6,r0%s\n",asl); + ot(" orr r3,r10,#0xb0000000 ;@ for old Z\n"); + OpGetFlags(type==0,1,0); // subtract + if (size<2) { + ot(" movs r2,r1,lsr #%i\n", size?16:24); + ot(" orreq r10,r10,#0x40000000 ;@ add potentially missed Z\n"); + } + ot(" andeq r10,r10,r3 ;@ fix Z\n"); ot("\n"); ot(";@ Save result:\n"); - EaWrite( 0, 1, dea,size,1); + EaWrite(11, 1, dea,size,0x0e00,1); - OpEnd(); + ot(" ldr r6,[r7,#0x54]\n"); + OpEnd(sea,dea); return 0; } @@ -483,6 +679,7 @@ int OpCmpEor(int op) { int rea=0,eor=0; int size=0,ea=0,use=0; + const char *asl=""; // Get EA and register EA rea=(op>>9)&7; @@ -490,41 +687,144 @@ int OpCmpEor(int op) size=(op>>6)&3; if (size>=3) return 1; ea=op&0x3f; + if (eor && (ea>>3) == 1) return 1; // not a valid mode for eor + // See if we can do this opcode: if (EaCanRead(ea,size)==0) return 1; if (eor && EaCanWrite(ea)==0) return 1; + if (EaAn(ea)&&(eor||size==0)) return 1; - use=OpBase(op); + use=OpBase(op,size); use&=~0x0e00; // Use 1 handler for register d0-7 if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=eor?8:4; + OpStart(op,ea); Cycles=4; + if(eor) { + if(ea>8) Cycles+=4; + if(size>=2) Cycles+=4; + } else { + if(size>=2) Cycles+=2; + } - ot(";@ Get EA into r10 and value into r0:\n"); - EaCalc (10,0x003f, ea,size); - EaRead (10, 0, ea,size,1); + ot(";@ Get EA into r11 and value into r0:\n"); + EaCalcReadNoSE(eor?11:-1,0,ea,size,0x003f); ot(";@ Get register operand into r1:\n"); - EaCalc (1 ,0x0e00, rea,size); - EaRead (1, 1, rea,size,1); + EaCalcReadNoSE(-1,1,rea,size,0x0e00); + + if (size<2) ot(" mov r0,r0,asl #%d\n\n",size?16:24); + if (size<2) asl=(char *)(size?",asl #16":",asl #24"); ot(";@ Do arithmetic:\n"); - if (eor==0) ot(" cmp r1,r0\n"); + if (eor==0) ot(" rsbs r1,r0,r1%s\n",asl); if (eor) { - ot(" eor r1,r0,r1\n"); + ot(" eor r1,r0,r1%s\n",asl); ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); } OpGetFlags(eor==0,0); // Cmp like subtract ot("\n"); - if (size>=2) Cycles+=4; // Correct? - if (ea==0x3c) Cycles-=4; + if (eor) EaWrite(11, 1,ea,size,0x003f,1); + + OpEnd(ea); + return 0; +} + +// Emit a Cmpm opcode, 1011ddd1 xx001sss (rrr=Adst, xx=size extension, sss=Asrc) +int OpCmpm(int op) +{ + int size=0,sea=0,dea=0,use=0; + const char *asl=""; + + // get size, get EAs + size=(op>>6)&3; if (size>=3) return 1; + sea=(op&7)|0x18; + dea=(op>>9)&0x3f; + + use=op&~0x0e07; // Use 1 handler for all registers.. + if (size==0&&sea==0x1f) use|=0x0007; // ..except (a7)+ + if (size==0&&dea==0x1f) use|=0x0e00; + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op,sea); Cycles=4; + + ot(";@ Get src operand into r11:\n"); + EaCalc (0,0x0007, sea,size,1); + EaRead (0, 11, sea,size,0x0007,1); + + ot(";@ Get dst operand into r0:\n"); + EaCalcReadNoSE(-1,0,dea,size,0x0e00); + + if (size<2) asl=(char *)(size?",asl #16":",asl #24"); + + ot(" rsbs r0,r11,r0%s\n",asl); + OpGetFlags(1,0); // Cmp like subtract + ot("\n"); + + OpEnd(sea); + return 0; +} + + +// Emit a Chk opcode, 0100ddd1 x0eeeeee (rrr=Dn, x=size extension, eeeeee=ea) +int OpChk(int op) +{ + int rea=0; + int size=0,ea=0,use=0; + + // Get EA and register EA + rea=(op>>9)&7; + if((op>>7)&1) + size=1; // word operation + else size=2; // long + ea=op&0x3f; + + if (EaAn(ea)) return 1; // not a valid mode + if (size!=1) return 1; // 000 variant only supports word + + // See if we can do this opcode: + if (EaCanRead(ea,size)==0) return 1; + + use=OpBase(op,size); + use&=~0x0e00; // Use 1 handler for register d0-7 + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op,ea); Cycles=10; + + ot(";@ Get value into r0:\n"); + EaCalcReadNoSE(-1,0,ea,size,0x003f); + + ot(";@ Get register operand into r1:\n"); + EaCalcReadNoSE(-1,1,rea,size,0x0e00); + + if (size<2) ot(" mov r0,r0,asl #%d\n",size?16:24); + if (size<2) ot(" mov r1,r1,asl #%d\n\n",size?16:24); + + ot(";@ get flags, including undocumented ones\n"); + ot(" and r3,r10,#0x80000000\n"); + ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); + OpGetFlags(0,0); + + ot(";@ is reg negative?\n"); + ot(" bmi chktrap%.4x\n",op); + + ot(";@ Do arithmetic:\n"); + ot(" bic r10,r10,#0x80000000 ;@ N\n"); + ot(" cmp r1,r0\n"); + ot(" bgt chktrap%.4x\n",op); + + ot(";@ old N remains\n"); + ot(" orr r10,r10,r3\n"); + OpEnd(ea); - if (eor) EaWrite(10, 1,ea,size,1); + ot("chktrap%.4x%s ;@ CHK exception:\n",op,ms?"":":"); + ot(" mov r0,#6\n"); + ot(" bl Exception\n"); + Cycles+=40; + OpEnd(ea); - OpEnd(); return 0; } diff --git a/Cyclone/OpBranch.cpp b/Cyclone/OpBranch.cpp index 145fd08..c37620a 100644 --- a/Cyclone/OpBranch.cpp +++ b/Cyclone/OpBranch.cpp @@ -1,26 +1,33 @@ // This file is part of the Cyclone 68000 Emulator -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + #include "app.h" -static void CheckPc() +// in/out address in r0, trashes all temp regs +static void CheckPc(void) { - ot(";@ Check Memory Base+pc (r4)\n"); - ot(" add lr,pc,#4\n"); - ot(" mov r0,r4\n"); +#if USE_CHECKPC_CALLBACK + #ifdef MEMHANDLERS_DIRECT_PREFIX + ot(" bl %scheckpc ;@ Call checkpc()\n", MEMHANDLERS_DIRECT_PREFIX); + #else + ot(";@ Check Memory Base+pc\n"); + ot(" mov lr,pc\n"); ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); - ot(" mov r4,r0\n"); ot("\n"); + #endif +#endif } -// Push 32-bit value in r1 - trashes r0-r3 +// Push 32-bit value in r1 - trashes r0-r3,r12,lr void OpPush32() { ot(";@ Push r1 onto stack\n"); @@ -31,7 +38,7 @@ void OpPush32() ot("\n"); } -// Push SR - trashes r0-r3 +// Push SR - trashes r0-r3,r12,lr void OpPushSr(int high) { ot(";@ Push SR:\n"); @@ -55,7 +62,7 @@ static void PopSr(int high) OpRegToFlags(high); } -// Pop PC - assumes r10=Memory Base - trashes r0-r3 +// Pop PC - trashes r0-r3 static void PopPc() { ot(";@ Pop PC:\n"); @@ -63,9 +70,15 @@ static void PopPc() ot(" add r1,r0,#4 ;@ Postincrement A7\n"); ot(" str r1,[r7,#0x3c] ;@ Save A7\n"); MemHandler(0,2); - ot(" add r4,r0,r10 ;@ r4=Memory Base+PC\n"); + ot(" ldr r1,[r7,#0x60] ;@ Get Memory base\n"); + ot(" add r0,r0,r1 ;@ Memory Base+PC\n"); ot("\n"); CheckPc(); +#if EMULATE_ADDRESS_ERRORS_JUMP + ot(" mov r4,r0\n"); +#else + ot(" bic r4,r0,#1\n"); +#endif } int OpTrap(int op) @@ -75,15 +88,13 @@ int OpTrap(int op) use=op&~0xf; if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(use); + OpStart(op,0x10); ot(" and r0,r8,#0xf ;@ Get trap number\n"); - ot(" orr r0,r0,#0x20\n"); - ot(" mov r0,r0,asl #2\n"); - ot(" str r0,[r7,#0x50] ;@ Save Exception Vector\n"); + ot(" orr r0,r0,#0x20 ;@ 32+n\n"); ot(" bl Exception\n"); ot("\n"); - Cycles=38; OpEnd(); + Cycles=38; OpEnd(0x10); return 0; } @@ -91,38 +102,44 @@ int OpTrap(int op) // --------------------- Opcodes 0x4e50+ --------------------- int OpLink(int op) { - int use=0; + int use=0,reg; use=op&~7; + reg=op&7; + if (reg==7) use=op; if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(use); + OpStart(op,0x10); - ot(";@ Get An\n"); - EaCalc(10, 7, 8, 2); - EaRead(10, 1, 8, 2, 1); + if(reg!=7) { + ot(";@ Get An\n"); + EaCalc(11, 7, 8, 2, 1); + EaRead(11, 1, 8, 2, 7, 1); + } ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n"); ot(" sub r0,r0,#4 ;@ A7-=4\n"); - ot(" mov r11,r0\n"); + ot(" mov r8,r0 ;@ abuse r8\n"); + if(reg==7) ot(" mov r1,r0\n"); ot("\n"); ot(";@ Write An to Stack\n"); MemHandler(1,2); ot(";@ Save to An\n"); - EaWrite(10,11, 8, 2, 1); + if(reg!=7) + EaWrite(11,8, 8, 2, 7, 1); ot(";@ Get offset:\n"); - EaCalc(0,0,0x3c,1); - EaRead(0,0,0x3c,1); + EaCalc(0,0,0x3c,1); // abused r8 is ok because of imm EA + EaRead(0,0,0x3c,1,0); - ot(" add r11,r11,r0 ;@ Add offset to A7\n"); - ot(" str r11,[r7,#0x3c]\n"); + ot(" add r8,r8,r0 ;@ Add offset to A7\n"); + ot(" str r8,[r7,#0x3c]\n"); ot("\n"); Cycles=16; - OpEnd(); + OpEnd(0x10); return 0; } @@ -134,61 +151,101 @@ int OpUnlk(int op) use=op&~7; if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(use); + OpStart(op,0x10); ot(";@ Get An\n"); - EaCalc(10, 7, 8, 2); - EaRead(10, 0, 8, 2, 1); + EaCalc(11, 0xf, 8, 2, 1); + EaRead(11, 0, 8, 2, 0xf, 1); - ot(" add r11,r0,#4 ;@ A7+=4\n"); + ot(" add r8,r0,#4 ;@ A7+=4, abuse r8\n"); ot("\n"); ot(";@ Pop An from stack:\n"); MemHandler(0,2); ot("\n"); - ot(" str r11,[r7,#0x3c] ;@ Save A7\n"); + ot(" str r8,[r7,#0x3c] ;@ Save A7\n"); ot("\n"); ot(";@ An = value from stack:\n"); - EaWrite(10, 0, 8, 2, 1); - + EaWrite(11, 0, 8, 2, 7, 1); + Cycles=12; - OpEnd(); + OpEnd(0x10); return 0; } // --------------------- Opcodes 0x4e70+ --------------------- +// 01001110 01110ttt int Op4E70(int op) { int type=0; - type=op&7; - // 01001110 01110ttt, reset/nop/stop/rte/rtd/rts/trapv/rtr - if (type==1) { OpStart(op); Cycles=4; OpEnd(); return 0; } // nop + type=op&7; // reset/nop/stop/rte/rtd/rts/trapv/rtr - if (type==3 || type==7) // rte/rtr + switch (type) { - OpStart(op); Cycles=20; - PopSr(type==3); - ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); - PopPc(); - if (type==3) CheckInterrupt(); + case 1: // nop + OpStart(op); + Cycles=4; OpEnd(); return 0; - } - if (type==5) // rts - { - OpStart(op); Cycles=16; - ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); + case 3: // rte + OpStart(op,0x10,0,0,1); Cycles=20; + PopSr(1); PopPc(); - OpEnd(); + ot(" ldr r1,[r7,#0x44] ;@ reload SR high\n"); + SuperChange(op,1); +#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO || EMULATE_HALT + ot(" ldr r1,[r7,#0x58]\n"); + ot(" bic r1,r1,#0x0c ;@ clear 'not processing instruction' and 'doing addr error' bits\n"); + ot(" str r1,[r7,#0x58]\n"); +#endif +#if EMULATE_ADDRESS_ERRORS_JUMP + ot(" tst r4,#1 ;@ address error?\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#endif + opend_check_interrupt = 1; + opend_check_trace = 1; + OpEnd(0x10,0); + return 0; + + case 5: // rts + OpStart(op,0x10); Cycles=16; + PopPc(); +#if EMULATE_ADDRESS_ERRORS_JUMP + ot(" tst r4,#1 ;@ address error?\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#endif + OpEnd(0x10); + return 0; + + case 6: // trapv + OpStart(op,0x10,0,1); Cycles=4; + ot(" tst r10,#0x10000000\n"); + ot(" subne r5,r5,#%i\n",34); + ot(" movne r0,#7 ;@ TRAPV exception\n"); + ot(" blne Exception\n"); + opend_op_changes_cycles = 1; + OpEnd(0x10,0); return 0; - } - return 1; + case 7: // rtr + OpStart(op,0x10); Cycles=20; + PopSr(0); + PopPc(); +#if EMULATE_ADDRESS_ERRORS_JUMP + ot(" tst r4,#1 ;@ address error?\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#endif + OpEnd(0x10); + return 0; + + default: + return 1; + } } // --------------------- Opcodes 0x4e80+ --------------------- -// Emit a Jsr/Jmp opcode, 01001110 1mEEEeee +// Emit a Jsr/Jmp opcode, 01001110 1meeeeee int OpJsr(int op) { int use=0; @@ -199,38 +256,46 @@ int OpJsr(int op) // See if we can do this opcode: if (EaCanRead(sea,-1)==0) return 1; - use=OpBase(op); + use=OpBase(op,0); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=14; // Correct? + OpStart(op,(op&0x40)?0:0x10); - ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); + ot(" ldr r11,[r7,#0x60] ;@ Get Memory base\n"); ot("\n"); - EaCalc(0,0x003f,sea,0); + EaCalc(12,0x003f,sea,0); - ot(";@ Jump - Get new PC from r0\n"); - if (op&0x40) + ot(";@ Jump - Get new PC from r12\n"); + ot(" add r0,r12,r11 ;@ Memory Base + New PC\n"); + ot("\n"); + CheckPc(); + if (!(op&0x40)) { - // Jmp - Get new PC from r0 - ot(" add r4,r0,r10 ;@ r4 = Memory Base + New PC\n"); - ot("\n"); + ot(" ldr r2,[r7,#0x3c]\n"); + ot(" sub r1,r4,r11 ;@ r1 = Old PC\n"); } - else - { - ot(";@ Jsr - Push old PC first\n"); - ot(" sub r1,r4,r10 ;@ r1 = Old PC\n"); - ot(" add r4,r0,r10 ;@ r4 = Memory Base + New PC\n"); - ot("\n"); - OpPush32(); +#if EMULATE_ADDRESS_ERRORS_JUMP + // jsr prefetches next instruction before pushing old PC, + // according to http://pasti.fxatari.com/68kdocs/68kPrefetch.html + ot(" mov r4,r0\n"); + ot(" tst r4,#1 ;@ address error?\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#else + ot(" bic r4,r0,#1\n"); +#endif - Cycles+=8; + if (!(op&0x40)) + { + ot(";@ Push old PC onto stack\n"); + ot(" sub r0,r2,#4 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,2); } - if (Amatch && sea==0x10) Cycles-=2; // (An) Correct? - - CheckPc(); + Cycles=(op&0x40) ? 4 : 12; + Cycles+=Ea_add_ns((op&0x40) ? g_jmp_cycle_table : g_jsr_cycle_table, sea); - OpEnd(); + OpEnd((op&0x40)?0:0x10); return 0; } @@ -238,7 +303,7 @@ int OpJsr(int op) // --------------------- Opcodes 0x50c8+ --------------------- // ARM version of 68000 condition codes: -static char *Cond[16]= +static const char * const Cond[16]= { "", "", "hi","ls","cc","cs","ne","eq", "vc","vs","pl","mi","ge","lt","gt","le" @@ -254,43 +319,93 @@ int OpDbra(int op) cc=(op>>8)&15; if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=10; - - ot(";@ Decrement Dn.w\n"); - ot(" and r1,r8,#0x0007\n"); - ot(" mov r1,r1,lsl #2\n"); - ot(" ldrsh r0,[r7,r1]\n"); - ot(" sub r0,r0,#1\n"); - ot(" strh r0,[r7,r1]\n"); - ot("\n"); + OpStart(op); - if (cc>=2) + switch (cc) { - ot(";@ Is the condition true?\n"); - if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n"); - ot(" msr cpsr_flg,r9 ;@ ARM flags = 68000 flags\n"); - if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000\n"); - ot(";@ If so, don't dbra\n"); - ot(" b%s DbraEnd%.4x\n",Cond[cc],op); - ot("\n"); + case 0: // T + case 1: // F + break; + case 2: // hi + ot(" tst r10,#0x60000000 ;@ hi: !C && !Z\n"); + ot(" beq DbraTrue\n\n"); + break; + case 3: // ls + ot(" tst r10,#0x60000000 ;@ ls: C || Z\n"); + ot(" bne DbraTrue\n\n"); + break; + default: + ot(";@ Is the condition true?\n"); + ot(" msr cpsr_flg,r10 ;@ ARM flags = 68000 flags\n"); + ot(";@ If so, don't dbra\n"); + ot(" b%s DbraTrue\n\n",Cond[cc]); + break; } - ot(";@ Check if Dn.w is -1\n"); - ot(" cmps r0,#-1\n"); - ot(" beq DbraEnd%.4x\n",op); - ot("\n"); + if (cc!=0) + { + ot(";@ Decrement Dn.w\n"); + ot(" and r1,r8,#0x0007\n"); + ot(" mov r1,r1,lsl #2\n"); + ot(" ldrsh r0,[r7,r1]\n"); + ot(" sub r0,r0,#1\n"); + ot(" strh r0,[r7,r1]\n"); + ot("\n"); - ot(";@ Get Branch offset:\n"); - ot(" ldrsh r0,[r4]\n"); - ot(" add r4,r4,r0 ;@ r4 = New PC\n"); - ot("\n"); - OpEnd(); + ot(";@ Check if Dn.w is -1\n"); + ot(" cmn r0,#1\n"); + +#if (USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA) || EMULATE_ADDRESS_ERRORS_JUMP + ot(" beq DbraMin1\n"); + ot("\n"); + + ot(";@ Get Branch offset:\n"); + ot(" ldrsh r0,[r4]\n"); + ot(" add r0,r4,r0 ;@ r0 = New PC\n"); + CheckPc(); +#if EMULATE_ADDRESS_ERRORS_JUMP + ot(" mov r4,r0\n"); + ot(" tst r4,#1 ;@ address error?\n"); + ot(" bne ExceptionAddressError_r_prg_r4\n"); +#else + ot(" bic r4,r0,#1\n"); +#endif +#else + ot("\n"); + ot(";@ Get Branch offset:\n"); + ot(" ldrnesh r0,[r4]\n"); + ot(" addeq r4,r4,#2 ;@ Skip branch offset\n"); + ot(" subeq r5,r5,#4 ;@ additional cycles\n"); + ot(" addne r4,r4,r0 ;@ r4 = New PC\n"); + ot(" bic r4,r4,#1\n"); // we do not emulate address errors + ot("\n"); +#endif + Cycles=12-2; + OpEnd(); + } - ot("DbraEnd%.4x%s\n", op, ms?"":":"); - Cycles+=2; - ot(" add r4,r4,#2 ;@ Skip branch offset\n"); - ot("\n"); - OpEnd(); + //if (cc==0||cc>=2) + if (op==0x50c8) + { + ot(";@ condition true:\n"); + ot("DbraTrue%s\n", ms?"":":"); + ot(" add r4,r4,#2 ;@ Skip branch offset\n"); + ot("\n"); + Cycles=12; + OpEnd(); + } + +#if (USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA) || EMULATE_ADDRESS_ERRORS_JUMP + if (op==0x51c8) + { + ot(";@ Dn.w is -1:\n"); + ot("DbraMin1%s\n", ms?"":":"); + ot(" add r4,r4,#2 ;@ Skip branch offset\n"); + ot("\n"); + Cycles=12+2; + OpEnd(); + } +#endif return 0; } @@ -299,9 +414,10 @@ int OpDbra(int op) // Emit a Branch opcode 0110cccc nn (cccc=condition) int OpBranch(int op) { - int size=0,use=0; + int size=0,use=0,checkpc=0; int offset=0; int cc=0; + const char *asr_r11=""; offset=(char)(op&0xff); cc=(op>>8)&15; @@ -310,38 +426,51 @@ int OpBranch(int op) if (offset==0) size=1; if (offset==-1) size=2; + if (size==2) size=0; // 000 model does not support long displacement if (size) use=op; // 16-bit or 32-bit else use=(op&0xff00)+1; // Use same opcode for all 8-bit branches if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=10; // Assume branch taken + OpStart(op,size?0x10:0); + Cycles=10; // Assume branch taken - ot(";@ Get Branch offset:\n"); - if (size) + switch (cc) { - EaCalc(0,0,0x3c,size); - EaRead(0,0,0x3c,size); - if (Amatch && size==1) Cycles-=4; + case 0: // T + case 1: // F + break; + case 2: // hi + ot(" tst r10,#0x60000000 ;@ hi: !C && !Z\n"); + ot(" bne BccDontBranch%i\n\n",8<=2) + if (size) { - ot(";@ Is the condition true?\n"); - if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n"); - ot(" msr cpsr_flg,r9 ;@ ARM flags = 68000 flags\n"); - if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000\n"); - - if (size==0) ot(" mov r0,r0,asr #24 ;@ ...shift down\n\n"); - - ot(" b%s DontBranch%.4x\n",Cond[cc^1],op); - - ot("\n"); + if (size<2) + { + ot(" ldrsh r11,[r4] ;@ Fetch Branch offset\n"); + } + else + { + ot(" ldrh r2,[r4] ;@ Fetch Branch offset\n"); + ot(" ldrh r11,[r4,#2]\n"); + ot(" orr r11,r11,r2,lsl #16\n"); + } } else { - if (size==0) ot(" mov r0,r0,asr #24 ;@ ...shift down\n\n"); + ot(" mov r11,r8,asl #24 ;@ Shift 8-bit signed offset up...\n\n"); + asr_r11=",asr #24"; } ot(";@ Branch taken - Add on r0 to PC\n"); @@ -349,35 +478,49 @@ int OpBranch(int op) if (cc==1) { ot(";@ Bsr - remember old PC\n"); - ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); - ot(" sub r1,r4,r10 ;@ r1 = Old PC\n"); + ot(" ldr r12,[r7,#0x60] ;@ Get Memory base\n"); + ot(" ldr r2,[r7,#0x3c]\n"); + ot(" sub r1,r4,r12 ;@ r1 = Old PC\n"); + if (size) ot(" add r1,r1,#%d\n",1<=2) + // since all "DontBranch" code is same for every size, output only once + if (cc>=2&&(op&0xff00)==0x6700) { - ot("DontBranch%.4x%s\n", op, ms?"":":"); - Cycles-=2; // Branch not taken - OpEnd(); + ot("BccDontBranch%i%s\n", 8<>6)&3; + type=(op>>6)&3; // Btst/Bchg/Bclr/Bset // Get source and target EA sea=(op>>9)&7; tea=op&0x003f; @@ -33,37 +35,43 @@ int OpBtstReg(int op) if (EaCanWrite(tea)==0) return 1; } - use=OpBase(op); + use=OpBase(op,size); use&=~0x0e00; // Use same handler for all registers if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; - if (tea<0x10) Cycles+=2; - if (type>0) Cycles+=4; + OpStart(op,tea); - ot(" mov r10,#1\n"); + if(type==1||type==3) { + Cycles=8; + } else { + Cycles=type?8:4; + if(size>=2) Cycles+=2; + } - EaCalc (0,0x0e00,sea,0); - EaRead (0, 0,sea,0); - ot(" bic r9,r9,#0x40000000 ;@ Blank Z flag\n"); - ot(" mov r10,r10,lsl r0 ;@ Make bit mask\n"); + EaCalcReadNoSE(-1,11,sea,0,0x0e00); + + EaCalcReadNoSE((type>0)?8:-1,0,tea,size,0x003f); + + if (tea>=0x10) + ot(" and r11,r11,#7 ;@ mem - do mod 8\n"); // size always 0 + else ot(" and r11,r11,#31 ;@ reg - do mod 32\n"); // size always 2 ot("\n"); - EaCalc(11,0x003f,tea,size); - EaRead(11, 0,tea,size); - ot(" tst r0,r10 ;@ Do arithmetic\n"); - ot(" orreq r9,r9,#0x40000000 ;@ Get Z flag\n"); + ot(" mov r1,#1\n"); + ot(" tst r0,r1,lsl r11 ;@ Do arithmetic\n"); + ot(" bicne r10,r10,#0x40000000\n"); + ot(" orreq r10,r10,#0x40000000 ;@ Get Z flag\n"); ot("\n"); if (type>0) { - if (type==1) ot(" eor r1,r0,r10 ;@ Toggle bit\n"); - if (type==2) ot(" bic r1,r0,r10 ;@ Clear bit\n"); - if (type==3) ot(" orr r1,r0,r10 ;@ Set bit\n"); + if (type==1) ot(" eor r1,r0,r1,lsl r11 ;@ Toggle bit\n"); + if (type==2) ot(" bic r1,r0,r1,lsl r11 ;@ Clear bit\n"); + if (type==3) ot(" orr r1,r0,r1,lsl r11 ;@ Set bit\n"); ot("\n"); - EaWrite(11, 1,tea,size); + EaWrite(8,1,tea,size,0x003f,0,0); } - OpEnd(); + OpEnd(tea); return 0; } @@ -83,43 +91,54 @@ int OpBtstImm(int op) if (tea<0x10) size=2; // For registers, 32-bits // See if we can do this opcode: - if (EaCanRead(tea,0)==0) return 1; + if (EaCanRead(tea,0)==0||EaAn(tea)||tea==0x3c) return 1; if (type>0) { if (EaCanWrite(tea)==0) return 1; } - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; - if (type<3 && tea<0x10) Cycles+=2; - if (type>0) Cycles+=4; + OpStart(op,sea,tea); - ot(" mov r10,#1\n"); ot("\n"); - EaCalc ( 0,0x0000,sea,0); - EaRead ( 0, 0,sea,0); - ot(" bic r9,r9,#0x40000000 ;@ Blank Z flag\n"); - ot(" mov r10,r10,lsl r0 ;@ Make bit mask\n"); + EaCalcReadNoSE(-1,0,sea,0,0); + ot(" mov r11,#1\n"); + ot(" bic r10,r10,#0x40000000 ;@ Blank Z flag\n"); + if (tea>=0x10) + ot(" and r0,r0,#7 ;@ mem - do mod 8\n"); // size always 0 + else ot(" and r0,r0,#0x1F ;@ reg - do mod 32\n"); // size always 2 + ot(" mov r11,r11,lsl r0 ;@ Make bit mask\n"); ot("\n"); - EaCalc (11,0x003f,tea,size); - EaRead (11, 0,tea,size); - ot(" tst r0,r10 ;@ Do arithmetic\n"); - ot(" orreq r9,r9,#0x40000000 ;@ Get Z flag\n"); + if(type==1||type==3) { + Cycles=12; + } else { + Cycles=type?12:8; + if(size>=2) Cycles+=2; + } + + EaCalcReadNoSE((type>0)?8:-1,0,tea,size,0x003f); + ot(" tst r0,r11 ;@ Do arithmetic\n"); + ot(" orreq r10,r10,#0x40000000 ;@ Get Z flag\n"); ot("\n"); if (type>0) { - if (type==1) ot(" eor r1,r0,r10 ;@ Toggle bit\n"); - if (type==2) ot(" bic r1,r0,r10 ;@ Clear bit\n"); - if (type==3) ot(" orr r1,r0,r10 ;@ Set bit\n"); + if (type==1) ot(" eor r1,r0,r11 ;@ Toggle bit\n"); + if (type==2) ot(" bic r1,r0,r11 ;@ Clear bit\n"); + if (type==3) ot(" orr r1,r0,r11 ;@ Set bit\n"); ot("\n"); - EaWrite(11, 1,tea,size); + EaWrite(8, 1,tea,size,0x003f,0,0); +#if CYCLONE_FOR_GENESIS && !MEMHANDLERS_CHANGE_CYCLES + // this is a bit hacky (device handlers might modify cycles) + if (tea==0x38||tea==0x39) + ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n"); +#endif } - OpEnd(); + OpEnd(sea,tea); return 0; } @@ -134,54 +153,73 @@ int OpNeg(int op) ea =op&0x003f; size=(op>>6)&3; if (size>=3) return 1; - switch (type) - { - case 1: case 2: case 3: break; - default: return 1; // todo - } - // See if we can do this opcode: - if (EaCanRead (ea,size)==0) return 1; + if (EaCanRead (ea,size)==0||EaAn(ea)) return 1; if (EaCanWrite(ea )==0) return 1; - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=size<2?4:6; + OpStart(op,ea); Cycles=size<2?4:6; + if(ea >= 0x10) Cycles*=2; - EaCalc (10,0x003f,ea,size); + EaCalc (11,0x003f,ea,size,0,0); - if (type!=1) EaRead (10,0,ea,size); // Don't need to read for 'clr' + if (type!=1) EaRead (11,0,ea,size,0x003f,0,0); // Don't need to read for 'clr' (or do we, for a dummy read?) if (type==1) ot("\n"); + if (type==0) + { + ot(";@ Negx:\n"); + GetXBit(1); + if(size!=2) ot(" mov r0,r0,asl #%i\n",size?16:24); + ot(" rscs r1,r0,#0 ;@ do arithmetic\n"); + ot(" orr r3,r10,#0xb0000000 ;@ for old Z\n"); + OpGetFlags(1,1,0); + if(size!=2) { + ot(" movs r1,r1,asr #%i\n",size?16:24); + ot(" orreq r10,r10,#0x40000000 ;@ possily missed Z\n"); + } + ot(" andeq r10,r10,r3 ;@ fix Z\n"); + ot("\n"); + } + if (type==1) { ot(";@ Clear:\n"); ot(" mov r1,#0\n"); - ot(" mov r9,#0x40000000 ;@ NZCV=0100\n"); + ot(" mov r10,#0x40000000 ;@ NZCV=0100\n"); ot("\n"); } if (type==2) { ot(";@ Neg:\n"); + if(size!=2) ot(" mov r0,r0,asl #%i\n",size?16:24); ot(" rsbs r1,r0,#0\n"); OpGetFlags(1,1); + if(size!=2) ot(" mov r1,r1,asr #%i\n",size?16:24); ot("\n"); } if (type==3) { ot(";@ Not:\n"); - ot(" mvn r1,r0\n"); + if(size!=2) { + ot(" mov r0,r0,asl #%i\n",size?16:24); + ot(" mvn r1,r0,asr #%i\n",size?16:24); + } + else + ot(" mvn r1,r0\n"); ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); OpGetFlags(0,0); ot("\n"); } - EaWrite(10, 1,ea,size); + if (type==1) eawrite_check_addrerr=1; + EaWrite(11, 1,ea,size,0x003f,0,0); - OpEnd(); + OpEnd(ea); return 0; } @@ -199,14 +237,14 @@ int OpSwap(int op) OpStart(op); Cycles=4; - EaCalc (10,0x0007,ea,2); - EaRead (10, 0,ea,2); + EaCalc (11,0x0007,ea,2,1); + EaRead (11, 0,ea,2,0x0007,1); ot(" mov r1,r0,ror #16\n"); ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); OpGetFlags(0,0); - EaWrite(10, 1,8,2); + EaWrite(11, 1,8,2,0x0007,1); OpEnd(); @@ -224,21 +262,21 @@ int OpTst(int op) size=(op>>6)&3; if (size>=3) return 1; // See if we can do this opcode: - if (EaCanWrite(sea)==0) return 1; + if (EaCanWrite(sea)==0||EaAn(sea)) return 1; - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; + OpStart(op,sea); Cycles=4; - EaCalc ( 0,0x003f,sea,size); - EaRead ( 0, 0,sea,size); + EaCalc ( 0,0x003f,sea,size,1); + EaRead ( 0, 0,sea,size,0x003f,1); ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n"); - ot(" mrs r9,cpsr ;@ r9=flags\n"); + ot(" mrs r10,cpsr ;@ r10=flags\n"); ot("\n"); - OpEnd(); + OpEnd(sea); return 0; } @@ -254,21 +292,21 @@ int OpExt(int op) size=(op>>6)&1; shift=32-(8<=2; + OpStart(op,ea,0,changed_cycles); Cycles=8; + if (ea<8) Cycles=4; - ot(" mov r1,#0\n"); + if (cc) + ot(" mov r1,#0\n"); - if (cc!=1) + switch (cc) { - ot(";@ Is the condition true?\n"); - if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n"); - ot(" msr cpsr_flg,r9 ;@ ARM flags = 68000 flags\n"); - if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n"); - ot(" mvn%s r1,r1\n",cond[cc]); + case 0: // T + ot(" mvn r1,#0\n"); + if (ea<8) Cycles+=2; + break; + case 1: // F + break; + case 2: // hi + ot(" tst r10,#0x60000000 ;@ hi: !C && !Z\n"); + ot(" mvneq r1,r1\n"); + if (ea<8) ot(" subeq r5,r5,#2 ;@ Extra cycles\n"); + break; + case 3: // ls + ot(" tst r10,#0x60000000 ;@ ls: C || Z\n"); + ot(" mvnne r1,r1\n"); + if (ea<8) ot(" subne r5,r5,#2 ;@ Extra cycles\n"); + break; + default: + ot(";@ Is the condition true?\n"); + ot(" msr cpsr_flg,r10 ;@ ARM flags = 68000 flags\n"); + ot(" mvn%s r1,r1\n",cond[cc]); + if (ea<8) ot(" sub%s r5,r5,#2 ;@ Extra cycles\n",cond[cc]); + break; } - if (ea<0x10) ot(" sub%s r5,r5,#2 ;@ Extra cycles\n",cond[cc]); ot("\n"); - EaCalc (0,0x003f, ea,size); - EaWrite(0, 1, ea,size); + eawrite_check_addrerr=1; + EaCalc (0,0x003f, ea,size,0,0); + EaWrite(0, 1, ea,size,0x003f,0,0); - OpEnd(); + opend_op_changes_cycles=changed_cycles; + OpEnd(ea,0); return 0; } // Emit a Asr/Lsr/Roxr/Ror opcode static int EmitAsr(int op,int type,int dir,int count,int size,int usereg) { - char pct[8]=""; + char pct[8]=""; // count int shift=32-(8<=1) sprintf(pct,"#%d",count); // Fixed count - if (count<0) - { - ot(" mov r2,r8,lsr #9 ;@ Get 'n'\n"); - ot(" and r2,r2,#7\n\n"); strcpy(pct,"r2"); - } - if (usereg) { ot(";@ Use Dn for count:\n"); - ot(" ldr r2,[r7,r2,lsl #2]\n"); + ot(" and r2,r8,#0x0e00\n"); + ot(" ldr r2,[r7,r2,lsr #7]\n"); ot(" and r2,r2,#63\n"); ot("\n"); + strcpy(pct,"r2"); + } + else if (count<0) + { + ot(" mov r2,r8,lsr #9 ;@ Get 'n'\n"); + ot(" and r2,r2,#7\n\n"); strcpy(pct,"r2"); } // Take 2*n cycles: @@ -353,22 +411,45 @@ static int EmitAsr(int op,int type,int dir,int count,int size,int usereg) // Asr/Lsr if (dir==0 && size<2) { - ot(";@ For shift right, also copy to lowest bits (to get carry bit):\n"); - ot(" orr r0,r0,r0,lsr #%d\n",32-(8<>9)&7; dir =(op>>8)&1; size =(op>>6)&3; - if (size>=3) return 1; // todo Asr EA + if (size>=3) return 1; // use OpAsrEa() usereg=(op>>5)&1; type =(op>>3)&3; @@ -480,27 +612,28 @@ int OpAsr(int op) // Use the same opcode for target registers: use=op&~0x0007; - // As long as count is not 8, use the same opcode for all shift counts:: - if (usereg==0 && count!=8) { use|=0x0e00; count=-1; } + // As long as count is not 8, use the same opcode for all shift counts: + if (usereg==0 && count!=8 && !(count==1&&type==2)) { use|=0x0e00; count=-1; } if (usereg) { use&=~0x0e00; count=-1; } // Use same opcode for all Dn if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=size<2?6:8; + OpStart(op,ea,0,count<0); Cycles=size<2?6:8; - EaCalc(10,0x0007, ea,size); - EaRead(10, 0, ea,size,1); + EaCalc(11,0x0007, ea,size,1); + EaRead(11, 0, ea,size,0x0007,1); EmitAsr(op,type,dir,count, size,usereg); - EaWrite(10, 0, ea,size,1); + EaWrite(11, 0, ea,size,0x0007,1); - OpEnd(); + opend_op_changes_cycles = (count<0); + OpEnd(ea,0); return 0; } -// Asr/l/Ror/l etc EA - 11100ttd 11eeeeee +// Asr/Lsr/Roxr/Ror etc EA - 11100ttd 11eeeeee int OpAsrEa(int op) { int use=0,type=0,dir=0,ea=0,size=1; @@ -514,20 +647,68 @@ int OpAsrEa(int op) if (EaCanRead(ea,0)==0) return 1; if (EaCanWrite(ea)==0) return 1; - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=8; + OpStart(op,ea); Cycles=6; // EmitAsr() will add 2 - EaCalc (10,0x003f,ea,size); - EaRead (10, 0,ea,size,1); + EaCalc (11,0x003f,ea,size,1); + EaRead (11, 0,ea,size,0x003f,1); - EmitAsr(op,type,dir,1, size,0); + EmitAsr(op,type,dir,1,size,0); - ot(";@ Save shifted value back to EA:\n"); - ot(" mov r1,r0\n"); - EaWrite(10, 1,ea,size,1); + EaWrite(11, 0,ea,size,0x003f,1); - OpEnd(); + OpEnd(ea); return 0; } + +int OpTas(int op, int gen_special) +{ + int ea=0; + int use=0; + + ea=op&0x003f; + + // See if we can do this opcode: + if (EaCanWrite(ea)==0 || EaAn(ea)) return 1; + + use=OpBase(op,0); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + if (!gen_special) OpStart(op,ea); + else + ot("Op%.4x_%s\n", op, ms?"":":"); + + Cycles=4; + if(ea>=8) Cycles+=10; + + EaCalc (11,0x003f,ea,0,1); + EaRead (11, 1,ea,0,0x003f,1); + + ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); + OpGetFlags(0,0); + ot("\n"); + +#if CYCLONE_FOR_GENESIS + // the original Sega hardware ignores write-back phase (to memory only) + if (ea < 0x10 || gen_special) { +#endif + ot(" orr r1,r1,#0x80000000 ;@ set bit7\n"); + + EaWrite(11, 1,ea,0,0x003f,1); +#if CYCLONE_FOR_GENESIS + } +#endif + + OpEnd(ea); + +#if (CYCLONE_FOR_GENESIS == 2) + if (!gen_special && ea >= 0x10) { + OpTas(op, 1); + } +#endif + + return 0; +} + diff --git a/Cyclone/OpMove.cpp b/Cyclone/OpMove.cpp index a6aa6e0..1e24566 100644 --- a/Cyclone/OpMove.cpp +++ b/Cyclone/OpMove.cpp @@ -1,15 +1,97 @@ // This file is part of the Cyclone 68000 Emulator -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + #include "app.h" +// Pack our flags into r1, in SR/CCR register format +// trashes r0,r2 +void OpFlagsToReg(int high) +{ + ot(" ldr r0,[r7,#0x4c] ;@ X bit\n"); + ot(" mov r1,r10,lsr #28 ;@ ____NZCV\n"); + ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n"); + ot(" tst r2,#1 ;@ 1 if C!=V\n"); + ot(" eorne r1,r1,#3 ;@ ____NZVC\n"); + ot("\n"); + if (high) ot(" ldrb r2,[r7,#0x44] ;@ Include SR high\n"); + ot(" and r0,r0,#0x20000000\n"); + ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n"); + if (high) ot(" orr r1,r1,r2,lsl #8\n"); + ot("\n"); +} + +// Convert SR/CRR register in r0 to our flags +// trashes r0,r1 +void OpRegToFlags(int high, int srh_reg) +{ + ot(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n"); + ot(" mov r2,r0,lsl #25\n"); + ot(" tst r1,#1 ;@ 1 if C!=V\n"); + ot(" eorne r0,r0,#3 ;@ ___XNZCV\n"); + ot(" str r2,[r7,#0x4c] ;@ Store X bit\n"); + ot(" mov r10,r0,lsl #28 ;@ r10=NZCV...\n"); + + if (high) + { + int mask=EMULATE_TRACE?0xa7:0x27; + ot(" mov r%i,r0,ror #8\n",srh_reg); + ot(" and r%i,r%i,#0x%02x ;@ only take defined bits\n",srh_reg,srh_reg,mask); + ot(" strb r%i,[r7,#0x44] ;@ Store SR high\n",srh_reg); + } + ot("\n"); +} + +void SuperEnd(void) +{ + ot(";@ ----------\n"); + ot(";@ tried execute privileged instruction in user mode\n"); + ot("WrongPrivilegeMode%s\n",ms?"":":"); +#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO + ot(" ldr r1,[r7,#0x58]\n"); + ot(" sub r4,r4,#2 ;@ last opcode wasn't executed - go back\n"); + ot(" orr r1,r1,#4 ;@ set activity bit: 'not processing instruction'\n"); + ot(" str r1,[r7,#0x58]\n"); +#else + ot(" sub r4,r4,#2 ;@ last opcode wasn't executed - go back\n"); +#endif + ot(" mov r0,#8 ;@ privilege violation\n"); + ot(" bl Exception\n"); + Cycles=34; + OpEnd(0); +} + +// does OSP and A7 swapping if needed +// new or old SR (not the one already in [r7,#0x44]) should be passed in r11 +// uses srh from srh_reg (loads if < 0), trashes r0,r11 +void SuperChange(int op,int srh_reg) +{ + ot(";@ A7 <-> OSP?\n"); + if (srh_reg < 0) { + ot(" ldr r0,[r7,#0x44] ;@ Get other SR high\n"); + srh_reg=0; + } + ot(" eor r0,r%i,r11\n",srh_reg); + ot(" tst r0,#0x20\n"); + ot(" beq no_sp_swap%.4x\n",op); + ot(" ;@ swap OSP and A7:\n"); + ot(" ldr r11,[r7,#0x3C] ;@ Get A7\n"); + ot(" ldr r0, [r7,#0x48] ;@ Get OSP\n"); + ot(" str r11,[r7,#0x48]\n"); + ot(" str r0, [r7,#0x3C]\n"); + ot("no_sp_swap%.4x%s\n", op, ms?"":":"); +} + + + // --------------------- Opcodes 0x1000+ --------------------- // Emit a Move opcode, 00xxdddd ddssssss int OpMove(int op) @@ -34,35 +116,59 @@ int OpMove(int op) case 0x2000: size=2; break; } - if (movea && size<1) return 1; // movea.b is invalid + if (size<1 && (movea || EaAn(sea))) return 1; // move.b An,* and movea.b * are invalid // See if we can do this opcode: if (EaCanRead (sea,size)==0) return 1; if (EaCanWrite(tea )==0) return 1; - use=OpBase(op); + use=OpBase(op,size); if (tea<0x38) use&=~0x0e00; // Use same handler for register ?0-7 - if (tea>=0x18 && tea<0x28 && (tea&7)==7) use|=0x0e00; // Specific handler for (a7)+ and -(a7) + if (tea==0x1f || tea==0x27) use|=0x0e00; // Specific handler for (a7)+ and -(a7) if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; + OpStart(op,sea,tea); Cycles=4; - EaCalc(0,0x003f,sea,size); - EaRead(0, 0,sea,size); - - ot(" adds r1,r0,#0 ;@ Defines NZ, clears CV\n"); + if (movea==0) + { + EaCalcRead(-1,0,sea,size,0x003f); + ot(" adds r1,r0,#0 ;@ Defines NZ, clears CV\n"); + ot(" mrs r10,cpsr ;@ r10=NZCV flags\n"); + ot("\n"); + } + else + { + EaCalcRead(-1,1,sea,size,0x003f); + size=2; // movea always expands to 32-bits + } - if (movea==0) ot(" mrs r9,cpsr ;@ r9=NZCV flags\n"); - ot("\n"); + eawrite_check_addrerr=1; +#if SPLIT_MOVEL_PD + if ((tea&0x38)==0x20 && size==2) { // -(An) + EaCalc (8,0x0e00,tea,size,0,0); + ot(" mov r11,r1\n"); + ot(" add r0,r8,#2\n"); + EaWrite(0, 1,tea,1,0x0e00,0,0); + EaWrite(8, 11,tea,1,0x0e00,1); + } + else +#endif + { + EaCalc (0,0x0e00,tea,size,0,0); + EaWrite(0, 1,tea,size,0x0e00,0,0); + } - if (movea) size=2; // movea always expands to 32-bits +#if CYCLONE_FOR_GENESIS && !MEMHANDLERS_CHANGE_CYCLES + // this is a bit hacky (device handlers might modify cycles) + if (tea==0x39||((0x10<=tea&&tea<0x30)&&size>=1)) + ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n"); +#endif - EaCalc (0,0x0e00,tea,size); - EaWrite(0, 1,tea,size); + if((tea&0x38)==0x20) Cycles-=2; // less cycles when dest is -(An) - OpEnd(); + OpEnd(sea,tea); return 0; } @@ -76,136 +182,82 @@ int OpLea(int op) sea= op&0x003f; tea=(op&0x0e00)>>9; tea|=8; - if (EaCanRead(sea,-1)==0) return 1; // See if we can do this opcode: + if (EaCanRead(sea,-1)==0) return 1; // See if we can do this opcode - use=OpBase(op); + use=OpBase(op,0); use&=~0x0e00; // Also use 1 handler for target ?0-7 if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=4; + OpStart(op,sea,tea); + eawrite_check_addrerr=1; EaCalc (1,0x003f,sea,0); // Lea EaCalc (0,0x0e00,tea,2); - EaWrite(0, 1,tea,2); + EaWrite(0, 1,tea,2,0x0e00); - if (Amatch) - { - // Correct? - if (sea< 0x18) Cycles+=4; - else if (sea==0x30) Cycles+=12; - else Cycles+=8; - } + Cycles=Ea_add_ns(g_lea_cycle_table,sea); - OpEnd(); + OpEnd(sea,tea); return 0; } // --------------------- Opcodes 0x40c0+ --------------------- - -// Pack our flags into r1, in SR/CCR register format -// trashes r0,r2 -void OpFlagsToReg(int high) -{ - ot(" mov r1,r9,lsr #28 ;@ ____NZCV\n"); - ot(" eor r0,r1,r1,ror #1 ;@ Bit 0=C^V\n"); - ot(" tst r0,#1 ;@ 1 if C!=V\n"); - ot(" eorne r1,r1,#3 ;@ ____NZVC\n"); - ot("\n"); - ot(" ldrb r0,[r7,#0x45] ;@ X bit\n"); - if (high) ot(" ldrb r2,[r7,#0x44] ;@ Include SR high\n"); - ot(" and r0,r0,#0x02\n"); - if (high) ot(" orr r1,r1,r2,lsl #8\n"); - ot(" orr r1,r1,r0,lsl #3 ;@ ___XNZVC\n"); - ot("\n"); -} - -// Convert SR/CRR register in r0 to our flags -// trashes r0,r1 -void OpRegToFlags(int high) -{ - ot(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n"); - ot(" mov r2,r0,lsr #3 ;@ r2=___XN\n"); - ot(" tst r1,#1 ;@ 1 if C!=V\n"); - ot(" eorne r0,r0,#3 ;@ ___XNZCV\n"); - ot(" strb r2,[r7,#0x45] ;@ Store X bit\n"); - ot(" mov r9,r0,lsl #28 ;@ r9=NZCV...\n"); - - if (high) - { - ot(" mov r0,r0,ror #8\n"); - ot(" strb r0,[r7,#0x44] ;@ Store SR high\n"); - } - ot("\n"); -} - -static void SuperCheck(int op) -{ - ot(" ldrb r0,[r7,#0x44] ;@ Get SR high\n"); - ot(" tst r0,#0x20 ;@ Check we are in supervisor mode\n"); - ot(" beq WrongMode%.4x ;@ No\n",op); - ot("\n"); -} - -static void SuperEnd(int op) -{ - ot("WrongMode%.4x%s\n",op,ms?"":":"); - ot(";@ todo - cause an exception\n"); - OpEnd(); -} - -// Move SR opcode, 01000tt0 11aaaaaa move to SR +// Move SR opcode, 01000tt0 11aaaaaa move SR int OpMoveSr(int op) { int type=0,ea=0; int use=0,size=1; - type=(op>>9)&3; + type=(op>>9)&3; // from SR, from CCR, to CCR, to SR ea=op&0x3f; + if(EaAn(ea)) return 1; // can't use An regs + switch(type) { - case 0: case 1: + case 0: if (EaCanWrite(ea)==0) return 1; // See if we can do this opcode: - break; + break; - default: return 1; // todo + case 1: + return 1; // no such op in 68000 case 2: case 3: if (EaCanRead(ea,size)==0) return 1; // See if we can do this opcode: - break; + break; } - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); - if (type==0) Cycles=8; - else if (type==1) Cycles=6; - else Cycles=12; - - if (Amatch && ea==0x3c) Cycles-=4; // Correct? - - if (type==0 || type==3) SuperCheck(op); + // 68000 model allows reading whole SR in user mode (but newer models don't) + OpStart(op,ea,0,0,type==3); + Cycles=12; + if (type==0) Cycles=(ea>=8)?8:6; if (type==0 || type==1) { + eawrite_check_addrerr=1; OpFlagsToReg(type==0); - EaCalc (0,0x003f,ea,size); - EaWrite(0, 1,ea,size); + EaCalc (0,0x003f,ea,size,0,0); + EaWrite(0, 1,ea,size,0x003f,0,0); } if (type==2 || type==3) { - EaCalc(0,0x003f,ea,size); - EaRead(0, 0,ea,size); - OpRegToFlags(type==3); - if (type==3) CheckInterrupt(); + EaCalcReadNoSE(-1,0,ea,size,0x003f); + OpRegToFlags(type==3,1); + if (type==3) { + SuperChange(op,1); + opend_check_interrupt = 1; + opend_check_trace = 1; + OpEnd(ea); + return 0; + } } - OpEnd(); - - if (type==0 || type==3) SuperEnd(op); + OpEnd(ea); return 0; } @@ -216,30 +268,64 @@ int OpArithSr(int op) { int type=0,ea=0; int use=0,size=0; + int sr_mask=EMULATE_TRACE?0xa7:0x27; type=(op>>9)&5; if (type==4) return 1; - size=(op>>6)&1; + size=(op>>6)&1; // ccr or sr? ea=0x3c; - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=16; + OpStart(op,ea,0,0,size!=0); Cycles=16; + + EaCalcRead(-1,0,ea,size,0x003f); - SuperCheck(op); + ot(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n"); + ot(" tst r1,#1 ;@ 1 if C!=V\n"); + ot(" eorne r0,r0,#3 ;@ ___XNZCV\n"); + ot(" ldr r2,[r7,#0x4c] ;@ Load old X bit\n"); + + // note: old srh is already in r11 (done by OpStart) + if (type==0) { + ot(" orr r10,r10,r0,lsl #28\n"); + ot(" orr r2,r2,r0,lsl #25 ;@ X bit\n"); + if (size!=0) { + ot(" orr r1,r11,r0,lsr #8\n"); + ot(" and r1,r1,#0x%02x ;@ mask-out unused bits\n",sr_mask); + } + } + if (type==1) { + ot(" and r10,r10,r0,lsl #28\n"); + ot(" and r2,r2,r0,lsl #25 ;@ X bit\n"); + if (size!=0) + ot(" and r1,r11,r0,lsr #8\n"); + } + if (type==5) { + ot(" eor r10,r10,r0,lsl #28\n"); + ot(" eor r2,r2,r0,lsl #25 ;@ X bit\n"); + if (size!=0) { + ot(" eor r1,r11,r0,lsr #8\n"); + ot(" and r1,r1,#0x%02x ;@ mask-out unused bits\n",sr_mask); + } + } - EaCalc(0,0x003f,ea,size); - EaRead(0, 10,ea,size); + ot(" str r2,[r7,#0x4c] ;@ Save X bit\n"); + if (size!=0) + ot(" strb r1,[r7,#0x44]\n"); + ot("\n"); - OpFlagsToReg(size); - if (type==0) ot(" orr r0,r1,r10\n"); - if (type==1) ot(" and r0,r1,r10\n"); - if (type==5) ot(" eor r0,r1,r10\n"); - OpRegToFlags(size); - if (size) CheckInterrupt(); + // we can't enter supervisor mode, nor unmask irqs just by using OR + if (size!=0 && type!=0) { + SuperChange(op,1); + ot("\n"); + opend_check_interrupt = 1; + } + // also can't set trace bit with AND + if (size!=0 && type!=1) + opend_check_trace = 1; - OpEnd(); - SuperEnd(op); + OpEnd(ea); return 0; } @@ -254,21 +340,23 @@ int OpPea(int op) ea=op&0x003f; if (ea<0x10) return 1; // Swap opcode if (EaCanRead(ea,-1)==0) return 1; // See if we can do this opcode: - use=OpBase(op); + use=OpBase(op,0); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); Cycles=20; + OpStart(op,ea); + ot(" ldr r11,[r7,#0x3c]\n"); EaCalc (1,0x003f, ea,0); ot("\n"); - ot(" ldr r0,[r7,#0x3c]\n"); - ot(" sub r0,r0,#4 ;@ Predecrement A7\n"); + ot(" sub r0,r11,#4 ;@ Predecrement A7\n"); ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); ot("\n"); MemHandler(1,2); // Write 32-bit ot("\n"); - OpEnd(); + Cycles=6+Ea_add_ns(g_pea_cycle_table,ea); + + OpEnd(ea); return 0; } @@ -280,87 +368,118 @@ int OpMovem(int op) int size=0,ea=0,cea=0,dir=0; int use=0,decr=0,change=0; - size=((op>>6)&1)+1; + size=((op>>6)&1)+1; // word, long ea=op&0x003f; - dir=(op>>10)&1; // Direction + dir=(op>>10)&1; // Direction (1==ea2reg) + + if (dir) { + if (ea<0x10 || ea>0x3b || (ea&0x38)==0x20) return 1; // Invalid EA + } else { + if (ea<0x10 || ea>0x39 || (ea&0x38)==0x18) return 1; + } - if (ea<0x10 || ea>0x39) return 1; // Invalid EA if ((ea&0x38)==0x18 || (ea&0x38)==0x20) change=1; if ((ea&0x38)==0x20) decr=1; // -(An), bitfield is decr - // See if we can do this opcode: - if (EaCanWrite(ea)==0) return 1; - cea=ea; if (change) cea=0x10; - use=OpBase(op); + use=OpBase(op,size); if (op!=use) { OpUse(op,use); return 0; } // Use existing handler - OpStart(op); + OpStart(op,ea,0,1); - ot(" stmdb sp!,{r9} ;@ Push r9\n"); ot(" ldrh r11,[r4],#2 ;@ r11=register mask\n"); - ot("\n"); - ot(";@ Get the address into r9:\n"); - EaCalc(9,0x003f,cea,size); + ot(";@ Get the address into r6:\n"); + EaCalc(6,0x003f,cea,size); - ot(";@ r10=Register Index*4:\n"); - if (decr) ot(" mov r10,#0x3c ;@ order reversed for -(An)\n"); - else ot(" mov r10,#0\n"); +#if !MEMHANDLERS_NEED_PREV_PC + // must save PC, need a spare register + ot(" str r4,[r7,#0x40] ;@ Save PC\n"); +#endif + + ot(";@ r4=Register Index*4:\n"); + if (decr) ot(" mov r4,#0x40 ;@ order reversed for -(An)\n"); + else ot(" mov r4,#-4\n"); ot("\n"); - ot("MoreReg%.4x%s\n",op, ms?"":":"); + ot(" tst r11,r11\n"); // sanity check + ot(" beq NoRegs%.4x\n",op); - ot(" tst r11,#1\n"); - ot(" beq SkipReg%.4x\n",op); +#if EMULATE_ADDRESS_ERRORS_IO ot("\n"); + ot(" tst r6,#1 ;@ address error?\n"); + ot(" movne r0,r6\n"); + ot(" bne ExceptionAddressError_%c_data\n",dir?'r':'w'); +#endif - if (decr) ot(" sub r9,r9,#%d ;@ Pre-decrement address\n",1<S\n"); - ot(" str r1,[r7,r10,lsr #7] ;@ S->T\n"); + ot(" str r0,[r7,r3,lsl #2] ;@ T->S\n"); + ot(" str r1,[r7,r2,lsr #7] ;@ S->T\n"); ot("\n"); OpEnd(); return 0; } + +// ------------------------- movep ------------------------------- +// 0000ddd1 0z001sss +// 0000sss1 1z001ddd (to mem) +int OpMovep(int op) +{ + int ea=0,rea=0; + int size=1,use=0,dir,aadd=0; + + use=op&0xf1f8; + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler (for all dests, srcs) + + // Get EA + ea = (op&0x0007)|0x28; + rea= (op&0x0e00)>>9; + dir = (op>>7)&1; + + // Find size extension + if(op&0x0040) size=2; + + OpStart(op,ea); + + if(dir) // reg to mem + { + EaCalcReadNoSE(-1,11,rea,size,0x0e00); + + EaCalc(8,0x000f,ea,size); + if(size==2) { // if operand is long + ot(" mov r1,r11,lsr #24 ;@ first byte\n"); + EaWrite(8,1,ea,0,0x000f); // store first byte + ot(" add r0,r8,#%i\n",(aadd+=2)); + ot(" mov r1,r11,lsr #16 ;@ second byte\n"); + EaWrite(0,1,ea,0,0x000f); // store second byte + ot(" add r0,r8,#%i\n",(aadd+=2)); + } else { + ot(" mov r0,r8\n"); + } + ot(" mov r1,r11,lsr #8 ;@ first or third byte\n"); + EaWrite(0,1,ea,0,0x000f); + ot(" add r0,r8,#%i\n",(aadd+=2)); + ot(" and r1,r11,#0xff\n"); + EaWrite(0,1,ea,0,0x000f); + } + else // mem to reg + { + EaCalc(6,0x000f,ea,size,1); + EaRead(6,11,ea,0,0x000f,1); // read first byte + ot(" add r0,r6,#2\n"); + EaRead(0,1,ea,0,0x000f,1); // read second byte + if(size==2) { // if operand is long + ot(" orr r11,r11,r1,lsr #8 ;@ second byte\n"); + ot(" add r0,r6,#4\n"); + EaRead(0,1,ea,0,0x000f,1); + ot(" orr r11,r11,r1,lsr #16 ;@ third byte\n"); + ot(" add r0,r6,#6\n"); + EaRead(0,1,ea,0,0x000f,1); + ot(" orr r1,r11,r1,lsr #24 ;@ fourth byte\n"); + } else { + ot(" orr r1,r11,r1,lsr #8 ;@ second byte\n"); + } + // store the result + EaCalc(0,0x0e00,rea,size,1); + EaWrite(0,1,rea,size,0x0e00,1); + ot(" ldr r6,[r7,#0x54]\n"); + } + + Cycles=(size==2)?24:16; + OpEnd(ea); + + return 0; +} + +// Emit a Stop/Reset opcodes, 01001110 011100t0 imm +int OpStopReset(int op) +{ + int type=(op>>1)&1; // stop/reset + + OpStart(op,0,0,0,1); + + if(type) { + // copy immediate to SR, stop the CPU and eat all remaining cycles. + ot(" ldrh r0,[r4],#2 ;@ Fetch the immediate\n"); + OpRegToFlags(1); + SuperChange(op,0); + + ot("\n"); + + ot(" ldr r0,[r7,#0x58]\n"); + ot(" mov r5,#0 ;@ eat cycles\n"); + ot(" orr r0,r0,#1 ;@ stopped\n"); + ot(" str r0,[r7,#0x58]\n"); + ot("\n"); + + Cycles = 4; + ot("\n"); + } + else + { + Cycles = 132; +#if USE_RESET_CALLBACK + ot(" str r4,[r7,#0x40] ;@ Save PC\n"); + ot(" mov r1,r10,lsr #28\n"); + ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n"); + ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n"); + ot(" ldr r11,[r7,#0x90] ;@ ResetCallback\n"); + ot(" tst r11,r11\n"); + ot(" movne lr,pc\n"); + ot(" bxne r11 ;@ call ResetCallback if it is defined\n"); + ot(" ldrb r10,[r7,#0x46] ;@ r10 = Load Flags (NZCV)\n"); + ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n"); + ot(" ldr r4,[r7,#0x40] ;@ Load PC\n"); + ot(" mov r10,r10,lsl #28\n"); + ot("\n"); +#endif + } + + OpEnd(); + + return 0; +} + diff --git a/Cyclone/app.h b/Cyclone/app.h index 082b109..3528b7b 100644 --- a/Cyclone/app.h +++ b/Cyclone/app.h @@ -1,49 +1,67 @@ // This file is part of the Cyclone 68000 Emulator -// Copyright (c) 2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2004,2011 FinalDave (emudave (at) gmail.com) +// Copyright (c) 2005-2011 Gražvydas "notaz" Ignotas (notasas (at) gmail.com) // This code is licensed under the GNU General Public License version 2.0 and the MAME License. // You can choose the license that has the most advantages for you. // SVN repository can be found at http://code.google.com/p/cyclone68000/ + #include #include #include #include +#ifndef CONFIG_FILE +#define CONFIG_FILE "config.h" +#endif +#include CONFIG_FILE + // Disa.c -#include "../Pico/Disa.h" +#include "Disa/Disa.h" // Ea.cpp -int EaCalc(int a,int mask,int ea,int size); -int EaRead(int a,int v,int ea,int size,int top=0); +extern int earead_check_addrerr; +extern int eawrite_check_addrerr; +extern int g_jmp_cycle_table[]; +extern int g_jsr_cycle_table[]; +extern int g_lea_cycle_table[]; +extern int g_pea_cycle_table[]; +extern int g_movem_cycle_table[]; +int Ea_add_ns(int *tab, int ea); // add nonstandard EA cycles +int EaCalc(int a,int mask,int ea,int size,int top=0,int sign_extend=1); // 6 +int EaRead(int a,int v,int ea,int size,int mask,int top=0,int sign_extend=1); // 7 +int EaCalcRead(int r_ea,int r,int ea,int size,int mask,int sign_extend=1); // 6 +int EaCalcReadNoSE(int r_ea,int r,int ea,int size,int mask); int EaCanRead(int ea,int size); -int EaWrite(int a,int v,int ea,int size,int top=0); +int EaWrite(int a,int v,int ea,int size,int mask,int top=0,int sign_extend_ea=1); int EaCanWrite(int ea); +int EaAn(int ea); // Main.cpp -extern int *CyJump; // Jump table -extern int ms; // If non-zero, output in Microsoft ARMASM format -extern char *Narm[4]; // Normal ARM Extensions for operand sizes 0,1,2 -extern char *Sarm[4]; // Sign-extend ARM Extensions for operand sizes 0,1,2 -extern int Cycles; // Current cycles for opcode -extern int Amatch; // If one, try to match A68K timing -extern int Accu; // Accuracy -extern int Debug; // Debug info +extern int *CyJump; // Jump table +extern int ms; // If non-zero, output in Microsoft ARMASM format +extern const char * const Narm[4]; // Normal ARM Extensions for operand sizes 0,1,2 +extern const char * const Sarm[4]; // Sign-extend ARM Extensions for operand sizes 0,1,2 +extern int Cycles; // Current cycles for opcode +extern int pc_dirty; // something changed PC during processing +extern int arm_op_count; // for stats void ot(const char *format, ...); void ltorg(); -void CheckInterrupt(); -int MemHandler(int type,int size); +int MemHandler(int type,int size,int addrreg=0,int need_addrerr_check=1); +void FlushPC(void); // OpAny.cpp -int OpGetFlags(int subtract,int xbit); +extern int g_op; +extern int opend_op_changes_cycles, opend_check_interrupt, opend_check_trace; +int OpGetFlags(int subtract,int xbit,int sprecialz=0); void OpUse(int op,int use); -void OpFirst(); -void OpStart(int op); -void OpEnd(); -int OpBase(int op); +void OpStart(int op,int sea=0,int tea=0,int op_changes_cycles=0,int supervisor_check=0); +void OpEnd(int sea=0,int tea=0); +int OpBase(int op,int size,int sepa=0); void OpAny(int op); //---------------------- @@ -54,9 +72,13 @@ int OpAddq(int op); int OpArithReg(int op); int OpMul(int op); int OpAbcd(int op); +int OpNbcd(int op); int OpAritha(int op); int OpAddx(int op); int OpCmpEor(int op); +int OpCmpm(int op); +int OpChk(int op); +int GetXBit(int subtract); // OpBranch.cpp void OpPush32(); @@ -79,12 +101,13 @@ int OpExt(int op); int OpSet(int op); int OpAsr(int op); int OpAsrEa(int op); +int OpTas(int op, int gen_special=0); // OpMove.cpp int OpMove(int op); int OpLea(int op); void OpFlagsToReg(int high); -void OpRegToFlags(int high); +void OpRegToFlags(int high,int srh_reg=0); int OpMoveSr(int op); int OpArithSr(int op); int OpPea(int op); @@ -92,3 +115,8 @@ int OpMovem(int op); int OpMoveq(int op); int OpMoveUsp(int op); int OpExg(int op); +int OpMovep(int op); +int OpStopReset(int op); +void SuperEnd(void); +void SuperChange(int op,int srh_reg=-1); + diff --git a/Cyclone/config.h b/Cyclone/config.h new file mode 100644 index 0000000..d73b5b5 --- /dev/null +++ b/Cyclone/config.h @@ -0,0 +1,182 @@ + + +/** + * Cyclone 68000 configuration file +**/ + + +/* + * If this option is enabled, Microsoft ARMASM compatible output is generated + * (output file - Cyclone.asm). Otherwise GNU as syntax is used (Cyclone.s). + */ +#define USE_MS_SYNTAX 0 + +/* + * Enable this option if you are going to use Cyclone to emulate Genesis / + * Mega Drive system. As VDP chip in these systems had control of the bus, + * several instructions were acting differently, for example TAS did'n have + * the write-back phase. That will be emulated, if this option is enabled. + */ +#define CYCLONE_FOR_GENESIS 0 + +/* + * This option compresses Cyclone's jumptable. Because of this the executable + * will be smaller and load slightly faster and less relocations will be needed. + * This also fixes the crash problem with 0xfffe and 0xffff opcodes. + * Warning: if you enable this, you MUST call CycloneInit() before calling + * CycloneRun(), or else it will crash. + */ +#define COMPRESS_JUMPTABLE 1 + +/* + * Address mask for memory hadlers. The bits set will be masked out of address + * parameter, which is passed to r/w memory handlers. + * Using 0xff000000 means that only 24 least significant bits should be used. + * Set to 0 if you want to mask unused address bits in the memory handlers yourself. + */ +#define MEMHANDLERS_ADDR_MASK 0 + +/* + * Cyclone keeps the 4 least significant bits of SR, PC+membase and it's cycle + * counter in ARM registers instead of the context for performance reasons. If you for + * any reason need to access them in your memory handlers, enable the options below, + * otherwise disable them to improve performance. + * + * MEMHANDLERS_NEED_PC updates .pc context field with PC value effective at the time + * when memhandler was called (opcode address + 2-10 bytes). + * MEMHANDLERS_NEED_PREV_PC updates .prev_pc context field to currently executed + * opcode address + 2. + * Note that .pc and .prev_pc values are always real pointers to memory, so you must + * subtract .membase to get M68k PC value. + * + * Warning: updating PC in memhandlers is dangerous, as Cyclone may internally + * increment the PC before fetching the next instruction and continue executing + * at wrong location. It's better to wait until Cyclone CycloneRun() finishes. + * + * Warning: if you enable MEMHANDLERS_CHANGE_CYCLES, you must also enable + * MEMHANDLERS_NEED_CYCLES, or else Cyclone will keep reloading the same cycle + * count and this will screw timing (if not cause a deadlock). + */ +#define MEMHANDLERS_NEED_PC 0 +#define MEMHANDLERS_NEED_PREV_PC 0 +#define MEMHANDLERS_NEED_FLAGS 0 +#define MEMHANDLERS_NEED_CYCLES 0 +#define MEMHANDLERS_CHANGE_PC 0 +#define MEMHANDLERS_CHANGE_FLAGS 0 +#define MEMHANDLERS_CHANGE_CYCLES 0 + +/* + * If the following macro is defined, Cyclone no longer calls read*, write*, + * fetch* and checkpc from it's context, it calls these functions directly + * instead, prefixed with prefix selected below. For example, if + * MEMHANDLERS_DIRECT_PREFIX is set to cyclone_, it will call cyclone_read8 + * on byte reads. + * This is to avoid indirect jumps, which are slower. It also saves one ARM + * instruction. + */ +/* MEMHANDLERS_DIRECT_PREFIX "cyclone_" */ + +/* + * If enabled, Cyclone will call .IrqCallback routine from it's context whenever it + * acknowledges an IRQ. IRQ level (.irq) is not cleared automatically, do this in your + * handler if needed. + * This function must either return vector number to use for interrupt exception, + * CYCLONE_INT_ACK_AUTOVECTOR to use autovector (this is the most common case), or + * CYCLONE_INT_ACK_SPURIOUS (least common case). + * If disabled, it simply uses appropriate autovector, clears the IRQ level and + * continues execution. + */ +#define USE_INT_ACK_CALLBACK 0 + +/* + * Enable this if you need old PC, flags or cycles; + * or you change cycles in your IrqCallback function. + */ +#define INT_ACK_NEEDS_STUFF 0 +#define INT_ACK_CHANGES_CYCLES 0 + +/* + * If enabled, .ResetCallback is called from the context, whenever RESET opcode is + * encountered. All context members are valid and can be changed. + * If disabled, RESET opcode acts as an NOP. + */ +#define USE_RESET_CALLBACK 0 + +/* + * If enabled, UnrecognizedCallback is called if an invalid opcode is + * encountered. All context members are valid and can be changed. The handler + * should return zero if you want Cyclone to gererate "Illegal Instruction" + * exception after this, or nonzero if not. In the later case you should change + * the PC by yourself, or else Cyclone will keep executing that opcode all over + * again. + * If disabled, "Illegal Instruction" exception is generated and execution is + * continued. + */ +#define USE_UNRECOGNIZED_CALLBACK 0 + +/* + * This option will also call UnrecognizedCallback for a-line and f-line + * (0xa*** and 0xf***) opcodes the same way as described above, only appropriate + * exceptions will be generated. + */ +#define USE_AFLINE_CALLBACK 0 + +/* + * This makes Cyclone to call checkpc from it's context whenever it changes the PC + * by a large value. It takes and should return the PC value in PC+membase form. + * The flags and cycle counter are not valid in this function. + */ +#define USE_CHECKPC_CALLBACK 1 + +/* + * This determines if checkpc() should be called after jumps when 8 and 16 bit + * displacement values were used. + */ +#define USE_CHECKPC_OFFSETBITS_16 1 +#define USE_CHECKPC_OFFSETBITS_8 0 + +/* + * Call checkpc() after DBcc jumps (which use 16bit displacement). Cyclone prior to + * 0.0087 never did that. + */ +#define USE_CHECKPC_DBRA 0 + +/* + * When this option is enabled Cyclone will do two word writes instead of one + * long write when handling MOVE.L or MOVEM.L with pre-decrementing destination, + * as described in Bart Trzynadlowski's doc (http://www.trzy.org/files/68knotes.txt). + * Enable this if you are emulating a 16 bit system. + */ +#define SPLIT_MOVEL_PD 1 + +/* + * Enable emulation of trace mode. Shouldn't cause any performance decrease, so it + * should be safe to keep this ON. + */ +#define EMULATE_TRACE 1 + +/* + * If enabled, address error exception will be generated if 68k code jumps to an + * odd address. Causes very small performance hit (2 ARM instructions for every + * emulated jump/return/exception in normal case). + * Note: checkpc() must not clear least significant bit of rebased address + * for this to work, as checks are performed after calling checkpc(). + */ +#define EMULATE_ADDRESS_ERRORS_JUMP 1 + +/* + * If enabled, address error exception will be generated if 68k code tries to + * access a word or longword at an odd address. The performance cost is also 2 ARM + * instructions per access (for address error checks). + */ +#define EMULATE_ADDRESS_ERRORS_IO 0 + +/* + * If an address error happens during another address error processing, + * the processor halts until it is reset (catastrophic system failure, as the manual + * states). This option enables halt emulation. + * Note that this might be not desired if it is known that emulated system should + * never reach this state. + */ +#define EMULATE_HALT 0 + -- 2.39.5