From 6003a768db2b013216715ea60692ddae35524d44 Mon Sep 17 00:00:00 2001 From: emudave Date: Mon, 7 Feb 2011 22:06:07 +0000 Subject: [PATCH] first check-in of Cyclone 0.069 under GPLv2 and MAME License. Pico Disa.c/h included too --- Cyclone/Cyclone.dsp | 170 ++++++++ Cyclone/Cyclone.dsw | 29 ++ Cyclone/Cyclone.h | 45 ++ Cyclone/Cyclone.txt | 390 ++++++++++++++++++ Cyclone/DoClean.bat | 20 + Cyclone/Ea.cpp | 296 +++++++++++++ Cyclone/License-GPLv2.txt | 339 +++++++++++++++ Cyclone/License.txt | 37 ++ Cyclone/Main.cpp | 277 +++++++++++++ Cyclone/OpAny.cpp | 117 ++++++ Cyclone/OpArith.cpp | 521 +++++++++++++++++++++++ Cyclone/OpBranch.cpp | 376 +++++++++++++++++ Cyclone/OpLogic.cpp | 523 +++++++++++++++++++++++ Cyclone/OpMove.cpp | 454 ++++++++++++++++++++ Cyclone/app.h | 85 ++++ Pico/Disa.c | 846 ++++++++++++++++++++++++++++++++++++++ Pico/Disa.h | 20 + test.txt | 1 - 18 files changed, 4545 insertions(+), 1 deletion(-) create mode 100644 Cyclone/Cyclone.dsp create mode 100644 Cyclone/Cyclone.dsw create mode 100644 Cyclone/Cyclone.h create mode 100644 Cyclone/Cyclone.txt create mode 100644 Cyclone/DoClean.bat create mode 100644 Cyclone/Ea.cpp create mode 100644 Cyclone/License-GPLv2.txt create mode 100644 Cyclone/License.txt create mode 100644 Cyclone/Main.cpp create mode 100644 Cyclone/OpAny.cpp create mode 100644 Cyclone/OpArith.cpp create mode 100644 Cyclone/OpBranch.cpp create mode 100644 Cyclone/OpLogic.cpp create mode 100644 Cyclone/OpMove.cpp create mode 100644 Cyclone/app.h create mode 100644 Pico/Disa.c create mode 100644 Pico/Disa.h delete mode 100644 test.txt diff --git a/Cyclone/Cyclone.dsp b/Cyclone/Cyclone.dsp new file mode 100644 index 0000000..7085e11 --- /dev/null +++ b/Cyclone/Cyclone.dsp @@ -0,0 +1,170 @@ +# Microsoft Developer Studio Project File - Name="Cyclone" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Cyclone - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Cyclone.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Cyclone.mak" CFG="Cyclone - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Cyclone - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Cyclone - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Cyclone - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 user32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "Cyclone - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Cyclone - Win32 Release" +# Name "Cyclone - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Cyclone.txt +# End Source File +# Begin Source File + +SOURCE=.\Ea.cpp +# End Source File +# Begin Source File + +SOURCE=.\Main.cpp +# End Source File +# Begin Source File + +SOURCE=.\OpAny.cpp +# End Source File +# Begin Source File + +SOURCE=.\OpArith.cpp +# End Source File +# Begin Source File + +SOURCE=.\OpBranch.cpp +# End Source File +# Begin Source File + +SOURCE=.\OpLogic.cpp +# End Source File +# Begin Source File + +SOURCE=.\OpMove.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\app.h +# End Source File +# Begin Source File + +SOURCE=.\Cyclone.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "Output" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Cyclone.asm +# End Source File +# Begin Source File + +SOURCE=.\Cyclone.s +# End Source File +# End Group +# Begin Group "Disassembler" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\Pico\Disa.c +# End Source File +# Begin Source File + +SOURCE=..\Pico\Disa.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\EmuInfo\68000\680x0bin.txt +# End Source File +# Begin Source File + +SOURCE=..\bits.txt +# End Source File +# End Target +# End Project diff --git a/Cyclone/Cyclone.dsw b/Cyclone/Cyclone.dsw new file mode 100644 index 0000000..a783183 --- /dev/null +++ b/Cyclone/Cyclone.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Cyclone"=.\Cyclone.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Cyclone/Cyclone.h b/Cyclone/Cyclone.h new file mode 100644 index 0000000..99fb1fc --- /dev/null +++ b/Cyclone/Cyclone.h @@ -0,0 +1,45 @@ + +// Cyclone 68000 Emulator - Header File + +// 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/ + +#ifdef __cplusplus +extern "C" { +#endif + +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] + 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] +}; + +void CycloneRun(struct Cyclone *pcy); + +#ifdef __cplusplus +} // End of extern "C" +#endif diff --git a/Cyclone/Cyclone.txt b/Cyclone/Cyclone.txt new file mode 100644 index 0000000..0b0d062 --- /dev/null +++ b/Cyclone/Cyclone.txt @@ -0,0 +1,390 @@ + + _____ __ + / ___/__ __ ____ / /___ ___ ___ ___________________ + / /__ / // // __// // _ \ / _ \/ -_) ___________________ + \___/ \_, / \__//_/ \___//_//_/\__/ ___________________ + /___/ + ___________________ ____ ___ ___ ___ ___ + ___________________ / __// _ \ / _ \ / _ \ / _ \ + ___________________ / _ \/ _ // // // // // // / + \___/\___/ \___/ \___/ \___/ + +___________________________________________________________________________ + + +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. + +___________________________________________________________________________ + + +What is it? +----------- + +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. + +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 + +v0.059: Added remainder to divide opcodes. + + +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 + r9 : Flags (NZCV) in highest four bits + (r10 : Temporary source value or Memory Base) + (r11 : Temporary register) + + +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. + +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. + +Open up Cyclone.dsw in Visual Studio 6.0, compile and run the project. +Cyclone.obj and Cyclone.o will be created. + +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. + + e.g. gcc Main.cpp OpAny.cpp OpArith.cpp OpBranch.cpp OpLogic.cpp OpMove.cpp Disa.c + Main.exe + + +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 + +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 +------------------ + +If you have used Starscream, A68K or Turbo68K or similar emulators you'll be familiar with this! + +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. + +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]. + +This would work, but remember some systems can execute code from RAM as well as ROM, and +that would fail. +So it's best to use byteswapped ROM and RAM if the 68000 can access it directly. +It's also faster for the memory handlers, because you can do this: + + return *(unsigned short *)(mem+a) + + +Declaring a 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: + + static unsigned int MyCheckPc(unsigned int pc) + static unsigned char MyRead8 (unsigned int a) + static unsigned short MyRead16 (unsigned int a) + static unsigned int MyRead32 (unsigned int a) + static void MyWrite8 (unsigned int a,unsigned char d) + static void MyWrite16(unsigned int a,unsigned short d) + static void MyWrite32(unsigned int a,unsigned int d) + +You can think of these functions representing the 68000's memory bus. +The Read and Write functions are called whenever the 68000 reads or writes memory. +For example you might set MyRead8 like this: + + unsigned char MyRead8(unsigned int a) + { + a&=0xffffff; // Clip address to 24-bits + + if (a=0xe00000) return RamData[(a^1)&0xffff]; + return 0xff; // Out of range memory access + } + +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: + + struct Cyclone MyCpu; + struct Cyclone MyCpu2; + +It's probably a good idea to initialise the memory to zero: + + memset(&MyCpu, 0,sizeof(MyCpu)); + memset(&MyCpu2,0,sizeof(MyCpu2)); + +Next point to your memory handlers: + + MyCpu.checkpc=MyCheckPc; + MyCpu.read8 =MyRead8; + MyCpu.read16 =MyRead16; + MyCpu.read32 =MyRead32; + MyCpu.write8 =MyWrite8; + MyCpu.write16=MyWrite16; + MyCpu.write32=MyWrite32; + +You also need to point the fetch handlers - for most systems out there you can just +point them at the read handlers: + MyCpu.fetch8 =MyRead8; + MyCpu.fetch16 =MyRead16; + MyCpu.fetch32 =MyRead32; + +( Why a different set of function pointers for fetch? + Well there are some systems, the main one being CPS2, which return different data + depending on whether the 'fetch' line on the 68000 bus is high or low. + 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(). + +The checkpc() function +---------------------- + +When Cyclone reads opcodes, it doesn't use a memory handler every time, this would be +far too slow, instead it uses a direct pointer to ARM memory. +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 +subtracts 'membase'. + +When a long jump happens, Cyclone calls checkpc(). If the PC is in a different bank, +for example Ram instead of Rom, change 'membase', recalculate the new PC and return it: + +static int MyCheckPc(unsigned int pc) +{ + pc-=MyCpu.membase; // Get the real program counter + + if (pc=0xff0000) MyCpu.membase=(int)RamMem-0xff0000; // Jump to Ram + + return MyCpu.membase+pc; // New program counter +} + +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). + + +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. + +Here is code which resets the 68000 (using your memory handlers): + + MyCpu.srh=0x27; // Set supervisor mode + MyCpu.a[7]=MyCpu.read32(0); // Get Stack Pointer + MyCpu.membase=0; + MyCpu.pc=MyCpu.checkpc(MyCpu.read32(4)); // Get Program Counter + +And that's ready to go. + + +Executing the 68000 +------------------- + +To execute the 68000, set the 'cycles' variable to the number of cycles you wish to execute, +and then call CycloneRun with a pointer to the Cyclone structure. + +e.g.: + // Execute 1000 cycles on the 68000: + MyCpu.cycles=1000; CycloneRun(&MyCpu); + +For each opcode, the number of cycles it took is subtracted and the function returns when +it reaches 0. + +e.g. + // Execute one instruction on the 68000: + MyCpu.cycles=0; CycloneRun(&MyCpu); + printf(" The opcode took %d cycles\n", -MyCpu.cycles); + +You should try to execute as many cycles as you can for maximum speed. +The number actually executed may be slightly more than requested, i.e. cycles may come +out with a small negative value: + +e.g. + int todo=12000000/60; // 12Mhz, for one 60hz frame + MyCpu.cycles=todo; CycloneRun(&MyCpu); + printf(" Actually executed %d cycles\n", todo-MyCpu.cycles); + +To calculate the number of cycles executed, use this formula: + Number of cycles requested - Cycle counter at the end + + +Interrupts +---------- + +Causing an interrupt is very simple, simply set the irq variable in the Cyclone structure +to the IRQ number. +To lower the IRQ line, set it to zero. + +e.g: + MyCpu.irq=6; // Interrupt level 6 + MyCpu.cycles=20000; CycloneRun(&MyCpu); + +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...) + +Accessing Cycle Counter +----------------------- + +The cycle counter in the Cyclone structure is not, by default, updated before +calling a memory handler, only at the end of an execution. + +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 *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). + +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' ;) + + +Emulating more than one CPU +--------------------------- + +Since everything is based on the structures, emulating more than one cpu at the same time +is just a matter of declaring more than one structures and timeslicing. You can emulate +as many 68000s as you want. +Just set up the memory handlers for each cpu and run each cpu for a certain number of cycles. + +e.g. + // Execute 1000 cycles on 68000 #1: + MyCpu.cycles=1000; CycloneRun(&MyCpu); + + // Execute 1000 cycles on 68000 #2: + MyCpu2.cycles=1000; CycloneRun(&MyCpu2); + + +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 + +* Charles Macdonald, for researching just about every console ever +* MameDev+FBA, for keeping on going and going and going diff --git a/Cyclone/DoClean.bat b/Cyclone/DoClean.bat new file mode 100644 index 0000000..e0119d4 --- /dev/null +++ b/Cyclone/DoClean.bat @@ -0,0 +1,20 @@ +ren Cyclone.obj Cyclone.ob_ + +del Cyclone.asm +del Cyclone.s +del Cyclone.exe +del *.obj +del vc60.idb +del vc60.pdb +del Cyclone.plg +del Cyclone.ncb +del Cyclone.pch +del Cyclone.ilk +del Cyclone.pdb + +del /q Release\*.* +rd Release +del /q Debug\*.* +rd Debug + +ren Cyclone.ob_ Cyclone.obj diff --git a/Cyclone/Ea.cpp b/Cyclone/Ea.cpp new file mode 100644 index 0000000..b1a3340 --- /dev/null +++ b/Cyclone/Ea.cpp @@ -0,0 +1,296 @@ + +#include "app.h" + +// --------------------------------------------------------------------------- +// 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) +{ + int i=0,low=0,needor=0; + int lsl=0; + + 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 ;) + + ot(" and r%d,r8,#0x%.4x\n",r,mask); + + // Find out amount to shift left: + lsl=shift-low; + + if (lsl) + { + ot(" mov r%d,r%d,",r,r); + if (lsl>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<=2) lsl=0; // Saves one opcode + + ot(";@ EaCalc : Get register index into r%d:\n",a); + + EaCalcReg(a,ea,mask,0,lsl); + return 0; + } + + ot(";@ EaCalc : Get '%s' into r%d:\n",text,a); + // (An), (An)+, -(An): + if (ea<0x28) + { + int step=1< 0xc0000000 +// Otherwise the ARM register v is sign extended, e.g. 0xc000 -> 0xffffc000 + +int EaRead(int a,int v,int ea,int size,int top) +{ + char text[32]=""; + int shift=0; + + shift=32-(8<=2) lsl=0; // Having a lsl #2 here saves one opcode + + 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 (top && shift) ot(" mov r%d,r%d,asl #%d\n",v,v,shift); + + ot("\n"); return 0; + } + + ot(";@ EaRead : Read '%s' (address in r%d) into r%d:\n",text,a,v); + + if (ea==0x3c) + { + int asl=0; + + if (top) asl=shift; + + if (v!=a || asl) ot(" mov r%d,r%d,asl #%d\n",v,a,asl); + ot("\n"); return 0; + } + + if (a!=0) ot(" mov r0,r%d\n",a); + + if (ea>=0x3a && ea<=0x3b) MemHandler(2,size); // Fetch + else MemHandler(0,size); // Read + + 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); + + ot("\n"); return 0; +} + +// Return 1 if we can read this ea +int EaCanRead(int ea,int size) +{ + if (size<0) + { + // LEA: + // These don't make sense?: + if (ea<0x10) return 0; // Register + if (ea==0x3c) return 0; // Immediate + if (ea>=0x18 && ea<0x28) return 0; // Pre/Post inc/dec An + } + + if (ea<=0x3c) return 1; + return 0; +} + +// --------------------------------------------------------------------------- +// 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 +// If a==0 and v==1 it's faster though. +int EaWrite(int a,int v,int ea,int size,int top) +{ + char text[32]=""; + int shift=0; + + if (top) shift=32-(8<=2) lsl=0; // Having a lsl #2 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 (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); + + ot("\n"); return 0; + } + + ot(";@ EaWrite: Write r%d into '%s' (address in r%d):\n",v,text,a); + + 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); + + MemHandler(1,size); // Call write handler + + ot("\n"); return 0; +} + +// Return 1 if we can write this ea +int EaCanWrite(int ea) +{ + if (ea<=0x3b) return 1; + return 0; +} +// --------------------------------------------------------------------------- diff --git a/Cyclone/License-GPLv2.txt b/Cyclone/License-GPLv2.txt new file mode 100644 index 0000000..0a98ec0 --- /dev/null +++ b/Cyclone/License-GPLv2.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file diff --git a/Cyclone/License.txt b/Cyclone/License.txt new file mode 100644 index 0000000..a7be9de --- /dev/null +++ b/Cyclone/License.txt @@ -0,0 +1,37 @@ +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. + +The GNU General Public License is included here in 'License-GPLv2.txt' + +The MAME License is quoted here: + + +Redistribution and use of this code or any derivative works are permitted +provided that the following conditions are met: + +* Redistributions may not be sold, nor may they be used in a commercial +product or activity. + +* Redistributions that are modified from the original source must include the +complete source code, including the source code for all components used by a +binary built from the modified sources. However, as a special exception, the +source code distributed need not include anything that is normally distributed +(in either source or binary form) with the major components (compiler, kernel, +and so on) of the operating system on which the executable runs, unless that +component itself accompanies the executable. + +* Redistributions must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Cyclone/Main.cpp b/Cyclone/Main.cpp new file mode 100644 index 0000000..29dfb3e --- /dev/null +++ b/Cyclone/Main.cpp @@ -0,0 +1,277 @@ + +#include "app.h" + +static FILE *AsmFile=NULL; + +static int CycloneVer=0x0069; // 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 + +void ot(char *format, ...) +{ + va_list valist=NULL; + va_start(valist,format); + if (AsmFile) vfprintf(AsmFile,format,valist); + va_end(valist); +} + +void ltorg() +{ + if (ms) ot(" LTORG\n"); + else ot(" .ltorg\n"); +} + +static void PrintException() +{ + 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 +} + +// Trashes r0 +void CheckInterrupt() +{ + ot(";@ CheckInterrupt:\n"); + ot(" ldrb r0,[r7,#0x47] ;@ Get IRQ level\n"); + ot(" tst r0,r0\n"); + ot(" blne DoInterrupt\n"); + ot("\n"); +} + +static void PrintFramework() +{ + ot(";@ --------------------------- Framework --------------------------\n"); + if (ms) ot("CycloneRun\n"); + else ot("CycloneRun:\n"); + + ot(" stmdb sp!,{r4-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(" 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("\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"); + 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("\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("\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"); + 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"); + 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"); + ot("\n"); + 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("\n"); + + ot("Exception%s\n", ms?"":":"); + ot("\n"); + ot(" mov r11,lr ;@ Preserve ARM return address\n"); + PrintException(); + ot(" mov pc,r11 ;@ Return\n"); + ot("\n"); +} + +// --------------------------------------------------------------------------- +// Call Read(r0), Write(r0,r1) or Fetch(r0) +// Trashes r0-r3 +int MemHandler(int type,int size) +{ + int func=0; + func=0x68+type*0xc+(size<<2); // Find correct offset + + if (Debug&4) ot(" str r4,[r7,#0x40] ;@ Save PC\n"); + if (Debug&3) ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n"); + + ot(" mov lr,pc\n"); + ot(" ldr pc,[r7,#0x%x] ;@ Call ",func); + + // Document what we are calling: + if (type==0) ot("read"); + if (type==1) ot("write"); + if (type==2) ot("fetch"); + + if (type==1) ot("%d(r0,r1)",8<>12); fflush(stdout); } // Update progress + + OpAny(op); + } + + ot("\n"); + + printf("]\n"); +} + +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>12,CycloneVer&0xfff); + + 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"); + ot(";@ SVN repository can be found at http://code.google.com/p/cyclone68000/\n\n"); + + CyJump=(int *)malloc(0x40000); if (CyJump==NULL) return 1; + memset(CyJump,0xff,0x40000); // Init to -1 + + 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("\n"); + + PrintFramework(); + PrintOpcodes(); + PrintJumpTable(); + + if (ms) ot(" END\n"); + + fclose(AsmFile); AsmFile=NULL; + + printf("Assembling...\n"); + // Assemble the file + if (ms) system("armasm Cyclone.asm"); + else system("as -o Cyclone.o Cyclone.s"); + printf("Done!\n\n"); + + free(CyJump); + return 0; +} + +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(); + return 0; +} diff --git a/Cyclone/OpAny.cpp b/Cyclone/OpAny.cpp new file mode 100644 index 0000000..9a149bb --- /dev/null +++ b/Cyclone/OpAny.cpp @@ -0,0 +1,117 @@ + +#include "app.h" + +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) +{ + return (unsigned short)( (OpData[a&15]<<8) | OpData[(a+1)&15] ); +} + +// For opcode 'op' use handler 'use' +void OpUse(int op,int use) +{ + char text[64]=""; + CyJump[op]=use; + + if (op!=use) return; + + // Disassemble opcode + DisaPc=0; + DisaText=text; + DisaWord=OpRead16; + + DisaGet(); + ot(";@ ---------- [%.4x] %s uses Op%.4x ----------\n",op,text,use); +} + +void OpFirst() +{ + ot(" ldrh r8,[r4],#2 ;@ Fetch first opcode\n"); + ot(" ldr pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n"); +} + +void OpStart(int op) +{ + Cycles=0; + OpUse(op,op); // This opcode obviously uses this handler + ot("Op%.4x%s\n", op, ms?"":":"); +} + +void OpEnd() +{ + ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\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(); + ot("\n"); +} + +int OpBase(int op) +{ + 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<0x38) return op&~7; // Use 1 handler for (a0)-(a7), etc... + return op; +} + +// Get flags, trashes r2 +int OpGetFlags(int subtract,int xbit) +{ + ot(" mrs r9,cpsr ;@ r9=flags\n"); + + if (subtract) ot(" eor r9,r9,#0x20000000 ;@ Invert carry\n"); + + if (Accu&1) if (xbit) + { + ot(" mov r2,r9,lsr #28\n"); + ot(" strb r2,[r7,#0x45] ;@ Save X bit\n"); + } + return 0; +} + +// ----------------------------------------------------------------- + +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); + +} diff --git a/Cyclone/OpArith.cpp b/Cyclone/OpArith.cpp new file mode 100644 index 0000000..db80147 --- /dev/null +++ b/Cyclone/OpArith.cpp @@ -0,0 +1,521 @@ + +#include "app.h" + +// --------------------- Opcodes 0x0000+ --------------------- +// Emit an Ori/And/Sub/Add/Eor/Cmp Immediate opcode, 0000ttt0 00aaaaaa +int OpArith(int op) +{ + int type=0,size=0; + int sea=0,tea=0; + int use=0; + + // Get source and target EA + type=(op>>9)&7; if (type==4 || type>=7) return 1; + size=(op>>6)&3; if (size>=3) return 1; + sea= 0x003c; + tea=op&0x003f; + + // See if we can do this opcode: + if (EaCanRead(tea,size)==0) return 1; + if (type!=6 && EaCanWrite(tea)==0) return 1; + + use=OpBase(op); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=4; + + EaCalc(10,0x0000, sea,size); + EaRead(10, 10, sea,size,1); + + EaCalc(11,0x003f, tea,size); + EaRead(11, 0, tea,size,1); + + 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<2 || type==5) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); // 0,1,5 + + if (type< 2) OpGetFlags(0,0); // Ori/And + if (type==2) OpGetFlags(1,1); // Sub: Subtract/X-bit + if (type==3) OpGetFlags(0,1); // Add: X-bit + if (type==5) OpGetFlags(0,0); // Eor + if (type==6) OpGetFlags(1,0); // Cmp: Subtract + ot("\n"); + + if (type!=6) + { + EaWrite(11, 1, tea,size,1); + } + + // Correct cycles: + if (type==6) + { + if (size>=2 && tea<0x10) Cycles+=2; + } + else + { + if (size>=2) Cycles+=4; + if (tea>=0x10) Cycles+=4; + if (Amatch && type==1 && size>=2 && tea<0x10) Cycles-=2; + } + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x5000+ --------------------- +int OpAddq(int op) +{ + // 0101nnnt xxeeeeee (nnn=#8,1-7 t=addq/subq xx=size, eeeeee=EA) + int num=0,type=0,size=0,ea=0; + int use=0; + char count[16]=""; + int shift=0; + + num =(op>>9)&7; if (num==0) num=8; + type=(op>>8)&1; + size=(op>>6)&3; if (size>=3) return 1; + ea = op&0x3f; + + // See if we can do this opcode: + if (EaCanRead (ea,size)==0) return 1; + if (EaCanWrite(ea )==0) return 1; + + use=op; if (ea<0x38) use&=~7; + if ((ea&0x38)==0x08) { size=2; use&=~0xc0; } // Every addq #n,An is 32-bit + + 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); + Cycles=ea<8?4:8; + if (size>=2 && ea!=8) Cycles+=4; + + EaCalc(10,0x003f, ea,size); + EaRead(10, 0, ea,size,1); + + 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,r2,#0x%.4x\n",7<>12)&5; + rea =(op>> 9)&7; + dir =(op>> 8)&1; + 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; + + use=OpBase(op); + use&=~0x0e00; // Use same opcode for Dn + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); 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); + + 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&1)==0) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); + + OpGetFlags(type==1,type&1); // 1==subtract + 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; } + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x80c0+ --------------------- +int OpMul(int op) +{ + // Div/Mul: 1m00nnns 11eeeeee (m=Mul, nnn=Register Dn, s=signed, eeeeee=EA) + int type=0,rea=0,sign=0,ea=0; + int use=0; + + type=(op>>14)&1; // div/mul + rea =(op>> 9)&7; + sign=(op>> 8)&1; + ea = op&0x3f; + + // See if we can do this opcode: + if (EaCanRead(ea,1)==0) return 1; + + use=OpBase(op); + use&=~0x0e00; // Use same for all registers + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=type?70:133; + + EaCalc(10,0x003f, ea, 1); + EaRead(10, 10, ea, 1); + + EaCalc (0,0x0e00,rea, 2); + EaRead (0, 2,rea, 2); + + if (type==0) + { + ot(" cmp r10,#0\n"); + ot(" moveq r10,#1 ;@ Divide by zero\n"); + 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("\n"); + ot(" cmp r2,#0\n"); + ot(" eorlt r11,r11,#1\n"); + ot(" rsblt r2,r2,#0 ;@ Make r2 positive\n"); + ot("\n"); + } + + ot(";@ Divide r2 by r10\n"); + ot(" mov r3,#0\n"); + ot(" mov r1,r10\n"); + ot("\n"); + ot(";@ Shift up divisor till it's just less than numerator\n"); + ot("Shift%.4x%s\n",op,ms?"":":"); + ot(" cmp r1,r2,lsr #1\n"); + ot(" movls r1,r1,lsl #1\n"); + ot(" bcc Shift%.4x\n",op); + ot("\n"); + + ot("Divide%.4x%s\n",op,ms?"":":"); + 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(" movne r1,r1,lsr #1\n"); + ot(" bne Divide%.4x\n",op); + ot("\n"); + + if (sign) + { + ot(" tst r11,r11\n"); + ot(" rsbne r3,r3,#0 ;@ Negate if result is negative\n"); + } + + ot(" mov r11,r2 ;@ Remainder\n"); + + ot(" adds r1,r3,#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"); + } + + 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 r2,r2,lsl #16\n"); + + if (sign==0) ot(" mov r10,r10,lsr #16\n"); + ot(" mov r2,r2,%s #16\n",shift); + ot("\n"); + + ot(" mul r1,r2,r10\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); + + + OpEnd(); + + return 0; +} + +// Get X Bit into carry - trashes r2 +static 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(" msr cpsr_flg,r2 ;@ Get into Carry\n"); + ot("\n"); + return 0; +} + +// --------------------- Opcodes 0x8100+ --------------------- +// 1t00ddd1 0000asss - sbcd/abcd Ds,Dd or -(As),-(Ad) +int OpAbcd(int op) +{ + int use=0; + int type=0,sea=0,addr=0,dea=0; + + type=(op>>14)&1; + dea =(op>> 9)&7; + addr=(op>> 3)&1; + sea = op &7; + + if (addr) { sea|=0x20; dea|=0x20; } + + use=op&~0x0e07; // Use same opcode for all registers + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=6; + + EaCalc( 0,0x0007, sea,0); + EaRead( 0, 10, sea,0,1); + EaCalc(11,0x0e00, dea,0); + EaRead(11, 1, dea,0,1); + + ot(" ldrb r2,[r7,#0x45] ;@ Get X bit\n"); + ot(" tst r2,#2\n"); + ot(" addne r10,r10,#0x01000000 ;@ Add carry bit\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); + } + 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("\n"); + + EaWrite(11, 1, dea,0,1); + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x90c0+ --------------------- +// Suba/Cmpa/Adda 1tt1nnnx 11eeeeee (tt=type, x=size, eeeeee=Source EA) +int OpAritha(int op) +{ + int use=0; + int type=0,size=0,sea=0,dea=0; + + // Suba/Cmpa/Adda/(invalid): + type=(op>>13)&3; if (type>=3) return 1; + + size=(op>>8)&1; size++; + dea=(op>>9)&7; dea|=8; // Dest=An + sea=op&0x003f; // Source + + // See if we can do this opcode: + if (EaCanRead(sea,size)==0) return 1; + + use=OpBase(op); + 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); + + EaCalc ( 0,0x0e00, dea,2); + EaRead ( 0, 1, dea,2); + + if (type==0) ot(" sub r1,r1,r10\n"); + if (type==1) ot(" cmp r1,r10 ;@ Defines NZCV\n"); + if (type==1) OpGetFlags(1,0); // Get Cmp flags + if (type==2) ot(" add r1,r1,r10\n"); + 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; } + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x9100+ --------------------- +// Emit a Subx/Addx opcode, 1t01ddd1 zz000sss addx.z Ds,Dd +int OpAddx(int op) +{ + int use=0; + int type=0,size=0,dea=0,sea=0; + + type=(op>>12)&5; + dea =(op>> 9)&7; + size=(op>> 6)&3; if (size>=3) return 1; + sea = op&0x3f; + + // 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 (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=8; + + 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); + + ot(";@ Do arithmetic:\n"); + GetXBit(type==1); + + if (type==5 && 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("\n"); + } + + if (type==1) ot(" sbcs r1,r1,r11\n"); + if (type==5) ot(" adcs r1,r1,r11\n"); + OpGetFlags(type==1,1); // subtract + ot("\n"); + + ot(";@ Save result:\n"); + EaWrite( 0, 1, dea,size,1); + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0xb000+ --------------------- +// Emit a Cmp/Eor opcode, 1011rrrt xxeeeeee (rrr=Dn, t=cmp/eor, xx=size extension, eeeeee=ea) +int OpCmpEor(int op) +{ + int rea=0,eor=0; + int size=0,ea=0,use=0; + + // Get EA and register EA + rea=(op>>9)&7; + eor=(op>>8)&1; + size=(op>>6)&3; if (size>=3) return 1; + ea=op&0x3f; + + // See if we can do this opcode: + if (EaCanRead(ea,size)==0) return 1; + if (eor && EaCanWrite(ea)==0) return 1; + + use=OpBase(op); + 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; + + ot(";@ Get EA into r10 and value into r0:\n"); + EaCalc (10,0x003f, ea,size); + EaRead (10, 0, ea,size,1); + + ot(";@ Get register operand into r1:\n"); + EaCalc (1 ,0x0e00, rea,size); + EaRead (1, 1, rea,size,1); + + ot(";@ Do arithmetic:\n"); + if (eor==0) ot(" cmp r1,r0\n"); + if (eor) + { + ot(" eor r1,r0,r1\n"); + 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(10, 1,ea,size,1); + + OpEnd(); + return 0; +} + diff --git a/Cyclone/OpBranch.cpp b/Cyclone/OpBranch.cpp new file mode 100644 index 0000000..0fef71d --- /dev/null +++ b/Cyclone/OpBranch.cpp @@ -0,0 +1,376 @@ + +#include "app.h" + +static void CheckPc() +{ + ot(";@ Check Memory Base+pc (r4)\n"); + ot(" add lr,pc,#4\n"); + ot(" mov r0,r4\n"); + ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n"); + ot(" mov r4,r0\n"); + ot("\n"); +} + +// Push 32-bit value in r1 - trashes r0-r3 +void OpPush32() +{ + ot(";@ Push r1 onto stack\n"); + ot(" ldr r0,[r7,#0x3c]\n"); + ot(" sub r0,r0,#4 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,2); + ot("\n"); +} + +// Push SR - trashes r0-r3 +void OpPushSr(int high) +{ + ot(";@ Push SR:\n"); + OpFlagsToReg(high); + ot(" ldr r0,[r7,#0x3c]\n"); + ot(" sub r0,r0,#2 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(1,1); + ot("\n"); +} + +// Pop SR - trashes r0-r3 +static void PopSr(int high) +{ + ot(";@ Pop SR:\n"); + ot(" ldr r0,[r7,#0x3c]\n"); + ot(" add r1,r0,#2 ;@ Postincrement A7\n"); + ot(" str r1,[r7,#0x3c] ;@ Save A7\n"); + MemHandler(0,1); + ot("\n"); + OpRegToFlags(high); +} + +// Pop PC - assumes r10=Memory Base - trashes r0-r3 +static void PopPc() +{ + ot(";@ Pop PC:\n"); + ot(" ldr r0,[r7,#0x3c]\n"); + 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("\n"); + CheckPc(); +} + +int OpTrap(int op) +{ + int use=0; + + use=op&~0xf; + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(use); + 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(" bl Exception\n"); + ot("\n"); + + Cycles=38; OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x4e50+ --------------------- +int OpLink(int op) +{ + int use=0; + + use=op&~7; + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(use); + + ot(";@ Get An\n"); + EaCalc(10, 7, 8, 2); + EaRead(10, 1, 8, 2, 1); + + ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n"); + ot(" sub r0,r0,#4 ;@ A7-=4\n"); + ot(" mov r11,r0\n"); + ot("\n"); + + ot(";@ Write An to Stack\n"); + MemHandler(1,2); + + ot(";@ Save to An\n"); + EaWrite(10,11, 8, 2, 1); + + ot(";@ Get offset:\n"); + EaCalc(0,0,0x3c,1); + EaRead(0,0,0x3c,1); + + ot(" add r11,r11,r0 ;@ Add offset to A7\n"); + ot(" str r11,[r7,#0x3c]\n"); + ot("\n"); + + Cycles=16; + OpEnd(); + return 0; +} + +// --------------------- Opcodes 0x4e58+ --------------------- +int OpUnlk(int op) +{ + int use=0; + + use=op&~7; + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(use); + + ot(";@ Get An\n"); + EaCalc(10, 7, 8, 2); + EaRead(10, 0, 8, 2, 1); + + ot(" add r11,r0,#4 ;@ A7+=4\n"); + ot("\n"); + ot(";@ Pop An from stack:\n"); + MemHandler(0,2); + ot("\n"); + ot(" str r11,[r7,#0x3c] ;@ Save A7\n"); + ot("\n"); + ot(";@ An = value from stack:\n"); + EaWrite(10, 0, 8, 2, 1); + + Cycles=12; + OpEnd(); + return 0; +} + +// --------------------- Opcodes 0x4e70+ --------------------- +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 + + if (type==3 || type==7) // rte/rtr + { + OpStart(op); Cycles=20; + PopSr(type==3); + ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); + PopPc(); + if (type==3) CheckInterrupt(); + OpEnd(); + return 0; + } + + if (type==5) // rts + { + OpStart(op); Cycles=16; + ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); + PopPc(); + OpEnd(); + return 0; + } + + return 1; +} + +// --------------------- Opcodes 0x4e80+ --------------------- +// Emit a Jsr/Jmp opcode, 01001110 1mEEEeee +int OpJsr(int op) +{ + int use=0; + int sea=0; + + sea=op&0x003f; + + // See if we can do this opcode: + if (EaCanRead(sea,-1)==0) return 1; + + use=OpBase(op); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=14; // Correct? + + ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n"); + ot("\n"); + EaCalc(0,0x003f,sea,0); + + ot(";@ Jump - Get new PC from r0\n"); + if (op&0x40) + { + // Jmp - Get new PC from r0 + ot(" add r4,r0,r10 ;@ r4 = Memory Base + New PC\n"); + ot("\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(); + + Cycles+=8; + } + + if (Amatch && sea==0x10) Cycles-=2; // (An) Correct? + + CheckPc(); + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x50c8+ --------------------- + +// ARM version of 68000 condition codes: +static char *Cond[16]= +{ + "", "", "hi","ls","cc","cs","ne","eq", + "vc","vs","pl","mi","ge","lt","gt","le" +}; + +// Emit a Dbra opcode, 0101cccc 11001nnn vv +int OpDbra(int op) +{ + int use=0; + int cc=0; + + use=op&~7; // Use same handler + 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"); + + if (cc>=2) + { + 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"); + } + + ot(";@ Check if Dn.w is -1\n"); + ot(" cmps r0,#-1\n"); + ot(" beq DbraEnd%.4x\n",op); + 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("DbraEnd%.4x%s\n", op, ms?"":":"); + Cycles+=2; + ot(" add r4,r4,#2 ;@ Skip branch offset\n"); + ot("\n"); + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x6000+ --------------------- +// Emit a Branch opcode 0110cccc nn (cccc=condition) +int OpBranch(int op) +{ + int size=0,use=0; + int offset=0; + int cc=0; + + offset=(char)(op&0xff); + cc=(op>>8)&15; + + // Special offsets: + if (offset==0) size=1; + if (offset==-1) size=2; + + 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 + + ot(";@ Get Branch offset:\n"); + if (size) + { + EaCalc(0,0,0x3c,size); + EaRead(0,0,0x3c,size); + if (Amatch && size==1) Cycles-=4; + } + + if (size==0) ot(" mov r0,r8,asl #24 ;@ Shift 8-bit signed offset up...\n\n"); + + if (cc>=2) + { + 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"); + } + else + { + if (size==0) ot(" mov r0,r0,asr #24 ;@ ...shift down\n\n"); + } + + ot(";@ Branch taken - Add on r0 to PC\n"); + + 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("\n"); + if (size) ot(" sub r4,r4,#%d ;@ (Branch is relative to Opcode+2)\n",1<=2) + { + ot("DontBranch%.4x%s\n", op, ms?"":":"); + Cycles-=2; // Branch not taken + OpEnd(); + } + + return 0; +} + diff --git a/Cyclone/OpLogic.cpp b/Cyclone/OpLogic.cpp new file mode 100644 index 0000000..0ff2e3f --- /dev/null +++ b/Cyclone/OpLogic.cpp @@ -0,0 +1,523 @@ +#include "app.h" + +// --------------------- Opcodes 0x0100+ --------------------- +// Emit a Btst (Register) opcode 0000nnn1 00aaaaaa +int OpBtstReg(int op) +{ + int use=0; + int type=0,sea=0,tea=0; + int size=0; + + type=(op>>6)&3; + // Get source and target EA + sea=(op>>9)&7; + tea=op&0x003f; + if (tea<0x10) size=2; // For registers, 32-bits + + if ((tea&0x38)==0x08) return 1; // movep + + // See if we can do this opcode: + if (EaCanRead(tea,0)==0) return 1; + if (type>0) + { + if (EaCanWrite(tea)==0) return 1; + } + + use=OpBase(op); + 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; + + ot(" mov r10,#1\n"); + + 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"); + 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("\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"); + ot("\n"); + EaWrite(11, 1,tea,size); + } + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x0800+ --------------------- +// Emit a Btst/Bchg/Bclr/Bset (Immediate) opcode 00001000 ttaaaaaa nn +int OpBtstImm(int op) +{ + int type=0,sea=0,tea=0; + int use=0; + int size=0; + + type=(op>>6)&3; + // Get source and target EA + sea= 0x003c; + tea=op&0x003f; + if (tea<0x10) size=2; // For registers, 32-bits + + // See if we can do this opcode: + if (EaCanRead(tea,0)==0) return 1; + if (type>0) + { + if (EaCanWrite(tea)==0) return 1; + } + + use=OpBase(op); + 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; + + 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"); + 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("\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"); + ot("\n"); + EaWrite(11, 1,tea,size); + } + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x4000+ --------------------- +int OpNeg(int op) +{ + // 01000tt0 xxeeeeee (tt=negx/clr/neg/not, xx=size, eeeeee=EA) + int type=0,size=0,ea=0,use=0; + + type=(op>>9)&3; + 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 (EaCanWrite(ea )==0) return 1; + + use=OpBase(op); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=size<2?4:6; + + EaCalc (10,0x003f,ea,size); + + if (type!=1) EaRead (10,0,ea,size); // Don't need to read for 'clr' + if (type==1) ot("\n"); + + if (type==1) + { + ot(";@ Clear:\n"); + ot(" mov r1,#0\n"); + ot(" mov r9,#0x40000000 ;@ NZCV=0100\n"); + ot("\n"); + } + + if (type==2) + { + ot(";@ Neg:\n"); + ot(" rsbs r1,r0,#0\n"); + OpGetFlags(1,1); + ot("\n"); + } + + if (type==3) + { + ot(";@ Not:\n"); + 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); + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x4840+ --------------------- +// Swap, 01001000 01000nnn swap Dn +int OpSwap(int op) +{ + int ea=0,use=0; + + ea=op&7; + use=op&~0x0007; // Use same opcode for all An + + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=4; + + EaCalc (10,0x0007,ea,2); + EaRead (10, 0,ea,2); + + 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); + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x4a00+ --------------------- +// Emit a Tst opcode, 01001010 xxeeeeee +int OpTst(int op) +{ + int sea=0; + int size=0,use=0; + + sea=op&0x003f; + size=(op>>6)&3; if (size>=3) return 1; + + // See if we can do this opcode: + if (EaCanWrite(sea)==0) return 1; + + use=OpBase(op); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=4; + + EaCalc ( 0,0x003f,sea,size); + EaRead ( 0, 0,sea,size); + + ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n"); + ot(" mrs r9,cpsr ;@ r9=flags\n"); + ot("\n"); + + OpEnd(); + return 0; +} + +// --------------------- Opcodes 0x4880+ --------------------- +// Emit an Ext opcode, 01001000 1x000nnn +int OpExt(int op) +{ + int ea=0; + int size=0,use=0; + int shift=0; + + ea=op&0x0007; + size=(op>>6)&1; + shift=32-(8<>8)&15; + ea=op&0x003f; + + if ((ea&0x38)==0x08) return 1; // dbra, not scc + + // See if we can do this opcode: + if (EaCanWrite(ea)==0) return 1; + + use=OpBase(op); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=8; + + if (ea<0x10) Cycles=4; + + ot(" mov r1,#0\n"); + + if (cc!=1) + { + 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]); + } + + 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); + + OpEnd(); + 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]=""; + 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,r2,#63\n"); + ot("\n"); + } + + // Take 2*n cycles: + if (count<0) ot(" sub r5,r5,r2,asl #1 ;@ Take 2*n cycles\n\n"); + else Cycles+=count<<1; + + if (type<2) + { + // 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 + usereg=(op>>5)&1; + type =(op>>3)&3; + + if (usereg==0) count=((count-1)&7)+1; // because ccc=000 means 8 + + // 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; } + 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; + + EaCalc(10,0x0007, ea,size); + EaRead(10, 0, ea,size,1); + + EmitAsr(op,type,dir,count, size,usereg); + + EaWrite(10, 0, ea,size,1); + + OpEnd(); + + return 0; +} + +// Asr/l/Ror/l etc EA - 11100ttd 11eeeeee +int OpAsrEa(int op) +{ + int use=0,type=0,dir=0,ea=0,size=1; + + type=(op>>9)&3; + dir =(op>>8)&1; + ea = op&0x3f; + + if (ea<0x10) return 1; + // See if we can do this opcode: + if (EaCanRead(ea,0)==0) return 1; + if (EaCanWrite(ea)==0) return 1; + + use=OpBase(op); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=8; + + EaCalc (10,0x003f,ea,size); + EaRead (10, 0,ea,size,1); + + 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); + + OpEnd(); + return 0; +} diff --git a/Cyclone/OpMove.cpp b/Cyclone/OpMove.cpp new file mode 100644 index 0000000..2a1b5fe --- /dev/null +++ b/Cyclone/OpMove.cpp @@ -0,0 +1,454 @@ + +#include "app.h" + +// --------------------- Opcodes 0x1000+ --------------------- +// Emit a Move opcode, 00xxdddd ddssssss +int OpMove(int op) +{ + int sea=0,tea=0; + int size=0,use=0; + int movea=0; + + // Get source and target EA + sea = op&0x003f; + tea =(op&0x01c0)>>3; + tea|=(op&0x0e00)>>9; + + if (tea>=8 && tea<0x10) movea=1; + + // Find size extension + switch (op&0x3000) + { + default: return 1; + case 0x1000: size=0; break; + case 0x3000: size=1; break; + case 0x2000: size=2; break; + } + + if (movea && size<1) return 1; // movea.b is invalid + + // See if we can do this opcode: + if (EaCanRead (sea,size)==0) return 1; + if (EaCanWrite(tea )==0) return 1; + + use=OpBase(op); + 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 (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); 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) ot(" mrs r9,cpsr ;@ r9=NZCV flags\n"); + ot("\n"); + + if (movea) size=2; // movea always expands to 32-bits + + EaCalc (0,0x0e00,tea,size); + EaWrite(0, 1,tea,size); + + OpEnd(); + return 0; +} + +// --------------------- Opcodes 0x41c0+ --------------------- +// Emit an Lea opcode, 0100nnn1 11aaaaaa +int OpLea(int op) +{ + int use=0; + int sea=0,tea=0; + + sea= op&0x003f; + tea=(op&0x0e00)>>9; tea|=8; + + if (EaCanRead(sea,-1)==0) return 1; // See if we can do this opcode: + + use=OpBase(op); + 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; + + EaCalc (1,0x003f,sea,0); // Lea + EaCalc (0,0x0e00,tea,2); + EaWrite(0, 1,tea,2); + + if (Amatch) + { + // Correct? + if (sea< 0x18) Cycles+=4; + else if (sea==0x30) Cycles+=12; + else Cycles+=8; + } + + OpEnd(); + + 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 +int OpMoveSr(int op) +{ + int type=0,ea=0; + int use=0,size=1; + + type=(op>>9)&3; + ea=op&0x3f; + + switch(type) + { + case 0: case 1: + if (EaCanWrite(ea)==0) return 1; // See if we can do this opcode: + break; + + default: return 1; // todo + + case 2: case 3: + if (EaCanRead(ea,size)==0) return 1; // See if we can do this opcode: + break; + } + + use=OpBase(op); + 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); + + if (type==0 || type==1) + { + OpFlagsToReg(type==0); + EaCalc (0,0x003f,ea,size); + EaWrite(0, 1,ea,size); + } + + if (type==2 || type==3) + { + EaCalc(0,0x003f,ea,size); + EaRead(0, 0,ea,size); + OpRegToFlags(type==3); + if (type==3) CheckInterrupt(); + } + + OpEnd(); + + if (type==0 || type==3) SuperEnd(op); + + return 0; +} + + +// Ori/Andi/Eori $nnnn,sr 0000t0t0 01111100 +int OpArithSr(int op) +{ + int type=0,ea=0; + int use=0,size=0; + + type=(op>>9)&5; if (type==4) return 1; + size=(op>>6)&1; + ea=0x3c; + + use=OpBase(op); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=16; + + SuperCheck(op); + + EaCalc(0,0x003f,ea,size); + EaRead(0, 10,ea,size); + + 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(); + + OpEnd(); + SuperEnd(op); + + return 0; +} + +// --------------------- Opcodes 0x4850+ --------------------- +// Emit an Pea opcode, 01001000 01aaaaaa +int OpPea(int op) +{ + int use=0; + int ea=0; + + 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); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=20; + + EaCalc (1,0x003f, ea,0); + ot("\n"); + ot(" ldr r0,[r7,#0x3c]\n"); + ot(" sub r0,r0,#4 ;@ Predecrement A7\n"); + ot(" str r0,[r7,#0x3c] ;@ Save A7\n"); + ot("\n"); + MemHandler(1,2); // Write 32-bit + ot("\n"); + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x4880+ --------------------- +// Emit a Movem opcode, 01001d00 1xeeeeee regmask +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; + ea=op&0x003f; + dir=(op>>10)&1; // Direction + + 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); + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); + + 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(";@ r10=Register Index*4:\n"); + if (decr) ot(" mov r10,#0x3c ;@ order reversed for -(An)\n"); + else ot(" mov r10,#0\n"); + + ot("\n"); + ot("MoreReg%.4x%s\n",op, ms?"":":"); + + ot(" tst r11,#1\n"); + ot(" beq SkipReg%.4x\n",op); + ot("\n"); + + if (decr) ot(" sub r9,r9,#%d ;@ Pre-decrement address\n",1<>3)&1; // Direction + use=op&~0x0007; // Use same opcode for all An + + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=4; + + ot(" ldrb r0,[r7,#0x44] ;@ Get SR\n"); + ot(" tst r0,#0x20 ;@ Check we are in supervisor mode\n"); + ot(" beq WrongMode%.4x ;@ No\n",op); + ot("\n"); + + if (dir) + { + EaCalc (0,0x0007,8,2); + ot(" ldr r1,[r7,#0x48] ;@ Get from USP\n\n"); + EaWrite(0, 1,8,2); + } + else + { + EaCalc (0,0x0007,8,2); + EaRead (0, 0,8,2); + ot(" str r0,[r7,#0x48] ;@ Put in USP\n\n"); + } + + OpEnd(); + + ot("WrongMode%.4x%s\n",op,ms?"":":"); + ot(";@ todo - cause an exception\n"); + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0x7000+ --------------------- +// Emit a Move Quick opcode, 0111nnn0 dddddddd moveq #dd,Dn +int OpMoveq(int op) +{ + int use=0; + + use=op&0xf100; // Use same opcode for all values + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=4; + + ot(" movs r0,r8,asl #24\n"); + ot(" and r1,r8,#0x0e00\n"); + ot(" mov r0,r0,asr #24 ;@ Sign extended Quick value\n"); + ot(" mrs r9,cpsr ;@ r9=NZ flags\n"); + ot(" str r0,[r7,r1,lsr #7] ;@ Store into Dn\n"); + ot("\n"); + + OpEnd(); + + return 0; +} + +// --------------------- Opcodes 0xc140+ --------------------- +// Emit a Exchange opcode: +// 1100ttt1 01000sss exg ds,dt +// 1100ttt1 01001sss exg as,at +// 1100ttt1 10001sss exg as,dt +int OpExg(int op) +{ + int use=0,type=0; + + type=op&0xf8; + + if (type!=0x40 && type!=0x48 && type!=0x88) return 1; // Not an exg opcode + + use=op&0xf1f8; // Use same opcode for all values + if (op!=use) { OpUse(op,use); return 0; } // Use existing handler + + OpStart(op); Cycles=6; + + ot(" and r10,r8,#0x0e00 ;@ Find T register\n"); + ot(" and r11,r8,#0x000f ;@ Find S register\n"); + if (type==0x48) ot(" orr r10,r10,#0x1000 ;@ T is an address register\n"); + ot("\n"); + ot(" ldr r0,[r7,r10,lsr #7] ;@ Get T\n"); + ot(" ldr r1,[r7,r11,lsl #2] ;@ Get S\n"); + ot("\n"); + ot(" str r0,[r7,r11,lsl #2] ;@ T->S\n"); + ot(" str r1,[r7,r10,lsr #7] ;@ S->T\n"); + ot("\n"); + + OpEnd(); + + return 0; +} diff --git a/Cyclone/app.h b/Cyclone/app.h new file mode 100644 index 0000000..f4795cc --- /dev/null +++ b/Cyclone/app.h @@ -0,0 +1,85 @@ + +#include +#include +#include +#include + +// Disa.c +#include "../Pico/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); +int EaCanRead(int ea,int size); +int EaWrite(int a,int v,int ea,int size,int top=0); +int EaCanWrite(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 +void ot(char *format, ...); +void ltorg(); +void CheckInterrupt(); +int MemHandler(int type,int size); + +// OpAny.cpp +int OpGetFlags(int subtract,int xbit); +void OpUse(int op,int use); +void OpFirst(); +void OpStart(int op); +void OpEnd(); +int OpBase(int op); +void OpAny(int op); + +//---------------------- +// OpArith.cpp +int OpArith(int op); +int OpLea(int op); +int OpAddq(int op); +int OpArithReg(int op); +int OpMul(int op); +int OpAbcd(int op); +int OpAritha(int op); +int OpAddx(int op); +int OpCmpEor(int op); + +// OpBranch.cpp +void OpPush32(); +void OpPushSr(int high); +int OpTrap(int op); +int OpLink(int op); +int OpUnlk(int op); +int Op4E70(int op); +int OpJsr(int op); +int OpBranch(int op); +int OpDbra(int op); + +// OpLogic.cpp +int OpBtstReg(int op); +int OpBtstImm(int op); +int OpNeg(int op); +int OpSwap(int op); +int OpTst(int op); +int OpExt(int op); +int OpSet(int op); +int OpAsr(int op); +int OpAsrEa(int op); + +// OpMove.cpp +int OpMove(int op); +int OpLea(int op); +void OpFlagsToReg(int high); +void OpRegToFlags(int high); +int OpMoveSr(int op); +int OpArithSr(int op); +int OpPea(int op); +int OpMovem(int op); +int OpMoveq(int op); +int OpMoveUsp(int op); +int OpExg(int op); diff --git a/Pico/Disa.c b/Pico/Disa.c new file mode 100644 index 0000000..dbab233 --- /dev/null +++ b/Pico/Disa.c @@ -0,0 +1,846 @@ + +// Dave's Disa 68000 Disassembler +#ifndef __GNUC__ +#pragma warning(disable:4115) +#endif + +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include "Disa.h" + +unsigned int DisaPc=0; +char *DisaText=NULL; // Text buffer to write in +static char Tasm[]="bwl?"; +static char Comment[64]=""; +unsigned short (CPU_CALL *DisaWord)(unsigned int a)=NULL; + +static unsigned int DisaLong(unsigned int a) +{ + unsigned int d=0; + if (DisaWord==NULL) return d; + + d= DisaWord(a)<<16; + d|=DisaWord(a+2)&0xffff; + return d; +} + +// Get text version of the effective address +int DisaGetEa(char *t,int ea,int size) +{ + ea&=0x3f; t[0]=0; + if ((ea&0x38)==0x00) { sprintf(t,"d%d",ea ); return 0; } // 000rrr + if ((ea&0x38)==0x08) { sprintf(t,"a%d",ea&7); return 0; } // 001rrr + if ((ea&0x38)==0x10) { sprintf(t,"(a%d)",ea&7); return 0; } // 010rrr + if ((ea&0x38)==0x18) { sprintf(t,"(a%d)+",ea&7); return 0; } // 011rrr + if ((ea&0x38)==0x20) { sprintf(t,"-(a%d)",ea&7); return 0; } // 100rrr + if ((ea&0x38)==0x28) { sprintf(t,"($%x,a%d)",DisaWord(DisaPc)&0xffff,ea&7); DisaPc+=2; return 0; } // 101rrr + + if ((ea&0x38)==0x30) + { + // 110nnn - An + Disp + D/An + int areg=0,ext=0,off=0,da=0,reg=0,wol=0,scale=0; + ext=DisaWord(DisaPc)&0xffff; + + areg=ea&7; + off=ext&0xff; da =ext&0x8000?'a':'d'; + reg=(ext>>12)&7; wol=ext&0x0800?'l':'w'; + scale=1<<((ext>>9)&3); + + if (scale<2) sprintf(t,"($%x,a%d,%c%d.%c)", off,areg,da,reg,wol); + else sprintf(t,"($%x,a%d,%c%d.%c*%d)",off,areg,da,reg,wol,scale); // 68020 + + DisaPc+=2; + return 0; + } + + if (ea==0x38) { sprintf(t,"$%x.w",DisaWord(DisaPc)&0xffff); DisaPc+=2; return 0; } // 111000 - Absolute short + if (ea==0x39) { sprintf(t,"$%x.l",DisaLong(DisaPc)); DisaPc+=4; return 0; } // 111001 - Absolute long + + if (ea==0x3a) + { + // 111010 - PC Relative + int ext=DisaWord(DisaPc)&0xffff; + sprintf(t,"($%x,pc)",ext); + sprintf(Comment,"; =%x",DisaPc+(short)ext); // Comment where pc+ext is + DisaPc+=2; + return 0; + } + + if (ea==0x3b) + { + // 111011 - PC Relative + D/An + int ext=0,off=0,da=0,reg=0,wol=0,scale=0; + ext=DisaWord(DisaPc)&0xffff; + + off=ext&0xff; da =ext&0x8000?'a':'d'; + reg=(ext>>12)&7; wol=ext&0x0800?'l':'w'; + scale=1<<((ext>>9)&3); + + if (scale<2) sprintf(t,"($%x,pc,%c%d.%c)", off,da,reg,wol); + else sprintf(t,"($%x,pc,%c%d.%c*%d)",off,da,reg,wol,scale); // 68020 + + sprintf(Comment,"; =%x",DisaPc+(char)off); // Comment where pc+ext is + DisaPc+=2; + return 0; + } + + if (ea==0x3c) + { + // 111100 - Immediate + switch (size) + { + case 0: sprintf(t,"#$%x",DisaWord(DisaPc)&0x00ff); DisaPc+=2; return 0; + case 1: sprintf(t,"#$%x",DisaWord(DisaPc)&0xffff); DisaPc+=2; return 0; + case 2: sprintf(t,"#$%x",DisaLong(DisaPc) ); DisaPc+=4; return 0; + } + return 1; + } + +// Unknown effective address + sprintf(t,"ea=(%d%d%d %d%d%d)", + (ea>>5)&1,(ea>>4)&1,(ea>>3)&1, + (ea>>2)&1,(ea>>1)&1, ea &1); + return 1; +} + +static void GetOffset(char *text) +{ + int off=(short)DisaWord(DisaPc); DisaPc+=2; + + if (off<0) sprintf(text,"-$%x",-off); + else sprintf(text,"$%x", off); +} + +// ================ Opcodes 0x0000+ ================ +static int DisaArithImm(int op) +{ + // Or/And/Sub/Add/Eor/Cmp Immediate 0000ttt0 xxDDDddd (tt=type, xx=size extension, DDDddd=Dest ea) + int dea=0; + char seat[64]="",deat[64]=""; + int type=0,size=0; + char *arith[8]={"or","and","sub","add","?","eor","cmp","?"}; + + type=(op>>9)&7; if (type==4 || type>=7) return 1; + size=(op>>6)&3; if (size>=3) return 1; + dea=op&0x3f; if (dea==0x3c) return 1; + + DisaGetEa(seat,0x3c,size); + DisaGetEa(deat,dea, size); + + sprintf(DisaText,"%si.%c %s, %s",arith[type],Tasm[size],seat,deat); + return 0; +} + +// ================ Opcodes 0x0108+ ================ +static int DisaMovep(int op) +{ + // movep.x (Aa),Dn - 0000nnn1 dx001aaa nn + int dn=0,dir=0,size=0,an=0; + char offset[32]=""; + + dn =(op>>9)&7; + dir =(op>>7)&1; + size=(op>>6)&1; size++; + an = op &7; + + GetOffset(offset); + if (dir) sprintf(DisaText,"movep.%c d%d, (%s,a%d)",Tasm[size],dn,offset,an); + else sprintf(DisaText,"movep.%c (%s,a%d), d%d",Tasm[size],offset,an,dn); + + return 0; +} + +// ================ Opcodes 0x007c+ ================ +static int DisaArithSr(int op) +{ + // Ori/Andi/Eori $nnnn,sr 0000t0tx 0s111100 + char *opcode[6]={"ori","andi","","","","eori"}; + char seat[64]=""; + int type=0,size=0; + + type=(op>>9)&5; + size=(op>>6)&1; + + DisaGetEa(seat,0x3c,size); + sprintf(DisaText,"%s.%c %s, %s", opcode[type], Tasm[size], seat, size?"sr":"ccr"); + + return 0; +} + +// ================ Opcodes 0x0100+ ================ +static int DisaBtstReg(int op) +{ + // Btst/Bchg/Bclr/Bset 0000nnn1 tteeeeee (nn=reg number, eeeeee=Dest ea) + int type=0; + int sea=0,dea=0; + char seat[64]="",deat[64]=""; + char *opcode[4]={"btst","bchg","bclr","bset"}; + + sea =(op>>9)&7; + type=(op>>6)&3; + dea= op&0x3f; + + if ((dea&0x38)==0x08) return 1; // movep + DisaGetEa(seat,sea,0); + DisaGetEa(deat,dea,0); + + sprintf(DisaText,"%s %s, %s",opcode[type],seat,deat); + return 0; +} + +// ================ Opcodes 0x0800+ ================ +static int DisaBtstImm(int op) +{ + // Btst/Bchg/Bclr/Bset 00001000 tteeeeee 00 nn (eeeeee=ea, nn=bit number) + int type=0; + char seat[64]="",deat[64]=""; + char *opcode[4]={"btst","bchg","bclr","bset"}; + + type=(op>>6)&3; + DisaGetEa(seat, 0x3c,0); + DisaGetEa(deat,op&0x3f,0); + + sprintf(DisaText,"%s %s, %s",opcode[type],seat,deat); + return 0; +} + +// ================ Opcodes 0x1000+ ================ +static int DisaMove(int op) +{ + // Move 00xxdddD DDssssss (xx=size extension, ssssss=Source EA, DDDddd=Dest ea) + int sea=0,dea=0; + char inst[64]="",seat[64]="",deat[64]=""; + char *movea=""; + int size=0; + + if ((op&0x01c0)==0x0040) movea="a"; // See if it's a movea opcode + + // Find size extension + switch (op&0x3000) + { + case 0x1000: size=0; break; + case 0x3000: size=1; break; + case 0x2000: size=2; break; + default: return 1; + } + + sea = op&0x003f; + DisaGetEa(seat,sea,size); + + dea =(op&0x01c0)>>3; + dea|=(op&0x0e00)>>9; + DisaGetEa(deat,dea,size); + + sprintf(inst,"move%s.%c",movea,Tasm[size]); + sprintf(DisaText,"%s %s, %s",inst,seat,deat); + return 0; +} + +// ================ Opcodes 0x4000+ ================ +static int DisaNeg(int op) +{ + // 01000tt0 xxeeeeee (tt=negx/clr/neg/not, xx=size, eeeeee=EA) + char eat[64]=""; + int type=0,size=0; + char *opcode[4]={"negx","clr","neg","not"}; + + type=(op>>9)&3; + size=(op>>6)&3; if (size>=3) return 1; + DisaGetEa(eat,op&0x3f,size); + + sprintf(DisaText,"%s.%c %s",opcode[type],Tasm[size],eat); + return 0; +} + +// ================ Opcodes 0x40c0+ ================ +static int DisaMoveSr(int op) +{ + // 01000tt0 11eeeeee (tt=type, xx=size, eeeeee=EA) + int type=0,ea=0; + char eat[64]=""; + + type=(op>>9)&3; + ea=op&0x3f; + DisaGetEa(eat,ea,1); + + switch (type) + { + default: sprintf(DisaText,"move sr, %s", eat); break; + case 1: sprintf(DisaText,"move ccr, %s",eat); break; + case 2: sprintf(DisaText,"move %s, ccr",eat); break; + case 3: sprintf(DisaText,"move %s, sr", eat); break; + } + return 0; +} + +// ================ Opcodes 0x41c0+ ================ +static int DisaLea(int op) +{ + // Lea 0100nnn1 11eeeeee (eeeeee=ea) + int sea=0,dea=0; + char seat[64]="",deat[64]=""; + + sea=op&0x003f; + DisaGetEa(seat,sea,0); + + dea=(op>>9)&7; dea|=8; + DisaGetEa(deat,dea,2); + + sprintf(DisaText,"lea %s, %s",seat,deat); + return 0; +} + +static int MakeRegList(char *list,int mask,int ea) +{ + int reverse=0,i=0,low=0,len=0; + + if ((ea&0x38)==0x20) reverse=1; // -(An), bitfield is reversed + + mask&=0xffff; list[0]=0; + + for (i=0;i<17;i++) + { + int bit=0; + + // Mask off bit i: + if (reverse) bit=0x8000>>i; else bit=1<0) if (list[len-1]=='/') list[len-1]=0; + return 0; +} + +// ================ Opcodes 0x4840+ ================ +static int DisaSwap(int op) +{ + // Swap, 01001000 01000nnn swap Dn + sprintf(DisaText,"swap d%d",op&7); + return 0; +} + +// ================ Opcodes 0x4850+ ================ +static int DisaPea(int op) +{ + // Pea 01001000 01eeeeee (eeeeee=ea) pea + int ea=0; + char eat[64]=""; + + ea=op&0x003f; if (ea<0x10) return 1; // swap opcode + DisaGetEa(eat,ea,2); + + sprintf(DisaText,"pea %s",eat); + return 0; +} + +// ================ Opcodes 0x4880+ ================ +static int DisaExt(int op) +{ + // Ext 01001000 1x000nnn (x=size, eeeeee=EA) + char eat[64]=""; + int size=0; + + size=(op>>6)&1; size++; + DisaGetEa(eat,op&0x3f,size); + + sprintf(DisaText,"ext.%c %s",Tasm[size],eat); + return 0; +} + +// ================ Opcodes 0x4890+ ================ +static int DisaMovem(int op) +{ + // Movem 01001d00 1xeeeeee regmask d=direction, x=size, eeeeee=EA + int dir=0,size=0; + int ea=0,mask=0; + char list[64]="",eat[64]=""; + + dir=(op>>10)&1; + size=((op>>6)&1)+1; + ea=op&0x3f; if (ea<0x10) return 1; // ext opcode + + mask=DisaWord(DisaPc)&0xffff; DisaPc+=2; + + MakeRegList(list,mask,ea); // Turn register mask into text + DisaGetEa(eat,ea,size); + + if (dir) sprintf(DisaText,"movem.%c %s, %s",Tasm[size],eat,list); + else sprintf(DisaText,"movem.%c %s, %s",Tasm[size],list,eat); + return 0; +} + +// ================ Opcodes 0x4e40+ ================ +static int DisaTrap(int op) +{ + sprintf(DisaText,"trap #%d",op&0xf); + return 0; +} + +// ================ Opcodes 0x4e50+ ================ +static int DisaLink(int op) +{ + // Link opcode, 01001110 01010nnn dd link An,#offset + char eat[64]=""; + char offset[32]=""; + + DisaGetEa(eat,(op&7)|8,0); + GetOffset(offset); + + sprintf(DisaText,"link %s,#%s",eat,offset); + + return 0; +} + +// ================ Opcodes 0x4e58+ ================ +static int DisaUnlk(int op) +{ + // Link opcode, 01001110 01011nnn dd unlk An + char eat[64]=""; + + DisaGetEa(eat,(op&7)|8,0); + sprintf(DisaText,"unlk %s",eat); + + return 0; +} + +// ================ Opcodes 0x4e60+ ================ +static int DisaMoveUsp(int op) +{ + // Move USP opcode, 01001110 0110dnnn move An to/from USP (d=direction) + int ea=0,dir=0; + char eat[64]=""; + + dir=(op>>3)&1; + ea=(op&7)|8; + DisaGetEa(eat,ea,0); + + if (dir) sprintf(DisaText,"move usp, %s",eat); + else sprintf(DisaText,"move %s, usp",eat); + return 0; +} + +// ================ Opcodes 0x4e70+ ================ +static int Disa4E70(int op) +{ + char *inst[8]={"reset","nop","stop","rte","rtd","rts","trapv","rtr"}; + int n=0; + + n=op&7; + + sprintf(DisaText,"%s",inst[n]); + + //todo - 'stop' with 16 bit data + + return 0; +} + +// ================ Opcodes 0x4a00+ ================ +static int DisaTst(int op) +{ + // Tst 01001010 xxeeeeee (eeeeee=ea) + int ea=0; + char eat[64]=""; + int size=0; + + ea=op&0x003f; + DisaGetEa(eat,ea,0); + size=(op>>6)&3; if (size>=3) return 1; + + sprintf(DisaText,"tst.%c %s",Tasm[size],eat); + return 0; +} + +// ================ Opcodes 0x4e80+ ================ +static int DisaJsr(int op) +{ + // Jsr/Jmp 0100 1110 1mEE Eeee (eeeeee=ea m=1=jmp) + int sea=0; + char seat[64]=""; + + sea=op&0x003f; + DisaGetEa(seat,sea,0); + + sprintf(DisaText,"j%s %s", op&0x40?"mp":"sr", seat); + return 0; +} + +// ================ Opcodes 0x5000+ ================ +static int DisaAddq(int op) +{ + // 0101nnnt xxeeeeee (nnn=#8,1-7 t=addq/subq xx=size, eeeeee=EA) + int num=0,type=0,size=0,ea=0; + char eat[64]=""; + + num =(op>>9)&7; if (num==0) num=8; + type=(op>>8)&1; + size=(op>>6)&3; if (size>=3) return 1; + ea = op&0x3f; + + DisaGetEa(eat,ea,size); + + sprintf(DisaText,"%s.%c #%d, %s",type?"subq":"addq",Tasm[size],num,eat); + return 0; +} + +// ================ Opcodes 0x50c0+ ================ +static int DisaSet(int op) +{ + // 0101cccc 11eeeeee (sxx ea) + static char *cond[16]= + {"t" ,"f", "hi","ls","cc","cs","ne","eq", + "vc","vs","pl","mi","ge","lt","gt","le"}; + char *cc=""; + int ea=0; + char eat[64]=""; + + cc=cond[(op>>8)&0xf]; // Get condition code + ea=op&0x3f; + if ((ea&0x38)==0x08) return 1; // dbra, not scc + + DisaGetEa(eat,ea,0); + sprintf(DisaText,"s%s %s",cc,eat); + return 0; +} + +// ================ Opcodes 0x50c8+ ================ +static int DisaDbra(int op) +{ + // 0101cccc 11001nnn offset (dbra/dbxx Rn,offset) + int dea=0; char deat[64]=""; + int pc=0,Offset=0; + + static char *BraCode[16]= + {"bt" ,"bra","bhi","bls","bcc","bcs","bne","beq", + "bvc","bvs","bpl","bmi","bge","blt","bgt","ble"}; + char *Bra=""; + + dea=op&7; + DisaGetEa(deat,dea,2); + + // Get condition code + Bra=BraCode[(op>>8)&0xf]; + + // Get offset + pc=DisaPc; + Offset=(short)DisaWord(DisaPc); DisaPc+=2; + + sprintf(DisaText,"d%s %s, %x",Bra,deat,pc+Offset); + return 0; +} + +// ================ Opcodes 0x6000+ ================ +static int DisaBranch(int op) +{ + // Branch 0110cccc nn (cccc=condition) + int pc=0,Offset=0; + + static char *BraCode[16]= + {"bra","bsr","bhi","bls","bcc","bcs","bne","beq", + "bvc","bvs","bpl","bmi","bge","blt","bgt","ble"}; + char *Bra=""; + + // Get condition code + Bra=BraCode[(op>>8)&0x0f]; + + // Get offset + pc=DisaPc; + Offset=(char)(op&0xff); + if (Offset== 0) { Offset=(short)DisaWord(DisaPc); DisaPc+=2; } + else if (Offset==-1) { Offset= DisaLong(DisaPc); DisaPc+=4; } + + sprintf(DisaText,"%s %x",Bra,pc+Offset); + return 0; +} + +// ================ Opcodes 0x7000+ ================ +static int DisaMoveq(int op) +{ + // Moveq 0111rrr0 nn (rrr=Dest register, nn=data) + + int dea=0; char deat[64]=""; + char *inst="moveq"; + int val=0; + + dea=(op>>9)&7; + DisaGetEa(deat,dea,2); + + val=(char)(op&0xff); + sprintf(DisaText,"%s #$%x, %s",inst,val,deat); + return 0; +} + +// ================ Opcodes 0x8000+ ================ +static int DisaArithReg(int op) +{ + // 1t0tnnnd xxeeeeee (tt=type:or/sub/and/add xx=size, eeeeee=EA) + int type=0,size=0,dir=0,rea=0,ea=0; + char reat[64]="",eat[64]=""; + char *opcode[]={"or","sub","","","and","add"}; + + type=(op>>12)&5; + rea =(op>> 9)&7; + dir =(op>> 8)&1; + size=(op>> 6)&3; if (size>=3) return 1; + ea = op&0x3f; + + if (dir && ea<0x10) return 1; // addx opcode + + DisaGetEa(reat,rea,size); + DisaGetEa( eat, ea,size); + + if (dir) sprintf(DisaText,"%s.%c %s, %s",opcode[type],Tasm[size],reat,eat); + else sprintf(DisaText,"%s.%c %s, %s",opcode[type],Tasm[size],eat,reat); + return 0; +} + +// ================ Opcodes 0x8100+ ================ +static int DisaAbcd(int op) +{ + // 1t00ddd1 0000asss - sbcd/abcd Ds,Dd or -(As),-(Ad) + int type=0; + int dn=0,addr=0,sn=0; + char *opcode[]={"sbcd","abcd"}; + + type=(op>>14)&1; + dn =(op>> 9)&7; + addr=(op>> 3)&1; + sn = op &7; + + if (addr) sprintf(DisaText,"%s -(a%d), -(a%d)",opcode[type],sn,dn); + else sprintf(DisaText,"%s d%d, d%d", opcode[type],sn,dn); + + return 0; +} + +// ================ Opcodes 0x80c0+ ================ +static int DisaMul(int op) +{ + // Div/Mul: 1m00nnns 11eeeeee (m=Mul, nnn=Register Dn, s=signed, eeeeee=EA) + int type=0,rea=0,sign=0,ea=0,size=1; + char reat[64]="",eat[64]=""; + char *opcode[2]={"div","mul"}; + + type=(op>>14)&1; // div/mul + rea =(op>> 9)&7; + sign=(op>> 8)&1; + ea = op&0x3f; + + DisaGetEa(reat,rea,size); + DisaGetEa( eat, ea,size); + + sprintf(DisaText,"%s%c.%c %s, %s",opcode[type],sign?'s':'u',Tasm[size],eat,reat); + return 0; +} + +// ================ Opcodes 0x90c0+ ================ +static int DisaAritha(int op) +{ + // Suba/Cmpa/Adda 1tt1nnnx 11eeeeee (tt=type, x=size, eeeeee=Source EA) + int type=0,size=0,sea=0,dea=0; + char seat[64]="",deat[64]=""; + char *aritha[4]={"suba","cmpa","adda",""}; + + type=(op>>13)&3; if (type>=3) return 1; + size=(op>>8)&1; size++; + dea =(op>>9)&7; dea|=8; // Dest=An + sea = op&0x003f; // Source + + DisaGetEa(seat,sea,size); + DisaGetEa(deat,dea,size); + + sprintf(DisaText,"%s.%c %s, %s",aritha[type],Tasm[size],seat,deat); + return 0; +} + +// ================ Opcodes 0xb000+ ================ +static int DisaCmpEor(int op) +{ + // Cmp/Eor 1011rrrt xxeeeeee (rrr=Dn, t=cmp/eor, xx=size extension, eeeeee=ea) + char reat[64]="",eat[64]=""; + int type=0,size=0; + + type=(op>>8)&1; + size=(op>>6)&3; if (size>=3) return 1; // cmpa opcode + if ((op&0xf138)==0xb108) return 1; // cmpm opcode + + DisaGetEa(reat,(op>>9)&7,size); + DisaGetEa(eat, op&0x3f, size); + + if (type) sprintf(DisaText,"eor.%c %s, %s",Tasm[size],reat,eat); + else sprintf(DisaText,"cmp.%c %s, %s",Tasm[size],eat,reat); + return 0; +} + +// ================ Opcodes 0xb108+ ================ +static int DisaCmpm(int op) +{ + // Cmpm 1011ddd1 xx001sss + int type=0,size=0,dea=0,sea=0; + char deat[64]="",seat[64]=""; + + type=(op>>12)&5; + dea =(op>> 9)&7; dea|=8; + size=(op>> 6)&3; if (size>=3) return 1; + sea = op&0x3f; + + DisaGetEa(deat,dea,size); + DisaGetEa(seat,sea,size); + + sprintf(DisaText,"cmpm.%c (%s)+, (%s)+",Tasm[size],seat,deat); + + return 0; +} + +// ================ Opcodes 0xc140+ ================ +// 1100ttt1 01000sss exg ds,dt +// 1100ttt1 01001sss exg as,at +// 1100ttt1 10001sss exg as,dt +static int DisaExg(int op) +{ + int tr=0,type=0,sr=0; + + tr =(op>>9)&7; + type= op&0xf8; + sr = op&7; + + if (type==0x40) sprintf(DisaText,"exg d%d, d%d",sr,tr); + else if (type==0x48) sprintf(DisaText,"exg a%d, a%d",sr,tr); + else if (type==0x88) sprintf(DisaText,"exg a%d, d%d",sr,tr); + else return 1; + + return 0; +} + +// ================ Opcodes 0xd100+ ================ +static int DisaAddx(int op) +{ + // 1t01ddd1 xx000sss addx + int type=0,size=0,dea=0,sea=0; + char deat[64]="",seat[64]=""; + char *opcode[6]={"","subx","","","","addx"}; + + type=(op>>12)&5; + dea =(op>> 9)&7; + size=(op>> 6)&3; if (size>=3) return 1; + sea = op&0x3f; + + DisaGetEa(deat,dea,size); + DisaGetEa(seat,sea,size); + + sprintf(DisaText,"%s.%c %s, %s",opcode[type],Tasm[size],seat,deat); + return 0; +} + +// ================ Opcodes 0xe000+ ================ +static char *AsrName[4]={"as","ls","rox","ro"}; +static int DisaAsr(int op) +{ + // Asr/l/Ror/l etc - 1110cccd xxuttnnn + // (ccc=count, d=direction xx=size extension, u=use reg for count, tt=type, nnn=register Dn) + int count=0,dir=0,size=0,usereg=0,type=0,num=0; + + count =(op>>9)&7; + dir =(op>>8)&1; + size =(op>>6)&3; if (size>=3) return 1; // todo Asr EA + usereg=(op>>5)&1; + type =(op>>3)&3; + num = op &7; // Register number + + if (usereg==0) count=((count-1)&7)+1; // because ccc=000 means 8 + + sprintf(DisaText,"%s%c.%c %c%d, d%d", + AsrName[type], dir?'l':'r', Tasm[size], + usereg?'d':'#', count, num); + return 0; +} + +static int DisaAsrEa(int op) +{ + // Asr/l/Ror/l etc EA - 11100ttd 11eeeeee + int type=0,dir=0,size=1; + char eat[64]=""; + + type=(op>>9)&3; + dir =(op>>8)&1; + DisaGetEa(eat,op&0x3f,size); + + sprintf(DisaText,"%s%c.w %s", AsrName[type], dir?'l':'r', eat); + return 0; +} + +// ================================================================= + +static int TryOp(int op) +{ + if ((op&0xf100)==0x0000) DisaArithImm(op); // Ori/And/Sub/Add/Eor/Cmp Immediate + if ((op&0xf5bf)==0x003c) DisaArithSr(op); // Ori/Andi/Eori $nnnn,sr + if ((op&0xf100)==0x0100) DisaBtstReg(op); + if ((op&0xf138)==0x0108) DisaMovep(op); + if ((op&0xff00)==0x0800) DisaBtstImm(op); // Btst/Bchg/Bclr/Bset + if ((op&0xc000)==0x0000) DisaMove(op); + if ((op&0xf900)==0x4000) DisaNeg(op); // Negx/Clr/Neg/Not + if ((op&0xf1c0)==0x41c0) DisaLea(op); + if ((op&0xf9c0)==0x40c0) DisaMoveSr(op); + if ((op&0xfff8)==0x4840) DisaSwap(op); + if ((op&0xffc0)==0x4840) DisaPea(op); + if ((op&0xffb8)==0x4880) DisaExt(op); + if ((op&0xfb80)==0x4880) DisaMovem(op); + if ((op&0xff00)==0x4a00) DisaTst(op); + if ((op&0xfff0)==0x4e40) DisaTrap(op); + if ((op&0xfff8)==0x4e50) DisaLink(op); + if ((op&0xfff8)==0x4e58) DisaUnlk(op); + if ((op&0xfff0)==0x4e60) DisaMoveUsp(op); + if ((op&0xfff8)==0x4e70) Disa4E70(op); + if ((op&0xff80)==0x4e80) DisaJsr(op); + if ((op&0xf000)==0x5000) DisaAddq(op); + if ((op&0xf0c0)==0x50c0) DisaSet(op); + if ((op&0xf0f8)==0x50c8) DisaDbra(op); + if ((op&0xf000)==0x6000) DisaBranch(op); + if ((op&0xa000)==0x8000) DisaArithReg(op); // Or/Sub/And/Add + if ((op&0xb1f0)==0x8100) DisaAbcd(op); + if ((op&0xb130)==0x9100) DisaAddx(op); + if ((op&0xb0c0)==0x80c0) DisaMul(op); + if ((op&0xf100)==0x7000) DisaMoveq(op); + if ((op&0x90c0)==0x90c0) DisaAritha(op); + if ((op&0xf000)==0xb000) DisaCmpEor(op); + if ((op&0xf138)==0xb108) DisaCmpm(op); + if ((op&0xf130)==0xc100) DisaExg(op); + if ((op&0xf000)==0xe000) DisaAsr(op); + if ((op&0xf8c0)==0xe0c0) DisaAsrEa(op); + + // Unknown opcoode + return 0; +} + +int DisaGet() +{ + int op=0; + if (DisaWord==NULL) return 1; + + Comment[0]=0; + DisaText[0]=0; // Assume opcode unknown + + op=DisaWord(DisaPc)&0xffff; DisaPc+=2; + TryOp(op); + strcat(DisaText,Comment); + + // Unknown opcoode + return 0; +} diff --git a/Pico/Disa.h b/Pico/Disa.h new file mode 100644 index 0000000..af10eb5 --- /dev/null +++ b/Pico/Disa.h @@ -0,0 +1,20 @@ + +// Dave's Disa 68000 Disassembler + +#ifdef __cplusplus +extern "C" { +#endif + +#define CPU_CALL + +extern unsigned int DisaPc; +extern char *DisaText; // Text buffer to write in + +extern unsigned short (CPU_CALL *DisaWord)(unsigned int a); +int DisaGetEa(char *t,int ea,int size); + +int DisaGet(); + +#ifdef __cplusplus +} // End of extern "C" +#endif diff --git a/test.txt b/test.txt deleted file mode 100644 index 0fde866..0000000 --- a/test.txt +++ /dev/null @@ -1 +0,0 @@ -this is a test -- 2.39.5