mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 07:17:45 -04:00
0.0088 release
git-svn-id: file:///home/notaz/opt/svn/PicoDrive@215 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
parent
ee5e024ce6
commit
0e11c502b0
20 changed files with 2084 additions and 655 deletions
|
@ -1,12 +1,16 @@
|
|||
|
||||
// Cyclone 68000 Emulator - Header File
|
||||
|
||||
// Most code (c) Copyright 2004 Dave, All rights reserved.
|
||||
// Some coding/bugfixing was done by notaz
|
||||
// (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
|
||||
|
@ -17,46 +21,74 @@ struct Cyclone
|
|||
{
|
||||
unsigned int d[8]; // [r7,#0x00]
|
||||
unsigned int a[8]; // [r7,#0x20]
|
||||
unsigned int pc; // [r7,#0x40] Memory Base+PC
|
||||
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 unused1; // [r7,#0x54] Unused
|
||||
int stopped; // [r7,#0x58] 1 == processor is in stopped state
|
||||
int cycles; // [r7,#0x5c]
|
||||
unsigned int prev_pc; // [r7,#0x50] Set to start address of currently executed opcode + 2 (if enabled in config.h)
|
||||
unsigned int reserved;// [r7,#0x54] Reserved for possible future use
|
||||
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 char (*read8 )(unsigned int a); // [r7,#0x68]
|
||||
unsigned short (*read16 )(unsigned int a); // [r7,#0x6c]
|
||||
unsigned int (*read32 )(unsigned int a); // [r7,#0x70]
|
||||
unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] called to recalc Memory Base+pc
|
||||
unsigned int (*read8 )(unsigned int a); // [r7,#0x68]
|
||||
unsigned int (*read16 )(unsigned int a); // [r7,#0x6c]
|
||||
unsigned int (*read32 )(unsigned int a); // [r7,#0x70]
|
||||
void (*write8 )(unsigned int a,unsigned char d); // [r7,#0x74]
|
||||
void (*write16)(unsigned int a,unsigned short d); // [r7,#0x78]
|
||||
void (*write32)(unsigned int a,unsigned int d); // [r7,#0x7c]
|
||||
unsigned char (*fetch8 )(unsigned int a); // [r7,#0x80]
|
||||
unsigned short (*fetch16)(unsigned int a); // [r7,#0x84]
|
||||
unsigned int (*fetch32)(unsigned int a); // [r7,#0x88]
|
||||
void (*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 (*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.
|
||||
};
|
||||
|
||||
// used only if Cyclone was compiled with compressed jumptable, see config.h
|
||||
void CycloneInit();
|
||||
// Initialize. Used only if Cyclone was compiled with compressed jumptable, see config.h
|
||||
void CycloneInit(void);
|
||||
|
||||
// run cyclone. Cycles should be specified in context (pcy->cycles)
|
||||
// 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); // auto-swaps a7<->osp if detects supervisor change
|
||||
unsigned int CycloneGetSr(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__
|
||||
|
||||
|
|
|
@ -18,36 +18,484 @@ ___________________________________________________________________________
|
|||
Replace (atsymbol) with @
|
||||
|
||||
Additional coding and bugfixes done by notaz, 2005-2007
|
||||
Homepage: http://mif.vu.lt/~grig2790/Cyclone/ , http://notaz.gp2x.de
|
||||
Homepage: http://notaz.gp2x.de
|
||||
e-mail: notasas(atsymbol)gmail.com
|
||||
___________________________________________________________________________
|
||||
|
||||
|
||||
What is it?
|
||||
-----------
|
||||
About
|
||||
-----
|
||||
|
||||
Cyclone 68000 is an emulator for the 68000 microprocessor, written in ARM 32-bit assembly.
|
||||
It is aimed at chips such as ARM7 and ARM9 cores, StrongARM and XScale, to interpret 68000
|
||||
code as fast as possible.
|
||||
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
|
||||
r9 : Flags (NZCV) in highest four bits
|
||||
(r10 : Temporary source value or Memory Base)
|
||||
(r11 : Temporary register)
|
||||
|
||||
Flags are mapped onto ARM flags whenever possible, which speeds up the processing of opcode.
|
||||
|
||||
|
||||
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.0087 notaz
|
||||
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" behaviour.
|
||||
+ Fixed flag behaviour of ROXR, ASL, LSR and NBCD in certain situations.
|
||||
+ 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 .
|
||||
|
@ -171,317 +619,3 @@ v0.0060
|
|||
v0.0059: Added remainder to divide opcodes.
|
||||
|
||||
|
||||
The new stuff
|
||||
-------------
|
||||
|
||||
Before using Cyclone, be sure to customize config.h to better suit your project. All options
|
||||
are documented inside that file.
|
||||
|
||||
IrqCallback has been changed a bit, unlike in previous version, it should not return anything.
|
||||
If you need to change IRQ level, you can safely do that in your handler.
|
||||
|
||||
Cyclone has changed quite a bit from the time when Dave stopped updating it, but the rest of
|
||||
documentation still applies, so read it if you haven't done that yet. If you have, check the
|
||||
"Accessing ..." parts.
|
||||
|
||||
|
||||
ARM Register Usage
|
||||
------------------
|
||||
|
||||
See source code for up to date of register usage, however a summary is here:
|
||||
|
||||
r0-3: Temporary registers
|
||||
r4 : Current PC + Memory Base (i.e. pointer to next opcode)
|
||||
r5 : Cycles remaining
|
||||
r6 : Pointer to Opcode Jump table
|
||||
r7 : Pointer to Cpu Context
|
||||
r8 : Current Opcode
|
||||
r9 : Flags (NZCV) in highest four bits
|
||||
(r10 : Temporary source value or Memory Base)
|
||||
(r11 : Temporary register)
|
||||
|
||||
|
||||
How to Compile
|
||||
--------------
|
||||
|
||||
Like Starscream and A68K, Cyclone uses a 'Core Creator' program which calculates and outputs
|
||||
all possible 68000 Opcodes and a jump table into files called Cyclone.s and .asm
|
||||
It then assembles these files into Cyclone.o and .obj
|
||||
|
||||
Cyclone.o is the GCC assembled version and Cyclone.obj is the Microsoft assembled version.
|
||||
|
||||
First unzip "Cyclone.zip" into a "Cyclone" directory.
|
||||
If you are compiling for Windows CE, find ARMASM.EXE (the Microsoft ARM assembler) and
|
||||
put it in the directory as well or put it on your path.
|
||||
|
||||
Open up Cyclone.dsw in Visual Studio 6.0, compile and run the project.
|
||||
Cyclone.obj and Cyclone.o will be created.
|
||||
|
||||
|
||||
Compiling without Visual C++
|
||||
----------------------------
|
||||
If you aren't using Visual C++, it still shouldn't be too hard to compile, just get a C compiler,
|
||||
compile all the CPPs and C file, link them into an EXE, and run the exe.
|
||||
|
||||
e.g. gcc Main.cpp OpAny.cpp OpArith.cpp OpBranch.cpp OpLogic.cpp OpMove.cpp Disa.c
|
||||
Main.exe
|
||||
|
||||
|
||||
Adding to your project
|
||||
----------------------
|
||||
|
||||
To add Cyclone to you project, add Cyclone.o or obj, and include Cyclone.h
|
||||
There is one structure: 'struct Cyclone', and one function: CycloneRun
|
||||
|
||||
Don't worry if this seem very minimal - its all you need to run as many 68000s as you want.
|
||||
It works with both C and C++.
|
||||
|
||||
Byteswapped Memory
|
||||
------------------
|
||||
|
||||
If you have used Starscream, A68K or Turbo68K or similar emulators you'll be familiar with this!
|
||||
|
||||
Any memory which the 68000 can access directly must be have every two bytes swapped around.
|
||||
This is to speed up 16-bit memory accesses, because the 68000 has Big-Endian memory
|
||||
and ARM has Little-Endian memory.
|
||||
|
||||
Now you may think you only technically have to byteswap ROM, not RAM, because
|
||||
16-bit RAM reads go through a memory handler and you could just return (mem[a]<<8) | mem[a+1].
|
||||
|
||||
This would work, but remember some systems can execute code from RAM as well as ROM, and
|
||||
that would fail.
|
||||
So it's best to use byteswapped ROM and RAM if the 68000 can access it directly.
|
||||
It's also faster for the memory handlers, because you can do this:
|
||||
|
||||
return *(unsigned short *)(mem+a)
|
||||
|
||||
|
||||
Declaring 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. For example to declare
|
||||
two 68000s:
|
||||
|
||||
struct Cyclone MyCpu;
|
||||
struct Cyclone MyCpu2;
|
||||
|
||||
It's probably a good idea to initialise the memory to zero:
|
||||
|
||||
memset(&MyCpu, 0,sizeof(MyCpu));
|
||||
memset(&MyCpu2,0,sizeof(MyCpu2));
|
||||
|
||||
Next point to your memory handlers:
|
||||
|
||||
MyCpu.checkpc=MyCheckPc;
|
||||
MyCpu.read8 =MyRead8;
|
||||
MyCpu.read16 =MyRead16;
|
||||
MyCpu.read32 =MyRead32;
|
||||
MyCpu.write8 =MyWrite8;
|
||||
MyCpu.write16=MyWrite16;
|
||||
MyCpu.write32=MyWrite32;
|
||||
|
||||
You also need to point the fetch handlers - for most systems out there you can just
|
||||
point them at the read handlers:
|
||||
MyCpu.fetch8 =MyRead8;
|
||||
MyCpu.fetch16 =MyRead16;
|
||||
MyCpu.fetch32 =MyRead32;
|
||||
|
||||
( Why a different set of function pointers for fetch?
|
||||
Well there are some systems, the main one being CPS2, which return different data
|
||||
depending on whether the 'fetch' line on the 68000 bus is high or low.
|
||||
If this is the case, you can set up different functions for fetch reads.
|
||||
Generally though you don't need to. )
|
||||
|
||||
Now you are nearly ready to reset the 68000, except you need one more function: checkpc().
|
||||
|
||||
The checkpc() function
|
||||
----------------------
|
||||
|
||||
When Cyclone reads opcodes, it doesn't use a memory handler every time, this would be
|
||||
far too slow, instead it uses a direct pointer to ARM memory.
|
||||
For example if your Rom image was at 0x3000000 and the program counter was $206,
|
||||
Cyclone's program counter would be 0x3000206.
|
||||
|
||||
The difference between an ARM address and a 68000 address is also stored in a variable called
|
||||
'membase'. In the above example it's 0x3000000. To retrieve the real PC, Cyclone just
|
||||
subtracts 'membase'.
|
||||
|
||||
When a long jump happens, Cyclone calls checkpc(). If the PC is in a different bank,
|
||||
for example Ram instead of Rom, change 'membase', recalculate the new PC and return it:
|
||||
|
||||
static int MyCheckPc(unsigned int pc)
|
||||
{
|
||||
pc-=MyCpu.membase; // Get the real program counter
|
||||
|
||||
if (pc<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.cpp).
|
||||
|
||||
|
||||
Almost there - Reset the 68000!
|
||||
-------------------------------
|
||||
|
||||
Next we need to Reset the 68000 to get the initial Program Counter and Stack Pointer. This
|
||||
is obtained from addresses 000000 and 000004.
|
||||
|
||||
Here is code which resets the 68000 (using your memory handlers):
|
||||
|
||||
MyCpu.srh=0x27; // Set supervisor mode
|
||||
MyCpu.a[7]=MyCpu.read32(0); // Get Stack Pointer
|
||||
MyCpu.membase=0;
|
||||
MyCpu.pc=MyCpu.checkpc(MyCpu.read32(4)); // Get Program Counter
|
||||
|
||||
And that's ready to go.
|
||||
|
||||
|
||||
Executing the 68000
|
||||
-------------------
|
||||
|
||||
To execute the 68000, set the 'cycles' variable to the number of cycles you wish to execute,
|
||||
and then call CycloneRun with a pointer to the Cyclone structure.
|
||||
|
||||
e.g.:
|
||||
// Execute 1000 cycles on the 68000:
|
||||
MyCpu.cycles=1000; CycloneRun(&MyCpu);
|
||||
|
||||
For each opcode, the number of cycles it took is subtracted and the function returns when
|
||||
it reaches 0.
|
||||
|
||||
e.g.
|
||||
// Execute one instruction on the 68000:
|
||||
MyCpu.cycles=0; CycloneRun(&MyCpu);
|
||||
printf(" The opcode took %d cycles\n", -MyCpu.cycles);
|
||||
|
||||
You should try to execute as many cycles as you can for maximum speed.
|
||||
The number actually executed may be slightly more than requested, i.e. cycles may come
|
||||
out with a small negative value:
|
||||
|
||||
e.g.
|
||||
int todo=12000000/60; // 12Mhz, for one 60hz frame
|
||||
MyCpu.cycles=todo; CycloneRun(&MyCpu);
|
||||
printf(" Actually executed %d cycles\n", todo-MyCpu.cycles);
|
||||
|
||||
To calculate the number of cycles executed, use this formula:
|
||||
Number of cycles requested - Cycle counter at the end
|
||||
|
||||
|
||||
Interrupts
|
||||
----------
|
||||
|
||||
Causing an interrupt is very simple, simply set the irq variable in the Cyclone structure
|
||||
to the IRQ number.
|
||||
To lower the IRQ line, set it to zero.
|
||||
|
||||
e.g:
|
||||
MyCpu.irq=6; // Interrupt level 6
|
||||
MyCpu.cycles=20000; CycloneRun(&MyCpu);
|
||||
|
||||
Note that the interrupt is not actually processed until the next call to CycloneRun,
|
||||
and the interrupt may not be taken until the 68000 interrupt mask is changed to allow it.
|
||||
|
||||
( The IRQ isn't checked on exiting from a memory handler: I don't think this will cause
|
||||
me any trouble because I've never needed to trigger an interrupt from a memory handler,
|
||||
but if someone needs to, let me know...)
|
||||
|
||||
|
||||
Accessing Cycle Counter
|
||||
-----------------------
|
||||
|
||||
The cycle counter in the Cyclone structure is not, by default, updated before
|
||||
calling a memory handler, only at the end of an execution.
|
||||
|
||||
*update*
|
||||
Now this is configurable in config.h, there is no 'debug' variable.
|
||||
|
||||
|
||||
Accessing Program Counter and registers
|
||||
---------------------------------------
|
||||
|
||||
You can read Cyclone's registers directly from the structure at any time (as far as I know).
|
||||
|
||||
The Program Counter, should you need to read or write it, is stored with membase
|
||||
added on. So use this formula to calculate the real 68000 program counter:
|
||||
|
||||
pc = MyCpu.pc - MyCpu.membase;
|
||||
|
||||
The program counter is stored in r4 during execution, and isn't written back to the
|
||||
structure until the end of execution, which means you can't read normally real it from
|
||||
a memory handler.
|
||||
|
||||
*update*
|
||||
Now this is configurable in config.h, there is no 'debug' variable. You can even enable
|
||||
access to SR if you need. However changing PC in memhandlers is still not safe, you should
|
||||
better clear cycles, wait untill CycloneRun() returns and then do whatever you need.
|
||||
|
||||
|
||||
Emulating more than one CPU
|
||||
---------------------------
|
||||
|
||||
Since everything is based on the structures, emulating more than one cpu at the same time
|
||||
is just a matter of declaring more than one structures and timeslicing. You can emulate
|
||||
as many 68000s as you want.
|
||||
Just set up the memory handlers for each cpu and run each cpu for a certain number of cycles.
|
||||
|
||||
e.g.
|
||||
// Execute 1000 cycles on 68000 #1:
|
||||
MyCpu.cycles=1000; CycloneRun(&MyCpu);
|
||||
|
||||
// Execute 1000 cycles on 68000 #2:
|
||||
MyCpu2.cycles=1000; CycloneRun(&MyCpu2);
|
||||
|
||||
|
||||
Thanks to...
|
||||
------------
|
||||
|
||||
* All the previous code-generating assembler cpu core guys!
|
||||
Who are iirc... Neill Corlett, Neil Bradley, Mike Coates, Darren Olafson
|
||||
and Bart Trzynadlowski
|
||||
|
||||
* Charles Macdonald, for researching just about every console ever
|
||||
* MameDev+FBA, for keeping on going and going and going
|
||||
|
||||
|
||||
-------------
|
||||
|
||||
Dave - 17th April 2004
|
||||
notaz - 17th July 2006
|
||||
|
||||
Homepage: http://www.finalburn.com/
|
||||
Dave's e-mail: dev(atsymbol)finalburn.com
|
||||
Replace (atsymbol) with @
|
||||
|
|
|
@ -272,7 +272,7 @@ static int DisaMoveSr(int op)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int OpChk(op)
|
||||
static int OpChk(int op)
|
||||
{
|
||||
int sea=0,dea=0;
|
||||
char seat[64]="",deat[64]="";
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
#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.
|
||||
|
||||
|
@ -149,7 +151,7 @@ int EaCalc(int a,int mask,int ea,int size,int top,int sign_extend)
|
|||
if (ea<0x28)
|
||||
{
|
||||
int step=1<<size, strr=a;
|
||||
int low=0,lsl,i;
|
||||
int low=0,lsl=0,i;
|
||||
|
||||
if ((ea&7)==7 && step<2) step=2; // move.b (a7)+ or -(a7) steps by 2 not 1
|
||||
|
||||
|
@ -340,8 +342,11 @@ int EaRead(int a,int v,int ea,int size,int mask,int top,int sign_extend)
|
|||
ot("\n"); return 0;
|
||||
}
|
||||
|
||||
if (ea>=0x3a && ea<=0x3b) MemHandler(2,size,a); // Fetch
|
||||
else MemHandler(0,size,a); // Read
|
||||
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)
|
||||
{
|
||||
|
@ -462,7 +467,11 @@ int EaWrite(int a,int v,int ea,int size,int mask,int top,int sign_extend_ea)
|
|||
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); // Call write handler
|
||||
MemHandler(1,size,a,eawrite_check_addrerr); // Call write handler
|
||||
|
||||
// not check by default, because most cases are rmw and
|
||||
// address was already checked before reading
|
||||
eawrite_check_addrerr = 0;
|
||||
|
||||
ot("\n"); return 0;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
static FILE *AsmFile=NULL;
|
||||
|
||||
static int CycloneVer=0x0087; // Version number of library
|
||||
static int CycloneVer=0x0088; // Version number of library
|
||||
int *CyJump=NULL; // Jump table
|
||||
int ms=USE_MS_SYNTAX; // If non-zero, output in Microsoft ARMASM format
|
||||
char *Narm[4]={ "b", "h","",""}; // Normal ARM Extensions for operand sizes 0,1,2
|
||||
|
@ -82,56 +82,19 @@ static void ChangeTAS(int norm)
|
|||
}
|
||||
#endif
|
||||
|
||||
// trashes all temp regs
|
||||
static void PrintException(int ints)
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO
|
||||
static void AddressErrorWrapper(char rw, char *dataprg, int iw)
|
||||
{
|
||||
if(!ints) {
|
||||
ot(" ;@ Cause an Exception - Vector address in r0\n");
|
||||
ot(" mov r11,r0\n");
|
||||
}
|
||||
|
||||
ot(" ldr r0,[r7,#0x44] ;@ Get SR high\n");
|
||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||
ot(" tst r0,#0x20\n");
|
||||
ot(";@ get our SP:\n");
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n");
|
||||
ot(" ldreq r1,[r7,#0x48] ;@ ...or OSP as our stack pointer\n");
|
||||
ot(" streq r0,[r7,#0x48]\n");
|
||||
ot(" moveq r0,r1\n");
|
||||
|
||||
ot(" sub r1,r4,r10 ;@ r1 = Old PC\n");
|
||||
ot(";@ Push r1 onto stack\n");
|
||||
ot(" sub r0,r0,#4 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,2);
|
||||
OpPushSr(1);
|
||||
|
||||
ot(" mov r0,r11\n");
|
||||
ot(";@ Read IRQ Vector:\n");
|
||||
MemHandler(0,2);
|
||||
if(ints) {
|
||||
ot(" tst r0,r0 ;@ uninitialized int vector?\n");
|
||||
ot(" moveq r0,#0x3c\n");
|
||||
ot(" moveq lr,pc\n");
|
||||
ot(" ldreq pc,[r7,#0x70] ;@ Call read32(r0) handler\n");
|
||||
}
|
||||
#if USE_CHECKPC_CALLBACK
|
||||
ot(" add r0,r0,r10 ;@ r0 = Memory Base + New PC\n");
|
||||
ot(" mov lr,pc\n");
|
||||
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
|
||||
ot(" mov r4,r0\n");
|
||||
#else
|
||||
ot(" add r4,r0,r10 ;@ r4 = Memory Base + New PC\n");
|
||||
#endif
|
||||
ot("ExceptionAddressError_%c_%s%s\n", rw, dataprg, ms?"":":");
|
||||
ot(" ldr r1,[r7,#0x44]\n");
|
||||
ot(" mov r10,#0x%02x\n", iw);
|
||||
ot(" mov r11,r0\n");
|
||||
ot(" tst r1,#0x20\n");
|
||||
ot(" orrne r10,r10,#4\n");
|
||||
ot(" b ExceptionAddressError\n");
|
||||
ot("\n");
|
||||
|
||||
if(!ints) {
|
||||
ot(" ldr r0,[r7,#0x44] ;@ Get SR high\n");
|
||||
ot(" bic r0,r0,#0xd8 ;@ clear trace and unused flags\n");
|
||||
ot(" orr r0,r0,#0x20 ;@ set supervisor mode\n");
|
||||
ot(" strb r0,[r7,#0x44]\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void FlushPC(void)
|
||||
{
|
||||
|
@ -144,6 +107,14 @@ void FlushPC(void)
|
|||
|
||||
static void PrintFramework()
|
||||
{
|
||||
int state_flags_to_check = 1; // stopped
|
||||
#if EMULATE_TRACE
|
||||
state_flags_to_check |= 2; // tracing
|
||||
#endif
|
||||
#if EMULATE_HALT
|
||||
state_flags_to_check |= 0x10; // halted
|
||||
#endif
|
||||
|
||||
ot(";@ --------------------------- Framework --------------------------\n");
|
||||
if (ms) ot("CycloneRun\n");
|
||||
else ot("CycloneRun:\n");
|
||||
|
@ -161,6 +132,10 @@ static void PrintFramework()
|
|||
ot(" mov r9,r9,lsl #28 ;@ r9 = Flags 0xf0000000, cpsr format\n");
|
||||
ot(" ;@ r10 = Source value / Memory Base\n");
|
||||
ot("\n");
|
||||
#if (CYCLONE_FOR_GENESIS == 2) || EMULATE_TRACE
|
||||
ot(" mov r2,#0\n");
|
||||
ot(" str r2,[r7,#0x98] ;@ clear custom CycloneEnd\n");
|
||||
#endif
|
||||
ot(";@ CheckInterrupt:\n");
|
||||
ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47]
|
||||
ot(" beq NoInts0\n");
|
||||
|
@ -170,12 +145,22 @@ static void PrintFramework()
|
|||
ot(" bgt CycloneDoInterrupt\n");
|
||||
ot("NoInts0%s\n", ms?"":":");
|
||||
ot("\n");
|
||||
ot(";@ Check if our processor is in stopped state and jump to opcode handler if not\n");
|
||||
ot(" ldr r0,[r7,#0x58]\n");
|
||||
ot(";@ Check if our processor is in special state\n");
|
||||
ot(";@ and jump to opcode handler if not\n");
|
||||
ot(" ldr r0,[r7,#0x58] ;@ state_flags\n");
|
||||
ot(" ldrh r8,[r4],#2 ;@ Fetch first opcode\n");
|
||||
ot(" tst r0,r0 ;@ stopped?\n");
|
||||
ot(" bne CycloneStopped\n");
|
||||
ot(" ldr pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
|
||||
ot(" tst r0,#0x%02x ;@ special state?\n", state_flags_to_check);
|
||||
ot(" ldreq pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
|
||||
ot("\n");
|
||||
ot("CycloneSpecial%s\n", ms?"":":");
|
||||
#if EMULATE_TRACE
|
||||
ot(" tst r0,#2 ;@ tracing?\n");
|
||||
ot(" bne CycloneDoTrace\n");
|
||||
#endif
|
||||
ot(";@ stopped or halted\n");
|
||||
ot(" mov r5,#0\n");
|
||||
ot(" str r5,[r7,#0x5C] ;@ eat all cycles\n");
|
||||
ot(" ldmia sp!,{r4-r11,pc} ;@ we are stopped, do nothing!\n");
|
||||
ot("\n");
|
||||
ot("\n");
|
||||
|
||||
|
@ -183,8 +168,8 @@ static void PrintFramework()
|
|||
ot("CycloneEnd%s\n", ms?"":":");
|
||||
ot(" sub r4,r4,#2\n");
|
||||
ot("CycloneEndNoBack%s\n", ms?"":":");
|
||||
#if (CYCLONE_FOR_GENESIS == 2)
|
||||
ot(" ldr r1,[r7,#0x54]\n");
|
||||
#if (CYCLONE_FOR_GENESIS == 2) || EMULATE_TRACE
|
||||
ot(" ldr r1,[r7,#0x98]\n");
|
||||
ot(" mov r9,r9,lsr #28\n");
|
||||
ot(" tst r1,r1\n");
|
||||
ot(" bxne r1 ;@ jump to alternative CycloneEnd\n");
|
||||
|
@ -195,86 +180,76 @@ static void PrintFramework()
|
|||
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
|
||||
ot(" strb r9,[r7,#0x46] ;@ Save Flags (NZCV)\n");
|
||||
ot(" ldmia sp!,{r4-r11,pc}\n");
|
||||
ot("\n");
|
||||
ot("CycloneStopped%s\n", ms?"":":");
|
||||
ot(" mov r5,#0\n");
|
||||
ot(" str r5,[r7,#0x5C] ;@ eat all cycles\n");
|
||||
ot(" ldmia sp!,{r4-r11,pc} ;@ we are stopped, do nothing!\n");
|
||||
ot("\n");
|
||||
|
||||
ltorg();
|
||||
ot("\n");
|
||||
ot("\n");
|
||||
|
||||
ot("CycloneInit%s\n", ms?"":":");
|
||||
#if COMPRESS_JUMPTABLE
|
||||
ot(";@ uncompress jump table\n");
|
||||
if (ms) ot("CycloneInit\n");
|
||||
else ot("CycloneInit:\n");
|
||||
ot(" ldr r12,=CycloneJumpTab\n");
|
||||
ot(" add r0,r12,#0xe000*4 ;@ ctrl code pointer\n");
|
||||
ot(" ldr r1,[r0,#-4]\n");
|
||||
ot(" tst r1,r1\n");
|
||||
ot(" movne pc,lr ;@ already uncompressed\n");
|
||||
ot(" add r3,r12,#0xa000*4 ;@ handler table pointer, r12=dest\n");
|
||||
ot("unc_loop%s\n", ms?"":":");
|
||||
ot(" ldrh r1,[r0],#2\n");
|
||||
ot(" and r2,r1,#0xf\n");
|
||||
ot(" bic r1,r1,#0xf\n");
|
||||
ot(" ldr r1,[r3,r1,lsr #2] ;@ r1=handler\n");
|
||||
ot(" cmp r2,#0xf\n");
|
||||
ot(" addeq r2,r2,#1 ;@ 0xf is really 0x10\n");
|
||||
ot(" tst r2,r2\n");
|
||||
ot(" ldreqh r2,[r0],#2 ;@ counter is in next word\n");
|
||||
ot(" tst r2,r2\n");
|
||||
ot(" beq unc_finish ;@ done decompressing\n");
|
||||
ot(" tst r1,r1\n");
|
||||
ot(" addeq r12,r12,r2,lsl #2 ;@ 0 handler means we should skip those bytes\n");
|
||||
ot(" beq unc_loop\n");
|
||||
ot("unc_loop_in%s\n", ms?"":":");
|
||||
ot(" subs r2,r2,#1\n");
|
||||
ot(" str r1,[r12],#4\n");
|
||||
ot(" bgt unc_loop_in\n");
|
||||
ot(" b unc_loop\n");
|
||||
ot("unc_finish%s\n", ms?"":":");
|
||||
ot(" ldr r12,=CycloneJumpTab\n");
|
||||
ot(" ;@ set a-line and f-line handlers\n");
|
||||
ot(" add r0,r12,#0xa000*4\n");
|
||||
ot(" ldr r1,[r0,#4] ;@ a-line handler\n");
|
||||
ot(" ldr r3,[r0,#8] ;@ f-line handler\n");
|
||||
ot(" mov r2,#0x1000\n");
|
||||
ot("unc_fill3%s\n", ms?"":":");
|
||||
ot(" subs r2,r2,#1\n");
|
||||
ot(" str r1,[r0],#4\n");
|
||||
ot(" bgt unc_fill3\n");
|
||||
ot(" add r0,r12,#0xf000*4\n");
|
||||
ot(" mov r2,#0x1000\n");
|
||||
ot("unc_fill4%s\n", ms?"":":");
|
||||
ot(" subs r2,r2,#1\n");
|
||||
ot(" str r3,[r0],#4\n");
|
||||
ot(" bgt unc_fill4\n");
|
||||
ot(" bx lr\n");
|
||||
ltorg();
|
||||
ot("\n");
|
||||
ot(";@ decompress jump table\n");
|
||||
ot(" ldr r12,=CycloneJumpTab\n");
|
||||
ot(" add r0,r12,#0xe000*4 ;@ ctrl code pointer\n");
|
||||
ot(" ldr r1,[r0,#-4]\n");
|
||||
ot(" tst r1,r1\n");
|
||||
ot(" movne pc,lr ;@ already uncompressed\n");
|
||||
ot(" add r3,r12,#0xa000*4 ;@ handler table pointer, r12=dest\n");
|
||||
ot("unc_loop%s\n", ms?"":":");
|
||||
ot(" ldrh r1,[r0],#2\n");
|
||||
ot(" and r2,r1,#0xf\n");
|
||||
ot(" bic r1,r1,#0xf\n");
|
||||
ot(" ldr r1,[r3,r1,lsr #2] ;@ r1=handler\n");
|
||||
ot(" cmp r2,#0xf\n");
|
||||
ot(" addeq r2,r2,#1 ;@ 0xf is really 0x10\n");
|
||||
ot(" tst r2,r2\n");
|
||||
ot(" ldreqh r2,[r0],#2 ;@ counter is in next word\n");
|
||||
ot(" tst r2,r2\n");
|
||||
ot(" beq unc_finish ;@ done decompressing\n");
|
||||
ot(" tst r1,r1\n");
|
||||
ot(" addeq r12,r12,r2,lsl #2 ;@ 0 handler means we should skip those bytes\n");
|
||||
ot(" beq unc_loop\n");
|
||||
ot("unc_loop_in%s\n", ms?"":":");
|
||||
ot(" subs r2,r2,#1\n");
|
||||
ot(" str r1,[r12],#4\n");
|
||||
ot(" bgt unc_loop_in\n");
|
||||
ot(" b unc_loop\n");
|
||||
ot("unc_finish%s\n", ms?"":":");
|
||||
ot(" ldr r12,=CycloneJumpTab\n");
|
||||
ot(" ;@ set a-line and f-line handlers\n");
|
||||
ot(" add r0,r12,#0xa000*4\n");
|
||||
ot(" ldr r1,[r0,#4] ;@ a-line handler\n");
|
||||
ot(" ldr r3,[r0,#8] ;@ f-line handler\n");
|
||||
ot(" mov r2,#0x1000\n");
|
||||
ot("unc_fill3%s\n", ms?"":":");
|
||||
ot(" subs r2,r2,#1\n");
|
||||
ot(" str r1,[r0],#4\n");
|
||||
ot(" bgt unc_fill3\n");
|
||||
ot(" add r0,r12,#0xf000*4\n");
|
||||
ot(" mov r2,#0x1000\n");
|
||||
ot("unc_fill4%s\n", ms?"":":");
|
||||
ot(" subs r2,r2,#1\n");
|
||||
ot(" str r3,[r0],#4\n");
|
||||
ot(" bgt unc_fill4\n");
|
||||
ot(" bx lr\n");
|
||||
ltorg();
|
||||
#else
|
||||
ot(";@ do nothing\n");
|
||||
if (ms) ot("CycloneInit\n");
|
||||
else ot("CycloneInit:\n");
|
||||
ot(" bx lr\n");
|
||||
ot("\n");
|
||||
ot(";@ do nothing\n");
|
||||
ot(" bx lr\n");
|
||||
#endif
|
||||
ot("\n");
|
||||
|
||||
// --------------
|
||||
// 68k: XNZVC, ARM: NZCV
|
||||
if (ms) ot("CycloneSetSr\n");
|
||||
else ot("CycloneSetSr:\n");
|
||||
ot("CycloneSetSr%s\n", ms?"":":");
|
||||
ot(" mov r2,r1,lsr #8\n");
|
||||
ot(" ldrb r3,[r0,#0x44] ;@ get SR high\n");
|
||||
ot(" eor r3,r3,r2\n");
|
||||
ot(" tst r3,#0x20\n");
|
||||
ot(" and r2,r2,#0xa7 ;@ only nonzero bits\n");
|
||||
// ot(" ldrb r3,[r0,#0x44] ;@ get SR high\n");
|
||||
// ot(" eor r3,r3,r2\n");
|
||||
// ot(" tst r3,#0x20\n");
|
||||
#if EMULATE_TRACE
|
||||
ot(" and r2,r2,#0xa7 ;@ only defined bits\n");
|
||||
#else
|
||||
ot(" and r2,r2,#0x27 ;@ only defined bits\n");
|
||||
#endif
|
||||
ot(" strb r2,[r0,#0x44] ;@ set SR high\n");
|
||||
ot(" bne setsr_noswap\n");
|
||||
ot(" ldr r2,[r0,#0x3C] ;@ Get A7\n");
|
||||
ot(" ldr r3,[r0,#0x48] ;@ Get OSP\n");
|
||||
ot(" str r3,[r0,#0x3C]\n");
|
||||
ot(" str r2,[r0,#0x48]\n");
|
||||
ot("setsr_noswap%s\n",ms?"":":");
|
||||
ot(" mov r2,r1,lsl #25\n");
|
||||
ot(" str r2,[r0,#0x4c] ;@ the X flag\n");
|
||||
ot(" bic r2,r1,#0xf3\n");
|
||||
|
@ -286,8 +261,8 @@ static void PrintFramework()
|
|||
ot(" bx lr\n");
|
||||
ot("\n");
|
||||
|
||||
if (ms) ot("CycloneGetSr\n");
|
||||
else ot("CycloneGetSr:\n");
|
||||
// --------------
|
||||
ot("CycloneGetSr%s\n", ms?"":":");
|
||||
ot(" ldrb r1,[r0,#0x46] ;@ flags\n");
|
||||
ot(" bic r2,r1,#0xf3\n");
|
||||
ot(" tst r1,#1\n");
|
||||
|
@ -302,8 +277,111 @@ static void PrintFramework()
|
|||
ot(" bx lr\n");
|
||||
ot("\n");
|
||||
|
||||
if (ms) ot("CycloneSetRealTAS\n");
|
||||
else ot("CycloneSetRealTAS:\n");
|
||||
// --------------
|
||||
ot("CyclonePack%s\n", ms?"":":");
|
||||
ot(" stmfd sp!,{r4,r5,lr}\n");
|
||||
ot(" mov r4,r0\n");
|
||||
ot(" mov r5,r1\n");
|
||||
ot(" mov r3,#16\n");
|
||||
ot(";@ 0x00-0x3f: DA registers\n");
|
||||
ot("c_pack_loop%s\n",ms?"":":");
|
||||
ot(" ldr r1,[r0],#4\n");
|
||||
ot(" subs r3,r3,#1\n");
|
||||
ot(" str r1,[r5],#4\n");
|
||||
ot(" bne c_pack_loop\n");
|
||||
ot(";@ 0x40: PC\n");
|
||||
ot(" ldr r0,[r4,#0x40] ;@ PC + Memory Base\n");
|
||||
ot(" ldr r1,[r4,#0x60] ;@ Memory base\n");
|
||||
ot(" sub r0,r0,r1\n");
|
||||
ot(" str r0,[r5],#4\n");
|
||||
ot(";@ 0x44: SR\n");
|
||||
ot(" mov r0,r4\n");
|
||||
ot(" bl CycloneGetSr\n");
|
||||
ot(" strh r0,[r5],#2\n");
|
||||
ot(";@ 0x46: IRQ level\n");
|
||||
ot(" ldrb r0,[r4,#0x47]\n");
|
||||
ot(" strb r0,[r5],#2\n");
|
||||
ot(";@ 0x48: other SP\n");
|
||||
ot(" ldr r0,[r4,#0x48]\n");
|
||||
ot(" str r0,[r5],#4\n");
|
||||
ot(";@ 0x4c: CPU state flags\n");
|
||||
ot(" ldr r0,[r4,#0x58]\n");
|
||||
ot(" str r0,[r5],#4\n");
|
||||
ot(" ldmfd sp!,{r4,r5,pc}\n");
|
||||
ot("\n");
|
||||
|
||||
// --------------
|
||||
ot("CycloneUnpack%s\n", ms?"":":");
|
||||
ot(" stmfd sp!,{r4,r5,lr}\n");
|
||||
ot(" mov r4,r0\n");
|
||||
ot(" mov r5,r1\n");
|
||||
ot(" mov r3,#16\n");
|
||||
ot(";@ 0x00-0x3f: DA registers\n");
|
||||
ot("c_unpack_loop%s\n",ms?"":":");
|
||||
ot(" ldr r1,[r5],#4\n");
|
||||
ot(" subs r3,r3,#1\n");
|
||||
ot(" str r1,[r0],#4\n");
|
||||
ot(" bne c_unpack_loop\n");
|
||||
ot(";@ 0x40: PC\n");
|
||||
ot(" ldr r0,[r5],#4 ;@ PC\n");
|
||||
#if USE_CHECKPC_CALLBACK
|
||||
ot(" mov r1,#0\n");
|
||||
ot(" str r1,[r4,#0x60] ;@ Memory base\n");
|
||||
ot(" mov lr,pc\n");
|
||||
ot(" ldr pc,[r4,#0x64] ;@ Call checkpc()\n");
|
||||
#else
|
||||
ot(" ldr r1,[r4,#0x60] ;@ Memory base\n");
|
||||
ot(" add r0,r0,r1 ;@ r0 = Memory Base + New PC\n");
|
||||
#endif
|
||||
ot(" str r0,[r4,#0x40] ;@ PC + Memory Base\n");
|
||||
ot(";@ 0x44: SR\n");
|
||||
ot(" ldrh r1,[r5],#2\n");
|
||||
ot(" mov r0,r4\n");
|
||||
ot(" bl CycloneSetSr\n");
|
||||
ot(";@ 0x46: IRQ level\n");
|
||||
ot(" ldrb r0,[r5],#2\n");
|
||||
ot(" strb r0,[r4,#0x47]\n");
|
||||
ot(";@ 0x48: other SP\n");
|
||||
ot(" ldr r0,[r5],#4\n");
|
||||
ot(" str r0,[r4,#0x48]\n");
|
||||
ot(";@ 0x4c: CPU state flags\n");
|
||||
ot(" ldr r0,[r5],#4\n");
|
||||
ot(" str r0,[r4,#0x58]\n");
|
||||
ot(" ldmfd sp!,{r4,r5,pc}\n");
|
||||
ot("\n");
|
||||
|
||||
// --------------
|
||||
ot("CycloneFlushIrq%s\n", ms?"":":");
|
||||
ot(" ldr r1,[r0,#0x44] ;@ Get SR high T_S__III and irq level\n");
|
||||
ot(" mov r2,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47]
|
||||
ot(" cmp r2,#6 ;@ irq>6 ?\n");
|
||||
ot(" andle r1,r1,#7 ;@ Get interrupt mask\n");
|
||||
ot(" cmple r2,r1 ;@ irq<=6: Is irq<=mask ?\n");
|
||||
ot(" movle r0,#0\n");
|
||||
ot(" bxle lr ;@ no ints\n");
|
||||
ot("\n");
|
||||
ot(" stmdb sp!,{r4,r5,r7-r11,lr}\n");
|
||||
ot(" mov r7,r0\n");
|
||||
ot(" mov r0,r2\n");
|
||||
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Flags (NZCV)\n");
|
||||
ot(" mov r5,#0\n");
|
||||
ot(" ldr r4,[r7,#0x40] ;@ r4 = Current PC + Memory Base\n");
|
||||
ot(" mov r9,r9,lsl #28 ;@ r9 = Flags 0xf0000000, cpsr format\n");
|
||||
ot(" adr r2,CycloneFlushIrqEnd\n");
|
||||
ot(" str r2,[r7,#0x98] ;@ set custom CycloneEnd\n");
|
||||
ot(" b CycloneDoInterrupt\n");
|
||||
ot("\n");
|
||||
ot("CycloneFlushIrqEnd%s\n", ms?"":":");
|
||||
ot(" rsb r0,r5,#0\n");
|
||||
ot(" str r4,[r7,#0x40] ;@ Save Current PC + Memory Base\n");
|
||||
ot(" strb r9,[r7,#0x46] ;@ Save Flags (NZCV)\n");
|
||||
ot(" ldmia sp!,{r4,r5,r7-r11,lr}\n");
|
||||
ot(" bx lr\n");
|
||||
ot("\n");
|
||||
ot("\n");
|
||||
|
||||
// --------------
|
||||
ot("CycloneSetRealTAS%s\n", ms?"":":");
|
||||
#if (CYCLONE_FOR_GENESIS == 2)
|
||||
ot(" ldr r12,=CycloneJumpTab\n");
|
||||
ot(" tst r0,r0\n");
|
||||
|
@ -316,49 +394,122 @@ static void PrintFramework()
|
|||
ChangeTAS(0);
|
||||
ot(" bx lr\n");
|
||||
ltorg();
|
||||
ot("\n");
|
||||
#else
|
||||
ot(" bx lr\n");
|
||||
ot("\n");
|
||||
#endif
|
||||
ot("\n");
|
||||
|
||||
ot(";@ DoInterrupt - r0=IRQ number\n");
|
||||
// --------------
|
||||
ot(";@ DoInterrupt - r0=IRQ level\n");
|
||||
ot("CycloneDoInterruptGoBack%s\n", ms?"":":");
|
||||
ot(" sub r4,r4,#2\n");
|
||||
ot("CycloneDoInterrupt%s\n", ms?"":":");
|
||||
ot(";@ Get IRQ Vector address:\n");
|
||||
ot(" mov r0,r0,asl #2\n");
|
||||
ot(" add r11,r0,#0x60\n");
|
||||
PrintException(1);
|
||||
|
||||
ot(" ldrb r0,[r7,#0x47] ;@ IRQ\n");
|
||||
ot(" mov r2,#0\n");
|
||||
ot(" orr r1,r0,#0x20 ;@ Supervisor mode + IRQ number\n");
|
||||
ot(" strb r1,[r7,#0x44] ;@ Put SR high\n");
|
||||
ot(" bic r8,r8,#0xff000000\n");
|
||||
ot(" orr r8,r8,r0,lsl #29 ;@ abuse r8\n");
|
||||
|
||||
ot(";@ Clear stopped states:\n");
|
||||
// Steps are from "M68000 8-/16-/32-BIT MICROPROCESSORS USER'S MANUAL", p. 6-4
|
||||
// but their order is based on http://pasti.fxatari.com/68kdocs/68kPrefetch.html
|
||||
// 1. Make a temporary copy of the status register and set the status register for exception processing.
|
||||
ot(" ldr r2,[r7,#0x58] ;@ state flags\n");
|
||||
ot(" and r0,r0,#7\n");
|
||||
ot(" orr r3,r0,#0x20 ;@ Supervisor mode + IRQ level\n");
|
||||
ot(" bic r2,r2,#3 ;@ clear stopped and trace states\n");
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO
|
||||
ot(" orr r2,r2,#4 ;@ set activity bit: 'not processing instruction'\n");
|
||||
#endif
|
||||
ot(" str r2,[r7,#0x58]\n");
|
||||
ot(" ldrb r10,[r7,#0x44] ;@ Get old SR high\n");
|
||||
ot(" strb r3,[r7,#0x44] ;@ Put new SR high\n");
|
||||
ot("\n");
|
||||
|
||||
// 3. Save the current processor context.
|
||||
ot(" ldr r1,[r7,#0x60] ;@ Get Memory base\n");
|
||||
ot(" ldr r11,[r7,#0x3c] ;@ Get A7\n");
|
||||
ot(" tst r10,#0x20\n");
|
||||
ot(";@ get our SP:\n");
|
||||
ot(" ldreq r2,[r7,#0x48] ;@ ...or OSP as our stack pointer\n");
|
||||
ot(" streq r11,[r7,#0x48]\n");
|
||||
ot(" moveq r11,r2\n");
|
||||
ot(";@ Push old PC onto stack\n");
|
||||
ot(" sub r0,r11,#4 ;@ Predecremented A7\n");
|
||||
ot(" sub r1,r4,r1 ;@ r1 = Old PC\n");
|
||||
MemHandler(1,2);
|
||||
ot(";@ Push old SR:\n");
|
||||
ot(" ldr r0,[r7,#0x4c] ;@ X bit\n");
|
||||
ot(" mov r1,r9,lsr #28 ;@ ____NZCV\n");
|
||||
ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n");
|
||||
ot(" tst r2,#1 ;@ 1 if C!=V\n");
|
||||
ot(" eorne r1,r1,#3 ;@ ____NZVC\n");
|
||||
ot(" and r0,r0,#0x20000000\n");
|
||||
ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n");
|
||||
ot(" orr r1,r1,r10,lsl #8 ;@ Include old SR high\n");
|
||||
ot(" sub r0,r11,#6 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,1,0,0); // already checked for address error by prev MemHandler
|
||||
ot("\n");
|
||||
|
||||
// 2. Obtain the exception vector.
|
||||
ot(" mov r11,r8,lsr #29\n");
|
||||
ot(" mov r0,r11\n");
|
||||
#if USE_INT_ACK_CALLBACK
|
||||
ot(";@ call IrqCallback if it is defined\n");
|
||||
#if INT_ACK_NEEDS_STUFF
|
||||
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
|
||||
ot(" mov r1,r9,lsr #28\n");
|
||||
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
|
||||
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
|
||||
#endif
|
||||
ot(" ldr r11,[r7,#0x8c] ;@ IrqCallback\n");
|
||||
ot(" tst r11,r11\n");
|
||||
ot(" movne lr,pc\n");
|
||||
ot(" movne pc,r11 ;@ call IrqCallback if it is defined\n");
|
||||
#if INT_ACK_CHANGES_STUFF
|
||||
ot(" ldr r3,[r7,#0x8c] ;@ IrqCallback\n");
|
||||
ot(" add lr,pc,#4*3\n");
|
||||
ot(" tst r3,r3\n");
|
||||
ot(" streqb r3,[r7,#0x47] ;@ just clear IRQ if there is no callback\n");
|
||||
ot(" mvneq r0,#0 ;@ and simulate -1 return\n");
|
||||
ot(" bxne r3\n");
|
||||
#if INT_ACK_CHANGES_CYCLES
|
||||
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
|
||||
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Load Flags (NZCV)\n");
|
||||
ot(" mov r9,r9,lsl #28\n");
|
||||
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
||||
#endif
|
||||
ot(";@ get IRQ vector address:\n");
|
||||
ot(" cmn r0,#1 ;@ returned -1?\n");
|
||||
ot(" addeq r0,r11,#0x18 ;@ use autovector then\n");
|
||||
ot(" cmn r0,#2 ;@ returned -2?\n"); // should be safe as above add should never result in -2
|
||||
ot(" moveq r0,#0x18 ;@ use spurious interrupt then\n");
|
||||
#else // !USE_INT_ACK_CALLBACK
|
||||
ot(";@ Clear irq:\n");
|
||||
ot(" mov r2,#0\n");
|
||||
ot(" strb r2,[r7,#0x47]\n");
|
||||
ot(" add r0,r0,#0x18 ;@ use autovector\n");
|
||||
#endif
|
||||
ot(" mov r0,r0,lsl #2 ;@ get vector address\n");
|
||||
ot("\n");
|
||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||
ot(";@ Read IRQ Vector:\n");
|
||||
MemHandler(0,2,0,0);
|
||||
ot(" tst r0,r0 ;@ uninitialized int vector?\n");
|
||||
ot(" moveq r0,#0x3c\n");
|
||||
ot(" moveq lr,pc\n");
|
||||
ot(" ldreq pc,[r7,#0x70] ;@ Call read32(r0) handler\n");
|
||||
#if USE_CHECKPC_CALLBACK
|
||||
ot(" add lr,pc,#4\n");
|
||||
ot(" add r0,r0,r10 ;@ r0 = Memory Base + New PC\n");
|
||||
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||
ot(" mov r4,r0\n");
|
||||
#else
|
||||
ot(" bic r4,r0,#1\n");
|
||||
#endif
|
||||
#else
|
||||
ot(" add r4,r0,r10 ;@ r4 = Memory Base + New PC\n");
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||
ot(" bic r4,r4,#1\n");
|
||||
#endif
|
||||
#endif
|
||||
ot("\n");
|
||||
|
||||
// 4. Obtain a new context and resume instruction processing.
|
||||
// note: the obtain part was already done in previous steps
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||
ot(" tst r4,#1\n");
|
||||
ot(" bne ExceptionAddressError_r_prg_r4\n");
|
||||
#endif
|
||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
||||
ot(" subs r5,r5,#44 ;@ Subtract cycles\n");
|
||||
|
@ -366,17 +517,276 @@ static void PrintFramework()
|
|||
ot(" b CycloneEnd\n");
|
||||
ot("\n");
|
||||
|
||||
// --------------
|
||||
// trashes all temp regs
|
||||
ot("Exception%s\n", ms?"":":");
|
||||
ot(" stmdb sp!,{lr} ;@ Preserve ARM return address\n");
|
||||
PrintException(0);
|
||||
ot(" ldmia sp!,{pc} ;@ Return\n");
|
||||
ot(" ;@ Cause an Exception - Vector number in r0\n");
|
||||
ot(" mov r11,lr ;@ Preserve ARM return address\n");
|
||||
ot(" bic r8,r8,#0xff000000\n");
|
||||
ot(" orr r8,r8,r0,lsl #24 ;@ abuse r8\n");
|
||||
|
||||
// 1. Make a temporary copy of the status register and set the status register for exception processing.
|
||||
ot(" ldr r10,[r7,#0x44] ;@ Get old SR high\n");
|
||||
ot(" ldr r2,[r7,#0x58] ;@ state flags\n");
|
||||
ot(" and r3,r10,#0x27 ;@ clear trace and unused flags\n");
|
||||
ot(" orr r3,r3,#0x20 ;@ set supervisor mode\n");
|
||||
ot(" bic r2,r2,#3 ;@ clear stopped and trace states\n");
|
||||
ot(" str r2,[r7,#0x58]\n");
|
||||
ot(" strb r3,[r7,#0x44] ;@ Put new SR high\n");
|
||||
ot("\n");
|
||||
|
||||
// 3. Save the current processor context.
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n");
|
||||
ot(" tst r10,#0x20\n");
|
||||
ot(";@ get our SP:\n");
|
||||
ot(" ldreq r2,[r7,#0x48] ;@ ...or OSP as our stack pointer\n");
|
||||
ot(" streq r0,[r7,#0x48]\n");
|
||||
ot(" moveq r0,r2\n");
|
||||
ot(";@ Push old PC onto stack\n");
|
||||
ot(" ldr r1,[r7,#0x60] ;@ Get Memory base\n");
|
||||
ot(" sub r0,r0,#4 ;@ Predecremented A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
ot(" sub r1,r4,r1 ;@ r1 = Old PC\n");
|
||||
MemHandler(1,2);
|
||||
ot(";@ Push old SR:\n");
|
||||
ot(" ldr r0,[r7,#0x4c] ;@ X bit\n");
|
||||
ot(" mov r1,r9,lsr #28 ;@ ____NZCV\n");
|
||||
ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n");
|
||||
ot(" tst r2,#1 ;@ 1 if C!=V\n");
|
||||
ot(" eorne r1,r1,#3 ;@ ____NZVC\n");
|
||||
ot(" and r0,r0,#0x20000000\n");
|
||||
ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n");
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ A7\n");
|
||||
ot(" orr r1,r1,r10,lsl #8 ;@ Include SR high\n");
|
||||
ot(" sub r0,r0,#2 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,1,0,0);
|
||||
ot("\n");
|
||||
|
||||
// 2. Obtain the exception vector
|
||||
ot(";@ Read Exception Vector:\n");
|
||||
ot(" mov r0,r8,lsr #24\n");
|
||||
ot(" mov r0,r0,lsl #2\n");
|
||||
MemHandler(0,2,0,0);
|
||||
ot(" ldr r3,[r7,#0x60] ;@ Get Memory base\n");
|
||||
#if USE_CHECKPC_CALLBACK
|
||||
ot(" add lr,pc,#4\n");
|
||||
ot(" add r0,r0,r3 ;@ r0 = Memory Base + New PC\n");
|
||||
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||
ot(" mov r4,r0\n");
|
||||
#else
|
||||
ot(" bic r4,r0,#1\n");
|
||||
#endif
|
||||
#else
|
||||
ot(" add r4,r0,r3 ;@ r4 = Memory Base + New PC\n");
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||
ot(" bic r4,r4,#1\n");
|
||||
#endif
|
||||
#endif
|
||||
ot("\n");
|
||||
|
||||
// 4. Resume execution.
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||
ot(" tst r4,#1\n");
|
||||
ot(" bne ExceptionAddressError_r_prg_r4\n");
|
||||
#endif
|
||||
ot(" bx r11 ;@ Return\n");
|
||||
ot("\n");
|
||||
|
||||
// --------------
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO
|
||||
// first some wrappers: I see no point inlining this code,
|
||||
// as it will be executed in really rare cases.
|
||||
AddressErrorWrapper('r', "data", 0x11);
|
||||
AddressErrorWrapper('r', "prg", 0x12);
|
||||
AddressErrorWrapper('w', "data", 0x01);
|
||||
// there are no program writes
|
||||
// cpu space is only for bus errors?
|
||||
ot("ExceptionAddressError_r_prg_r4%s\n", ms?"":":");
|
||||
ot(" ldr r1,[r7,#0x44]\n");
|
||||
ot(" ldr r3,[r7,#0x60] ;@ Get Memory base\n");
|
||||
ot(" mov r10,#0x12\n");
|
||||
ot(" sub r11,r4,r3\n");
|
||||
ot(" tst r1,#0x20\n");
|
||||
ot(" orrne r10,r10,#4\n");
|
||||
ot("\n");
|
||||
|
||||
ot("ExceptionAddressError%s\n", ms?"":":");
|
||||
ot(";@ r10 - info word (without instruction/not bit), r11 - faulting address\n");
|
||||
|
||||
// 1. Make a temporary copy of the status register and set the status register for exception processing.
|
||||
ot(" ldrb r0,[r7,#0x44] ;@ Get old SR high\n");
|
||||
ot(" ldr r2,[r7,#0x58] ;@ state flags\n");
|
||||
ot(" and r3,r0,#0x27 ;@ clear trace and unused flags\n");
|
||||
ot(" orr r3,r3,#0x20 ;@ set supervisor mode\n");
|
||||
ot(" strb r3,[r7,#0x44] ;@ Put new SR high\n");
|
||||
ot(" bic r2,r2,#3 ;@ clear stopped and trace states\n");
|
||||
ot(" tst r2,#4\n");
|
||||
ot(" orrne r10,r10,#8 ;@ complete info word\n");
|
||||
ot(" orr r2,r2,#4 ;@ set activity bit: 'not processing instruction'\n");
|
||||
#if EMULATE_HALT
|
||||
ot(" tst r2,#8\n");
|
||||
ot(" orrne r2,r2,#0x10 ;@ HALT\n");
|
||||
ot(" orr r2,r2,#8 ;@ processing address error\n");
|
||||
ot(" str r2,[r7,#0x58]\n");
|
||||
ot(" movne r5,#0\n");
|
||||
ot(" bne CycloneEndNoBack ;@ bye bye\n");
|
||||
#else
|
||||
ot(" str r2,[r7,#0x58]\n");
|
||||
#endif
|
||||
ot(" and r9,r9,#0xf0000000\n");
|
||||
ot(" orr r9,r9,r0,lsl #4 ;@ some preparations for SR push\n");
|
||||
ot("\n");
|
||||
|
||||
// 3. Save the current processor context + additional information.
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n");
|
||||
ot(" tst r9,#0x200\n");
|
||||
ot(";@ get our SP:\n");
|
||||
ot(" ldreq r2,[r7,#0x48] ;@ ...or OSP as our stack pointer\n");
|
||||
ot(" streq r0,[r7,#0x48]\n");
|
||||
ot(" moveq r0,r2\n");
|
||||
// PC
|
||||
ot(";@ Push old PC onto stack\n");
|
||||
ot(" ldr r1,[r7,#0x60] ;@ Get Memory base\n");
|
||||
ot(" sub r0,r0,#4 ;@ Predecremented A7\n");
|
||||
ot(" sub r1,r4,r1 ;@ r1 = Old PC\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,2,0,EMULATE_HALT);
|
||||
// SR
|
||||
ot(";@ Push old SR:\n");
|
||||
ot(" ldr r0,[r7,#0x4c] ;@ X bit\n");
|
||||
ot(" mov r1,r9,ror #28 ;@ ____NZCV\n");
|
||||
ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n");
|
||||
ot(" tst r2,#1 ;@ 1 if C!=V\n");
|
||||
ot(" eorne r1,r1,#3 ;@ ____NZVC\n");
|
||||
ot(" and r0,r0,#0x20000000\n");
|
||||
ot(" orr r1,r1,r0,lsr #25 ;@ ___XNZVC\n");
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ A7\n");
|
||||
ot(" and r9,r9,#0xf0000000\n");
|
||||
ot(" sub r0,r0,#2 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,1,0,0);
|
||||
// IR (instruction register)
|
||||
ot(";@ Push IR:\n");
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ A7\n");
|
||||
ot(" mov r1,r8\n");
|
||||
ot(" sub r0,r0,#2 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,1,0,0);
|
||||
// access address
|
||||
ot(";@ Push address:\n");
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ A7\n");
|
||||
ot(" mov r1,r11\n");
|
||||
ot(" sub r0,r0,#4 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,2,0,0);
|
||||
// information word
|
||||
ot(";@ Push info word:\n");
|
||||
ot(" ldr r0,[r7,#0x3c] ;@ A7\n");
|
||||
ot(" mov r1,r10\n");
|
||||
ot(" sub r0,r0,#2 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,1,0,0);
|
||||
ot("\n");
|
||||
|
||||
// 2. Obtain the exception vector
|
||||
ot(";@ Read Exception Vector:\n");
|
||||
ot(" mov r0,#0x0c\n");
|
||||
MemHandler(0,2,0,0);
|
||||
ot(" ldr r3,[r7,#0x60] ;@ Get Memory base\n");
|
||||
#if USE_CHECKPC_CALLBACK
|
||||
ot(" add lr,pc,#4\n");
|
||||
ot(" add r0,r0,r3 ;@ r0 = Memory Base + New PC\n");
|
||||
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
|
||||
ot(" mov r4,r0\n");
|
||||
#else
|
||||
ot(" add r4,r0,r3 ;@ r4 = Memory Base + New PC\n");
|
||||
#endif
|
||||
ot("\n");
|
||||
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP && EMULATE_HALT
|
||||
ot(" tst r4,#1\n");
|
||||
ot(" bne ExceptionAddressError_r_prg_r4\n");
|
||||
#else
|
||||
ot(" bic r4,r4,#1\n");
|
||||
#endif
|
||||
|
||||
// 4. Resume execution.
|
||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
||||
ot(" subs r5,r5,#50 ;@ Subtract cycles\n");
|
||||
ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
|
||||
ot(" b CycloneEnd\n");
|
||||
ot("\n");
|
||||
#endif
|
||||
|
||||
// --------------
|
||||
#if EMULATE_TRACE
|
||||
// expects srh and irq level in r1, next opcode already fetched to r8
|
||||
ot("CycloneDoTraceWithChecks%s\n", ms?"":":");
|
||||
ot(" ldr r0,[r7,#0x58]\n");
|
||||
ot(" cmp r5,#0\n");
|
||||
ot(" orr r0,r0,#2 ;@ go to trace mode\n");
|
||||
ot(" str r0,[r7,#0x58]\n");
|
||||
ot(" blt CycloneEnd\n"); // should take care of situation where we come here when already tracing
|
||||
ot(";@ CheckInterrupt:\n");
|
||||
ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n");
|
||||
ot(" beq CycloneDoTrace\n");
|
||||
ot(" cmp r0,#6 ;@ irq>6 ?\n");
|
||||
ot(" andle r1,r1,#7 ;@ Get interrupt mask\n");
|
||||
ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n");
|
||||
ot(" bgt CycloneDoInterruptGoBack\n");
|
||||
ot("\n");
|
||||
|
||||
// expects next opcode to be already fetched to r8
|
||||
ot("CycloneDoTrace%s\n", ms?"":":");
|
||||
ot(" str r5,[r7,#0x9c] ;@ save cycles\n");
|
||||
ot(" ldr r1,[r7,#0x98]\n");
|
||||
ot(" mov r5,#0\n");
|
||||
ot(" str r1,[r7,#0xa0]\n");
|
||||
ot(" adr r0,TraceEnd\n");
|
||||
ot(" str r0,[r7,#0x98] ;@ store TraceEnd as CycloneEnd hadler\n");
|
||||
ot(" ldr pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
|
||||
ot("\n");
|
||||
|
||||
ot("TraceEnd%s\n", ms?"":":");
|
||||
ot(" ldr r2,[r7,#0x58]\n");
|
||||
ot(" ldr r0,[r7,#0x9c] ;@ restore cycles\n");
|
||||
ot(" ldr r1,[r7,#0xa0] ;@ old CycloneEnd handler\n");
|
||||
ot(" mov r9,r9,lsl #28\n");
|
||||
ot(" add r5,r0,r5\n");
|
||||
ot(" str r1,[r7,#0x98]\n");
|
||||
ot(";@ still tracing?\n"); // exception might have happend
|
||||
ot(" tst r2,#2\n");
|
||||
ot(" beq TraceDisabled\n");
|
||||
ot(";@ trace exception\n");
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO
|
||||
ot(" ldr r1,[r7,#0x58]\n");
|
||||
ot(" mov r0,#9\n");
|
||||
ot(" orr r1,r1,#4 ;@ set activity bit: 'not processing instruction'\n");
|
||||
ot(" str r1,[r7,#0x58]\n");
|
||||
#else
|
||||
ot(" mov r0,#9\n");
|
||||
#endif
|
||||
ot(" bl Exception\n");
|
||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
||||
ot(" subs r5,r5,#34 ;@ Subtract cycles\n");
|
||||
ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
|
||||
ot(" b CycloneEnd\n");
|
||||
ot("\n");
|
||||
ot("TraceDisabled%s\n", ms?"":":");
|
||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
||||
ot(" cmp r5,#0\n");
|
||||
ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
|
||||
ot(" b CycloneEnd\n");
|
||||
ot("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Call Read(r0), Write(r0,r1) or Fetch(r0)
|
||||
// Trashes r0-r3,r12,lr
|
||||
int MemHandler(int type,int size,int addrreg)
|
||||
int MemHandler(int type,int size,int addrreg,int need_addrerr_check)
|
||||
{
|
||||
int func=0;
|
||||
func=0x68+type*0xc+(size<<2); // Find correct offset
|
||||
|
@ -403,9 +813,24 @@ int MemHandler(int type,int size,int addrreg)
|
|||
ot(" bic r0,r%i,#0x%08x\n", addrreg, MEMHANDLERS_ADDR_MASK & 0x000000ff);
|
||||
addrreg=0;
|
||||
#endif
|
||||
|
||||
#if EMULATE_ADDRESS_ERRORS_IO
|
||||
if (size > 0 && need_addrerr_check)
|
||||
{
|
||||
ot(" add lr,pc,#4*%i\n", addrreg==0?2:3); // helps to prevent interlocks
|
||||
if (addrreg != 0) ot(" mov r0,r%i\n", addrreg);
|
||||
ot(" tst r0,#1 ;@ address error?\n");
|
||||
switch (type) {
|
||||
case 0: ot(" bne ExceptionAddressError_r_data\n"); break;
|
||||
case 1: ot(" bne ExceptionAddressError_w_data\n"); break;
|
||||
case 2: ot(" bne ExceptionAddressError_r_prg\n"); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (addrreg != 0)
|
||||
{
|
||||
ot(" add lr,pc,#4\n"); // helps to prevent interlocks
|
||||
ot(" add lr,pc,#4\n");
|
||||
ot(" mov r0,r%i\n", addrreg);
|
||||
}
|
||||
else
|
||||
|
@ -442,7 +867,14 @@ static void PrintOpcodes()
|
|||
|
||||
// Emit null opcode:
|
||||
ot("Op____%s ;@ Called if an opcode is not recognised\n", ms?"":":");
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO
|
||||
ot(" ldr r1,[r7,#0x58]\n");
|
||||
ot(" sub r4,r4,#2\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\n");
|
||||
#endif
|
||||
#if USE_UNRECOGNIZED_CALLBACK
|
||||
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
|
||||
ot(" mov r1,r9,lsr #28\n");
|
||||
|
@ -457,10 +889,10 @@ static void PrintOpcodes()
|
|||
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
||||
ot(" mov r9,r9,lsl #28\n");
|
||||
ot(" tst r0,r0\n");
|
||||
ot(" moveq r0,#0x10\n");
|
||||
ot(" moveq r0,#4\n");
|
||||
ot(" bleq Exception\n");
|
||||
#else
|
||||
ot(" mov r0,#0x10\n");
|
||||
ot(" mov r0,#4\n");
|
||||
ot(" bl Exception\n");
|
||||
#endif
|
||||
ot("\n");
|
||||
|
@ -484,10 +916,10 @@ static void PrintOpcodes()
|
|||
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
||||
ot(" mov r9,r9,lsl #28\n");
|
||||
ot(" tst r0,r0\n");
|
||||
ot(" moveq r0,#0x28\n");
|
||||
ot(" moveq r0,#0x0a\n");
|
||||
ot(" bleq Exception\n");
|
||||
#else
|
||||
ot(" mov r0,#0x28\n");
|
||||
ot(" mov r0,#0x0a\n");
|
||||
ot(" bl Exception\n");
|
||||
#endif
|
||||
ot("\n");
|
||||
|
@ -510,10 +942,10 @@ static void PrintOpcodes()
|
|||
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
||||
ot(" mov r9,r9,lsl #28\n");
|
||||
ot(" tst r0,r0\n");
|
||||
ot(" moveq r0,#0x2c\n");
|
||||
ot(" moveq r0,#0x0b\n");
|
||||
ot(" bleq Exception\n");
|
||||
#else
|
||||
ot(" mov r0,#0x2c\n");
|
||||
ot(" mov r0,#0x0b\n");
|
||||
ot(" bl Exception\n");
|
||||
#endif
|
||||
ot("\n");
|
||||
|
@ -661,6 +1093,7 @@ static int CycloneMake()
|
|||
{
|
||||
int i;
|
||||
char *name="Cyclone.s";
|
||||
const char *globl=ms?"export":".global";
|
||||
|
||||
// Open the assembly file
|
||||
if (ms) name="Cyclone.asm";
|
||||
|
@ -680,32 +1113,24 @@ static int CycloneMake()
|
|||
for(i=0xa000; i<0xb000; i++) CyJump[i] = -2; // a-line emulation
|
||||
for(i=0xf000; i<0x10000; i++) CyJump[i] = -3; // f-line emulation
|
||||
|
||||
if (ms)
|
||||
{
|
||||
ot(" area |.text|, code\n");
|
||||
ot(" export CycloneInit\n");
|
||||
ot(" export CycloneRun\n");
|
||||
ot(" export CycloneSetSr\n");
|
||||
ot(" export CycloneGetSr\n");
|
||||
ot(" export CycloneSetRealTAS\n");
|
||||
ot(" export CycloneVer\n");
|
||||
ot("\n");
|
||||
ot("CycloneVer dcd 0x%.4x\n",CycloneVer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ot(" .global CycloneInit\n");
|
||||
ot(" .global CycloneRun\n");
|
||||
ot(" .global CycloneSetSr\n");
|
||||
ot(" .global CycloneGetSr\n");
|
||||
ot(" .global CycloneVer\n");
|
||||
ot(ms?" area |.text|, code\n":" .text\n .align 4\n\n");
|
||||
ot(" %s CycloneInit\n",globl);
|
||||
ot(" %s CycloneRun\n",globl);
|
||||
ot(" %s CycloneSetSr\n",globl);
|
||||
ot(" %s CycloneGetSr\n",globl);
|
||||
ot(" %s CycloneFlushIrq\n",globl);
|
||||
ot(" %s CyclonePack\n",globl);
|
||||
ot(" %s CycloneUnpack\n",globl);
|
||||
ot(" %s CycloneVer\n",globl);
|
||||
#if (CYCLONE_FOR_GENESIS == 2)
|
||||
ot(" .global CycloneSetRealTAS\n");
|
||||
ot(" .global CycloneDoInterrupt\n");
|
||||
ot(" .global CycloneJumpTab\n");
|
||||
ot(" %s CycloneSetRealTAS\n",globl);
|
||||
ot(" %s CycloneDoInterrupt\n",globl);
|
||||
ot(" %s CycloneDoTrace\n",globl);
|
||||
ot(" %s CycloneJumpTab\n",globl);
|
||||
#endif
|
||||
ot("CycloneVer: .long 0x%.4x\n",CycloneVer);
|
||||
}
|
||||
ot("\n");
|
||||
ot(ms?"CycloneVer dcd 0x":"CycloneVer: .long 0x");
|
||||
ot("%.4x\n",CycloneVer);
|
||||
ot("\n");
|
||||
|
||||
PrintFramework();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
#include "app.h"
|
||||
|
||||
int opend_op_changes_cycles, opend_check_interrupt, opend_check_trace;
|
||||
|
||||
static unsigned char OpData[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
|
||||
static unsigned short CPU_CALL OpRead16(unsigned int a)
|
||||
|
@ -62,15 +64,17 @@ void OpStart(int op, int sea, int tea, int op_changes_cycles, int supervisor_che
|
|||
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 op_changes_cycles, int check_interrupt)
|
||||
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 (op_changes_cycles)
|
||||
if (opend_op_changes_cycles)
|
||||
{
|
||||
ot(" ldr r0,[r7,#0x5c] ;@ Load Cycles\n");
|
||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
||||
|
@ -85,12 +89,22 @@ void OpEnd(int sea, int tea, int op_changes_cycles, int check_interrupt)
|
|||
#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 (check_interrupt)
|
||||
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");
|
||||
ot(" ldr r1,[r7,#0x44] ;@ Get SR high T_S__III and irq level\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");
|
||||
|
|
|
@ -346,7 +346,7 @@ int OpMul(int op)
|
|||
if (type==0) // div
|
||||
{
|
||||
ot("divzero%.4x%s\n",op,ms?"":":");
|
||||
ot(" mov r0,#0x14 ;@ Divide by zero\n");
|
||||
ot(" mov r0,#5 ;@ Divide by zero\n");
|
||||
ot(" bl Exception\n");
|
||||
Cycles+=38;
|
||||
OpEnd(ea);
|
||||
|
@ -553,16 +553,18 @@ int OpAritha(int op)
|
|||
|
||||
// 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)
|
||||
//if (type == 1)
|
||||
{
|
||||
EaCalcReadNoSE(-1,0,sea,size,0x003f);
|
||||
EaCalcReadNoSE(type!=1?10:-1,11,dea,2,0x0e00);
|
||||
}
|
||||
#if 0
|
||||
else
|
||||
{
|
||||
EaCalcReadNoSE(type!=1?10:-1,11,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");
|
||||
|
@ -803,7 +805,7 @@ int OpChk(int op)
|
|||
OpEnd(ea);
|
||||
|
||||
ot("chktrap%.4x%s ;@ CHK exception:\n",op,ms?"":":");
|
||||
ot(" mov r0,#0x18\n");
|
||||
ot(" mov r0,#6\n");
|
||||
ot(" bl Exception\n");
|
||||
Cycles+=40;
|
||||
OpEnd(ea);
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
|
||||
#include "app.h"
|
||||
|
||||
static void CheckPc(int reg)
|
||||
// in/out address in r0, trashes all temp regs
|
||||
static void CheckPc(void)
|
||||
{
|
||||
#if USE_CHECKPC_CALLBACK
|
||||
ot(";@ Check Memory Base+pc (r%i)\n",reg);
|
||||
if (reg != 0)
|
||||
ot(" mov r0,r%i\n", reg);
|
||||
ot(";@ Check Memory Base+pc\n");
|
||||
ot(" mov lr,pc\n");
|
||||
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
|
||||
ot(" mov r4,r0\n");
|
||||
#else
|
||||
ot(" bic r4,r%d,#1\n",reg); // we do not emulate address errors
|
||||
#endif
|
||||
ot("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Push 32-bit value in r1 - trashes r0-r3,r12,lr
|
||||
|
@ -61,7 +57,12 @@ static void PopPc()
|
|||
MemHandler(0,2);
|
||||
ot(" add r0,r0,r10 ;@ Memory Base+PC\n");
|
||||
ot("\n");
|
||||
CheckPc(0);
|
||||
CheckPc();
|
||||
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||
ot(" mov r4,r0\n");
|
||||
#else
|
||||
ot(" bic r4,r0,#1\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
int OpTrap(int op)
|
||||
|
@ -73,8 +74,7 @@ int OpTrap(int op)
|
|||
|
||||
OpStart(op,0x10);
|
||||
ot(" and r0,r8,#0xf ;@ Get trap number\n");
|
||||
ot(" orr r0,r0,#0x20\n");
|
||||
ot(" mov r0,r0,asl #2\n");
|
||||
ot(" orr r0,r0,#0x20 ;@ 32+n\n");
|
||||
ot(" bl Exception\n");
|
||||
ot("\n");
|
||||
|
||||
|
@ -177,14 +177,30 @@ int Op4E70(int op)
|
|||
PopSr(1);
|
||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||
PopPc();
|
||||
SuperChange(op);
|
||||
OpEnd(0x10,0,0,1);
|
||||
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;
|
||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||
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;
|
||||
|
||||
|
@ -192,9 +208,10 @@ int Op4E70(int op)
|
|||
OpStart(op,0x10,0,1); Cycles=4;
|
||||
ot(" tst r9,#0x10000000\n");
|
||||
ot(" subne r5,r5,#%i\n",34);
|
||||
ot(" movne r0,#0x1c ;@ TRAPV exception\n");
|
||||
ot(" movne r0,#7 ;@ TRAPV exception\n");
|
||||
ot(" blne Exception\n");
|
||||
OpEnd(0x10,0,1);
|
||||
opend_op_changes_cycles = 1;
|
||||
OpEnd(0x10,0);
|
||||
return 0;
|
||||
|
||||
case 7: // rtr
|
||||
|
@ -202,6 +219,10 @@ int Op4E70(int op)
|
|||
PopSr(0);
|
||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||
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;
|
||||
|
||||
|
@ -231,21 +252,32 @@ int OpJsr(int op)
|
|||
ot("\n");
|
||||
EaCalc(11,0x003f,sea,0);
|
||||
|
||||
if (!(op&0x40))
|
||||
{
|
||||
ot(";@ Jsr - Push old PC first\n");
|
||||
ot(" ldr r0,[r7,#0x3c]\n");
|
||||
ot(" sub r1,r4,r10 ;@ r1 = Old PC\n");
|
||||
ot(";@ Push r1 onto stack\n");
|
||||
ot(" sub r0,r0,#4 ;@ Predecrement A7\n");
|
||||
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
|
||||
MemHandler(1,2);
|
||||
}
|
||||
ot(";@ Jump - Get new PC from r11\n");
|
||||
ot(" add r0,r11,r10 ;@ Memory Base + New PC\n");
|
||||
ot("\n");
|
||||
CheckPc();
|
||||
if (!(op&0x40))
|
||||
{
|
||||
ot(" ldr r2,[r7,#0x3c]\n");
|
||||
ot(" sub r1,r4,r10 ;@ 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
|
||||
|
||||
CheckPc(0);
|
||||
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);
|
||||
|
@ -310,14 +342,21 @@ int OpDbra(int op)
|
|||
ot(";@ Check if Dn.w is -1\n");
|
||||
ot(" cmn r0,#1\n");
|
||||
|
||||
#if USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA
|
||||
#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 ;@ r4 = New PC\n");
|
||||
CheckPc(0);
|
||||
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");
|
||||
|
@ -343,7 +382,7 @@ int OpDbra(int op)
|
|||
OpEnd();
|
||||
}
|
||||
|
||||
#if USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA
|
||||
#if (USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA) || EMULATE_ADDRESS_ERRORS_JUMP
|
||||
if (op==0x51c8)
|
||||
{
|
||||
ot(";@ Dn.w is -1:\n");
|
||||
|
@ -451,15 +490,15 @@ int OpBranch(int op)
|
|||
#if USE_CHECKPC_CALLBACK
|
||||
if (offset==-1) checkpc=1;
|
||||
#endif
|
||||
if (checkpc)
|
||||
{
|
||||
CheckPc(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ot(" bic r4,r0,#1\n"); // we do not emulate address errors
|
||||
ot("\n");
|
||||
}
|
||||
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);
|
||||
|
||||
|
|
|
@ -207,6 +207,7 @@ int OpNeg(int op)
|
|||
ot("\n");
|
||||
}
|
||||
|
||||
if (type==1) eawrite_check_addrerr=1;
|
||||
EaWrite(10, 1,ea,size,0x003f,0,0);
|
||||
|
||||
OpEnd(ea);
|
||||
|
@ -360,10 +361,12 @@ int OpSet(int op)
|
|||
|
||||
ot("\n");
|
||||
|
||||
eawrite_check_addrerr=1;
|
||||
EaCalc (0,0x003f, ea,size,0,0);
|
||||
EaWrite(0, 1, ea,size,0x003f,0,0);
|
||||
|
||||
OpEnd(ea,0,changed_cycles);
|
||||
opend_op_changes_cycles=changed_cycles;
|
||||
OpEnd(ea,0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -614,7 +617,8 @@ int OpAsr(int op)
|
|||
|
||||
EaWrite(10, 0, ea,size,0x0007,1);
|
||||
|
||||
OpEnd(ea,0,count<0);
|
||||
opend_op_changes_cycles = (count<0);
|
||||
OpEnd(ea,0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ void OpFlagsToReg(int high)
|
|||
|
||||
// Convert SR/CRR register in r0 to our flags
|
||||
// trashes r0,r1
|
||||
void OpRegToFlags(int high)
|
||||
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");
|
||||
|
@ -32,9 +32,10 @@ void OpRegToFlags(int high)
|
|||
|
||||
if (high)
|
||||
{
|
||||
ot(" mov r0,r0,ror #8\n");
|
||||
ot(" and r0,r0,#0xa7 ;@ only take defined bits\n");
|
||||
ot(" strb r0,[r7,#0x44] ;@ Store SR high\n");
|
||||
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");
|
||||
}
|
||||
|
@ -44,8 +45,15 @@ 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(" mov r0,#0x20 ;@ privilege violation\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);
|
||||
|
@ -53,13 +61,15 @@ void SuperEnd(void)
|
|||
|
||||
// does OSP and A7 swapping if needed
|
||||
// new or old SR (not the one already in [r7,#0x44]) should be passed in r11
|
||||
// trashes r0,r11
|
||||
void SuperChange(int op,int load_srh)
|
||||
// uses srh from srh_reg (loads if < 0), trashes r0,r11
|
||||
void SuperChange(int op,int srh_reg)
|
||||
{
|
||||
ot(";@ A7 <-> OSP?\n");
|
||||
if (load_srh)
|
||||
if (srh_reg < 0) {
|
||||
ot(" ldr r0,[r7,#0x44] ;@ Get other SR high\n");
|
||||
ot(" eor r0,r0,r11\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");
|
||||
|
@ -122,6 +132,7 @@ int OpMove(int op)
|
|||
|
||||
if (movea) size=2; // movea always expands to 32-bits
|
||||
|
||||
eawrite_check_addrerr=1;
|
||||
#if SPLIT_MOVEL_PD
|
||||
if ((tea&0x38)==0x20 && size==2) { // -(An)
|
||||
EaCalc (10,0x0e00,tea,size,0,0);
|
||||
|
@ -167,6 +178,7 @@ int OpLea(int op)
|
|||
|
||||
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);
|
||||
|
@ -214,6 +226,7 @@ int OpMoveSr(int op)
|
|||
|
||||
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);
|
||||
|
@ -222,13 +235,17 @@ int OpMoveSr(int op)
|
|||
if (type==2 || type==3)
|
||||
{
|
||||
EaCalcReadNoSE(-1,0,ea,size,0x003f);
|
||||
OpRegToFlags(type==3);
|
||||
OpRegToFlags(type==3,1);
|
||||
if (type==3) {
|
||||
SuperChange(op,0);
|
||||
SuperChange(op,1);
|
||||
opend_check_interrupt = 1;
|
||||
opend_check_trace = 1;
|
||||
OpEnd(ea);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
OpEnd(ea,0,0,type==3);
|
||||
OpEnd(ea);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -239,6 +256,7 @@ 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?
|
||||
|
@ -249,19 +267,53 @@ int OpArithSr(int op)
|
|||
|
||||
OpStart(op,ea,0,0,size!=0); Cycles=16;
|
||||
|
||||
EaCalc(10,0x003f,ea,size);
|
||||
EaRead(10, 10,ea,size,0x003f);
|
||||
EaCalcRead(-1,0,ea,size,0x003f);
|
||||
|
||||
OpFlagsToReg(size);
|
||||
if (type==0) ot(" orr r0,r1,r10\n");
|
||||
if (type==1) ot(" and r0,r1,r10\n");
|
||||
if (type==5) ot(" eor r0,r1,r10\n");
|
||||
OpRegToFlags(size);
|
||||
if (size && type!=0) { // we can't enter supervisor mode, nor unmask irqs just by using OR
|
||||
SuperChange(op,0);
|
||||
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 r9,r9,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 r9,r9,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 r9,r9,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);
|
||||
}
|
||||
}
|
||||
|
||||
OpEnd(ea,0,0,size!=0 && type!=0);
|
||||
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;
|
||||
}
|
||||
|
@ -338,6 +390,13 @@ int OpMovem(int op)
|
|||
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 r10,r10,#%d ;@ r10=Next Register\n",decr?-4:4);
|
||||
|
@ -350,6 +409,7 @@ int OpMovem(int op)
|
|||
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,r10] ;@ Save value into Dn/An\n");
|
||||
}
|
||||
|
@ -391,7 +451,8 @@ int OpMovem(int op)
|
|||
|
||||
Cycles+=Ea_add_ns(g_movem_cycle_table,ea);
|
||||
|
||||
OpEnd(ea,0,1);
|
||||
opend_op_changes_cycles = 1;
|
||||
OpEnd(ea);
|
||||
ltorg();
|
||||
ot("\n");
|
||||
|
||||
|
@ -413,6 +474,7 @@ int OpMoveUsp(int op)
|
|||
|
||||
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);
|
||||
|
@ -568,11 +630,12 @@ int OpStopReset(int op)
|
|||
|
||||
ot("\n");
|
||||
|
||||
ot(" mov r0,#1\n");
|
||||
ot(" str r0,[r7,#0x58] ;@ stopped\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");
|
||||
|
||||
ot(" mov r5,#0 ;@ eat cycles\n");
|
||||
Cycles = 4;
|
||||
ot("\n");
|
||||
}
|
||||
|
|
|
@ -4,12 +4,17 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.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[];
|
||||
|
@ -35,15 +40,16 @@ 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 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 op_changes_cycles=0,int check_interrupt=0);
|
||||
void OpEnd(int sea=0,int tea=0);
|
||||
int OpBase(int op,int size,int sepa=0);
|
||||
void OpAny(int op);
|
||||
|
||||
|
@ -90,7 +96,7 @@ int OpTas(int op, int gen_special=0);
|
|||
int OpMove(int op);
|
||||
int OpLea(int op);
|
||||
void OpFlagsToReg(int high);
|
||||
void OpRegToFlags(int high);
|
||||
void OpRegToFlags(int high,int srh_reg=0);
|
||||
int OpMoveSr(int op);
|
||||
int OpArithSr(int op);
|
||||
int OpPea(int op);
|
||||
|
@ -101,5 +107,5 @@ int OpExg(int op);
|
|||
int OpMovep(int op);
|
||||
int OpStopReset(int op);
|
||||
void SuperEnd(void);
|
||||
void SuperChange(int op,int load_srh=1);
|
||||
void SuperChange(int op,int srh_reg=-1);
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
|
||||
/*
|
||||
* If this option is enabled, Microsoft ARMASM compatible output is generated.
|
||||
* Otherwise GNU as syntax is used.
|
||||
* 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
|
||||
#define USE_MS_SYNTAX 0
|
||||
|
||||
/*
|
||||
* Enable this option if you are going to use Cyclone to emulate Genesis /
|
||||
|
@ -18,7 +18,7 @@
|
|||
* the write-back phase. That will be emulated, if this option is enabled.
|
||||
* This option also alters timing slightly.
|
||||
*/
|
||||
#define CYCLONE_FOR_GENESIS 0
|
||||
#define CYCLONE_FOR_GENESIS 0
|
||||
|
||||
/*
|
||||
* This option compresses Cyclone's jumptable. Because of this the executable
|
||||
|
@ -27,7 +27,7 @@
|
|||
* Warning: if you enable this, you MUST call CycloneInit() before calling
|
||||
* CycloneRun(), or else it will crash.
|
||||
*/
|
||||
#define COMPRESS_JUMPTABLE 1
|
||||
#define COMPRESS_JUMPTABLE 1
|
||||
|
||||
/*
|
||||
* Address mask for memory hadlers. The bits set will be masked out of address
|
||||
|
@ -35,51 +35,62 @@
|
|||
* 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
|
||||
#define MEMHANDLERS_ADDR_MASK 0
|
||||
|
||||
/*
|
||||
* Cyclone keeps the 4 least significant bits of SR, PC+membase and it's cycle
|
||||
* count in ARM registers instead of the context for performance reasons. If you for
|
||||
* 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 + unknown amount).
|
||||
* 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.
|
||||
* 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 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 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 enabled, Cyclone will call IrqCallback routine from it's context whenever it
|
||||
* acknowledges an IRQ. IRQ level is not cleared automatically, do this in your
|
||||
* hadler if needed. PC, flags and cycles are valid in the context and can be read.
|
||||
* If disabled, it simply clears the IRQ level and continues execution.
|
||||
* 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 1
|
||||
#define USE_INT_ACK_CALLBACK 0
|
||||
|
||||
/*
|
||||
* Enable this if you need/change PC, flags or cycles in your IrqCallback function.
|
||||
* 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_STUFF 0
|
||||
#define INT_ACK_NEEDS_STUFF 0
|
||||
#define INT_ACK_CHANGES_CYCLES 0
|
||||
|
||||
/*
|
||||
* If enabled, ResetCallback is called from the context, whenever RESET opcode is
|
||||
* 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
|
||||
#define USE_RESET_CALLBACK 0
|
||||
|
||||
/*
|
||||
* If enabled, UnrecognizedCallback is called if an invalid opcode is
|
||||
|
@ -91,34 +102,34 @@
|
|||
* If disabled, "Illegal Instruction" exception is generated and execution is
|
||||
* continued.
|
||||
*/
|
||||
#define USE_UNRECOGNIZED_CALLBACK 1
|
||||
#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 1
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#define USE_CHECKPC_DBRA 0
|
||||
|
||||
/*
|
||||
* When this option is enabled Cyclone will do two word writes instead of one
|
||||
|
@ -126,4 +137,36 @@
|
|||
* 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
|
||||
#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
|
||||
|
||||
|
|
172
cpu/Cyclone/config_mamegp2x.h
Normal file
172
cpu/Cyclone/config_mamegp2x.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This option also alters timing slightly.
|
||||
*/
|
||||
#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 0xff000000
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
172
cpu/Cyclone/config_pico.h
Normal file
172
cpu/Cyclone/config_pico.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This option also alters timing slightly.
|
||||
*/
|
||||
#define CYCLONE_FOR_GENESIS 2
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
#define MEMHANDLERS_CHANGE_PC 0
|
||||
#define MEMHANDLERS_CHANGE_FLAGS 0
|
||||
#define MEMHANDLERS_CHANGE_CYCLES 0
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
172
cpu/Cyclone/config_uae4all.h
Normal file
172
cpu/Cyclone/config_uae4all.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This option also alters timing slightly.
|
||||
*/
|
||||
#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 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
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* 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 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 1
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
CFLAGS = -Wall
|
||||
ifdef CONFIG_FILE
|
||||
CFLAGS += -DCONFIG_FILE=\"$(CONFIG_FILE)\"
|
||||
endif
|
||||
|
||||
all : cyclone.s
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Makefile for MS Visual C
|
||||
|
||||
CPP=cl.exe
|
||||
CPP_PROJ=/nologo /ML /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" $(RC_FLAGS) /YX /FD /c
|
||||
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
|
||||
|
@ -37,7 +37,7 @@ OpMove.obj : ..\OpMove.cpp ..\app.h
|
|||
$(CPP) $(CPP_PROJ) ..\OpMove.cpp
|
||||
|
||||
Disa.obj : ..\disa\Disa.c ..\disa\Disa.h
|
||||
$(CPP) /nologo /ML /W4 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /c ..\disa\Disa.c
|
||||
$(CPP) $(CPP_PROJ) ..\disa\Disa.c
|
||||
|
||||
..\app.h : ..\config.h
|
||||
|
||||
|
@ -56,5 +56,4 @@ CLEAN :
|
|||
-@erase "Cyclone.exe"
|
||||
-@erase "Cyclone.asm"
|
||||
-@erase "Cyclone.s"
|
||||
-@erase "Cyclone.o"
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ RSC=rc.exe
|
|||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# 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
|
||||
|
@ -65,8 +65,8 @@ LINK32=link.exe
|
|||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE 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
|
||||
|
|
BIN
cpu/Cyclone/tests/test_trace.bin
Executable file
BIN
cpu/Cyclone/tests/test_trace.bin
Executable file
Binary file not shown.
140
cpu/Cyclone/tests/test_trace.s
Normal file
140
cpu/Cyclone/tests/test_trace.s
Normal file
|
@ -0,0 +1,140 @@
|
|||
| 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
|
Loading…
Add table
Add a link
Reference in a new issue