switch Cyclone to submodule on it's own git repo

that version also has it's license clearly stated by fdave
This commit is contained in:
notaz 2013-06-22 19:16:11 +03:00
parent e743be2070
commit 1b85bf1c23
38 changed files with 4 additions and 7740 deletions

3
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "platform/libpicofe"] [submodule "platform/libpicofe"]
path = platform/libpicofe path = platform/libpicofe
url = git://notaz.gp2x.de/~notaz/libpicofe.git url = git://notaz.gp2x.de/~notaz/libpicofe.git
[submodule "cpu/cyclone"]
path = cpu/cyclone
url = git://notaz.gp2x.de/~notaz/cyclone68000.git

View file

@ -1,97 +0,0 @@
// Cyclone 68000 Emulator - Header File
// (c) Copyright 2004 Dave, All rights reserved.
// (c) 2005-2007 notaz
// Cyclone 68000 is free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#ifndef __CYCLONE_H__
#define __CYCLONE_H__
#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 (.membase) + 68k PC
unsigned char srh; // [r7,#0x44] Status Register high (T_S__III)
unsigned char unused; // [r7,#0x45] Unused
unsigned char flags; // [r7,#0x46] Flags (ARM order: ____NZCV) [68k order is XNZVC]
unsigned char irq; // [r7,#0x47] IRQ level
unsigned int osp; // [r7,#0x48] Other Stack Pointer (USP/SSP)
unsigned int xc; // [r7,#0x4c] Extend flag (bit29: ??X? _)
unsigned int prev_pc; // [r7,#0x50] Set to start address of currently executed opcode + 2 (if enabled in config.h)
unsigned int jumptab; // [r7,#0x54] Jump table pointer
int state_flags; // [r7,#0x58] bit: 0: stopped state, 1: trace state, 2: activity bit, 3: addr error, 4: fatal halt
int cycles; // [r7,#0x5c] Number of cycles to execute - 1. Updates to cycles left after CycloneRun()
int membase; // [r7,#0x60] Memory Base (ARM address minus 68000 address)
unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] called to recalc Memory Base+pc
unsigned int (*read8 )(unsigned int a); // [r7,#0x68]
unsigned int (*read16 )(unsigned int a); // [r7,#0x6c]
unsigned int (*read32 )(unsigned int a); // [r7,#0x70]
void (*write8 )(unsigned int a,unsigned char d); // [r7,#0x74]
void (*write16)(unsigned int a,unsigned short d); // [r7,#0x78]
void (*write32)(unsigned int a,unsigned int d); // [r7,#0x7c]
unsigned int (*fetch8 )(unsigned int a); // [r7,#0x80]
unsigned int (*fetch16)(unsigned int a); // [r7,#0x84]
unsigned int (*fetch32)(unsigned int a); // [r7,#0x88]
int (*IrqCallback)(int int_level); // [r7,#0x8c] optional irq callback function, see config.h
void (*ResetCallback)(void); // [r7,#0x90] if enabled in config.h, calls this whenever RESET opcode is encountered.
int (*UnrecognizedCallback)(void); // [r7,#0x94] if enabled in config.h, calls this whenever unrecognized opcode is encountered.
unsigned int internal[6]; // [r7,#0x98] reserved for internal use, do not change.
};
// Initialize. Used only if Cyclone was compiled with compressed jumptable, see config.h
void CycloneInit(void);
// Reset
void CycloneReset(struct Cyclone *pcy);
// Run cyclone. Cycles should be specified in context (pcy->cycles)
void CycloneRun(struct Cyclone *pcy);
// Utility functions to get and set SR
void CycloneSetSr(struct Cyclone *pcy, unsigned int sr);
unsigned int CycloneGetSr(const struct Cyclone *pcy);
// Generates irq exception if needed (if pcy->irq > mask).
// Returns cycles used for exception if it was generated, 0 otherwise.
int CycloneFlushIrq(struct Cyclone *pcy);
// Functions for saving and restoring state.
// CycloneUnpack() uses checkpc(), so it must be initialized.
// save_buffer must point to buffer of 128 (0x80) bytes of size.
void CyclonePack(const struct Cyclone *pcy, void *save_buffer);
void CycloneUnpack(struct Cyclone *pcy, const void *save_buffer);
// genesis: if 1, switch to normal TAS handlers
void CycloneSetRealTAS(int use_real);
// These values are special return values for IrqCallback.
// Causes an interrupt autovector (0x18 + interrupt level) to be taken.
// This happens in a real 68K if VPA or AVEC is asserted during an interrupt
// acknowledge cycle instead of DTACK (the most common situation).
#define CYCLONE_INT_ACK_AUTOVECTOR -1
// Causes the spurious interrupt vector (0x18) to be taken
// This happens in a real 68K if BERR is asserted during the interrupt
// acknowledge cycle (i.e. no devices responded to the acknowledge).
#define CYCLONE_INT_ACK_SPURIOUS -2
#ifdef __cplusplus
} // End of extern "C"
#endif
#endif // __CYCLONE_H__

View file

@ -1,627 +0,0 @@
_____ __
/ ___/__ __ ____ / /___ ___ ___ ___________________
/ /__ / // // __// // _ \ / _ \/ -_) ___________________
\___/ \_, / \__//_/ \___//_//_/\__/ ___________________
/___/
___________________ ____ ___ ___ ___ ___
___________________ / __// _ \ / _ \ / _ \ / _ \
___________________ / _ \/ _ // // // // // // /
\___/\___/ \___/ \___/ \___/
___________________________________________________________________________
Cyclone 68000 (c) Copyright 2004 Dave. Free for non-commercial use
Homepage: http://www.finalburn.com/
Dave's e-mail: emudave(atsymbol)googlemail.com
Replace (atsymbol) with @
Additional coding and bugfixes done by notaz, 2005-2007
Homepage: http://notaz.gp2x.de
e-mail: notasas(atsymbol)gmail.com
___________________________________________________________________________
About
-----
Cyclone 68000 is an emulator for the 68000 microprocessor, written in ARM 32-bit assembly.
It is aimed at chips such as ARM7 and ARM9 cores, StrongARM and XScale, to interpret 68000
code as fast as possible. It can emulate all 68000 instructions quite accurately, instruction
timing was synchronized with MAME's Musashi. Most 68k features are emulated (trace mode,
address errors), but prefetch is not emulated.
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 file called Cyclone.s or Cyclone.asm.
Only Cyclone.h and the mentioned .s or .asm file will be needed for your project, other files
are here to produce or test it.
First unzip "Cyclone.zip" into a "Cyclone" directory. The next thing to do is to edit config.h
file to tune Cyclone for your project. There are lots of options in config.h, but all of them
are documented and have defaults. You should set a define value to 1 to enable option, and
to 0 to disable.
After you are done with config.h, save it and compile Cyclone. If you are using Linux, Cygwin,
mingw or similar, you can simply cd to Cyclone/proj and type "make". If you are under Windows
and have Visual Studio installed, you can import cyclone.dsp in the proj/ directory and compile
it from there (this will produce cyclone.exe which you will have to run to get .s or .asm).
You can also use Microsoft command line compile tools by entering Cyclone/proj directory and
typing "nmake -f Makefile.win". Note that this step is done only to produce .s or .asm, and it
is done using native tools on your PC (not using cross-compiler or similar).
The .s file is meant to be compiled with GNU assembler, and .asm with ARMASM.EXE
(the Microsoft ARM assembler). Once you have the file, you can add it to your
Makefile/project/whatever.
Adding to your project
----------------------
Compiling the .s or .asm (from previous step) for your target platform may require custom
build rules in your Makefile/project.
If you use some gcc-based toolchain, you will need to add Cyclone.o to an object list in
the Makefile. GNU make will use "as" to build Cyclone.o from Cyclone.s by default, so
you may need to define correct cross-assembler by setting AS variable like this:
AS = arm-linux-as
This might be different in your case, basically it should be same prefix as for gcc.
You may also need to specify floating point type in your assembler flags for Cyclone.o
to link properly. This is done like this:
ASFLAGS = -mfloat-abi=soft
Note that Cyclone does not use floating points, this is just to make the linker happy.
If you are using Visual Studio, you may need to add "custom build step", which creates
Cyclone.obj from Cyclone.asm (asmasm.exe Cyclone.asm). Alternatively you can create
Cyclone.obj by using armasm once and then just add it to you project.
Don't worry if this seem very minimal - its all you need to run as many 68000s as you want.
It works with both C and C++.
Byteswapped Memory
------------------
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 (in most cases).
Now you may think you only technically have to byteswap ROM, not RAM, because
16-bit RAM reads go through a memory handler and you could just return (mem[a]<<8) | mem[a+1].
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 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<RomLength) return RomData[a^1]; // ^1 because the memory is byteswapped
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 (don't forget to include Cyclone.h).
For example to declare two 68000s:
struct Cyclone MyCpu;
struct Cyclone MyCpu2;
It's probably a good idea to initialize 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 a few more functions,
one of them is: 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 68k 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<RomLength) MyCpu.membase=(int)RomMem; // Jump to Rom
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.c).
The exact cases when checkpc() is called can be configured in config.h.
Initialization
--------------
Add a call to CycloneInit(). This is really only needed to be called once at startup
if you enabled COMPRESS_JUMPTABLE in config.h, but you can add this in any case,
it won't hurt.
Almost there - Reset the 68000!
-------------------------------
Cyclone doesn't provide a reset function, so next we need to Reset the 68000 to get
the initial Program Counter and Stack Pointer. This is obtained from addresses
000000 and 000004.
Here is code which resets the 68000 (using your memory handlers):
MyCpu.state_flags=0; // Go to default state (not stopped, halted, etc.)
MyCpu.srh=0x27; // Set supervisor mode
MyCpu.a[7]=MyCpu.read32(0); // Get Stack Pointer
MyCpu.membase=0; // Will be set by checkpc()
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 negative number. The result is stored back to MyCpu.cycles.
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.
If you need to force interrupt processing, you can use CycloneFlushIrq() function.
It is the same as doing
MyCpu.cycles=0; CycloneRun(&MyCpu);
but is better optimized and doesn't update .cycles (returns them instead).
This function can't be used from memory handlers and has no effect if interrupt is masked.
The IRQ isn't checked on exiting from a memory handler. If you need to cause interrupt
check immediately, you should change cycle counter to 0 to cause a return from CycloneRun(),
and then call CycloneRun() again or just call CycloneFlushIrq(). Note that you need to
enable MEMHANDLERS_CHANGE_CYCLES in config.h for this to work.
If you need to do something during the interrupt acknowledge (the moment when interrupt
is taken), you can set USE_INT_ACK_CALLBACK in config.h and specify IrqCallback function.
This function should update the IRQ level (.irq variable in context) and return the
interrupt vector number. But for most cases it should return special constant
CYCLONE_INT_ACK_AUTOVECTOR so that Cyclone uses autovectors, which is what most real
systems were doing. Another less commonly used option is to return CYCLONE_INT_ACK_SPURIOUS
for spurious interrupt.
Accessing Program Counter and registers
---------------------------------------
You can read most Cyclone's registers directly from the structure at any time.
However, the PC value, CCR and cycle counter are cached in ARM registers and can't
be accessed from memory handlers by default. They are written back and can be
accessed after execution.
But if you need to access the mentioned registers during execution, you can set
MEMHANDLERS_NEED_* and MEMHANDLERS_CHANGE_* options in config.h
The Program Counter, should you need to read or write it, is stored with membase
added on. So use this formula to calculate the real 68000 program counter:
pc = MyCpu.pc - MyCpu.membase;
For performance reasons Cyclone keeps the status register split into .srh
(status register "high" supervisor byte), .xc for the X flag, and .flags for remaining
CCR flags (in ARM order). To easily read/write the status register as normal 68k
16bit SR register, use CycloneGetSr() and CycloneSetSr() utility functions.
Emulating more than one CPU
---------------------------
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);
Quick API reference
-------------------
void CycloneInit(void);
Initializes Cyclone. Must be called if the jumptable is compressed,
doesn't matter otherwise.
void CycloneRun(struct Cyclone *pcy);
Runs cyclone for pcy->cycles. Writes amount of cycles left back to
pcy->cycles (always negative).
unsigned int CycloneGetSr(const struct Cyclone *pcy);
Reads status register in internal form from pcy, converts to standard 68k SR and returns it.
void CycloneSetSr(struct Cyclone *pcy, unsigned int sr);
Takes standard 68k status register (sr), and updates Cyclone context with it.
int CycloneFlushIrq(struct Cyclone *pcy);
If .irq is greater than IRQ mask in SR, or it is equal to 7 (NMI), processes interrupt
exception and returns number of cycles used. Otherwise, does nothing and returns 0.
void CyclonePack(const struct Cyclone *pcy, void *save_buffer);
Writes Cyclone state to save_buffer. This allows to avoid all the trouble figuring what
actually needs to be saved from the Cyclone structure, as saving whole struct Cyclone
to a file will also save various pointers, which may become invalid after your program
is restarted, so simply reloading the structure will cause a crash. save_buffer size
should be 128 bytes (now it is really using less, but this allows future expansion).
void CycloneUnpack(struct Cyclone *pcy, const void *save_buffer);
Reloads Cyclone state from save_buffer, which was previously saved by CyclonePack().
This function uses checkpc() callback to rebase the PC, so .checkpc must be initialized
before calling it.
Callbacks:
.checkpc
unsigned int (*checkpc)(unsigned int pc);
This function is called when PC changes are performed in 68k code or because of exceptions.
It is passed ARM pointer and should return ARM pointer casted to int. It must also update
.membase if needed. See "The checkpc() function" section above.
unsigned int (*read8 )(unsigned int a);
unsigned int (*read16 )(unsigned int a);
unsigned int (*read32 )(unsigned int a);
These are the read memory handler callbacks. They are called when 68k code reads from memory.
The parameter is a 68k address in data space, return value is a data value read. Data value
doesn't have to be masked to 8 or 16 bits for read8 or read16, Cyclone will do that itself
if needed.
unsigned int (*fetch8 )(unsigned int a);
unsigned int (*fetch16)(unsigned int a);
unsigned int (*fetch32)(unsigned int a);
Same as above, but these are reads from program space (PC relative reads mostly).
void (*write8 )(unsigned int a,unsigned char d);
void (*write16)(unsigned int a,unsigned short d);
void (*write32)(unsigned int a,unsigned int d);
These are called when 68k code writes to data space. d is the data value.
int (*IrqCallback)(int int_level);
This function is called when Cyclone acknowledges an interrupt. The parameter is the IRQ
level being acknowledged, and return value is exception vector to use, or one of these special
values: CYCLONE_INT_ACK_AUTOVECTOR or CYCLONE_INT_ACK_SPURIOUS. Can be disabled in config.h.
See "Interrupts" section for more information.
void (*ResetCallback)(void);
Cyclone will call this function if it encounters RESET 68k instruction.
Can be disabled in config.h.
int (*UnrecognizedCallback)(void);
Cyclone will call this function if it encounters illegal instructions (including A-line and
F-line ones). Can be tuned / disabled in config.h.
Function codes
--------------
Cyclone doesn't pass function codes to it's memory handlers, but they can be calculated:
FC2: just use supervisor state bit from status register (eg. (MyCpu.srh & 0x20) >> 5)
FC1: if we are in fetch* function, then 1, else 0.
FC0: if we are in read* or write*, then 1, else 0.
CPU state (all FC bits set) is active in IrqCallback function.
References
----------
These documents were used while writing Cyclone and should be useful for those who want to
understand deeper how the 68000 works.
MOTOROLA M68000 FAMILY Programmer's Reference Manual
common name: 68kPM.pdf
M68000 8-/16-/32-Bit Microprocessors User's Manual
common name: MC68000UM.pdf
68000 Undocumented Behavior Notes by Bart Trzynadlowski
http://www.trzy.org/files/68knotes.txt
Instruction prefetch on the Motorola 68000 processor by Jorge Cwik
http://pasti.fxatari.com/68kdocs/68kPrefetch.html
ARM Register Usage
------------------
See source code for up to date of register usage, however a summary is here:
r0-3: Temporary registers
r4 : Current PC + Memory Base (i.e. pointer to next opcode)
r5 : Cycles remaining
r6 : Pointer to Opcode Jump table
r7 : Pointer to Cpu Context
r8 : Current Opcode
r10 : Flags (NZCV) in highest four bits
(r11 : Temporary register)
Flags are mapped onto ARM flags whenever possible, which speeds up the processing of opcode.
r9 is not used intentionally, because AAPCS defines it as "platform register", so it's
reserved in some systems.
Thanks to...
------------
* All the previous code-generating assembler cpu core guys!
Who are iirc... Neill Corlett, Neil Bradley, Mike Coates, Darren Olafson
Karl Stenerud and Bart Trzynadlowski
* Charles Macdonald, for researching just about every console ever
* MameDev+FBA, for keeping on going and going and going
What's New
----------
v0.0099 notaz
* Cyclone no longer uses r9, because AAPCS defines it as "platform register",
so it's reserved in some systems.
* Made SPLIT_MOVEL_PD to affect MOVEM too.
v0.0088 notaz
- Reduced amount of code in opcode handlers by ~23% by doing the following:
- Removed duplicate opcode handlers
- Optimized code to use less ARM instructions
- Merged some duplicate handler endings
+ Cyclone now does better job avoiding pipeline interlocks.
+ Replaced incorrect handler of DBT with proper one.
+ Changed "MOVEA (An)+ An" behavior.
+ Fixed flag behavior of ROXR, ASL, LSR and NBCD in certain situations.
Hopefully got them right now.
+ Cyclone no longer sets most significant bits while pushing PC to stack.
Amiga Kickstart depends on this.
+ Added optional trace mode emulation.
+ Added optional address error emulation.
+ Additional functionality added for MAME and other ports (see config.h).
+ Added return value for IrqCallback to make it suitable for emulating devices which
pass the vector number during interrupt acknowledge cycle. For usual autovector
processing this function must return CYCLONE_INT_ACK_AUTOVECTOR, so those who are
upgrading must add "return CYCLONE_INT_ACK_AUTOVECTOR;" to their IrqCallback functions.
* Updated documentation.
v0.0086 notaz
+ Cyclone now can be customized to better suit your project, see config.h .
+ Added an option to compress the jumptable at compile-time. Must call CycloneInit()
at runtime to decompress it if enabled (see config.h).
+ Added missing CHK opcode handler (used by SeaQuest DSV).
+ Added missing TAS opcode handler (Gargoyles,Bubba N Stix,...). As in real genesis,
memory write-back phase is ignored (but can be enabled in config.h if needed).
+ Added missing NBCD and TRAPV opcode handlers.
+ Added missing addressing mode for CMP/EOR.
+ Added some minor optimizations.
- Removed 216 handlers for 2927 opcodes which were generated for invalid addressing modes.
+ Fixed flags for ASL, NEG, NEGX, DIVU, ADDX, SUBX, ROXR.
+ Bugs fixed in MOVEP, LINK, ADDQ, DIVS handlers.
* Undocumented flags for CHK, ABCD, SBCD and NBCD are now emulated the same way as in Musashi.
+ Added Uninitialized Interrupt emulation.
+ Altered timing for about half of opcodes to match Musashi's.
v0.0082 Reesy
+ Change cyclone to clear cycles before returning when halted
+ Added Irq call back function. This allows emulators to be notified
when cyclone has taken an interrupt allowing them to set internal flags
which can help fix timing problems.
v0.0081 notaz
+ .asm version was broken and did not compile with armasm. Fixed.
+ Finished implementing Stop opcode. Now it really stops the processor.
v0.0080 notaz
+ Added real cmpm opcode, it was using eor handler before this.
Fixes Dune and Sensible Soccer.
v0.0078 notaz
note: these bugs were actually found Reesy, I reimplemented these by
using his changelog as a guide.
+ Fixed a problem with divu which was using long divisor instead of word.
Fixes gear switching in Top Gear 2.
+ Fixed btst opcode, The bit to test should shifted a max of 31 or 7
depending on if a register or memory location is being tested.
+ Fixed abcd,sbcd. They did bad decimal correction on invalid BCD numbers
Score counters in Streets of Rage level end work now.
+ Changed flag handling of abcd,sbcd,addx,subx,asl,lsl,...
Some ops did not have flag handling at all.
Some ops must not change Z flag when result is zero, but they did.
Shift ops must not change X if shift count is zero, but they did.
There are probably still some flag problems left.
+ Patially implemented Stop and Reset opcodes - Fixes Thunderforce IV
v0.0075 notaz
+ Added missing displacement addressing mode for movem (Fantastic Dizzy)
+ Added OSP <-> A7 swapping code in opcodes, which change privilege mode
+ Implemented privilege violation, line emulator and divide by zero exceptions
+ Added negx opcode (Shining Force works!)
+ Added overflow detection for divs/divu
v0.0072 notaz
note: I could only get v0.0069 cyclone, so I had to implement these myself using Dave's
changelog as a guide.
+ Fixed a problem with divs - remainder should be negative when divident is negative
+ Added movep opcode (Sonic 3 works)
+ Fixed a problem with DBcc incorrectly decrementing if the condition is true (Shadow of the Beast)
v0.0069
+ Added SBCD and the flags for ABCD/SBCD. Score and time now works in games such as
Rolling Thunder 2, Ghouls 'N Ghosts
+ Fixed a problem with addx and subx with 8-bit and 16-bit values.
Ghouls 'N' Ghosts now works!
v0.0068
+ Added ABCD opcode (Streets of Rage works now!)
v0.0067
+ Added dbCC (After Burner)
+ Added asr EA (Sonic 1 Boss/Labyrinth Zone)
+ Added andi/ori/eori ccr (Altered Beast)
+ Added trap (After Burner)
+ Added special case for move.b (a7)+ and -(a7), stepping by 2
After Burner is playable! Eternal Champions shows more
+ Fixed lsr.b/w zero flag (Ghostbusters)
Rolling Thunder 2 now works!
+ Fixed N flag for .b and .w arithmetic. Golden Axe works!
v0.0066
+ Fixed a stupid typo for exg (orr r10,r10, not orr r10,r8), which caused alignment
crashes on Strider
v0.0065
+ Fixed a problem with immediate values - they weren't being shifted up correctly for some
opcodes. Spiderman works, After Burner shows a bit of graphics.
+ Fixed a problem with EA:"110nnn" extension word. 32-bit offsets were being decoded as 8-bit
offsets by mistake. Castlevania Bloodlines seems fine now.
+ Added exg opcode
+ Fixed asr opcode (Sonic jumping left is fixed)
+ Fixed a problem with the carry bit in rol.b (Marble Madness)
v0.0064
+ Added rtr
+ Fixed addq/subq.l (all An opcodes are 32-bit) (Road Rash)
+ Fixed various little timings
v0.0063
+ Added link/unlk opcodes
+ Fixed various little timings
+ Fixed a problem with dbCC opcode being emitted at set opcodes
+ Improved long register access, the EA fetch now does ldr r0,[r7,r0,lsl #2] whenever
possible, saving 1 or 2 cycles on many opcodes, which should give a nice speed up.
+ May have fixed N flag on ext opcode?
+ Added dasm for link opcode.
v0.0062
* I was a bit too keen with the Arithmetic opcodes! Some of them should have been abcd,
exg and addx. Removed the incorrect opcodes, pending re-adding them as abcd, exg and addx.
+ Changed unknown opcodes to act as nops.
Not very technical, but fun - a few more games show more graphics ;)
v0.0060
+ Fixed divu (EA intro)
+ Added sf (set false) opcode - SOR2
* Todo: pea/link/unlk opcodes
v0.0059: Added remainder to divide opcodes.

View file

@ -1,886 +0,0 @@
// Dave's Disa 68000 Disassembler
#ifndef __GNUC__
#pragma warning(disable:4115)
#endif
#include <stdio.h>
#include <string.h>
#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 (*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;
}
static int OpChk(int op)
{
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,"chk %s, %s",seat,deat);
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<<i;
bit&=mask;
if (bit==0 || i==8)
{
// low to i-1 are a continuous section, add it:
char add[16]="";
int ad=low&8?'a':'d';
if (low==i-1) sprintf(add,"%c%d/", ad,low&7);
if (low< i-1) sprintf(add,"%c%d-%c%d/",ad,low&7, ad,(i-1)&7);
strcat(list,add);
low=i; // Next section
}
if (bit==0) low=i+1;
}
// Knock off trailing '/'
len=strlen(list);
if (len>0) if (list[len-1]=='/') list[len-1]=0;
return 0;
}
// ================ Opcodes 0x4800+ ================
static int DisaNbcd(int op)
{
// Nbcd 01001000 00eeeeee (eeeeee=ea)
int ea=0;
char eat[64]="";
ea=op&0x003f;
DisaGetEa(eat,ea,0);
sprintf(DisaText,"nbcd %s",eat);
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;
}
static int DisaTas(int op)
{
// Tas 01001010 11eeeeee (eeeeee=ea)
int ea=0;
char eat[64]="";
ea=op&0x003f;
DisaGetEa(eat,ea,0);
sprintf(DisaText,"tas %s",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;
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;
}
static int DisaCmpm(int op)
{
char seat[64]="",deat[64]="";
int type=0,size=0,sea,dea;
type=(op>>8)&1;
size=(op>>6)&3; if (size>=3) return 1;
sea=(op&7)|0x18;
dea=(op>>9)&0x3f;
DisaGetEa(seat,sea,size);
DisaGetEa(deat,dea,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,mem;
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&7;
mem = op&8;
if(mem) { sea+=0x20; dea+=0x20; }
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&0xf140)==0x4100) OpChk(op);
if ((op&0xf1c0)==0x41c0) DisaLea(op);
if ((op&0xf9c0)==0x40c0) DisaMoveSr(op);
if ((op&0xffc0)==0x4800) DisaNbcd(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&0xffc0)==0x4ac0) DisaTas(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;
}

View file

@ -1,18 +0,0 @@
// Dave's Disa 68000 Disassembler
#ifdef __cplusplus
extern "C" {
#endif
extern unsigned int DisaPc;
extern char *DisaText; // Text buffer to write in
extern unsigned short (*DisaWord)(unsigned int a);
int DisaGetEa(char *t,int ea,int size);
int DisaGet();
#ifdef __cplusplus
} // End of extern "C"
#endif

View file

@ -1,493 +0,0 @@
#include "app.h"
int earead_check_addrerr = 1, eawrite_check_addrerr = 0;
// some ops use non-standard cycle counts for EAs, so are listed here.
// all constants borrowed from the MUSASHI core by Karl Stenerud.
/* Extra cycles for JMP instruction (000, 010) */
int g_jmp_cycle_table[8] =
{
4, /* EA_MODE_AI */
6, /* EA_MODE_DI */
10, /* EA_MODE_IX */
6, /* EA_MODE_AW */
8, /* EA_MODE_AL */
6, /* EA_MODE_PCDI */
10, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for JSR instruction (000, 010) */
int g_jsr_cycle_table[8] =
{
4, /* EA_MODE_AI */
6, /* EA_MODE_DI */
10, /* EA_MODE_IX */
6, /* EA_MODE_AW */
8, /* EA_MODE_AL */
6, /* EA_MODE_PCDI */
10, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for LEA instruction (000, 010) */
int g_lea_cycle_table[8] =
{
4, /* EA_MODE_AI */
8, /* EA_MODE_DI */
12, /* EA_MODE_IX */
8, /* EA_MODE_AW */
12, /* EA_MODE_AL */
8, /* EA_MODE_PCDI */
12, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for PEA instruction (000, 010) */
int g_pea_cycle_table[8] =
{
6, /* EA_MODE_AI */
10, /* EA_MODE_DI */
14, /* EA_MODE_IX */
10, /* EA_MODE_AW */
14, /* EA_MODE_AL */
10, /* EA_MODE_PCDI */
14, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for MOVEM instruction (000, 010) */
int g_movem_cycle_table[8] =
{
0, /* EA_MODE_AI */
4, /* EA_MODE_DI */
6, /* EA_MODE_IX */
4, /* EA_MODE_AW */
8, /* EA_MODE_AL */
0, /* EA_MODE_PCDI */
0, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
// add nonstandard EA
int Ea_add_ns(int *tab, int ea)
{
if(ea<0x10) return 0;
if((ea&0x38)==0x10) return tab[0]; // (An) (ai)
if(ea<0x28) return 0;
if(ea<0x30) return tab[1]; // ($nn,An) (di)
if(ea<0x38) return tab[2]; // ($nn,An,Rn) (ix)
if(ea==0x38) return tab[3]; // (aw)
if(ea==0x39) return tab[4]; // (al)
if(ea==0x3a) return tab[5]; // ($nn,PC) (pcdi)
if(ea==0x3b) return tab[6]; // ($nn,pc,Rn) (pcix)
if(ea==0x3c) return tab[7]; // #$nnnn (i)
return 0;
}
// ---------------------------------------------------------------------------
// Gets the offset of a register for an ea, and puts it in 'r'
// Shifted left by 'shift'
// Doesn't trash anything
static int EaCalcReg(int r,int ea,int mask,int forceor,int shift,int noshift=0)
{
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<<low; // This is the max we can do
if (ea>=8)
{
needor=1; // Need to OR to access A0-7
if ((g_op>>low)&8) { needor=0; mask|=8<<low; } // 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);
if (needor) ot(" orr r%d,r%d,#0x%x ;@ A0-7\n",r,r,8<<low);
// Find out amount to shift left:
lsl=shift-low;
if (lsl&&!noshift)
{
ot(" mov r%d,r%d,",r,r);
if (lsl>0) ot("lsl #%d\n", lsl);
else ot("lsr #%d\n",-lsl);
}
return 0;
}
// EaCalc - ARM Register 'a' = Effective Address
// If ea>=0x10, trashes r0,r2 and r3, else nothing
// size values 0, 1, 2 ~ byte, word, long
// mask shows usable bits in r8
int EaCalc(int a,int mask,int ea,int size,int top,int sign_extend)
{
char text[32]="";
int func=0;
DisaPc=2; DisaGetEa(text,ea,size); // Get text version of the effective address
func=0x68+(size<<2); // Get correct read handler
if (ea<0x10)
{
int noshift=0;
if (size>=2||(size==0&&(top||!sign_extend))) noshift=1; // Saves one opcode
ot(";@ EaCalc : Get register index into r%d:\n",a);
EaCalcReg(a,ea,mask,0,2,noshift);
return 0;
}
ot(";@ EaCalc : Get '%s' into r%d:\n",text,a);
// (An), (An)+, -(An)
if (ea<0x28)
{
int step=1<<size, strr=a;
int low=0,lsl=0,i;
if ((ea&7)==7 && step<2) step=2; // move.b (a7)+ or -(a7) steps by 2 not 1
if (ea==0x1f||ea==0x27) // A7 handlers are always separate
{
ot(" ldr r%d,[r7,#0x3c] ;@ A7\n",a);
}
else
{
EaCalcReg(2,ea,mask,0,0,1);
if(mask)
for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is
lsl=2-low; // Having a lsl #x here saves one opcode
if (lsl>=0) ot(" ldr r%d,[r7,r2,lsl #%i]\n",a,lsl);
else if (lsl<0) ot(" ldr r%d,[r7,r2,lsr #%i]\n",a,-lsl);
}
if ((ea&0x38)==0x18) // (An)+
{
ot(" add r3,r%d,#%d ;@ Post-increment An\n",a,step);
strr=3;
}
if ((ea&0x38)==0x20) // -(An)
ot(" sub r%d,r%d,#%d ;@ Pre-decrement An\n",a,a,step);
if ((ea&0x38)==0x18||(ea&0x38)==0x20)
{
if (ea==0x1f||ea==0x27)
{
ot(" str r%d,[r7,#0x3c] ;@ A7\n",strr);
}
else
{
if (lsl>=0) ot(" str r%d,[r7,r2,lsl #%i]\n",strr,lsl);
else if (lsl<0) ot(" str r%d,[r7,r2,lsr #%i]\n",strr,-lsl);
}
}
if ((ea&0x38)==0x20) Cycles+=size<2 ? 6:10; // -(An) Extra cycles
else Cycles+=size<2 ? 4:8; // (An),(An)+ Extra cycles
return 0;
}
if (ea<0x30) // ($nn,An) (di)
{
ot(" ldrsh r0,[r4],#2 ;@ Fetch offset\n"); pc_dirty=1;
EaCalcReg(2,8,mask,0,0);
ot(" ldr r2,[r7,r2,lsl #2]\n");
ot(" add r%d,r0,r2 ;@ Add on offset\n",a);
Cycles+=size<2 ? 8:12; // Extra cycles
return 0;
}
if (ea<0x38) // ($nn,An,Rn) (ix)
{
ot(";@ Get extension word into r3:\n");
ot(" ldrh r3,[r4],#2 ;@ ($Disp,PC,Rn)\n"); pc_dirty=1;
ot(" mov r2,r3,lsr #10\n");
ot(" tst r3,#0x0800 ;@ Is Rn Word or Long\n");
ot(" and r2,r2,#0x3c ;@ r2=Index of Rn\n");
ot(" ldreqsh r2,[r7,r2] ;@ r2=Rn.w\n");
ot(" ldrne r2,[r7,r2] ;@ r2=Rn.l\n");
ot(" mov r0,r3,asl #24 ;@ r0=Get 8-bit signed Disp\n");
ot(" add r3,r2,r0,asr #24 ;@ r3=Disp+Rn\n");
EaCalcReg(2,8,mask,1,0);
ot(" ldr r2,[r7,r2,lsl #2]\n");
ot(" add r%d,r2,r3 ;@ r%d=Disp+An+Rn\n",a,a);
Cycles+=size<2 ? 10:14; // Extra cycles
return 0;
}
if (ea==0x38) // (aw)
{
ot(" ldrsh r%d,[r4],#2 ;@ Fetch Absolute Short address\n",a); pc_dirty=1;
Cycles+=size<2 ? 8:12; // Extra cycles
return 0;
}
if (ea==0x39) // (al)
{
ot(" ldrh r2,[r4],#2 ;@ Fetch Absolute Long address\n");
ot(" ldrh r0,[r4],#2\n"); pc_dirty=1;
ot(" orr r%d,r0,r2,lsl #16\n",a);
Cycles+=size<2 ? 12:16; // Extra cycles
return 0;
}
if (ea==0x3a) // ($nn,PC) (pcdi)
{
ot(" ldr r0,[r7,#0x60] ;@ Get Memory base\n");
ot(" sub r0,r4,r0 ;@ Real PC\n");
ot(" ldrsh r2,[r4],#2 ;@ Fetch extension\n"); pc_dirty=1;
ot(" mov r0,r0,lsl #8\n");
ot(" add r%d,r2,r0,asr #8 ;@ ($nn,PC)\n",a);
Cycles+=size<2 ? 8:12; // Extra cycles
return 0;
}
if (ea==0x3b) // ($nn,pc,Rn) (pcix)
{
ot(" ldr r0,[r7,#0x60] ;@ Get Memory base\n");
ot(" ldrh r3,[r4] ;@ Get extension word\n");
ot(" sub r0,r4,r0 ;@ r0=PC\n");
ot(" add r4,r4,#2\n"); pc_dirty=1;
ot(" mov r0,r0,asl #8 ;@ use only 24bits of PC\n");
ot(" mov r2,r3,lsr #10\n");
ot(" tst r3,#0x0800 ;@ Is Rn Word or Long\n");
ot(" and r2,r2,#0x3c ;@ r2=Index of Rn\n");
ot(" ldreqsh r2,[r7,r2] ;@ r2=Rn.w\n");
ot(" ldrne r2,[r7,r2] ;@ r2=Rn.l\n");
ot(" mov r3,r3,asl #24 ;@ r3=Get 8-bit signed Disp\n");
ot(" add r2,r2,r3,asr #24 ;@ r2=Disp+Rn\n");
ot(" add r%d,r2,r0,asr #8 ;@ r%d=Disp+PC+Rn\n",a,a);
Cycles+=size<2 ? 10:14; // Extra cycles
return 0;
}
if (ea==0x3c) // #$nnnn (i)
{
if (size<2)
{
ot(" ldr%s r%d,[r4],#2 ;@ Fetch immediate value\n",Sarm[size&3],a); pc_dirty=1;
Cycles+=4; // Extra cycles
return 0;
}
ot(" ldrh r2,[r4],#2 ;@ Fetch immediate value\n");
ot(" ldrh r3,[r4],#2\n"); pc_dirty=1;
ot(" orr r%d,r3,r2,lsl #16\n",a);
Cycles+=8; // Extra cycles
return 0;
}
return 1;
}
// ---------------------------------------------------------------------------
// Read effective address in (ARM Register 'a') to ARM register 'v'
// 'a' and 'v' can be anything but 0 is generally best (for both)
// If (ea<0x10) nothing is trashed, else r0-r3 is trashed
// If 'top' is given, the ARM register v shifted to the top, e.g. 0xc000 -> 0xc0000000
// If top is 0 and sign_extend is not, then ARM register v is sign extended,
// e.g. 0xc000 -> 0xffffc000 (else it may or may not be sign extended)
int EaRead(int a,int v,int ea,int size,int mask,int top,int sign_extend)
{
char text[32]="";
int shift=0;
shift=32-(8<<size);
DisaPc=2; DisaGetEa(text,ea,size); // Get text version of the effective address
if (ea<0x10)
{
int lsl=0,low=0,nsarm=size&3,i;
if (size>=2||(size==0&&(top||!sign_extend))) {
if(mask)
for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is
lsl=2-low; // Having a lsl #2 here saves one opcode
}
if (top||!sign_extend) nsarm=3;
ot(";@ EaRead : Read register[r%d] into r%d:\n",a,v);
if (lsl>0) ot(" ldr%s r%d,[r7,r%d,lsl #%i]\n",Narm[nsarm],v,a,lsl);
else if (lsl<0) ot(" ldr%s r%d,[r7,r%d,lsr #%i]\n",Narm[nsarm],v,a,-lsl);
else ot(" ldr%s r%d,[r7,r%d]\n",Sarm[nsarm],v,a);
if (top&&shift) ot(" mov r%d,r%d,asl #%d\n",v,v,shift);
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 (asl) ot(" mov r%d,r%d,asl #%d\n",v,a,asl);
else if (v!=a) ot(" mov r%d,r%d\n",v,a);
ot("\n"); return 0;
}
if (ea>=0x3a && ea<=0x3b) MemHandler(2,size,a,earead_check_addrerr); // Fetch
else MemHandler(0,size,a,earead_check_addrerr); // Read
// defaults to 1, as most things begins with a read
earead_check_addrerr=1;
if (sign_extend)
{
int d_reg=0;
if (shift) {
ot(" mov r%d,r%d,asl #%d\n",v,d_reg,shift);
d_reg=v;
}
if (!top && shift) {
ot(" mov r%d,r%d,asr #%d\n",v,d_reg,shift);
d_reg=v;
}
if (d_reg != v)
ot(" mov r%d,r%d\n",v,d_reg);
}
else
{
if (top && shift)
ot(" mov r%d,r0,asl #%d\n",v,shift);
else if (v!=0)
ot(" mov r%d,r0\n",v);
}
ot("\n"); return 0;
}
// calculate EA and read
// if (ea < 0x10) nothing is trashed
// if (ea == 0x3c) r2 and r3 are trashed
// else r0-r3 are trashed
// size values 0, 1, 2 ~ byte, word, long
// r_ea is reg to store ea in (-1 means ea is not needed), r is dst reg
// if sign_extend is 0, non-32bit values will have MS bits undefined
int EaCalcRead(int r_ea,int r,int ea,int size,int mask,int sign_extend)
{
if (ea<0x10)
{
if (r_ea==-1)
{
r_ea=r;
if (!sign_extend) size=2;
}
}
else if (ea==0x3c) // #imm
{
r_ea=r;
}
else
{
if (r_ea==-1) r_ea=0;
}
EaCalc (r_ea,mask,ea,size,0,sign_extend);
EaRead (r_ea, r,ea,size,mask,0,sign_extend);
return 0;
}
int EaCalcReadNoSE(int r_ea,int r,int ea,int size,int mask)
{
return EaCalcRead(r_ea,r,ea,size,mask,0);
}
// Return 1 if we can read this ea
int EaCanRead(int ea,int size)
{
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,r12,lr; 'a' can be 0 or 2+, 'v' can be 1 or higher
// If a==0 and v==1 it's faster though.
int EaWrite(int a,int v,int ea,int size,int mask,int top,int sign_extend_ea)
{
char text[32]="";
int shift=0;
if(a == 1) { printf("Error! EaWrite a==1 !\n"); return 1; }
if (top) shift=32-(8<<size);
DisaPc=2; DisaGetEa(text,ea,size); // Get text version of the effective address
if (ea<0x10)
{
int lsl=0,low=0,i;
if (size>=2||(size==0&&(top||!sign_extend_ea))) {
if(mask)
for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is
lsl=2-low; // Having a lsl #x here saves one opcode
}
ot(";@ EaWrite: r%d into register[r%d]:\n",v,a);
if (shift) ot(" mov r%d,r%d,asr #%d\n",v,v,shift);
if (lsl>0) ot(" str%s r%d,[r7,r%d,lsl #%i]\n",Narm[size&3],v,a,lsl);
else if (lsl<0) ot(" str%s r%d,[r7,r%d,lsr #%i]\n",Narm[size&3],v,a,-lsl);
else ot(" str%s r%d,[r7,r%d]\n",Narm[size&3],v,a);
ot("\n"); return 0;
}
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 (shift) ot(" mov r1,r%d,asr #%d\n",v,shift);
else if (v!=1) ot(" mov r1,r%d\n",v);
MemHandler(1,size,a,eawrite_check_addrerr); // Call write handler
// not check by default, because most cases are rmw and
// address was already checked before reading
eawrite_check_addrerr = 0;
ot("\n"); return 0;
}
// Return 1 if we can write this ea
int EaCanWrite(int ea)
{
if (ea<=0x39) return 1; // 3b?
return 0;
}
// ---------------------------------------------------------------------------
// Return 1 if EA is An reg
int EaAn(int ea)
{
if((ea&0x38)==8) return 1;
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -1,207 +0,0 @@
#include "app.h"
int opend_op_changes_cycles, opend_check_interrupt, opend_check_trace;
static unsigned char OpData[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static unsigned short 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 OpStart(int op, int sea, int tea, int op_changes_cycles, int supervisor_check)
{
int last_op_count=arm_op_count;
Cycles=0;
OpUse(op,op); // This opcode obviously uses this handler
ot("Op%.4x%s\n", op, ms?"":":");
if (supervisor_check)
{
// checks for supervisor bit, if not set, jumps to SuperEnd()
// also sets r11 to SR high value, SuperChange() uses this
ot(" ldr r11,[r7,#0x44] ;@ Get SR high\n");
}
if ((sea >= 0x10 && sea != 0x3c) || (tea >= 0x10 && tea != 0x3c))
{
#if MEMHANDLERS_NEED_PREV_PC
ot(" str r4,[r7,#0x50] ;@ Save prev PC + 2\n");
#endif
#if MEMHANDLERS_NEED_CYCLES
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
#endif
}
if (supervisor_check)
{
ot(" tst r11,#0x20 ;@ Check we are in supervisor mode\n");
ot(" beq WrongPrivilegeMode ;@ No\n");
}
if ((sea >= 0x10 && sea != 0x3c) || (tea >= 0x10 && tea != 0x3c)) {
#if MEMHANDLERS_CHANGE_CYCLES
if (op_changes_cycles)
ot(" mov r5,#0\n");
#endif
}
if (last_op_count!=arm_op_count)
ot("\n");
pc_dirty = 1;
opend_op_changes_cycles = opend_check_interrupt = opend_check_trace = 0;
}
void OpEnd(int sea, int tea)
{
int did_fetch=0;
opend_check_trace = opend_check_trace && EMULATE_TRACE;
#if MEMHANDLERS_CHANGE_CYCLES
if ((sea >= 0x10 && sea != 0x3c) || (tea >= 0x10 && tea != 0x3c))
{
if (opend_op_changes_cycles)
{
ot(" ldr r0,[r7,#0x5c] ;@ Load Cycles\n");
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
ot(" add r5,r0,r5\n");
did_fetch=1;
}
else
{
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
}
}
#endif
if (!did_fetch)
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
if (opend_check_trace)
ot(" ldr r1,[r7,#0x44]\n");
ot(" subs r5,r5,#%d ;@ Subtract cycles\n",Cycles);
if (opend_check_trace)
{
ot(";@ CheckTrace:\n");
ot(" tst r1,#0x80\n");
ot(" bne CycloneDoTraceWithChecks\n");
ot(" cmp r5,#0\n");
}
if (opend_check_interrupt)
{
ot(" blt CycloneEnd\n");
ot(";@ CheckInterrupt:\n");
if (!opend_check_trace)
ot(" ldr r1,[r7,#0x44]\n");
ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47]
ot(" ldreq pc,[r6,r8,asl #2] ;@ Jump to next opcode handler\n");
ot(" cmp r0,#6 ;@ irq>6 ?\n");
ot(" andle r1,r1,#7 ;@ Get interrupt mask\n");
ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n");
ot(" ldrle pc,[r6,r8,asl #2] ;@ Jump to next opcode handler\n");
ot(" b CycloneDoInterruptGoBack\n");
}
else
{
ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
ot(" b CycloneEnd\n");
}
ot("\n");
}
int OpBase(int op,int size,int sepa)
{
int ea=op&0x3f; // Get Effective Address
if (ea<0x10) return sepa?(op&~0x7):(op&~0xf); // Use 1 handler for d0-d7 and a0-a7
if (size==0&&(ea==0x1f || ea==0x27)) return op; // Specific handler for (a7)+ and -(a7)
if (ea<0x38) return op&~7; // Use 1 handler for (a0)-(a7), etc...
return op;
}
// Get flags, trashes r2
int OpGetFlags(int subtract,int xbit,int specialz)
{
if (specialz) ot(" orr r2,r10,#0xb0000000 ;@ for old Z\n");
ot(" mrs r10,cpsr ;@ r10=flags\n");
if (specialz) ot(" andeq r10,r10,r2 ;@ fix Z\n");
if (subtract) ot(" eor r10,r10,#0x20000000 ;@ Invert carry\n");
if (xbit)
{
ot(" str r10,[r7,#0x4c] ;@ Save X bit\n");
}
return 0;
}
// -----------------------------------------------------------------
int g_op;
void OpAny(int op)
{
memset(OpData,0x33,sizeof(OpData));
OpData[0]=(unsigned char)(op>>8);
OpData[1]=(unsigned char)op;
g_op=op;
if ((op&0xf100)==0x0000) OpArith(op); // +
if ((op&0xc000)==0x0000) OpMove(op); // +
if ((op&0xf5bf)==0x003c) OpArithSr(op); // + Ori/Andi/Eori $nnnn,sr
if ((op&0xf100)==0x0100) OpBtstReg(op); // +
if ((op&0xf138)==0x0108) OpMovep(op); // +
if ((op&0xff00)==0x0800) OpBtstImm(op); // +
if ((op&0xf900)==0x4000) OpNeg(op); // +
if ((op&0xf140)==0x4100) OpChk(op); // +
if ((op&0xf1c0)==0x41c0) OpLea(op); // +
if ((op&0xf9c0)==0x40c0) OpMoveSr(op); // +
if ((op&0xffc0)==0x4800) OpNbcd(op); // +
if ((op&0xfff8)==0x4840) OpSwap(op); // +
if ((op&0xffc0)==0x4840) OpPea(op); // +
if ((op&0xffb8)==0x4880) OpExt(op); // +
if ((op&0xfb80)==0x4880) OpMovem(op); // +
if ((op&0xff00)==0x4a00) OpTst(op); // +
if ((op&0xffc0)==0x4ac0) OpTas(op); // +
if ((op&0xfff0)==0x4e40) OpTrap(op); // +
if ((op&0xfff8)==0x4e50) OpLink(op); // +
if ((op&0xfff8)==0x4e58) OpUnlk(op); // +
if ((op&0xfff0)==0x4e60) OpMoveUsp(op); // +
if ((op&0xfff8)==0x4e70) Op4E70(op); // + Reset/Rts etc
if ((op&0xfffd)==0x4e70) OpStopReset(op);// +
if ((op&0xff80)==0x4e80) OpJsr(op); // +
if ((op&0xf000)==0x5000) OpAddq(op); // +
if ((op&0xf0c0)==0x50c0) OpSet(op); // +
if ((op&0xf0f8)==0x50c8) OpDbra(op); // +
if ((op&0xf000)==0x6000) OpBranch(op); // +
if ((op&0xf100)==0x7000) OpMoveq(op); // +
if ((op&0xa000)==0x8000) OpArithReg(op); // + Or/Sub/And/Add
if ((op&0xb1f0)==0x8100) OpAbcd(op); // +
if ((op&0xb0c0)==0x80c0) OpMul(op); // +
if ((op&0x90c0)==0x90c0) OpAritha(op); // +
if ((op&0xb130)==0x9100) OpAddx(op); // +
if ((op&0xf000)==0xb000) OpCmpEor(op); // +
if ((op&0xf138)==0xb108) OpCmpm(op); // +
if ((op&0xf130)==0xc100) OpExg(op); // +
if ((op&0xf000)==0xe000) OpAsr(op); // + Asr/l/Ror/l etc
if ((op&0xf8c0)==0xe0c0) OpAsrEa(op); // +
if (op==0xffff)
{
SuperEnd();
}
}

View file

@ -1,819 +0,0 @@
#include "app.h"
// --------------------- Opcodes 0x0000+ ---------------------
// Emit an Ori/And/Sub/Add/Eor/Cmp Immediate opcode, 0000ttt0 ssaaaaaa
int OpArith(int op)
{
int type=0,size=0;
int sea=0,tea=0;
int use=0;
const char *shiftstr="";
// Get source and target EA
type=(op>>9)&7; if (type==4 || type>=7) return 1;
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 (EaCanWrite(tea)==0 || EaAn(tea)) return 1;
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op, sea, tea); Cycles=4;
// imm must be read first
EaCalcReadNoSE(-1,10,sea,size,0);
EaCalcReadNoSE((type!=6)?11:-1,0,tea,size,0x003f);
if (size<2) shiftstr=(char *)(size?",asl #16":",asl #24");
if (size<2) ot(" mov r10,r10,asl #%i\n",size?16:24);
ot(";@ Do arithmetic:\n");
if (type==0) ot(" orr r1,r10,r0%s\n",shiftstr);
if (type==1) ot(" and r1,r10,r0%s\n",shiftstr);
if (type==2||type==6)
ot(" rsbs r1,r10,r0%s ;@ Defines NZCV\n",shiftstr);
if (type==3) ot(" adds r1,r10,r0%s ;@ Defines NZCV\n",shiftstr);
if (type==5) ot(" eor r1,r10,r0%s\n",shiftstr);
if (type<2 || type==5) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); // 0,1,5
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,0x003f,1);
}
// Correct cycles:
if (type==6)
{
if (size>=2 && tea<0x10) Cycles+=2;
}
else
{
if (size>=2) Cycles+=4;
if (tea>=8) Cycles+=4;
if (type==1 && size>=2 && tea<8) Cycles-=2;
}
OpEnd(sea,tea);
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;
if (size == 0 && EaAn(ea) ) return 1;
use=OpBase(op,size,1);
if (num!=8) use|=0x0e00; // If num is not 8, use same handler
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea);
Cycles=ea<8?4:8;
if(type==0&&size==1) Cycles=ea<0x10?4:8;
if(size>=2) Cycles=ea<0x10?8:12;
if (size>0 && (ea&0x38)==0x08) size=2; // addq.w #n,An is also 32-bit
EaCalcReadNoSE(11,0,ea,size,0x003f);
shift=32-(8<<size);
if (num!=8)
{
int lsr=9-shift;
ot(" and r2,r8,#0x0e00 ;@ Get quick value\n");
if (lsr>=0) sprintf(count,"r2,lsr #%d", lsr);
else sprintf(count,"r2,lsl #%d", -lsr);
ot("\n");
}
else
{
sprintf(count,"#0x%.4x",8<<shift);
}
if (size<2) ot(" mov r0,r0,asl #%d\n\n",size?16:24);
if (type==0) ot(" adds r1,r0,%s\n",count);
if (type==1) ot(" subs r1,r0,%s\n",count);
if ((ea&0x38)!=0x08) OpGetFlags(type,1);
ot("\n");
EaWrite(11, 1, ea,size,0x003f,1);
OpEnd(ea);
return 0;
}
// --------------------- Opcodes 0x8000+ ---------------------
// 1t0tnnnd xxeeeeee (tt=type:or/sub/and/add xx=size, eeeeee=EA)
int OpArithReg(int op)
{
int use=0;
int type=0,size=0,dir=0,rea=0,ea=0;
const char *asl="";
const char *strop=0;
type=(op>>12)&5;
rea =(op>> 9)&7;
dir =(op>> 8)&1; // er,re
size=(op>> 6)&3; if (size>=3) return 1;
ea = op&0x3f;
if (dir && ea<0x10) return 1; // addx/subx opcode
// See if we can do this opcode:
if (dir==0 && EaCanRead (ea,size)==0) return 1;
if (dir && EaCanWrite(ea)==0) return 1;
if ((size==0||!(type&1))&&EaAn(ea)) return 1;
use=OpBase(op,size);
use&=~0x0e00; // Use same opcode for Dn
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea); Cycles=4;
EaCalcReadNoSE(dir?11:-1,0,ea,size,0x003f);
EaCalcReadNoSE(dir?-1:11,1,rea,size,0x0e00);
ot(";@ Do arithmetic:\n");
if (type==0) strop = "orr";
if (type==1) strop = (char *) (dir ? "subs" : "rsbs");
if (type==4) strop = "and";
if (type==5) strop = "adds";
if (size==0) asl=",asl #24";
if (size==1) asl=",asl #16";
if (size<2) ot(" mov r0,r0%s\n",asl);
ot(" %s r1,r0,r1%s\n",strop,asl);
if ((type&1)==0) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(type==1,type&1); // 1==subtract
ot("\n");
ot(";@ Save result:\n");
if (size<2) ot(" mov r1,r1,asr #%d\n",size?16:24);
if (dir) EaWrite(11, 1, ea,size,0x003f,0,0);
else EaWrite(11, 1,rea,size,0x0e00,0,0);
if(rea==ea) {
if(ea<8) Cycles=(size>=2)?8:4; else Cycles+=(size>=2)?26:14;
} else if(dir) {
Cycles+=4;
if(size>=2) Cycles+=4;
} else {
if(size>=2) {
Cycles+=2;
if(ea<0x10||ea==0x3c) Cycles+=2;
}
}
OpEnd(ea);
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||EaAn(ea)) return 1;
use=OpBase(op,1);
use&=~0x0e00; // Use same for all registers
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea);
if(type) Cycles=54;
else Cycles=sign?158:140;
EaCalcReadNoSE(-1,0,ea,1,0x003f);
EaCalc(11,0x0e00,rea, 2);
EaRead(11, 2,rea, 2,0x0e00);
ot(" movs r1,r0,asl #16\n");
if (type==0) // div
{
// the manual says C is always cleared, but neither Musashi nor FAME do that
//ot(" bic r10,r10,#0x20000000 ;@ always clear C\n");
ot(" beq divzero%.4x ;@ division by zero\n",op);
ot("\n");
if (sign)
{
ot(" mov r12,#0 ;@ r12 = 1 or 2 if the result is negative\n");
ot(" tst r2,r2\n");
ot(" orrmi r12,r12,#2\n");
ot(" rsbmi r2,r2,#0 ;@ Make r2 positive\n");
ot("\n");
ot(" movs r0,r1,asr #16\n");
ot(" orrmi r12,r12,#1\n");
ot(" rsbmi r0,r0,#0 ;@ Make r0 positive\n");
ot("\n");
ot(";@ detect the nasty 0x80000000 / -1 situation\n");
ot(" mov r3,r2,asr #31\n");
ot(" eors r3,r3,r1,asr #16\n");
ot(" beq wrendofop%.4x\n",op);
}
else
{
ot(" mov r0,r1,lsr #16 ;@ use only 16 bits of divisor\n");
}
ot("\n");
ot(";@ Divide r2 by r0\n");
ot(" mov r3,#0\n");
ot(" mov r1,r0\n");
ot("\n");
ot(";@ Shift up divisor till it's just less than numerator\n");
ot("Shift%.4x%s\n",op,ms?"":":");
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,r0\n");
ot(" movne r1,r1,lsr #1\n");
ot(" bne Divide%.4x\n",op);
ot("\n");
ot(";@r3==quotient,r2==remainder\n");
if (sign)
{
// sign correction
ot(" and r1,r12,#1\n");
ot(" teq r1,r12,lsr #1\n");
ot(" rsbne r3,r3,#0 ;@ negate if quotient is negative\n");
ot(" tst r12,#2\n");
ot(" rsbne r2,r2,#0 ;@ negate the remainder if divident was negative\n");
ot("\n");
// signed overflow check
ot(" mov r1,r3,asl #16\n");
ot(" cmp r3,r1,asr #16 ;@ signed overflow?\n");
ot(" orrne r10,r10,#0x10000000 ;@ set overflow flag\n");
ot(" bne endofop%.4x ;@ overflow!\n",op);
ot("\n");
ot("wrendofop%.4x%s\n",op,ms?"":":");
}
else
{
// overflow check
ot(" movs r1,r3,lsr #16 ;@ check for overflow condition\n");
ot(" orrne r10,r10,#0x10000000 ;@ set overflow flag\n");
ot(" bne endofop%.4x ;@ overflow!\n",op);
ot("\n");
}
ot(" mov r1,r3,lsl #16 ;@ Clip to 16-bits\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot(" mov r1,r1,lsr #16\n");
ot(" orr r1,r1,r2,lsl #16 ;@ Insert remainder\n");
}
if (type==1)
{
ot(";@ Get 16-bit signs right:\n");
ot(" mov r0,r1,%s #16\n",sign?"asr":"lsr");
ot(" mov r2,r2,lsl #16\n");
ot(" mov r2,r2,%s #16\n",sign?"asr":"lsr");
ot("\n");
ot(" mul r1,r2,r0\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
}
ot("\n");
EaWrite(11, 1,rea, 2,0x0e00,1);
if (type==0) ot("endofop%.4x%s\n",op,ms?"":":");
OpEnd(ea);
if (type==0) // div
{
ot("divzero%.4x%s\n",op,ms?"":":");
ot(" mov r0,#5 ;@ Divide by zero\n");
ot(" bl Exception\n");
Cycles+=38;
OpEnd(ea);
ot("\n");
}
return 0;
}
// Get X Bit into carry - trashes r2
int GetXBit(int subtract)
{
ot(";@ Get X bit:\n");
ot(" ldr r2,[r7,#0x4c]\n");
if (subtract) ot(" mvn r2,r2 ;@ Invert it\n");
ot(" msr cpsr_flg,r2 ;@ Get into Carry\n");
ot("\n");
return 0;
}
// --------------------- Opcodes 0x8100+ ---------------------
// 1t00ddd1 0000asss - sbcd/abcd Ds,Dd or -(As),-(Ad)
int OpAbcd(int op)
{
int use=0;
int type=0,sea=0,mem=0,dea=0;
type=(op>>14)&1; // sbcd/abcd
dea =(op>> 9)&7;
mem =(op>> 3)&1;
sea = op &7;
if (mem) { sea|=0x20; dea|=0x20; }
use=op&~0x0e07; // Use same opcode for all registers..
if (sea==0x27) use|=0x0007; // ___x.b -(a7)
if (dea==0x27) use|=0x0e00; // ___x.b -(a7)
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea,dea); Cycles=6;
if (mem)
{
ot(";@ Get src/dest EA vals\n");
EaCalc (0,0x000f, sea,0,1);
EaRead (0, 6, sea,0,0x000f,1);
EaCalcReadNoSE(11,0,dea,0,0x0e00);
}
else
{
ot(";@ Get src/dest reg vals\n");
EaCalcReadNoSE(-1,6,sea,0,0x0007);
EaCalcReadNoSE(11,0,dea,0,0x0e00);
ot(" mov r6,r6,asl #24\n");
}
ot(" mov r1,r0,asl #24\n\n");
ot(" bic r10,r10,#0xb1000000 ;@ clear all flags except old Z\n");
if (type)
{
ot(" ldr r0,[r7,#0x4c] ;@ Get X bit\n");
ot(" mov r3,#0x00f00000\n");
ot(" and r2,r3,r1,lsr #4\n");
ot(" tst r0,#0x20000000\n");
ot(" and r0,r3,r6,lsr #4\n");
ot(" add r0,r0,r2\n");
ot(" addne r0,r0,#0x00100000\n");
// ot(" tst r0,#0x00800000\n");
// ot(" orreq r10,r10,#0x01000000 ;@ Undefined V behavior\n");
ot(" cmp r0,#0x00900000\n");
ot(" addhi r0,r0,#0x00600000 ;@ Decimal adjust units\n");
ot(" mov r2,r1,lsr #28\n");
ot(" add r0,r0,r2,lsl #24\n");
ot(" mov r2,r6,lsr #28\n");
ot(" add r0,r0,r2,lsl #24\n");
ot(" cmp r0,#0x09900000\n");
ot(" orrhi r10,r10,#0x20000000 ;@ C\n");
ot(" subhi r0,r0,#0x0a000000\n");
// ot(" and r3,r10,r0,lsr #3 ;@ Undefined V behavior part II\n");
// ot(" orr r10,r10,r3,lsl #4 ;@ V\n");
ot(" movs r0,r0,lsl #4\n");
ot(" orrmi r10,r10,#0x90000000 ;@ Undefined N+V behavior\n"); // this is what Musashi really does
ot(" bicne r10,r10,#0x40000000 ;@ Z flag\n");
}
else
{
ot(" ldr r0,[r7,#0x4c] ;@ Get X bit\n");
ot(" mov r3,#0x00f00000\n");
ot(" and r2,r3,r6,lsr #4\n");
ot(" tst r0,#0x20000000\n");
ot(" and r0,r3,r1,lsr #4\n");
ot(" sub r0,r0,r2\n");
ot(" subne r0,r0,#0x00100000\n");
// ot(" tst r0,#0x00800000\n");
// ot(" orreq r10,r10,#0x01000000 ;@ Undefined V behavior\n");
ot(" cmp r0,#0x00900000\n");
ot(" subhi r0,r0,#0x00600000 ;@ Decimal adjust units\n");
ot(" mov r2,r1,lsr #28\n");
ot(" add r0,r0,r2,lsl #24\n");
ot(" mov r2,r6,lsr #28\n");
ot(" sub r0,r0,r2,lsl #24\n");
ot(" cmp r0,#0x09900000\n");
ot(" orrhi r10,r10,#0xa0000000 ;@ N and C\n");
ot(" addhi r0,r0,#0x0a000000\n");
// ot(" and r3,r10,r0,lsr #3 ;@ Undefined V behavior part II\n");
// ot(" orr r10,r10,r3,lsl #4 ;@ V\n");
ot(" movs r0,r0,lsl #4\n");
// ot(" orrmi r10,r10,#0x80000000 ;@ Undefined N behavior\n");
ot(" bicne r10,r10,#0x40000000 ;@ Z flag\n");
}
ot(" str r10,[r7,#0x4c] ;@ Save X bit\n");
ot("\n");
EaWrite(11, 0, dea,0,0x0e00,1);
ot(" ldr r6,[r7,#0x54]\n");
OpEnd(sea,dea);
return 0;
}
// 01001000 00eeeeee - nbcd <ea>
int OpNbcd(int op)
{
int use=0;
int ea=0;
ea=op&0x3f;
if(EaCanWrite(ea)==0||EaAn(ea)) return 1;
use=OpBase(op,0);
if(op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea); Cycles=6;
if(ea >= 8) Cycles+=2;
EaCalcReadNoSE(6,0,ea,0,0x003f);
// this is rewrite of Musashi's code
ot(" ldr r2,[r7,#0x4c]\n");
ot(" bic r10,r10,#0xb0000000 ;@ clear all flags, except Z\n");
ot(" mov r0,r0,asl #24\n");
ot(" and r2,r2,#0x20000000\n");
ot(" add r2,r0,r2,lsr #5 ;@ add X\n");
ot(" rsb r11,r2,#0x9a000000 ;@ do arithmetic\n");
ot(" cmp r11,#0x9a000000\n");
ot(" beq finish%.4x\n",op);
ot("\n");
ot(" mvn r3,r11,lsr #31 ;@ Undefined V behavior\n",op);
ot(" and r2,r11,#0x0f000000\n");
ot(" cmp r2,#0x0a000000\n");
ot(" andeq r11,r11,#0xf0000000\n");
ot(" addeq r11,r11,#0x10000000\n");
ot(" and r3,r3,r11,lsr #31 ;@ Undefined V behavior part II\n",op);
ot(" movs r1,r11,asr #24\n");
ot(" bicne r10,r10,#0x40000000 ;@ Z\n");
ot(" orr r10,r10,r3,lsl #28 ;@ save V\n",op);
ot(" orr r10,r10,#0x20000000 ;@ C\n");
ot("\n");
EaWrite(6, 1, ea,0,0x3f,0,0);
ot("finish%.4x%s\n",op,ms?"":":");
ot(" tst r11,r11\n");
ot(" orrmi r10,r10,#0x80000000 ;@ N\n");
ot(" str r10,[r7,#0x4c] ;@ Save X\n");
ot("\n");
ot(" ldr r6,[r7,#0x54]\n");
OpEnd(ea);
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;
const char *asr="";
// 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,size);
use&=~0x0e00; // Use same opcode for An
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea); Cycles=(size==2)?6:8;
if(size==2&&(sea<0x10||sea==0x3c)) Cycles+=2;
if(type==1) Cycles=6;
// EA calculation order defines how situations like suba.w (A0)+, A0 get handled.
// different emus act differently in this situation, I couldn't fugure which is right behaviour.
//if (type == 1)
{
EaCalcReadNoSE(-1,0,sea,size,0x003f);
EaCalcReadNoSE(type!=1?11:-1,1,dea,2,0x0e00);
}
#if 0
else
{
EaCalcReadNoSE(type!=1?11:-1,1,dea,2,0x0e00);
EaCalcReadNoSE(-1,0,sea,size,0x003f);
}
#endif
if (size<2) ot(" mov r0,r0,asl #%d\n\n",size?16:24);
if (size<2) asr=(char *)(size?",asr #16":",asr #24");
if (type==0) ot(" sub r1,r1,r0%s\n",asr);
if (type==1) ot(" cmp r1,r0%s ;@ Defines NZCV\n",asr);
if (type==1) OpGetFlags(1,0); // Get Cmp flags
if (type==2) ot(" add r1,r1,r0%s\n",asr);
ot("\n");
if (type!=1) EaWrite(11, 1, dea,2,0x0e00);
OpEnd(sea);
return 0;
}
// --------------------- Opcodes 0x9100+ ---------------------
// Emit a Subx/Addx opcode, 1t01ddd1 zz00rsss addx.z Ds,Dd
int OpAddx(int op)
{
int use=0;
int type=0,size=0,dea=0,sea=0,mem=0;
const char *asl="";
type=(op>>14)&1;
dea =(op>> 9)&7;
size=(op>> 6)&3; if (size>=3) return 1;
sea = op&7;
mem =(op>> 3)&1;
// See if we can do this opcode:
if (EaCanRead(sea,size)==0) return 1;
if (EaCanWrite(dea)==0) return 1;
if (mem) { sea+=0x20; dea+=0x20; }
use=op&~0x0e07; // Use same opcode for Dn
if (size==0&&sea==0x27) use|=0x0007; // ___x.b -(a7)
if (size==0&&dea==0x27) use|=0x0e00; // ___x.b -(a7)
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea,dea); Cycles=4;
if(size>=2) Cycles+=4;
if(sea>=0x10) Cycles+=2;
if (mem)
{
ot(";@ Get src/dest EA vals\n");
EaCalc (0,0x000f, sea,size,1);
EaRead (0, 6, sea,size,0x000f,1);
EaCalcReadNoSE(11,0,dea,size,0x0e00);
}
else
{
ot(";@ Get src/dest reg vals\n");
EaCalcReadNoSE(-1,6,sea,size,0x0007);
EaCalcReadNoSE(11,0,dea,size,0x0e00);
if (size<2) ot(" mov r6,r6,asl #%d\n\n",size?16:24);
}
if (size<2) asl=(char *)(size?",asl #16":",asl #24");
ot(";@ Do arithmetic:\n");
GetXBit(type==0);
if (type==1 && size<2)
{
ot(";@ Make sure the carry bit will tip the balance:\n");
ot(" mvn r2,#0\n");
ot(" orr r6,r6,r2,lsr #%i\n",(size==0)?8:16);
ot("\n");
}
if (type==0) ot(" rscs r1,r6,r0%s\n",asl);
if (type==1) ot(" adcs r1,r6,r0%s\n",asl);
ot(" orr r3,r10,#0xb0000000 ;@ for old Z\n");
OpGetFlags(type==0,1,0); // subtract
if (size<2) {
ot(" movs r2,r1,lsr #%i\n", size?16:24);
ot(" orreq r10,r10,#0x40000000 ;@ add potentially missed Z\n");
}
ot(" andeq r10,r10,r3 ;@ fix Z\n");
ot("\n");
ot(";@ Save result:\n");
EaWrite(11, 1, dea,size,0x0e00,1);
ot(" ldr r6,[r7,#0x54]\n");
OpEnd(sea,dea);
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;
const char *asl="";
// 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;
if (eor && (ea>>3) == 1) return 1; // not a valid mode for eor
// See if we can do this opcode:
if (EaCanRead(ea,size)==0) return 1;
if (eor && EaCanWrite(ea)==0) return 1;
if (EaAn(ea)&&(eor||size==0)) return 1;
use=OpBase(op,size);
use&=~0x0e00; // Use 1 handler for register d0-7
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea); Cycles=4;
if(eor) {
if(ea>8) Cycles+=4;
if(size>=2) Cycles+=4;
} else {
if(size>=2) Cycles+=2;
}
ot(";@ Get EA into r11 and value into r0:\n");
EaCalcReadNoSE(eor?11:-1,0,ea,size,0x003f);
ot(";@ Get register operand into r1:\n");
EaCalcReadNoSE(-1,1,rea,size,0x0e00);
if (size<2) ot(" mov r0,r0,asl #%d\n\n",size?16:24);
if (size<2) asl=(char *)(size?",asl #16":",asl #24");
ot(";@ Do arithmetic:\n");
if (eor==0) ot(" rsbs r1,r0,r1%s\n",asl);
if (eor)
{
ot(" eor r1,r0,r1%s\n",asl);
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
}
OpGetFlags(eor==0,0); // Cmp like subtract
ot("\n");
if (eor) EaWrite(11, 1,ea,size,0x003f,1);
OpEnd(ea);
return 0;
}
// Emit a Cmpm opcode, 1011ddd1 xx001sss (rrr=Adst, xx=size extension, sss=Asrc)
int OpCmpm(int op)
{
int size=0,sea=0,dea=0,use=0;
const char *asl="";
// get size, get EAs
size=(op>>6)&3; if (size>=3) return 1;
sea=(op&7)|0x18;
dea=(op>>9)&0x3f;
use=op&~0x0e07; // Use 1 handler for all registers..
if (size==0&&sea==0x1f) use|=0x0007; // ..except (a7)+
if (size==0&&dea==0x1f) use|=0x0e00;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea); Cycles=4;
ot(";@ Get src operand into r11:\n");
EaCalc (0,0x0007, sea,size,1);
EaRead (0, 11, sea,size,0x0007,1);
ot(";@ Get dst operand into r0:\n");
EaCalcReadNoSE(-1,0,dea,size,0x0e00);
if (size<2) asl=(char *)(size?",asl #16":",asl #24");
ot(" rsbs r0,r11,r0%s\n",asl);
OpGetFlags(1,0); // Cmp like subtract
ot("\n");
OpEnd(sea);
return 0;
}
// Emit a Chk opcode, 0100ddd1 x0eeeeee (rrr=Dn, x=size extension, eeeeee=ea)
int OpChk(int op)
{
int rea=0;
int size=0,ea=0,use=0;
// Get EA and register EA
rea=(op>>9)&7;
if((op>>7)&1)
size=1; // word operation
else size=2; // long
ea=op&0x3f;
if (EaAn(ea)) return 1; // not a valid mode
if (size!=1) return 1; // 000 variant only supports word
// See if we can do this opcode:
if (EaCanRead(ea,size)==0) return 1;
use=OpBase(op,size);
use&=~0x0e00; // Use 1 handler for register d0-7
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea); Cycles=10;
ot(";@ Get value into r0:\n");
EaCalcReadNoSE(-1,0,ea,size,0x003f);
ot(";@ Get register operand into r1:\n");
EaCalcReadNoSE(-1,1,rea,size,0x0e00);
if (size<2) ot(" mov r0,r0,asl #%d\n",size?16:24);
if (size<2) ot(" mov r1,r1,asl #%d\n\n",size?16:24);
ot(";@ get flags, including undocumented ones\n");
ot(" and r3,r10,#0x80000000\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot(";@ is reg negative?\n");
ot(" bmi chktrap%.4x\n",op);
ot(";@ Do arithmetic:\n");
ot(" bic r10,r10,#0x80000000 ;@ N\n");
ot(" cmp r1,r0\n");
ot(" bgt chktrap%.4x\n",op);
ot(";@ old N remains\n");
ot(" orr r10,r10,r3\n");
OpEnd(ea);
ot("chktrap%.4x%s ;@ CHK exception:\n",op,ms?"":":");
ot(" mov r0,#6\n");
ot(" bl Exception\n");
Cycles+=40;
OpEnd(ea);
return 0;
}

View file

@ -1,517 +0,0 @@
#include "app.h"
// in/out address in r0, trashes all temp regs
static void CheckPc(void)
{
#if USE_CHECKPC_CALLBACK
#ifdef MEMHANDLERS_DIRECT_PREFIX
ot(" bl %scheckpc ;@ Call checkpc()\n", MEMHANDLERS_DIRECT_PREFIX);
#else
ot(";@ Check Memory Base+pc\n");
ot(" mov lr,pc\n");
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
ot("\n");
#endif
#endif
}
// Push 32-bit value in r1 - trashes r0-r3,r12,lr
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,r12,lr
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 - 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(" ldr r1,[r7,#0x60] ;@ Get Memory base\n");
ot(" add r0,r0,r1 ;@ Memory Base+PC\n");
ot("\n");
CheckPc();
#if EMULATE_ADDRESS_ERRORS_JUMP
ot(" mov r4,r0\n");
#else
ot(" bic r4,r0,#1\n");
#endif
}
int OpTrap(int op)
{
int use=0;
use=op&~0xf;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,0x10);
ot(" and r0,r8,#0xf ;@ Get trap number\n");
ot(" orr r0,r0,#0x20 ;@ 32+n\n");
ot(" bl Exception\n");
ot("\n");
Cycles=38; OpEnd(0x10);
return 0;
}
// --------------------- Opcodes 0x4e50+ ---------------------
int OpLink(int op)
{
int use=0,reg;
use=op&~7;
reg=op&7;
if (reg==7) use=op;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,0x10);
if(reg!=7) {
ot(";@ Get An\n");
EaCalc(11, 7, 8, 2, 1);
EaRead(11, 1, 8, 2, 7, 1);
}
ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n");
ot(" sub r0,r0,#4 ;@ A7-=4\n");
ot(" mov r8,r0 ;@ abuse r8\n");
if(reg==7) ot(" mov r1,r0\n");
ot("\n");
ot(";@ Write An to Stack\n");
MemHandler(1,2);
ot(";@ Save to An\n");
if(reg!=7)
EaWrite(11,8, 8, 2, 7, 1);
ot(";@ Get offset:\n");
EaCalc(0,0,0x3c,1); // abused r8 is ok because of imm EA
EaRead(0,0,0x3c,1,0);
ot(" add r8,r8,r0 ;@ Add offset to A7\n");
ot(" str r8,[r7,#0x3c]\n");
ot("\n");
Cycles=16;
OpEnd(0x10);
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(op,0x10);
ot(";@ Get An\n");
EaCalc(11, 0xf, 8, 2, 1);
EaRead(11, 0, 8, 2, 0xf, 1);
ot(" add r8,r0,#4 ;@ A7+=4, abuse r8\n");
ot("\n");
ot(";@ Pop An from stack:\n");
MemHandler(0,2);
ot("\n");
ot(" str r8,[r7,#0x3c] ;@ Save A7\n");
ot("\n");
ot(";@ An = value from stack:\n");
EaWrite(11, 0, 8, 2, 7, 1);
Cycles=12;
OpEnd(0x10);
return 0;
}
// --------------------- Opcodes 0x4e70+ ---------------------
// 01001110 01110ttt
int Op4E70(int op)
{
int type=0;
type=op&7; // reset/nop/stop/rte/rtd/rts/trapv/rtr
switch (type)
{
case 1: // nop
OpStart(op);
Cycles=4;
OpEnd();
return 0;
case 3: // rte
OpStart(op,0x10,0,0,1); Cycles=20;
PopSr(1);
PopPc();
ot(" ldr r1,[r7,#0x44] ;@ reload SR high\n");
SuperChange(op,1);
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO || EMULATE_HALT
ot(" ldr r1,[r7,#0x58]\n");
ot(" bic r1,r1,#0x0c ;@ clear 'not processing instruction' and 'doing addr error' bits\n");
ot(" str r1,[r7,#0x58]\n");
#endif
#if EMULATE_ADDRESS_ERRORS_JUMP
ot(" tst r4,#1 ;@ address error?\n");
ot(" bne ExceptionAddressError_r_prg_r4\n");
#endif
opend_check_interrupt = 1;
opend_check_trace = 1;
OpEnd(0x10,0);
return 0;
case 5: // rts
OpStart(op,0x10); Cycles=16;
PopPc();
#if EMULATE_ADDRESS_ERRORS_JUMP
ot(" tst r4,#1 ;@ address error?\n");
ot(" bne ExceptionAddressError_r_prg_r4\n");
#endif
OpEnd(0x10);
return 0;
case 6: // trapv
OpStart(op,0x10,0,1); Cycles=4;
ot(" tst r10,#0x10000000\n");
ot(" subne r5,r5,#%i\n",34);
ot(" movne r0,#7 ;@ TRAPV exception\n");
ot(" blne Exception\n");
opend_op_changes_cycles = 1;
OpEnd(0x10,0);
return 0;
case 7: // rtr
OpStart(op,0x10); Cycles=20;
PopSr(0);
PopPc();
#if EMULATE_ADDRESS_ERRORS_JUMP
ot(" tst r4,#1 ;@ address error?\n");
ot(" bne ExceptionAddressError_r_prg_r4\n");
#endif
OpEnd(0x10);
return 0;
default:
return 1;
}
}
// --------------------- Opcodes 0x4e80+ ---------------------
// Emit a Jsr/Jmp opcode, 01001110 1meeeeee
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,0);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,(op&0x40)?0:0x10);
ot(" ldr r11,[r7,#0x60] ;@ Get Memory base\n");
ot("\n");
EaCalc(12,0x003f,sea,0);
ot(";@ Jump - Get new PC from r12\n");
ot(" add r0,r12,r11 ;@ Memory Base + New PC\n");
ot("\n");
CheckPc();
if (!(op&0x40))
{
ot(" ldr r2,[r7,#0x3c]\n");
ot(" sub r1,r4,r11 ;@ r1 = Old PC\n");
}
#if EMULATE_ADDRESS_ERRORS_JUMP
// jsr prefetches next instruction before pushing old PC,
// according to http://pasti.fxatari.com/68kdocs/68kPrefetch.html
ot(" mov r4,r0\n");
ot(" tst r4,#1 ;@ address error?\n");
ot(" bne ExceptionAddressError_r_prg_r4\n");
#else
ot(" bic r4,r0,#1\n");
#endif
if (!(op&0x40))
{
ot(";@ Push old PC onto stack\n");
ot(" sub r0,r2,#4 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
MemHandler(1,2);
}
Cycles=(op&0x40) ? 4 : 12;
Cycles+=Ea_add_ns((op&0x40) ? g_jmp_cycle_table : g_jsr_cycle_table, sea);
OpEnd((op&0x40)?0:0x10);
return 0;
}
// --------------------- Opcodes 0x50c8+ ---------------------
// ARM version of 68000 condition codes:
static const char * const 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);
switch (cc)
{
case 0: // T
case 1: // F
break;
case 2: // hi
ot(" tst r10,#0x60000000 ;@ hi: !C && !Z\n");
ot(" beq DbraTrue\n\n");
break;
case 3: // ls
ot(" tst r10,#0x60000000 ;@ ls: C || Z\n");
ot(" bne DbraTrue\n\n");
break;
default:
ot(";@ Is the condition true?\n");
ot(" msr cpsr_flg,r10 ;@ ARM flags = 68000 flags\n");
ot(";@ If so, don't dbra\n");
ot(" b%s DbraTrue\n\n",Cond[cc]);
break;
}
if (cc!=0)
{
ot(";@ Decrement Dn.w\n");
ot(" and r1,r8,#0x0007\n");
ot(" mov r1,r1,lsl #2\n");
ot(" ldrsh r0,[r7,r1]\n");
ot(" sub r0,r0,#1\n");
ot(" strh r0,[r7,r1]\n");
ot("\n");
ot(";@ Check if Dn.w is -1\n");
ot(" cmn r0,#1\n");
#if (USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA) || EMULATE_ADDRESS_ERRORS_JUMP
ot(" beq DbraMin1\n");
ot("\n");
ot(";@ Get Branch offset:\n");
ot(" ldrsh r0,[r4]\n");
ot(" add r0,r4,r0 ;@ r0 = New PC\n");
CheckPc();
#if EMULATE_ADDRESS_ERRORS_JUMP
ot(" mov r4,r0\n");
ot(" tst r4,#1 ;@ address error?\n");
ot(" bne ExceptionAddressError_r_prg_r4\n");
#else
ot(" bic r4,r0,#1\n");
#endif
#else
ot("\n");
ot(";@ Get Branch offset:\n");
ot(" ldrnesh r0,[r4]\n");
ot(" addeq r4,r4,#2 ;@ Skip branch offset\n");
ot(" subeq r5,r5,#4 ;@ additional cycles\n");
ot(" addne r4,r4,r0 ;@ r4 = New PC\n");
ot(" bic r4,r4,#1\n"); // we do not emulate address errors
ot("\n");
#endif
Cycles=12-2;
OpEnd();
}
//if (cc==0||cc>=2)
if (op==0x50c8)
{
ot(";@ condition true:\n");
ot("DbraTrue%s\n", ms?"":":");
ot(" add r4,r4,#2 ;@ Skip branch offset\n");
ot("\n");
Cycles=12;
OpEnd();
}
#if (USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA) || EMULATE_ADDRESS_ERRORS_JUMP
if (op==0x51c8)
{
ot(";@ Dn.w is -1:\n");
ot("DbraMin1%s\n", ms?"":":");
ot(" add r4,r4,#2 ;@ Skip branch offset\n");
ot("\n");
Cycles=12+2;
OpEnd();
}
#endif
return 0;
}
// --------------------- Opcodes 0x6000+ ---------------------
// Emit a Branch opcode 0110cccc nn (cccc=condition)
int OpBranch(int op)
{
int size=0,use=0,checkpc=0;
int offset=0;
int cc=0;
const char *asr_r11="";
offset=(char)(op&0xff);
cc=(op>>8)&15;
// Special offsets:
if (offset==0) size=1;
if (offset==-1) size=2;
if (size==2) size=0; // 000 model does not support long displacement
if (size) use=op; // 16-bit or 32-bit
else use=(op&0xff00)+1; // Use same opcode for all 8-bit branches
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,size?0x10:0);
Cycles=10; // Assume branch taken
switch (cc)
{
case 0: // T
case 1: // F
break;
case 2: // hi
ot(" tst r10,#0x60000000 ;@ hi: !C && !Z\n");
ot(" bne BccDontBranch%i\n\n",8<<size);
break;
case 3: // ls
ot(" tst r10,#0x60000000 ;@ ls: C || Z\n");
ot(" beq BccDontBranch%i\n\n",8<<size);
break;
default:
ot(";@ Is the condition true?\n");
ot(" msr cpsr_flg,r10 ;@ ARM flags = 68000 flags\n");
ot(" b%s BccDontBranch%i\n\n",Cond[cc^1],8<<size);
break;
}
if (size)
{
if (size<2)
{
ot(" ldrsh r11,[r4] ;@ Fetch Branch offset\n");
}
else
{
ot(" ldrh r2,[r4] ;@ Fetch Branch offset\n");
ot(" ldrh r11,[r4,#2]\n");
ot(" orr r11,r11,r2,lsl #16\n");
}
}
else
{
ot(" mov r11,r8,asl #24 ;@ Shift 8-bit signed offset up...\n\n");
asr_r11=",asr #24";
}
ot(";@ Branch taken - Add on r0 to PC\n");
if (cc==1)
{
ot(";@ Bsr - remember old PC\n");
ot(" ldr r12,[r7,#0x60] ;@ Get Memory base\n");
ot(" ldr r2,[r7,#0x3c]\n");
ot(" sub r1,r4,r12 ;@ r1 = Old PC\n");
if (size) ot(" add r1,r1,#%d\n",1<<size);
ot("\n");
ot(";@ Push r1 onto stack\n");
ot(" sub r0,r2,#4 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
MemHandler(1,2);
ot("\n");
Cycles=18; // always 18
}
ot(" add r0,r4,r11%s ;@ r4 = New PC\n",asr_r11);
#if USE_CHECKPC_CALLBACK && USE_CHECKPC_OFFSETBITS_8
if (offset!=0 && offset!=-1) checkpc=1;
#endif
#if USE_CHECKPC_CALLBACK && USE_CHECKPC_OFFSETBITS_16
if (offset==0) checkpc=1;
#endif
#if USE_CHECKPC_CALLBACK
if (offset==-1) checkpc=1;
#endif
if (checkpc) CheckPc();
#if EMULATE_ADDRESS_ERRORS_JUMP
ot(" mov r4,r0\n");
ot(" tst r4,#1 ;@ address error?\n");
ot(" bne ExceptionAddressError_r_prg_r4\n");
#else
ot(" bic r4,r0,#1\n");
#endif
ot("\n");
OpEnd(size?0x10:0);
// since all "DontBranch" code is same for every size, output only once
if (cc>=2&&(op&0xff00)==0x6700)
{
ot("BccDontBranch%i%s\n", 8<<size, ms?"":":");
if (size) ot(" add r4,r4,#%d\n",1<<size);
Cycles+=(size==1) ? 2 : -2; // Branch not taken
OpEnd(0);
}
return 0;
}

View file

@ -1,702 +0,0 @@
#include "app.h"
// --------------------- Opcodes 0x0100+ ---------------------
// Emit a Btst (Register) opcode 0000nnn1 ttaaaaaa
int OpBtstReg(int op)
{
int use=0;
int type=0,sea=0,tea=0;
int size=0;
type=(op>>6)&3; // Btst/Bchg/Bclr/Bset
// 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,size);
use&=~0x0e00; // Use same handler for all registers
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,tea);
if(type==1||type==3) {
Cycles=8;
} else {
Cycles=type?8:4;
if(size>=2) Cycles+=2;
}
EaCalcReadNoSE(-1,11,sea,0,0x0e00);
EaCalcReadNoSE((type>0)?8:-1,0,tea,size,0x003f);
if (tea>=0x10)
ot(" and r11,r11,#7 ;@ mem - do mod 8\n"); // size always 0
else ot(" and r11,r11,#31 ;@ reg - do mod 32\n"); // size always 2
ot("\n");
ot(" mov r1,#1\n");
ot(" tst r0,r1,lsl r11 ;@ Do arithmetic\n");
ot(" bicne r10,r10,#0x40000000\n");
ot(" orreq r10,r10,#0x40000000 ;@ Get Z flag\n");
ot("\n");
if (type>0)
{
if (type==1) ot(" eor r1,r0,r1,lsl r11 ;@ Toggle bit\n");
if (type==2) ot(" bic r1,r0,r1,lsl r11 ;@ Clear bit\n");
if (type==3) ot(" orr r1,r0,r1,lsl r11 ;@ Set bit\n");
ot("\n");
EaWrite(8,1,tea,size,0x003f,0,0);
}
OpEnd(tea);
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||EaAn(tea)||tea==0x3c) return 1;
if (type>0)
{
if (EaCanWrite(tea)==0) return 1;
}
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea,tea);
ot("\n");
EaCalcReadNoSE(-1,0,sea,0,0);
ot(" mov r11,#1\n");
ot(" bic r10,r10,#0x40000000 ;@ Blank Z flag\n");
if (tea>=0x10)
ot(" and r0,r0,#7 ;@ mem - do mod 8\n"); // size always 0
else ot(" and r0,r0,#0x1F ;@ reg - do mod 32\n"); // size always 2
ot(" mov r11,r11,lsl r0 ;@ Make bit mask\n");
ot("\n");
if(type==1||type==3) {
Cycles=12;
} else {
Cycles=type?12:8;
if(size>=2) Cycles+=2;
}
EaCalcReadNoSE((type>0)?8:-1,0,tea,size,0x003f);
ot(" tst r0,r11 ;@ Do arithmetic\n");
ot(" orreq r10,r10,#0x40000000 ;@ Get Z flag\n");
ot("\n");
if (type>0)
{
if (type==1) ot(" eor r1,r0,r11 ;@ Toggle bit\n");
if (type==2) ot(" bic r1,r0,r11 ;@ Clear bit\n");
if (type==3) ot(" orr r1,r0,r11 ;@ Set bit\n");
ot("\n");
EaWrite(8, 1,tea,size,0x003f,0,0);
#if CYCLONE_FOR_GENESIS && !MEMHANDLERS_CHANGE_CYCLES
// this is a bit hacky (device handlers might modify cycles)
if (tea==0x38||tea==0x39)
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
#endif
}
OpEnd(sea,tea);
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;
// See if we can do this opcode:
if (EaCanRead (ea,size)==0||EaAn(ea)) return 1;
if (EaCanWrite(ea )==0) return 1;
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea); Cycles=size<2?4:6;
if(ea >= 0x10) Cycles*=2;
EaCalc (11,0x003f,ea,size,0,0);
if (type!=1) EaRead (11,0,ea,size,0x003f,0,0); // Don't need to read for 'clr' (or do we, for a dummy read?)
if (type==1) ot("\n");
if (type==0)
{
ot(";@ Negx:\n");
GetXBit(1);
if(size!=2) ot(" mov r0,r0,asl #%i\n",size?16:24);
ot(" rscs r1,r0,#0 ;@ do arithmetic\n");
ot(" orr r3,r10,#0xb0000000 ;@ for old Z\n");
OpGetFlags(1,1,0);
if(size!=2) {
ot(" movs r1,r1,asr #%i\n",size?16:24);
ot(" orreq r10,r10,#0x40000000 ;@ possily missed Z\n");
}
ot(" andeq r10,r10,r3 ;@ fix Z\n");
ot("\n");
}
if (type==1)
{
ot(";@ Clear:\n");
ot(" mov r1,#0\n");
ot(" mov r10,#0x40000000 ;@ NZCV=0100\n");
ot("\n");
}
if (type==2)
{
ot(";@ Neg:\n");
if(size!=2) ot(" mov r0,r0,asl #%i\n",size?16:24);
ot(" rsbs r1,r0,#0\n");
OpGetFlags(1,1);
if(size!=2) ot(" mov r1,r1,asr #%i\n",size?16:24);
ot("\n");
}
if (type==3)
{
ot(";@ Not:\n");
if(size!=2) {
ot(" mov r0,r0,asl #%i\n",size?16:24);
ot(" mvn r1,r0,asr #%i\n",size?16:24);
}
else
ot(" mvn r1,r0\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot("\n");
}
if (type==1) eawrite_check_addrerr=1;
EaWrite(11, 1,ea,size,0x003f,0,0);
OpEnd(ea);
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 (11,0x0007,ea,2,1);
EaRead (11, 0,ea,2,0x0007,1);
ot(" mov r1,r0,ror #16\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
EaWrite(11, 1,8,2,0x0007,1);
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||EaAn(sea)) return 1;
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea); Cycles=4;
EaCalc ( 0,0x003f,sea,size,1);
EaRead ( 0, 0,sea,size,0x003f,1);
ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n");
ot(" mrs r10,cpsr ;@ r10=flags\n");
ot("\n");
OpEnd(sea);
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<<size);
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
EaCalc (11,0x0007,ea,size+1,0,0);
EaRead (11, 0,ea,size+1,0x0007,0,0);
ot(" mov r0,r0,asl #%d\n",shift);
ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n");
ot(" mrs r10,cpsr ;@ r10=flags\n");
ot(" mov r1,r0,asr #%d\n",shift);
ot("\n");
EaWrite(11, 1,ea,size+1,0x0007,0,0);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x50c0+ ---------------------
// Emit a Set cc opcode, 0101cccc 11eeeeee
int OpSet(int op)
{
int cc=0,ea=0;
int size=0,use=0,changed_cycles=0;
static const char * const cond[16]=
{
"al","", "hi","ls","cc","cs","ne","eq",
"vc","vs","pl","mi","ge","lt","gt","le"
};
cc=(op>>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,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
changed_cycles=ea<8 && cc>=2;
OpStart(op,ea,0,changed_cycles); Cycles=8;
if (ea<8) Cycles=4;
if (cc)
ot(" mov r1,#0\n");
switch (cc)
{
case 0: // T
ot(" mvn r1,#0\n");
if (ea<8) Cycles+=2;
break;
case 1: // F
break;
case 2: // hi
ot(" tst r10,#0x60000000 ;@ hi: !C && !Z\n");
ot(" mvneq r1,r1\n");
if (ea<8) ot(" subeq r5,r5,#2 ;@ Extra cycles\n");
break;
case 3: // ls
ot(" tst r10,#0x60000000 ;@ ls: C || Z\n");
ot(" mvnne r1,r1\n");
if (ea<8) ot(" subne r5,r5,#2 ;@ Extra cycles\n");
break;
default:
ot(";@ Is the condition true?\n");
ot(" msr cpsr_flg,r10 ;@ ARM flags = 68000 flags\n");
ot(" mvn%s r1,r1\n",cond[cc]);
if (ea<8) ot(" sub%s r5,r5,#2 ;@ Extra cycles\n",cond[cc]);
break;
}
ot("\n");
eawrite_check_addrerr=1;
EaCalc (0,0x003f, ea,size,0,0);
EaWrite(0, 1, ea,size,0x003f,0,0);
opend_op_changes_cycles=changed_cycles;
OpEnd(ea,0);
return 0;
}
// Emit a Asr/Lsr/Roxr/Ror opcode
static int EmitAsr(int op,int type,int dir,int count,int size,int usereg)
{
char pct[8]=""; // count
int shift=32-(8<<size);
if (count>=1) sprintf(pct,"#%d",count); // Fixed count
if (usereg)
{
ot(";@ Use Dn for count:\n");
ot(" and r2,r8,#0x0e00\n");
ot(" ldr r2,[r7,r2,lsr #7]\n");
ot(" and r2,r2,#63\n");
ot("\n");
strcpy(pct,"r2");
}
else if (count<0)
{
ot(" mov r2,r8,lsr #9 ;@ Get 'n'\n");
ot(" and r2,r2,#7\n\n"); strcpy(pct,"r2");
}
// Take 2*n cycles:
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, use loworder bits for the operation:\n");
ot(" mov r0,r0,%s #%d\n",type?"lsr":"asr",32-(8<<size));
ot("\n");
}
if (type==0 && dir) ot(" adds r3,r0,#0 ;@ save old value for V flag calculation, also clear V\n");
ot(";@ Shift register:\n");
if (type==0) ot(" movs r0,r0,%s %s\n",dir?"asl":"asr",pct);
if (type==1) ot(" movs r0,r0,%s %s\n",dir?"lsl":"lsr",pct);
OpGetFlags(0,0);
if (usereg) { // store X only if count is not 0
ot(" cmp %s,#0 ;@ shifting by 0?\n",pct);
ot(" biceq r10,r10,#0x20000000 ;@ if so, clear carry\n");
ot(" strne r10,[r7,#0x4c] ;@ else Save X bit\n");
} else {
// count will never be 0 if we use immediate
ot(" str r10,[r7,#0x4c] ;@ Save X bit\n");
}
ot("\n");
if (dir==0 && size<2)
{
ot(";@ restore after right shift:\n");
ot(" movs r0,r0,lsl #%d\n",32-(8<<size));
if (type)
ot(" orrmi r10,r10,#0x80000000 ;@ Potentially missed N flag\n");
ot("\n");
}
if (type==0 && dir) {
ot(";@ calculate V flag (set if sign bit changes at anytime):\n");
ot(" mov r1,#0x80000000\n");
ot(" ands r3,r3,r1,asr %s\n", pct);
ot(" cmpne r3,r1,asr %s\n", pct);
ot(" eoreq r1,r0,r3\n"); // above check doesn't catch (-1)<<(32+), so we need this
ot(" tsteq r1,#0x80000000\n");
ot(" orrne r10,r10,#0x10000000\n");
ot("\n");
}
}
// --------------------------------------
if (type==2)
{
int wide=8<<size;
// Roxr
if(count == 1)
{
if(dir==0) {
if(size!=2) {
ot(" orr r0,r0,r0,lsr #%i\n", size?16:24);
ot(" bic r0,r0,#0x%x\n", 1<<(32-wide));
}
GetXBit(0);
ot(" movs r0,r0,rrx\n");
OpGetFlags(0,1);
} else {
ot(" ldr r3,[r7,#0x4c]\n");
ot(" movs r0,r0,lsl #1\n");
OpGetFlags(0,1);
ot(" tst r3,#0x20000000\n");
ot(" orrne r0,r0,#0x%x\n", 1<<(32-wide));
ot(" bicne r10,r10,#0x40000000 ;@ clear Z in case it got there\n");
}
ot(" bic r10,r10,#0x10000000 ;@ make suve V is clear\n");
return 0;
}
if (usereg)
{
if (size==2)
{
ot(" subs r2,r2,#33\n");
ot(" addmis r2,r2,#33 ;@ Now r2=0-%d\n",wide);
}
else
{
ot(";@ Reduce r2 until <0:\n");
ot("Reduce_%.4x%s\n",op,ms?"":":");
ot(" subs r2,r2,#%d\n",wide+1);
ot(" bpl Reduce_%.4x\n",op);
ot(" adds r2,r2,#%d ;@ Now r2=0-%d\n",wide+1,wide);
}
ot(" beq norotx_%.4x\n",op);
ot("\n");
}
if (usereg||count < 0)
{
if (dir) ot(" rsb r2,r2,#%d ;@ Reverse direction\n",wide+1);
}
else
{
if (dir) ot(" mov r2,#%d ;@ Reversed\n",wide+1-count);
else ot(" mov r2,#%d\n",count);
}
if (shift) ot(" mov r0,r0,lsr #%d ;@ Shift down\n",shift);
ot("\n");
ot(";@ First get X bit (middle):\n");
ot(" ldr r3,[r7,#0x4c]\n");
ot(" rsb r1,r2,#%d\n",wide);
ot(" and r3,r3,#0x20000000\n");
ot(" mov r3,r3,lsr #29\n");
ot(" mov r3,r3,lsl r1\n");
ot(";@ Rotate bits:\n");
ot(" orr r3,r3,r0,lsr r2 ;@ Orr right part\n");
ot(" rsbs r2,r2,#%d ;@ should also clear ARM V\n",wide+1);
ot(" orrs r0,r3,r0,lsl r2 ;@ Orr left part, set flags\n");
ot("\n");
if (shift) ot(" movs r0,r0,lsl #%d ;@ Shift up and get correct NC flags\n",shift);
OpGetFlags(0,!usereg);
if (usereg) { // store X only if count is not 0
ot(" str r10,[r7,#0x4c] ;@ if not 0, Save X bit\n");
ot(" b nozerox%.4x\n",op);
ot("norotx_%.4x%s\n",op,ms?"":":");
ot(" ldr r2,[r7,#0x4c]\n");
ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot(" and r2,r2,#0x20000000\n");
ot(" orr r10,r10,r2 ;@ C = old_X\n");
ot("nozerox%.4x%s\n",op,ms?"":":");
}
ot("\n");
}
// --------------------------------------
if (type==3)
{
// Ror
if (size<2)
{
ot(";@ Mirror value in whole 32 bits:\n");
if (size<=0) ot(" orr r0,r0,r0,lsr #8\n");
if (size<=1) ot(" orr r0,r0,r0,lsr #16\n");
ot("\n");
}
ot(";@ Rotate register:\n");
if (!dir) ot(" adds r0,r0,#0 ;@ first clear V and C\n"); // ARM does not clear C if rot count is 0
if (count<0)
{
if (dir) ot(" rsb %s,%s,#32\n",pct,pct);
ot(" movs r0,r0,ror %s\n",pct);
}
else
{
int ror=count;
if (dir) ror=32-ror;
if (ror&31) ot(" movs r0,r0,ror #%d\n",ror);
}
OpGetFlags(0,0);
if (dir)
{
ot(" bic r10,r10,#0x30000000 ;@ clear CV\n");
ot(";@ Get carry bit from bit 0:\n");
if (usereg)
{
ot(" cmp %s,#32 ;@ rotating by 0?\n",pct);
ot(" tstne r0,#1 ;@ no, check bit 0\n");
}
else
ot(" tst r0,#1\n");
ot(" orrne r10,r10,#0x20000000\n");
}
ot("\n");
}
// --------------------------------------
return 0;
}
// Emit a Asr/Lsr/Roxr/Ror opcode - 1110cccd xxuttnnn
// (ccc=count, d=direction(r,l) xx=size extension, u=use reg for count, tt=type, nnn=register Dn)
int OpAsr(int op)
{
int ea=0,use=0;
int count=0,dir=0;
int size=0,usereg=0,type=0;
count =(op>>9)&7;
dir =(op>>8)&1;
size =(op>>6)&3;
if (size>=3) return 1; // use OpAsrEa()
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 && !(count==1&&type==2)) { use|=0x0e00; count=-1; }
if (usereg) { use&=~0x0e00; count=-1; } // Use same opcode for all Dn
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea,0,count<0); Cycles=size<2?6:8;
EaCalc(11,0x0007, ea,size,1);
EaRead(11, 0, ea,size,0x0007,1);
EmitAsr(op,type,dir,count, size,usereg);
EaWrite(11, 0, ea,size,0x0007,1);
opend_op_changes_cycles = (count<0);
OpEnd(ea,0);
return 0;
}
// Asr/Lsr/Roxr/Ror 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,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea); Cycles=6; // EmitAsr() will add 2
EaCalc (11,0x003f,ea,size,1);
EaRead (11, 0,ea,size,0x003f,1);
EmitAsr(op,type,dir,1,size,0);
EaWrite(11, 0,ea,size,0x003f,1);
OpEnd(ea);
return 0;
}
int OpTas(int op, int gen_special)
{
int ea=0;
int use=0;
ea=op&0x003f;
// See if we can do this opcode:
if (EaCanWrite(ea)==0 || EaAn(ea)) return 1;
use=OpBase(op,0);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
if (!gen_special) OpStart(op,ea);
else
ot("Op%.4x_%s\n", op, ms?"":":");
Cycles=4;
if(ea>=8) Cycles+=10;
EaCalc (11,0x003f,ea,0,1);
EaRead (11, 1,ea,0,0x003f,1);
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot("\n");
#if CYCLONE_FOR_GENESIS
// the original Sega hardware ignores write-back phase (to memory only)
if (ea < 0x10 || gen_special) {
#endif
ot(" orr r1,r1,#0x80000000 ;@ set bit7\n");
EaWrite(11, 1,ea,0,0x003f,1);
#if CYCLONE_FOR_GENESIS
}
#endif
OpEnd(ea);
#if (CYCLONE_FOR_GENESIS == 2)
if (!gen_special && ea >= 0x10) {
OpTas(op, 1);
}
#endif
return 0;
}

View file

@ -1,685 +0,0 @@
#include "app.h"
// Pack our flags into r1, in SR/CCR register format
// trashes r0,r2
void OpFlagsToReg(int high)
{
ot(" ldr r0,[r7,#0x4c] ;@ X bit\n");
ot(" mov r1,r10,lsr #28 ;@ ____NZCV\n");
ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n");
ot(" tst r2,#1 ;@ 1 if C!=V\n");
ot(" eorne r1,r1,#3 ;@ ____NZVC\n");
ot("\n");
if (high) ot(" ldrb r2,[r7,#0x44] ;@ Include SR high\n");
ot(" and r0,r0,#0x20000000\n");
ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n");
if (high) ot(" orr r1,r1,r2,lsl #8\n");
ot("\n");
}
// Convert SR/CRR register in r0 to our flags
// trashes r0,r1
void OpRegToFlags(int high, int srh_reg)
{
ot(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n");
ot(" mov r2,r0,lsl #25\n");
ot(" tst r1,#1 ;@ 1 if C!=V\n");
ot(" eorne r0,r0,#3 ;@ ___XNZCV\n");
ot(" str r2,[r7,#0x4c] ;@ Store X bit\n");
ot(" mov r10,r0,lsl #28 ;@ r10=NZCV...\n");
if (high)
{
int mask=EMULATE_TRACE?0xa7:0x27;
ot(" mov r%i,r0,ror #8\n",srh_reg);
ot(" and r%i,r%i,#0x%02x ;@ only take defined bits\n",srh_reg,srh_reg,mask);
ot(" strb r%i,[r7,#0x44] ;@ Store SR high\n",srh_reg);
}
ot("\n");
}
void SuperEnd(void)
{
ot(";@ ----------\n");
ot(";@ tried execute privileged instruction in user mode\n");
ot("WrongPrivilegeMode%s\n",ms?"":":");
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO
ot(" ldr r1,[r7,#0x58]\n");
ot(" sub r4,r4,#2 ;@ last opcode wasn't executed - go back\n");
ot(" orr r1,r1,#4 ;@ set activity bit: 'not processing instruction'\n");
ot(" str r1,[r7,#0x58]\n");
#else
ot(" sub r4,r4,#2 ;@ last opcode wasn't executed - go back\n");
#endif
ot(" mov r0,#8 ;@ privilege violation\n");
ot(" bl Exception\n");
Cycles=34;
OpEnd(0);
}
// does OSP and A7 swapping if needed
// new or old SR (not the one already in [r7,#0x44]) should be passed in r11
// uses srh from srh_reg (loads if < 0), trashes r0,r11
void SuperChange(int op,int srh_reg)
{
ot(";@ A7 <-> OSP?\n");
if (srh_reg < 0) {
ot(" ldr r0,[r7,#0x44] ;@ Get other SR high\n");
srh_reg=0;
}
ot(" eor r0,r%i,r11\n",srh_reg);
ot(" tst r0,#0x20\n");
ot(" beq no_sp_swap%.4x\n",op);
ot(" ;@ swap OSP and A7:\n");
ot(" ldr r11,[r7,#0x3C] ;@ Get A7\n");
ot(" ldr r0, [r7,#0x48] ;@ Get OSP\n");
ot(" str r11,[r7,#0x48]\n");
ot(" str r0, [r7,#0x3C]\n");
ot("no_sp_swap%.4x%s\n", op, ms?"":":");
}
// --------------------- Opcodes 0x1000+ ---------------------
// Emit a Move opcode, 00xxdddd ddssssss
int OpMove(int op)
{
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 (size<1 && (movea || EaAn(sea))) return 1; // move.b An,* and movea.b * are invalid
// See if we can do this opcode:
if (EaCanRead (sea,size)==0) return 1;
if (EaCanWrite(tea )==0) return 1;
use=OpBase(op,size);
if (tea<0x38) use&=~0x0e00; // Use same handler for register ?0-7
if (tea==0x1f || tea==0x27) use|=0x0e00; // Specific handler for (a7)+ and -(a7)
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea,tea); Cycles=4;
if (movea==0)
{
EaCalcRead(-1,0,sea,size,0x003f);
ot(" adds r1,r0,#0 ;@ Defines NZ, clears CV\n");
ot(" mrs r10,cpsr ;@ r10=NZCV flags\n");
ot("\n");
}
else
{
EaCalcRead(-1,1,sea,size,0x003f);
size=2; // movea always expands to 32-bits
}
eawrite_check_addrerr=1;
#if SPLIT_MOVEL_PD
if ((tea&0x38)==0x20 && size==2) { // -(An)
EaCalc (8,0x0e00,tea,size,0,0);
ot(" mov r11,r1\n");
ot(" add r0,r8,#2\n");
EaWrite(0, 1,tea,1,0x0e00,0,0);
EaWrite(8, 11,tea,1,0x0e00,1);
}
else
#endif
{
EaCalc (0,0x0e00,tea,size,0,0);
EaWrite(0, 1,tea,size,0x0e00,0,0);
}
#if CYCLONE_FOR_GENESIS && !MEMHANDLERS_CHANGE_CYCLES
// this is a bit hacky (device handlers might modify cycles)
if (tea==0x39||((0x10<=tea&&tea<0x30)&&size>=1))
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
#endif
if((tea&0x38)==0x20) Cycles-=2; // less cycles when dest is -(An)
OpEnd(sea,tea);
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,0);
use&=~0x0e00; // Also use 1 handler for target ?0-7
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,sea,tea);
eawrite_check_addrerr=1;
EaCalc (1,0x003f,sea,0); // Lea
EaCalc (0,0x0e00,tea,2);
EaWrite(0, 1,tea,2,0x0e00);
Cycles=Ea_add_ns(g_lea_cycle_table,sea);
OpEnd(sea,tea);
return 0;
}
// --------------------- Opcodes 0x40c0+ ---------------------
// Move SR opcode, 01000tt0 11aaaaaa move SR
int OpMoveSr(int op)
{
int type=0,ea=0;
int use=0,size=1;
type=(op>>9)&3; // from SR, from CCR, to CCR, to SR
ea=op&0x3f;
if(EaAn(ea)) return 1; // can't use An regs
switch(type)
{
case 0:
if (EaCanWrite(ea)==0) return 1; // See if we can do this opcode:
break;
case 1:
return 1; // no such op in 68000
case 2: case 3:
if (EaCanRead(ea,size)==0) return 1; // See if we can do this opcode:
break;
}
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
// 68000 model allows reading whole SR in user mode (but newer models don't)
OpStart(op,ea,0,0,type==3);
Cycles=12;
if (type==0) Cycles=(ea>=8)?8:6;
if (type==0 || type==1)
{
eawrite_check_addrerr=1;
OpFlagsToReg(type==0);
EaCalc (0,0x003f,ea,size,0,0);
EaWrite(0, 1,ea,size,0x003f,0,0);
}
if (type==2 || type==3)
{
EaCalcReadNoSE(-1,0,ea,size,0x003f);
OpRegToFlags(type==3,1);
if (type==3) {
SuperChange(op,1);
opend_check_interrupt = 1;
opend_check_trace = 1;
OpEnd(ea);
return 0;
}
}
OpEnd(ea);
return 0;
}
// Ori/Andi/Eori $nnnn,sr 0000t0t0 01111100
int OpArithSr(int op)
{
int type=0,ea=0;
int use=0,size=0;
int sr_mask=EMULATE_TRACE?0xa7:0x27;
type=(op>>9)&5; if (type==4) return 1;
size=(op>>6)&1; // ccr or sr?
ea=0x3c;
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea,0,0,size!=0); Cycles=16;
EaCalcRead(-1,0,ea,size,0x003f);
ot(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n");
ot(" tst r1,#1 ;@ 1 if C!=V\n");
ot(" eorne r0,r0,#3 ;@ ___XNZCV\n");
ot(" ldr r2,[r7,#0x4c] ;@ Load old X bit\n");
// note: old srh is already in r11 (done by OpStart)
if (type==0) {
ot(" orr r10,r10,r0,lsl #28\n");
ot(" orr r2,r2,r0,lsl #25 ;@ X bit\n");
if (size!=0) {
ot(" orr r1,r11,r0,lsr #8\n");
ot(" and r1,r1,#0x%02x ;@ mask-out unused bits\n",sr_mask);
}
}
if (type==1) {
ot(" and r10,r10,r0,lsl #28\n");
ot(" and r2,r2,r0,lsl #25 ;@ X bit\n");
if (size!=0)
ot(" and r1,r11,r0,lsr #8\n");
}
if (type==5) {
ot(" eor r10,r10,r0,lsl #28\n");
ot(" eor r2,r2,r0,lsl #25 ;@ X bit\n");
if (size!=0) {
ot(" eor r1,r11,r0,lsr #8\n");
ot(" and r1,r1,#0x%02x ;@ mask-out unused bits\n",sr_mask);
}
}
ot(" str r2,[r7,#0x4c] ;@ Save X bit\n");
if (size!=0)
ot(" strb r1,[r7,#0x44]\n");
ot("\n");
// we can't enter supervisor mode, nor unmask irqs just by using OR
if (size!=0 && type!=0) {
SuperChange(op,1);
ot("\n");
opend_check_interrupt = 1;
}
// also can't set trace bit with AND
if (size!=0 && type!=1)
opend_check_trace = 1;
OpEnd(ea);
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,0);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea);
ot(" ldr r11,[r7,#0x3c]\n");
EaCalc (1,0x003f, ea,0);
ot("\n");
ot(" sub r0,r11,#4 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
ot("\n");
MemHandler(1,2); // Write 32-bit
ot("\n");
Cycles=6+Ea_add_ns(g_pea_cycle_table,ea);
OpEnd(ea);
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; // word, long
ea=op&0x003f;
dir=(op>>10)&1; // Direction (1==ea2reg)
if (dir) {
if (ea<0x10 || ea>0x3b || (ea&0x38)==0x20) return 1; // Invalid EA
} else {
if (ea<0x10 || ea>0x39 || (ea&0x38)==0x18) return 1;
}
if ((ea&0x38)==0x18 || (ea&0x38)==0x20) change=1;
if ((ea&0x38)==0x20) decr=1; // -(An), bitfield is decr
cea=ea; if (change) cea=0x10;
use=OpBase(op,size);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op,ea,0,1);
ot(" ldrh r11,[r4],#2 ;@ r11=register mask\n");
ot("\n");
ot(";@ Get the address into r6:\n");
EaCalc(6,0x003f,cea,size);
#if !MEMHANDLERS_NEED_PREV_PC
// must save PC, need a spare register
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
#endif
ot(";@ r4=Register Index*4:\n");
if (decr) ot(" mov r4,#0x40 ;@ order reversed for -(An)\n");
else ot(" mov r4,#-4\n");
ot("\n");
ot(" tst r11,r11\n"); // sanity check
ot(" beq NoRegs%.4x\n",op);
#if EMULATE_ADDRESS_ERRORS_IO
ot("\n");
ot(" tst r6,#1 ;@ address error?\n");
ot(" movne r0,r6\n");
ot(" bne ExceptionAddressError_%c_data\n",dir?'r':'w');
#endif
ot("\n");
ot("Movemloop%.4x%s\n",op, ms?"":":");
ot(" add r4,r4,#%d ;@ r4=Next Register\n",decr?-4:4);
ot(" movs r11,r11,lsr #1\n");
ot(" bcc Movemloop%.4x\n",op);
ot("\n");
if (decr) ot(" sub r6,r6,#%d ;@ Pre-decrement address\n",1<<size);
if (dir)
{
ot(" ;@ Copy memory to register:\n",1<<size);
earead_check_addrerr=0; // already checked
EaRead (6,0,ea,size,0x003f);
ot(" str r0,[r7,r4] ;@ Save value into Dn/An\n");
}
else
{
ot(" ;@ Copy register to memory:\n",1<<size);
ot(" ldr r1,[r7,r4] ;@ Load value from Dn/An\n");
#if SPLIT_MOVEL_PD
if (decr && size==2) { // -(An)
ot(" add r0,r6,#2\n");
EaWrite(0,1,ea,1,0x003f,0,0);
ot(" ldr r1,[r7,r4] ;@ Load value from Dn/An\n");
ot(" mov r0,r6\n");
EaWrite(0,1,ea,1,0x003f,1);
}
else
#endif
{
EaWrite(6,1,ea,size,0x003f);
}
}
if (decr==0) ot(" add r6,r6,#%d ;@ Post-increment address\n",1<<size);
ot(" sub r5,r5,#%d ;@ Take some cycles\n",2<<size);
ot(" tst r11,r11\n");
ot(" bne Movemloop%.4x\n",op);
ot("\n");
if (change)
{
ot(";@ Write back address:\n");
EaCalc (0,0x0007,8|(ea&7),2);
EaWrite(0, 6,8|(ea&7),2,0x0007);
}
ot("NoRegs%.4x%s\n",op, ms?"":":");
ot(" ldr r4,[r7,#0x40]\n");
ot(" ldr r6,[r7,#0x54] ;@ restore Opcode Jump table\n");
ot("\n");
if(dir) { // er
if (ea==0x3a) Cycles=16; // ($nn,PC)
else if (ea==0x3b) Cycles=18; // ($nn,pc,Rn)
else Cycles=12;
} else {
Cycles=8;
}
Cycles+=Ea_add_ns(g_movem_cycle_table,ea);
opend_op_changes_cycles = 1;
OpEnd(ea);
ot("\n");
return 0;
}
// --------------------- Opcodes 0x4e60+ ---------------------
// Emit a Move USP opcode, 01001110 0110dnnn move An to/from USP
int OpMoveUsp(int op)
{
int use=0,dir=0;
dir=(op>>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,0,0,0,1); Cycles=4;
if (dir)
{
eawrite_check_addrerr=1;
ot(" ldr r1,[r7,#0x48] ;@ Get from USP\n\n");
EaCalc (0,0x000f,8,2,1);
EaWrite(0, 1,8,2,0x000f,1);
}
else
{
EaCalc (0,0x000f,8,2,1);
EaRead (0, 0,8,2,0x000f,1);
ot(" str r0,[r7,#0x48] ;@ Put in USP\n\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 r10,cpsr ;@ r10=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 r2,r8,#0x0e00 ;@ Find T register\n");
ot(" and r3,r8,#0x000f ;@ Find S register\n");
if (type==0x48) ot(" orr r2,r2,#0x1000 ;@ T is an address register\n");
ot("\n");
ot(" ldr r0,[r7,r2,lsr #7] ;@ Get T\n");
ot(" ldr r1,[r7,r3,lsl #2] ;@ Get S\n");
ot("\n");
ot(" str r0,[r7,r3,lsl #2] ;@ T->S\n");
ot(" str r1,[r7,r2,lsr #7] ;@ S->T\n");
ot("\n");
OpEnd();
return 0;
}
// ------------------------- movep -------------------------------
// 0000ddd1 0z001sss
// 0000sss1 1z001ddd (to mem)
int OpMovep(int op)
{
int ea=0,rea=0;
int size=1,use=0,dir,aadd=0;
use=op&0xf1f8;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler (for all dests, srcs)
// Get EA
ea = (op&0x0007)|0x28;
rea= (op&0x0e00)>>9;
dir = (op>>7)&1;
// Find size extension
if(op&0x0040) size=2;
OpStart(op,ea);
if(dir) // reg to mem
{
EaCalcReadNoSE(-1,11,rea,size,0x0e00);
EaCalc(8,0x000f,ea,size);
if(size==2) { // if operand is long
ot(" mov r1,r11,lsr #24 ;@ first byte\n");
EaWrite(8,1,ea,0,0x000f); // store first byte
ot(" add r0,r8,#%i\n",(aadd+=2));
ot(" mov r1,r11,lsr #16 ;@ second byte\n");
EaWrite(0,1,ea,0,0x000f); // store second byte
ot(" add r0,r8,#%i\n",(aadd+=2));
} else {
ot(" mov r0,r8\n");
}
ot(" mov r1,r11,lsr #8 ;@ first or third byte\n");
EaWrite(0,1,ea,0,0x000f);
ot(" add r0,r8,#%i\n",(aadd+=2));
ot(" and r1,r11,#0xff\n");
EaWrite(0,1,ea,0,0x000f);
}
else // mem to reg
{
EaCalc(6,0x000f,ea,size,1);
EaRead(6,11,ea,0,0x000f,1); // read first byte
ot(" add r0,r6,#2\n");
EaRead(0,1,ea,0,0x000f,1); // read second byte
if(size==2) { // if operand is long
ot(" orr r11,r11,r1,lsr #8 ;@ second byte\n");
ot(" add r0,r6,#4\n");
EaRead(0,1,ea,0,0x000f,1);
ot(" orr r11,r11,r1,lsr #16 ;@ third byte\n");
ot(" add r0,r6,#6\n");
EaRead(0,1,ea,0,0x000f,1);
ot(" orr r1,r11,r1,lsr #24 ;@ fourth byte\n");
} else {
ot(" orr r1,r11,r1,lsr #8 ;@ second byte\n");
}
// store the result
EaCalc(0,0x0e00,rea,size,1);
EaWrite(0,1,rea,size,0x0e00,1);
ot(" ldr r6,[r7,#0x54]\n");
}
Cycles=(size==2)?24:16;
OpEnd(ea);
return 0;
}
// Emit a Stop/Reset opcodes, 01001110 011100t0 imm
int OpStopReset(int op)
{
int type=(op>>1)&1; // stop/reset
OpStart(op,0,0,0,1);
if(type) {
// copy immediate to SR, stop the CPU and eat all remaining cycles.
ot(" ldrh r0,[r4],#2 ;@ Fetch the immediate\n");
OpRegToFlags(1);
SuperChange(op,0);
ot("\n");
ot(" ldr r0,[r7,#0x58]\n");
ot(" mov r5,#0 ;@ eat cycles\n");
ot(" orr r0,r0,#1 ;@ stopped\n");
ot(" str r0,[r7,#0x58]\n");
ot("\n");
Cycles = 4;
ot("\n");
}
else
{
Cycles = 132;
#if USE_RESET_CALLBACK
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
ot(" mov r1,r10,lsr #28\n");
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
ot(" ldr r11,[r7,#0x90] ;@ ResetCallback\n");
ot(" tst r11,r11\n");
ot(" movne lr,pc\n");
ot(" bxne r11 ;@ call ResetCallback if it is defined\n");
ot(" ldrb r10,[r7,#0x46] ;@ r10 = Load Flags (NZCV)\n");
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
ot(" mov r10,r10,lsl #28\n");
ot("\n");
#endif
}
OpEnd();
return 0;
}

View file

@ -1,111 +0,0 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#ifndef CONFIG_FILE
#define CONFIG_FILE "config.h"
#endif
#include CONFIG_FILE
// Disa.c
#include "Disa/Disa.h"
// Ea.cpp
extern int earead_check_addrerr;
extern int eawrite_check_addrerr;
extern int g_jmp_cycle_table[];
extern int g_jsr_cycle_table[];
extern int g_lea_cycle_table[];
extern int g_pea_cycle_table[];
extern int g_movem_cycle_table[];
int Ea_add_ns(int *tab, int ea); // add nonstandard EA cycles
int EaCalc(int a,int mask,int ea,int size,int top=0,int sign_extend=1); // 6
int EaRead(int a,int v,int ea,int size,int mask,int top=0,int sign_extend=1); // 7
int EaCalcRead(int r_ea,int r,int ea,int size,int mask,int sign_extend=1); // 6
int EaCalcReadNoSE(int r_ea,int r,int ea,int size,int mask);
int EaCanRead(int ea,int size);
int EaWrite(int a,int v,int ea,int size,int mask,int top=0,int sign_extend_ea=1);
int EaCanWrite(int ea);
int EaAn(int ea);
// Main.cpp
extern int *CyJump; // Jump table
extern int ms; // If non-zero, output in Microsoft ARMASM format
extern const char * const Narm[4]; // Normal ARM Extensions for operand sizes 0,1,2
extern const char * const Sarm[4]; // Sign-extend ARM Extensions for operand sizes 0,1,2
extern int Cycles; // Current cycles for opcode
extern int pc_dirty; // something changed PC during processing
extern int arm_op_count; // for stats
void ot(const char *format, ...);
void ltorg();
int MemHandler(int type,int size,int addrreg=0,int need_addrerr_check=1);
void FlushPC(void);
// OpAny.cpp
extern int g_op;
extern int opend_op_changes_cycles, opend_check_interrupt, opend_check_trace;
int OpGetFlags(int subtract,int xbit,int sprecialz=0);
void OpUse(int op,int use);
void OpStart(int op,int sea=0,int tea=0,int op_changes_cycles=0,int supervisor_check=0);
void OpEnd(int sea=0,int tea=0);
int OpBase(int op,int size,int sepa=0);
void OpAny(int op);
//----------------------
// 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 OpNbcd(int op);
int OpAritha(int op);
int OpAddx(int op);
int OpCmpEor(int op);
int OpCmpm(int op);
int OpChk(int op);
int GetXBit(int subtract);
// OpBranch.cpp
void OpPush32();
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);
int OpTas(int op, int gen_special=0);
// OpMove.cpp
int OpMove(int op);
int OpLea(int op);
void OpFlagsToReg(int high);
void OpRegToFlags(int high,int srh_reg=0);
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);
int OpMovep(int op);
int OpStopReset(int op);
void SuperEnd(void);
void SuperChange(int op,int srh_reg=-1);

View file

@ -1,182 +0,0 @@
/**
* Cyclone 68000 configuration file
**/
/*
* If this option is enabled, Microsoft ARMASM compatible output is generated
* (output file - Cyclone.asm). Otherwise GNU as syntax is used (Cyclone.s).
*/
#define USE_MS_SYNTAX 0
/*
* Enable this option if you are going to use Cyclone to emulate Genesis /
* Mega Drive system. As VDP chip in these systems had control of the bus,
* several instructions were acting differently, for example TAS did'n have
* the write-back phase. That will be emulated, if this option is enabled.
*/
#define CYCLONE_FOR_GENESIS 0
/*
* This option compresses Cyclone's jumptable. Because of this the executable
* will be smaller and load slightly faster and less relocations will be needed.
* This also fixes the crash problem with 0xfffe and 0xffff opcodes.
* Warning: if you enable this, you MUST call CycloneInit() before calling
* CycloneRun(), or else it will crash.
*/
#define COMPRESS_JUMPTABLE 1
/*
* Address mask for memory hadlers. The bits set will be masked out of address
* parameter, which is passed to r/w memory handlers.
* Using 0xff000000 means that only 24 least significant bits should be used.
* Set to 0 if you want to mask unused address bits in the memory handlers yourself.
*/
#define MEMHANDLERS_ADDR_MASK 0
/*
* Cyclone keeps the 4 least significant bits of SR, PC+membase and it's cycle
* counter in ARM registers instead of the context for performance reasons. If you for
* any reason need to access them in your memory handlers, enable the options below,
* otherwise disable them to improve performance.
*
* MEMHANDLERS_NEED_PC updates .pc context field with PC value effective at the time
* when memhandler was called (opcode address + 2-10 bytes).
* MEMHANDLERS_NEED_PREV_PC updates .prev_pc context field to currently executed
* opcode address + 2.
* Note that .pc and .prev_pc values are always real pointers to memory, so you must
* subtract .membase to get M68k PC value.
*
* Warning: updating PC in memhandlers is dangerous, as Cyclone may internally
* increment the PC before fetching the next instruction and continue executing
* at wrong location. It's better to wait until Cyclone CycloneRun() finishes.
*
* Warning: if you enable MEMHANDLERS_CHANGE_CYCLES, you must also enable
* MEMHANDLERS_NEED_CYCLES, or else Cyclone will keep reloading the same cycle
* count and this will screw timing (if not cause a deadlock).
*/
#define MEMHANDLERS_NEED_PC 0
#define MEMHANDLERS_NEED_PREV_PC 0
#define MEMHANDLERS_NEED_FLAGS 0
#define MEMHANDLERS_NEED_CYCLES 0
#define MEMHANDLERS_CHANGE_PC 0
#define MEMHANDLERS_CHANGE_FLAGS 0
#define MEMHANDLERS_CHANGE_CYCLES 0
/*
* If the following macro is defined, Cyclone no longer calls read*, write*,
* fetch* and checkpc from it's context, it calls these functions directly
* instead, prefixed with prefix selected below. For example, if
* MEMHANDLERS_DIRECT_PREFIX is set to cyclone_, it will call cyclone_read8
* on byte reads.
* This is to avoid indirect jumps, which are slower. It also saves one ARM
* instruction.
*/
/* MEMHANDLERS_DIRECT_PREFIX "cyclone_" */
/*
* If enabled, Cyclone will call .IrqCallback routine from it's context whenever it
* acknowledges an IRQ. IRQ level (.irq) is not cleared automatically, do this in your
* handler if needed.
* This function must either return vector number to use for interrupt exception,
* CYCLONE_INT_ACK_AUTOVECTOR to use autovector (this is the most common case), or
* CYCLONE_INT_ACK_SPURIOUS (least common case).
* If disabled, it simply uses appropriate autovector, clears the IRQ level and
* continues execution.
*/
#define USE_INT_ACK_CALLBACK 0
/*
* Enable this if you need old PC, flags or cycles;
* or you change cycles in your IrqCallback function.
*/
#define INT_ACK_NEEDS_STUFF 0
#define INT_ACK_CHANGES_CYCLES 0
/*
* If enabled, .ResetCallback is called from the context, whenever RESET opcode is
* encountered. All context members are valid and can be changed.
* If disabled, RESET opcode acts as an NOP.
*/
#define USE_RESET_CALLBACK 0
/*
* If enabled, UnrecognizedCallback is called if an invalid opcode is
* encountered. All context members are valid and can be changed. The handler
* should return zero if you want Cyclone to gererate "Illegal Instruction"
* exception after this, or nonzero if not. In the later case you should change
* the PC by yourself, or else Cyclone will keep executing that opcode all over
* again.
* If disabled, "Illegal Instruction" exception is generated and execution is
* continued.
*/
#define USE_UNRECOGNIZED_CALLBACK 0
/*
* This option will also call UnrecognizedCallback for a-line and f-line
* (0xa*** and 0xf***) opcodes the same way as described above, only appropriate
* exceptions will be generated.
*/
#define USE_AFLINE_CALLBACK 0
/*
* This makes Cyclone to call checkpc from it's context whenever it changes the PC
* by a large value. It takes and should return the PC value in PC+membase form.
* The flags and cycle counter are not valid in this function.
*/
#define USE_CHECKPC_CALLBACK 1
/*
* This determines if checkpc() should be called after jumps when 8 and 16 bit
* displacement values were used.
*/
#define USE_CHECKPC_OFFSETBITS_16 1
#define USE_CHECKPC_OFFSETBITS_8 0
/*
* Call checkpc() after DBcc jumps (which use 16bit displacement). Cyclone prior to
* 0.0087 never did that.
*/
#define USE_CHECKPC_DBRA 0
/*
* When this option is enabled Cyclone will do two word writes instead of one
* long write when handling MOVE.L or MOVEM.L with pre-decrementing destination,
* as described in Bart Trzynadlowski's doc (http://www.trzy.org/files/68knotes.txt).
* Enable this if you are emulating a 16 bit system.
*/
#define SPLIT_MOVEL_PD 1
/*
* Enable emulation of trace mode. Shouldn't cause any performance decrease, so it
* should be safe to keep this ON.
*/
#define EMULATE_TRACE 1
/*
* If enabled, address error exception will be generated if 68k code jumps to an
* odd address. Causes very small performance hit (2 ARM instructions for every
* emulated jump/return/exception in normal case).
* Note: checkpc() must not clear least significant bit of rebased address
* for this to work, as checks are performed after calling checkpc().
*/
#define EMULATE_ADDRESS_ERRORS_JUMP 1
/*
* If enabled, address error exception will be generated if 68k code tries to
* access a word or longword at an odd address. The performance cost is also 2 ARM
* instructions per access (for address error checks).
*/
#define EMULATE_ADDRESS_ERRORS_IO 0
/*
* If an address error happens during another address error processing,
* the processor halts until it is reset (catastrophic system failure, as the manual
* states). This option enables halt emulation.
* Note that this might be not desired if it is known that emulated system should
* never reach this state.
*/
#define EMULATE_HALT 0

View file

@ -1,44 +0,0 @@
/**
* Cyclone 68000 configuration file
*
* Used for mamegp2x Cyclone build.
* See config.h in Cyclone directory for option descriptions.
**/
#define USE_MS_SYNTAX 0
#define CYCLONE_FOR_GENESIS 0
#define COMPRESS_JUMPTABLE 1
#define MEMHANDLERS_ADDR_MASK 0xff000000
#define MEMHANDLERS_NEED_PC 1
#define MEMHANDLERS_NEED_PREV_PC 1
#define MEMHANDLERS_NEED_FLAGS 0
#define MEMHANDLERS_NEED_CYCLES 1
#define MEMHANDLERS_CHANGE_PC 0
#define MEMHANDLERS_CHANGE_FLAGS 0
#define MEMHANDLERS_CHANGE_CYCLES 1
#define USE_INT_ACK_CALLBACK 1
#define INT_ACK_NEEDS_STUFF 0
#define INT_ACK_CHANGES_CYCLES 0
#define USE_RESET_CALLBACK 0
#define USE_UNRECOGNIZED_CALLBACK 0
#define USE_AFLINE_CALLBACK 0
#define USE_CHECKPC_CALLBACK 1
#define USE_CHECKPC_OFFSETBITS_16 1
#define USE_CHECKPC_OFFSETBITS_8 0
#define USE_CHECKPC_DBRA 0
#define SPLIT_MOVEL_PD 1
#define EMULATE_TRACE 1
#define EMULATE_ADDRESS_ERRORS_JUMP 1
#define EMULATE_ADDRESS_ERRORS_IO 0
#define EMULATE_HALT 0

View file

@ -1,44 +0,0 @@
/**
* Cyclone 68000 configuration file
*
* Used for UAE4ALL Cyclone build.
* See config.h in Cyclone directory for option descriptions.
**/
#define USE_MS_SYNTAX 0
#define CYCLONE_FOR_GENESIS 0
#define COMPRESS_JUMPTABLE 1
#define MEMHANDLERS_ADDR_MASK 0
#define MEMHANDLERS_NEED_PC 1
#define MEMHANDLERS_NEED_PREV_PC 0
#define MEMHANDLERS_NEED_FLAGS 0
#define MEMHANDLERS_NEED_CYCLES 1
#define MEMHANDLERS_CHANGE_PC 0
#define MEMHANDLERS_CHANGE_FLAGS 0
#define MEMHANDLERS_CHANGE_CYCLES 1
#define USE_INT_ACK_CALLBACK 1
#define INT_ACK_NEEDS_STUFF 0
#define INT_ACK_CHANGES_CYCLES 0
#define USE_RESET_CALLBACK 0
#define USE_UNRECOGNIZED_CALLBACK 1
#define USE_AFLINE_CALLBACK 1
#define USE_CHECKPC_CALLBACK 1
#define USE_CHECKPC_OFFSETBITS_16 1
#define USE_CHECKPC_OFFSETBITS_8 0
#define USE_CHECKPC_DBRA 0
#define SPLIT_MOVEL_PD 1
#define EMULATE_TRACE 1
#define EMULATE_ADDRESS_ERRORS_JUMP 1
#define EMULATE_ADDRESS_ERRORS_IO 1
#define EMULATE_HALT 0

Binary file not shown.

View file

@ -1,150 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned long _dontcare1[4];
char signature[4]; // 'EPOC'
unsigned long iCpu; // 0x1000 = X86, 0x2000 = ARM, 0x4000 = M*Core
unsigned long iCheckSumCode; // sum of all 32 bit words in .text
unsigned long _dontcare3[5];
unsigned long iCodeSize; // size of code, import address table, constant data and export dir |+30
unsigned long _dontcare4[12];
unsigned long iCodeOffset; // file offset to code section |+64
unsigned long _dontcare5[2];
unsigned long iCodeRelocOffset; // relocations for code and const |+70
unsigned long iDataRelocOffset; // relocations for data
unsigned long iPriority; // priority of this process (EPriorityHigh=450)
} E32ImageHeader;
typedef struct {
unsigned long iSize; // size of this relocation section
unsigned long iNumberOfRelocs; // number of relocations in this section
} E32RelocSection;
typedef struct {
unsigned long base;
unsigned long size;
} reloc_page_header;
// E32Image relocation section consists of a number of pages
// every page has 8 byte header and a number or 16bit relocation entries
// entry format:
// 0x3000 | <12bit_reloc_offset>
//
// if we have page_header.base == 0x1000 and a reloc entry 0x3110,
// it means that 32bit value at offset 0x1110 of .text section
// is relocatable
int main(int argc, char *argv[])
{
FILE *f = 0;
unsigned char pattern[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56 };
unsigned char *buff, *p;
unsigned long patt_offset; // pattern offset in .text section
unsigned long size = 0, i, symbols, insert_pos, *handler;
unsigned short reloc_entry;
E32ImageHeader *header;
E32RelocSection *reloc_section;
reloc_page_header *reloc_page;
if(argc != 3) {
printf("usage: %s <e32_exe> <nsymbols>\n\n", argv[0]);
printf("note: this was written to fix a problem caused by as v.2.9-psion-98r2 and shouldn't be used for anything else.\n", argv[0]);
return 1;
}
f = fopen(argv[1], "rb+");
if(!f) {
printf("%s: couldn't open %s\n", argv[0], argv[1]);
return 2;
}
symbols = atoi(argv[2]);
// read the file
fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET);
buff = (unsigned char *) malloc(size);
fread(buff,1,size,f);
header = (E32ImageHeader *) buff;
if(strncmp(header->signature, "EPOC", 4) || header->iCpu != 0x2000) {
printf("%s: not a E32 executable image for ARM target.\n", argv[0]);
fclose(f);
free(buff);
return 2;
}
// find the pattern
for(i = 0; i < size-8; i++)
if(memcmp(buff+i, pattern, 8) == 0) break;
if(i == size-8 || i < 4) {
printf("%s: failed to find the pattern.\n", argv[0]);
fclose(f);
free(buff);
return 3;
}
patt_offset = i - header->iCodeOffset;
// find suitable reloc section
reloc_section = (E32RelocSection *) (buff + header->iCodeRelocOffset);
for(i = 0, p = buff+header->iCodeRelocOffset+8; i < reloc_section->iSize; ) {
reloc_page = (reloc_page_header *) p;
if(patt_offset - reloc_page->base >= 0 && patt_offset - reloc_page->base < 0x1000 - symbols*4) break;
i += reloc_page->size;
p += reloc_page->size;
}
if(i >= reloc_section->iSize) {
printf("%s: suitable reloc section not found.\n", argv[0]);
fclose(f);
free(buff);
return 4;
}
// now find the insert pos and update everything
insert_pos = p + reloc_page->size - buff;
reloc_page->size += symbols*2;
reloc_section->iSize += symbols*2;
reloc_section->iNumberOfRelocs += symbols;
header->iDataRelocOffset += symbols*2; // data reloc section is now also pushed a little
header->iPriority = 450; // let's boost our priority :)
// replace the placeholders themselves
handler = (unsigned long *) (buff + patt_offset + header->iCodeOffset - 4);
for(i = 1; i <= symbols; i++)
*(handler+i) = *handler;
// recalculate checksum
header->iCheckSumCode = 0;
for(i = 0, p = buff+header->iCodeOffset; i < header->iCodeSize; i+=4, p+=4)
header->iCheckSumCode += *(unsigned long *) p;
// check for possible padding
if(!*(buff+insert_pos-1)) insert_pos -= 2;
// write all this joy
fseek(f,0,SEEK_SET);
fwrite(buff, 1, insert_pos, f);
// write new reloc entries
for(i = 0; i < symbols; i++) {
handler++;
reloc_entry = ((unsigned char *) handler - buff - reloc_page->base - header->iCodeOffset) | 0x3000;
fwrite(&reloc_entry, 1, 2, f);
}
// write the remaining data
fwrite(buff+insert_pos, 1, size-insert_pos, f);
// done at last!
fclose(f);
free(buff);
return 0;
}

View file

@ -1,133 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#define symbols 2
int main(int argc, char *argv[])
{
FILE *f = 0;
unsigned char pattern[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56 };
unsigned char *buff, *p;
unsigned long patt_offset; // pattern offset in .text section
unsigned long size = 0, i, insert_pos, *handler;//, symbols;
unsigned short reloc_entry;
IMAGE_BASE_RELOCATION *reloc_page;
IMAGE_DOS_HEADER *dos_header;
IMAGE_FILE_HEADER *file_header;
IMAGE_SECTION_HEADER *sect_header, *relocsect_header = 0, *codesect_header = 0;
if(argc != 2) {
printf("usage: %s <pe_exe_or_app_before_petran>\n\n", argv[0]);
printf("note: this was written to fix a problem related to Cyclone and as v.2.9-psion-98r2 and shouldn't be used for anything else. See Readme.\n", argv[0]);
return 1;
}
f = fopen(argv[1], "rb+");
if(!f) {
printf("%s: couldn't open %s\n", argv[0], argv[1]);
return 2;
}
//symbols = atoi(argv[2]);
// read the file
fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET);
buff = (unsigned char *) malloc(size);
fread(buff,1,size,f);
dos_header = (IMAGE_DOS_HEADER *) buff;
file_header= (IMAGE_FILE_HEADER *) (buff+dos_header->e_lfanew+4);
sect_header= (IMAGE_SECTION_HEADER *) (buff+dos_header->e_lfanew+4+sizeof(IMAGE_FILE_HEADER)+sizeof(IMAGE_OPTIONAL_HEADER32));
if(size < 0x500 || dos_header->e_magic != IMAGE_DOS_SIGNATURE ||
*(DWORD *)(buff+dos_header->e_lfanew) != IMAGE_NT_SIGNATURE || file_header->Machine != 0x0A00) {
printf("%s: not a PE executable image for ARM target.\n", argv[0]);
fclose(f);
free(buff);
return 2;
}
// scan all sections for data and reloc sections
for(i = 0; i < file_header->NumberOfSections; i++, sect_header++) {
if(strncmp(sect_header->Name, ".text", 5) == 0) codesect_header = sect_header;
else if(strncmp(sect_header->Name, ".reloc", 6) == 0) relocsect_header = sect_header;
}
if(!codesect_header || !relocsect_header) {
printf("%s: failed to find reloc and/or data section.\n", argv[0]);
fclose(f);
free(buff);
return 3;
}
if(relocsect_header != sect_header-1) {
printf("%s: bug: reloc section is not last, this is unexpected and not supported.\n", argv[0]);
fclose(f);
free(buff);
return 4;
}
// find the pattern
for(i = codesect_header->PointerToRawData; i < size-8; i+=2)
if(memcmp(buff+i, pattern, 8) == 0) break;
if(i == size-8 || i < 4) {
printf("%s: failed to find the pattern.\n", argv[0]);
fclose(f);
free(buff);
return 5;
}
// calculate pattern offset in RVA (relative virtual address)
patt_offset = i - codesect_header->PointerToRawData + codesect_header->VirtualAddress;
// replace the placeholders themselves
handler = (unsigned long *) (buff + i - 4);
for(i = 1; i <= symbols; i++)
*(handler+i) = *handler;
// find suitable reloc section
for(i = 0, p = buff+relocsect_header->PointerToRawData; i < relocsect_header->SizeOfRawData; ) {
reloc_page = (IMAGE_BASE_RELOCATION *) p;
if(patt_offset - reloc_page->VirtualAddress >= 0 && patt_offset - reloc_page->VirtualAddress < 0x1000 - symbols*2) break;
i += reloc_page->SizeOfBlock;
p += reloc_page->SizeOfBlock;
}
if(i >= relocsect_header->SizeOfRawData) {
printf("%s: suitable reloc section not found.\n", argv[0]);
fclose(f);
free(buff);
return 6;
}
// now find the insert pos and update everything
insert_pos = p + reloc_page->SizeOfBlock - buff;
reloc_page->SizeOfBlock += symbols*2;
relocsect_header->SizeOfRawData += symbols*2;
// check for possible padding
if(!*(buff+insert_pos-1)) insert_pos -= 2;
// write all this joy
fseek(f,0,SEEK_SET);
fwrite(buff, 1, insert_pos, f);
// write new reloc entries
for(i = 0; i < symbols; i++) {
handler++;
reloc_entry = (unsigned short)(((unsigned char *) handler - buff) - reloc_page->VirtualAddress - codesect_header->PointerToRawData + codesect_header->VirtualAddress) | 0x3000; // quite nasty
fwrite(&reloc_entry, 1, 2, f);
}
// write the remaining data
fwrite(buff+insert_pos, 1, size-insert_pos, f);
// done at last!
fclose(f);
free(buff);
return 0;
}

View file

@ -1,42 +0,0 @@
*update*
Use the "compress jumtable" Cyclone config.h option to fix this issue
(no patcher will be needed then).
There is a problem with Cyclone on symbian platform:
GNU as generates COFF object files, which allow max of 0xFFFF (65535) relocation
entries. Cyclone uses a jumptable of 0x10000 (65536, 1 for every opcode-word)
antries. When the executable is loaded, all jumptable entries must be relocated
to point to right code location. Because of this limitation, Cyclone's jumptable is
incomplete (misses 2 entries), and if M68k opcodes 0xFFFE or 0xFFFF are ever
encoundered when emulating, your emulator will crash.
I have written a little patcher to fix that. It writes those two missing entries and
marks them as relocatable. Placeholders must not be deleted just after the jumttable
in the Cyclone source code.
This version works with intermediate PE executable, which is used both for APPs and EXEs,
and is produced by gcc toolkit just before running petran. So it's best to insert
this in your makefile, in the rule which builds your APP/EXE, just after last 'ld'
statement, for example:
$(EPOCTRGUREL)\PICODRIVEN.APP : $(EPOCBLDUREL)\PICODRIVEN.in $(EPOCSTATLINKUREL)\EDLL.LIB $(LIBSUREL)
...
ld -s -e _E32Dll -u _E32Dll --dll \
"$(EPOCBLDUREL)\PICODRIVEN.exp" \
-Map "$(EPOCTRGUREL)\PICODRIVEN.APP.map" -o "$(EPOCBLDUREL)\PICODRIVEN.APP" \
"$(EPOCSTATLINKUREL)\EDLL.LIB" --whole-archive "$(EPOCBLDUREL)\PICODRIVEN.in" \
--no-whole-archive $(LIBSUREL) $(USERLDFLAGS)
-$(ERASE) "$(EPOCBLDUREL)\PICODRIVEN.exp"
patchtable_symb2 "$(EPOCBLDUREL)\PICODRIVEN.APP"
petran "$(EPOCBLDUREL)\PICODRIVEN.APP" "$@" \
-nocall -uid1 0x10000079 -uid2 0x100039ce -uid3 0x1000c193
-$(ERASE) "$(EPOCBLDUREL)\PICODRIVEN.APP"
perl -S ecopyfile.pl "$@" "PICODRIVEN.APP"
This is also compatible with ECompXL.
To test if this thing worked, you can load crash_cyclone.bin in your emulator.

View file

@ -1,42 +0,0 @@
CFLAGS = -Wall
ifdef CONFIG_FILE
CFLAGS += -DCONFIG_FILE=\"$(CONFIG_FILE)\"
endif
all : cyclone.s
cyclone.s : Cyclone.exe
./Cyclone.exe
Cyclone.exe : Main.o Ea.o OpAny.o OpArith.o OpBranch.o OpLogic.o Disa.o OpMove.o
$(CC) $^ -o $@ -lstdc++
Main.o : ../Main.cpp ../app.h
$(CC) $(CFLAGS) ../Main.cpp -c -o $@
Ea.o : ../Ea.cpp ../app.h
$(CC) $(CFLAGS) ../Ea.cpp -c -o $@
OpAny.o : ../OpAny.cpp ../app.h
$(CC) $(CFLAGS) ../OpAny.cpp -c -o $@
OpArith.o : ../OpArith.cpp ../app.h
$(CC) $(CFLAGS) ../OpArith.cpp -c -o $@
OpBranch.o : ../OpBranch.cpp ../app.h
$(CC) $(CFLAGS) ../OpBranch.cpp -c -o $@
OpLogic.o : ../OpLogic.cpp ../app.h
$(CC) $(CFLAGS) ../OpLogic.cpp -c -o $@
OpMove.o : ../OpMove.cpp ../app.h
$(CC) $(CFLAGS) ../OpMove.cpp -c -o $@
Disa.o : ../Disa/Disa.c ../Disa/Disa.h
$(CC) $(CFLAGS) ../Disa/Disa.c -c -o $@
../app.h : ../config.h
clean :
$(RM) *.o Cyclone.exe Cyclone.s

View file

@ -1,59 +0,0 @@
# Makefile for MS Visual C
CPP=cl.exe
CPP_PROJ=/nologo /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
LINK32=link.exe
LINK32_FLAGS=user32.lib /nologo /subsystem:console /machine:I386 /out:Cyclone.exe
ALL : cyclone.s
cyclone.s : Cyclone.exe
Cyclone.exe
Cyclone.exe : Main.obj Ea.obj OpAny.obj OpArith.obj OpBranch.obj OpLogic.obj Disa.obj OpMove.obj
$(LINK32) Main.obj Ea.obj OpAny.obj OpArith.obj OpBranch.obj OpLogic.obj Disa.obj OpMove.obj $(LINK32_FLAGS)
Main.obj : ..\Main.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\Main.cpp
Ea.obj : ..\Ea.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\Ea.cpp
OpAny.obj : ..\OpAny.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpAny.cpp
OpArith.obj : ..\OpArith.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpArith.cpp
OpBranch.obj : ..\OpBranch.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpBranch.cpp
OpLogic.obj : ..\OpLogic.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpLogic.cpp
OpMove.obj : ..\OpMove.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpMove.cpp
Disa.obj : ..\disa\Disa.c ..\disa\Disa.h
$(CPP) $(CPP_PROJ) ..\disa\Disa.c
..\app.h : ..\config.h
CLEAN :
-@erase "Disa.obj"
-@erase "Ea.obj"
-@erase "Main.obj"
-@erase "OpAny.obj"
-@erase "OpArith.obj"
-@erase "OpBranch.obj"
-@erase "OpLogic.obj"
-@erase "OpMove.obj"
-@erase "vc60.idb"
-@erase "vc60.pch"
-@erase "Cyclone.exe"
-@erase "Cyclone.asm"
-@erase "Cyclone.s"

View file

@ -1,146 +0,0 @@
# 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 /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
# ADD CPP /nologo /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
# ADD BASE RSC /l 0x427 /d "NDEBUG"
# ADD RSC /l 0x427 /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 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 /out:"cyclone.exe"
!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 /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
# ADD CPP /nologo /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
# ADD BASE RSC /l 0x427 /d "_DEBUG"
# ADD RSC /l 0x427 /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 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 /out:"cyclone.exe" /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=..\disa\Disa.c
# 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=..\config.h
# End Source File
# Begin Source File
SOURCE=..\Cyclone.h
# End Source File
# Begin Source File
SOURCE=..\disa\Disa.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
# End Target
# End Project

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,119 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static FILE *f;
#define bswap16(x) (x=(unsigned short)((x<<8)|(x>>8)))
#define bswap32(x) (x=((x<<24)|((x<<8)&0xff0000)|((x>>8)&0x00ff00)|((unsigned)x>>24)))
static void write_op(unsigned short op, unsigned short word0, unsigned short word1, unsigned short word2)
{
bswap16(op);
bswap16(word0);
bswap16(word1);
bswap16(word2);
fwrite(&op, 1, sizeof(op), f);
fwrite(&word0, 1, sizeof(word0), f);
fwrite(&word1, 1, sizeof(word1), f);
fwrite(&word2, 1, sizeof(word2), f);
}
static void write32(unsigned int a)
{
bswap32(a);
fwrite(&a, 1, sizeof(a), f);
}
static int op_check(unsigned short op)
{
if ((op&0xf000) == 0x6000) return 0; // Bxx
if ((op&0xf0f8) == 0x50c8) return 0; // DBxx
if ((op&0xff80) == 0x4e80) return 0; // Jsr
if ((op&0xf000) == 0xa000) return 0; // a-line
if ((op&0xf000) == 0xf000) return 0; // f-line
if ((op&0xfff8)==0x4e70&&op!=0x4e71&&op!=0x4e76) return 0; // reset, rte, rts
if ((op&0x3f) >= 0x28) op = (op&~0x3f) | (rand() % 0x28);
return 1;
}
static unsigned short safe_rand(void)
{
unsigned short op;
/* avoid branch opcodes */
do
{
op = rand();
}
while (!op_check(op));
return op;
}
int main()
{
int i, op;
srand(time(0));
f = fopen("test_misc2.bin", "wb");
if (!f) return 1;
write32(0x00ff8000); // stack
write32(0x300); // IP
for (i=0x100/4-2; i; i--)
{
write32(0x200+i*4); // exception vectors
}
for (i=0x100/4; i; i--)
{
write32(0); // pad
}
for (i=0x100/4; i; i--)
{
write32(0x4e734e73); // fill with rte instructions
}
for (op = 0; op < 0x10000; op++)
{
if ((op&0xf000) == 0x6000) // Bxx
{
if ((op&0x00ff) == 0)
write_op(op, 6, 0, 0);
}
else if ((op&0xf0f8)==0x50c8) // DBxx
{
write_op(op, 6, 0, 0);
}
else if ((op&0xff80)==0x4e80) // Jsr
{
int addr = 0x300 + op*8 + 8;
if ((op&0x3f) == 0x39)
write_op(op, addr >> 16, addr & 0xffff, 0);
}
else if ((op&0xf000)==0xa000 || (op&0xf000)==0xf000) // a-line, f-line
{
if (op != 0xa000 && op != 0xf000) continue;
}
else if ((op&0xfff8)==0x4e70&&op!=0x4e71&&op!=0x4e76); // rte, rts, stop, reset
else
{
write_op(op, safe_rand(), safe_rand(), safe_rand());
}
}
// jump to the beginning
write_op(0x4ef8, 0x300, 0x4ef8, 0x300);
write_op(0x4ef8, 0x300, 0x4ef8, 0x300);
fclose(f);
return 0;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,140 +0,0 @@
| Processor: 68K
| Target Assembler: 680x0 Assembler by GNU project
| ___________________________________________________________________________
| Segment type: Pure code
| segment "ROM"
dword_0: .long 0 | DATA XREF: ROM:00007244r
| sub_764E+3Eo ...
| initial interrupt stack pointer
dword_4: .long _start | DATA XREF: ROM:00007248r
| ROM:000142C2w
| reset initial PC
dword_8: .long 0x4DE | DATA XREF: sub_20050+B54w
.long 0x490
.long 0x4AA | illegal instruction
.long 0x4C4
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long _trace | trace
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x548 | Level 1 Interrupt Autovector
.long 0x548 | 2 = ext interrupt
.long 0x548
.long 0x592 | 4 = horizontal interrupt?
.long 0x548
.long 0x594 | 6 = verticai interrupt?
.long 0x552
dword_80: .long 0x45C | DATA XREF: ROM:00152F29o
| trap vector table? trap 0?
.long 0x1738
.long 0x171C
.long 0x1754
.long 0x1700
.long 0x556
.long 0x57A
.long 0x548
.long 0x548
.long 0x7CE | 9
.long 0x548
.long 0x548
.long 0x548
.long 0x548
.long 0x548
.long 0x548
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
.long 0x4DE
aSegaGenesis: .ascii "SEGA GENESIS " | DATA XREF: ROM:00045C6Ao
aCSega1994_jul: .ascii "(C)SEGA 1994.JUL"
aDumpedByTsd: .ascii "Dumped By TSD "
aShiningForce2: .ascii "SHINING FORCE 2 "
aGmMk131500: .ascii "GM MK-1315 -00"
.word 0x8921 | checksum
aJ: .ascii "J " | IO_Support
.long 0 | Rom_Start_Adress
dword_1A4: .long 0x1FFFFF | DATA XREF: sub_28008+F66o
| Rom_End_Adress
.long 0xFF0000 | Ram_Start_Adress
.long 0xFFFFFF | Ram_End_Adress
aRaa: .ascii "RA° "<0>" "<0><1><0>" ?"<0xFF> | Modem_Infos
.ascii " "
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
aU: .ascii "U " | Countries
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
.byte 0x20 |
_trace:
nop
nop
rte
.globl _start
_start:
move.l #0xFFFFFFFF, %d0
move.l #0xFFFFFFFF, %d1
move.w #0xa711, %sr
move.l #0x1, %d2
move.l #0x8000, %d3
negx.l %d0
negx.l %d1
move.w #0x270f, %sr
negx.b %d2
negx.w %d3
_loop:
bra _loop
nop
nop
nop
nop

View file

@ -1,3 +0,0 @@
void CycloneInitIdle(void);
void CycloneFinishIdle(void);

View file

@ -1,176 +0,0 @@
@ vim:filetype=armasm
@ ranges/opcodes (idle, normal):
@ 71xx, 73xx - bne.s (8bit offset)
@ 75xx, 77xx - beq.s (8bit offset)
@ 7dxx, 7fxx - bra.s (8bit offset)
.data
.align 2
have_patches:
.word 0
.equ patch_desc_table_size, 10
patch_desc_table:
.word (0x71fa<<16) | 0x66fa, idle_detector_bcc8, idle_bne, Op6601 @ bne.s
.word (0x71f8<<16) | 0x66f8, idle_detector_bcc8, idle_bne, Op6601 @ bne.s
.word (0x71f6<<16) | 0x66f6, idle_detector_bcc8, idle_bne, Op6601 @ bne.s
.word (0x71f2<<16) | 0x66f2, idle_detector_bcc8, idle_bne, Op6601 @ bne.s
.word (0x75fa<<16) | 0x67fa, idle_detector_bcc8, idle_beq, Op6701 @ beq.s
.word (0x75f8<<16) | 0x67f8, idle_detector_bcc8, idle_beq, Op6701 @ beq.s
.word (0x75f6<<16) | 0x67f6, idle_detector_bcc8, idle_beq, Op6701 @ beq.s
.word (0x75f2<<16) | 0x67f2, idle_detector_bcc8, idle_beq, Op6701 @ beq.s
.word (0x7dfe<<16) | 0x60fe, idle_detector_bcc8, idle_bra, Op6001 @ bra.s
.word (0x7dfc<<16) | 0x60fc, idle_detector_bcc8, idle_bra, Op6001 @ bra.s
.text
.align 2
.global CycloneInitIdle
CycloneInitIdle:
ldr r3, =CycloneJumpTab
ldr r2, =patch_desc_table
mov r12,#patch_desc_table_size
cii_loop:
ldrh r0, [r2]
ldr r1, [r2, #4] @ detector
str r1, [r3, r0, lsl #2]
ldrh r0, [r2, #2]
ldr r1, [r2, #8] @ idle
add r0, r3, r0, lsl #2
str r1, [r0]
ldr r1, [r2, #12] @ normal
str r1, [r0, #0x800]
add r2, r2, #16
subs r12,r12,#1
bgt cii_loop
ldr r0, =have_patches
mov r1, #1
str r1, [r0]
bx lr
.global CycloneFinishIdle
CycloneFinishIdle:
ldr r0, =have_patches
ldr r0, [r0]
tst r0, r0
bxeq lr
ldr r3, =CycloneJumpTab
ldr r2, =patch_desc_table
mov r12,#patch_desc_table_size
cfi_loop:
ldrh r0, [r2]
ldr r1, [r2, #12] @ normal
str r1, [r3, r0, lsl #2]
ldrh r0, [r2, #2]
ldr r1, =Op____
add r0, r3, r0, lsl #2
str r1, [r0]
str r1, [r0, #0x800]
add r2, r2, #16
subs r12,r12,#1
bgt cfi_loop
ldr r0, =have_patches
mov r1, #0
str r1, [r0]
bx lr
.macro inc_counter cond
@ ldr\cond r0, [r7, #0x60]
@ mov r11,lr
@ sub r0, r4, r0
@ sub r0, r0, #2
@ bl\cond SekRegisterIdleHit
@ mov lr, r11
.endm
idle_bra:
mov r5, #2
inc_counter
b Op6001
idle_bne:
msr cpsr_flg, r10
movne r5, #2 @ 2 is intentional due to strange timing issues
inc_counter ne
b Op6601
idle_beq:
msr cpsr_flg, r10 ;@ ARM flags = 68000 flags
moveq r5, #2
inc_counter eq
b Op6701
@ @@@ @
idle_detector_bcc8:
ldr r0, =(Pico+0x22208) @ Pico.m
ldr r1, =idledet_start_frame
ldr r0, [r0, #0x1c] @ ..frame_count
ldr r1, [r1]
cmp r0, r1
blt exit_detector @ not yet
mov r0, r8, asl #24 @ Shift 8-bit signed offset up...
add r0, r4, r0, asr #24 @ jump dest
bic r0, r0, #1
mov r1, #0
sub r1, r1, r8, lsl #24
mov r1, r1, lsr #24
sub r1, r1, #2
bic r1, r1, #1
bl SekIsIdleCode
tst r0, r0
and r2, r8, #0x00ff
orr r2, r2, #0x7100
orreq r2, r2, #0x0200
mov r0, r8, lsr #8
cmp r0, #0x66
orrgt r2, r2, #0x0400 @ 67xx (beq)
orrlt r2, r2, #0x0c00 @ 60xx (bra)
@ r2 = patch_opcode
sub r0, r4, #2
ldrh r1, [r0]
mov r11,r2
mov r3, r7
bl SekRegisterIdlePatch
cmp r0, #1 @ 0 - ok to patch, 1 - no patch, 2 - remove detector
strlth r11,[r4, #-2]
ble exit_detector
@ remove detector from Cyclone
mov r0, r8, lsr #8
cmp r0, #0x66
ldrlt r1, =Op6001
ldreq r1, =Op6601
ldrgt r1, =Op6701
ldr r3, =CycloneJumpTab
str r1, [r3, r8, lsl #2]
bx r1
exit_detector:
mov r0, r8, lsr #8
cmp r0, #0x66
blt Op6001
beq Op6601
b Op6701

1
cpu/cyclone Submodule

@ -0,0 +1 @@
Subproject commit a6905b4de17f4d772c7742065f2863b77ddf0b31