mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 15:27:46 -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
|
// Cyclone 68000 Emulator - Header File
|
||||||
|
|
||||||
// Most code (c) Copyright 2004 Dave, All rights reserved.
|
// (c) Copyright 2004 Dave, All rights reserved.
|
||||||
// Some coding/bugfixing was done by notaz
|
// (c) 2005-2007 notaz
|
||||||
// Cyclone 68000 is free for non-commercial use.
|
// Cyclone 68000 is free for non-commercial use.
|
||||||
|
|
||||||
// For commercial use, separate licencing terms must be obtained.
|
// For commercial use, separate licencing terms must be obtained.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __CYCLONE_H__
|
||||||
|
#define __CYCLONE_H__
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,46 +21,74 @@ struct Cyclone
|
||||||
{
|
{
|
||||||
unsigned int d[8]; // [r7,#0x00]
|
unsigned int d[8]; // [r7,#0x00]
|
||||||
unsigned int a[8]; // [r7,#0x20]
|
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 srh; // [r7,#0x44] Status Register high (T_S__III)
|
||||||
unsigned char unused; // [r7,#0x45] Unused
|
unsigned char unused; // [r7,#0x45] Unused
|
||||||
unsigned char flags; // [r7,#0x46] Flags (ARM order: ____NZCV) [68k order is XNZVC]
|
unsigned char flags; // [r7,#0x46] Flags (ARM order: ____NZCV) [68k order is XNZVC]
|
||||||
unsigned char irq; // [r7,#0x47] IRQ level
|
unsigned char irq; // [r7,#0x47] IRQ level
|
||||||
unsigned int osp; // [r7,#0x48] Other Stack Pointer (USP/SSP)
|
unsigned int osp; // [r7,#0x48] Other Stack Pointer (USP/SSP)
|
||||||
unsigned int xc; // [r7,#0x4c] Extend flag (bit29: ??X? _)
|
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 prev_pc; // [r7,#0x50] Set to start address of currently executed opcode + 2 (if enabled in config.h)
|
||||||
unsigned int unused1; // [r7,#0x54] Unused
|
unsigned int reserved;// [r7,#0x54] Reserved for possible future use
|
||||||
int stopped; // [r7,#0x58] 1 == processor is in stopped state
|
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]
|
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)
|
int membase; // [r7,#0x60] Memory Base (ARM address minus 68000 address)
|
||||||
unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] - Called to recalc Memory Base+pc
|
unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] called to recalc Memory Base+pc
|
||||||
unsigned char (*read8 )(unsigned int a); // [r7,#0x68]
|
unsigned int (*read8 )(unsigned int a); // [r7,#0x68]
|
||||||
unsigned short (*read16 )(unsigned int a); // [r7,#0x6c]
|
unsigned int (*read16 )(unsigned int a); // [r7,#0x6c]
|
||||||
unsigned int (*read32 )(unsigned int a); // [r7,#0x70]
|
unsigned int (*read32 )(unsigned int a); // [r7,#0x70]
|
||||||
void (*write8 )(unsigned int a,unsigned char d); // [r7,#0x74]
|
void (*write8 )(unsigned int a,unsigned char d); // [r7,#0x74]
|
||||||
void (*write16)(unsigned int a,unsigned short d); // [r7,#0x78]
|
void (*write16)(unsigned int a,unsigned short d); // [r7,#0x78]
|
||||||
void (*write32)(unsigned int a,unsigned int d); // [r7,#0x7c]
|
void (*write32)(unsigned int a,unsigned int d); // [r7,#0x7c]
|
||||||
unsigned char (*fetch8 )(unsigned int a); // [r7,#0x80]
|
unsigned int (*fetch8 )(unsigned int a); // [r7,#0x80]
|
||||||
unsigned short (*fetch16)(unsigned int a); // [r7,#0x84]
|
unsigned int (*fetch16)(unsigned int a); // [r7,#0x84]
|
||||||
unsigned int (*fetch32)(unsigned int a); // [r7,#0x88]
|
unsigned int (*fetch32)(unsigned int a); // [r7,#0x88]
|
||||||
void (*IrqCallback)(int int_level); // [r7,#0x8c] - optional irq callback function, see config.h
|
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.
|
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.
|
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
|
// Initialize. Used only if Cyclone was compiled with compressed jumptable, see config.h
|
||||||
void CycloneInit();
|
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);
|
void CycloneRun(struct Cyclone *pcy);
|
||||||
|
|
||||||
// utility functions to get and set SR
|
// Utility functions to get and set SR
|
||||||
void CycloneSetSr(struct Cyclone *pcy, unsigned int sr); // auto-swaps a7<->osp if detects supervisor change
|
void CycloneSetSr(struct Cyclone *pcy, unsigned int sr);
|
||||||
unsigned int CycloneGetSr(struct Cyclone *pcy);
|
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
|
// genesis: if 1, switch to normal TAS handlers
|
||||||
void CycloneSetRealTAS(int use_real);
|
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
|
#ifdef __cplusplus
|
||||||
} // End of extern "C"
|
} // End of extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // __CYCLONE_H__
|
||||||
|
|
||||||
|
|
|
@ -18,36 +18,484 @@ ___________________________________________________________________________
|
||||||
Replace (atsymbol) with @
|
Replace (atsymbol) with @
|
||||||
|
|
||||||
Additional coding and bugfixes done by notaz, 2005-2007
|
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
|
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.
|
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
|
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.
|
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
|
What's New
|
||||||
----------
|
----------
|
||||||
v0.0087 notaz
|
v0.0088 notaz
|
||||||
- Reduced amount of code in opcode handlers by ~23% by doing the following:
|
- Reduced amount of code in opcode handlers by ~23% by doing the following:
|
||||||
- Removed duplicate opcode handlers
|
- Removed duplicate opcode handlers
|
||||||
- Optimized code to use less ARM instructions
|
- Optimized code to use less ARM instructions
|
||||||
- Merged some duplicate handler endings
|
- Merged some duplicate handler endings
|
||||||
+ Cyclone now does better job avoiding pipeline interlocks.
|
+ Cyclone now does better job avoiding pipeline interlocks.
|
||||||
+ Replaced incorrect handler of DBT with proper one.
|
+ Replaced incorrect handler of DBT with proper one.
|
||||||
+ Changed "MOVEA (An)+ An" behaviour.
|
+ Changed "MOVEA (An)+ An" behavior.
|
||||||
+ Fixed flag behaviour of ROXR, ASL, LSR and NBCD in certain situations.
|
+ Fixed flag behavior of ROXR, ASL, LSR and NBCD in certain situations.
|
||||||
Hopefully got them right now.
|
Hopefully got them right now.
|
||||||
+ Cyclone no longer sets most significant bits while pushing PC to stack.
|
+ Cyclone no longer sets most significant bits while pushing PC to stack.
|
||||||
Amiga Kickstart depends on this.
|
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).
|
+ 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
|
v0.0086 notaz
|
||||||
+ Cyclone now can be customized to better suit your project, see config.h .
|
+ 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.
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OpChk(op)
|
static int OpChk(int op)
|
||||||
{
|
{
|
||||||
int sea=0,dea=0;
|
int sea=0,dea=0;
|
||||||
char seat[64]="",deat[64]="";
|
char seat[64]="",deat[64]="";
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
#include "app.h"
|
#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.
|
// some ops use non-standard cycle counts for EAs, so are listed here.
|
||||||
// all constants borrowed from the MUSASHI core by Karl Stenerud.
|
// 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)
|
if (ea<0x28)
|
||||||
{
|
{
|
||||||
int step=1<<size, strr=a;
|
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
|
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;
|
ot("\n"); return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ea>=0x3a && ea<=0x3b) MemHandler(2,size,a); // Fetch
|
if (ea>=0x3a && ea<=0x3b) MemHandler(2,size,a,earead_check_addrerr); // Fetch
|
||||||
else MemHandler(0,size,a); // Read
|
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)
|
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);
|
if (shift) ot(" mov r1,r%d,asr #%d\n",v,shift);
|
||||||
else if (v!=1) ot(" mov r1,r%d\n",v);
|
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;
|
ot("\n"); return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
static FILE *AsmFile=NULL;
|
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 *CyJump=NULL; // Jump table
|
||||||
int ms=USE_MS_SYNTAX; // If non-zero, output in Microsoft ARMASM format
|
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
|
char *Narm[4]={ "b", "h","",""}; // Normal ARM Extensions for operand sizes 0,1,2
|
||||||
|
@ -82,56 +82,19 @@ static void ChangeTAS(int norm)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// trashes all temp regs
|
#if EMULATE_ADDRESS_ERRORS_JUMP || EMULATE_ADDRESS_ERRORS_IO
|
||||||
static void PrintException(int ints)
|
static void AddressErrorWrapper(char rw, char *dataprg, int iw)
|
||||||
{
|
{
|
||||||
if(!ints) {
|
ot("ExceptionAddressError_%c_%s%s\n", rw, dataprg, ms?"":":");
|
||||||
ot(" ;@ Cause an Exception - Vector address in r0\n");
|
ot(" ldr r1,[r7,#0x44]\n");
|
||||||
ot(" mov r11,r0\n");
|
ot(" mov r10,#0x%02x\n", iw);
|
||||||
}
|
ot(" mov r11,r0\n");
|
||||||
|
ot(" tst r1,#0x20\n");
|
||||||
ot(" ldr r0,[r7,#0x44] ;@ Get SR high\n");
|
ot(" orrne r10,r10,#4\n");
|
||||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
ot(" b ExceptionAddressError\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("\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)
|
void FlushPC(void)
|
||||||
{
|
{
|
||||||
|
@ -144,6 +107,14 @@ void FlushPC(void)
|
||||||
|
|
||||||
static void PrintFramework()
|
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");
|
ot(";@ --------------------------- Framework --------------------------\n");
|
||||||
if (ms) ot("CycloneRun\n");
|
if (ms) ot("CycloneRun\n");
|
||||||
else 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(" mov r9,r9,lsl #28 ;@ r9 = Flags 0xf0000000, cpsr format\n");
|
||||||
ot(" ;@ r10 = Source value / Memory Base\n");
|
ot(" ;@ r10 = Source value / Memory Base\n");
|
||||||
ot("\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(";@ CheckInterrupt:\n");
|
||||||
ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47]
|
ot(" movs r0,r1,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47]
|
||||||
ot(" beq NoInts0\n");
|
ot(" beq NoInts0\n");
|
||||||
|
@ -170,12 +145,22 @@ static void PrintFramework()
|
||||||
ot(" bgt CycloneDoInterrupt\n");
|
ot(" bgt CycloneDoInterrupt\n");
|
||||||
ot("NoInts0%s\n", ms?"":":");
|
ot("NoInts0%s\n", ms?"":":");
|
||||||
ot("\n");
|
ot("\n");
|
||||||
ot(";@ Check if our processor is in stopped state and jump to opcode handler if not\n");
|
ot(";@ Check if our processor is in special state\n");
|
||||||
ot(" ldr r0,[r7,#0x58]\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(" ldrh r8,[r4],#2 ;@ Fetch first opcode\n");
|
||||||
ot(" tst r0,r0 ;@ stopped?\n");
|
ot(" tst r0,#0x%02x ;@ special state?\n", state_flags_to_check);
|
||||||
ot(" bne CycloneStopped\n");
|
ot(" ldreq pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
|
||||||
ot(" ldr 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");
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
|
@ -183,8 +168,8 @@ static void PrintFramework()
|
||||||
ot("CycloneEnd%s\n", ms?"":":");
|
ot("CycloneEnd%s\n", ms?"":":");
|
||||||
ot(" sub r4,r4,#2\n");
|
ot(" sub r4,r4,#2\n");
|
||||||
ot("CycloneEndNoBack%s\n", ms?"":":");
|
ot("CycloneEndNoBack%s\n", ms?"":":");
|
||||||
#if (CYCLONE_FOR_GENESIS == 2)
|
#if (CYCLONE_FOR_GENESIS == 2) || EMULATE_TRACE
|
||||||
ot(" ldr r1,[r7,#0x54]\n");
|
ot(" ldr r1,[r7,#0x98]\n");
|
||||||
ot(" mov r9,r9,lsr #28\n");
|
ot(" mov r9,r9,lsr #28\n");
|
||||||
ot(" tst r1,r1\n");
|
ot(" tst r1,r1\n");
|
||||||
ot(" bxne r1 ;@ jump to alternative CycloneEnd\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(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
|
||||||
ot(" strb r9,[r7,#0x46] ;@ Save Flags (NZCV)\n");
|
ot(" strb r9,[r7,#0x46] ;@ Save Flags (NZCV)\n");
|
||||||
ot(" ldmia sp!,{r4-r11,pc}\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();
|
ltorg();
|
||||||
|
ot("\n");
|
||||||
|
ot("\n");
|
||||||
|
|
||||||
|
ot("CycloneInit%s\n", ms?"":":");
|
||||||
#if COMPRESS_JUMPTABLE
|
#if COMPRESS_JUMPTABLE
|
||||||
ot(";@ uncompress jump table\n");
|
ot(";@ decompress jump table\n");
|
||||||
if (ms) ot("CycloneInit\n");
|
ot(" ldr r12,=CycloneJumpTab\n");
|
||||||
else ot("CycloneInit:\n");
|
ot(" add r0,r12,#0xe000*4 ;@ ctrl code pointer\n");
|
||||||
ot(" ldr r12,=CycloneJumpTab\n");
|
ot(" ldr r1,[r0,#-4]\n");
|
||||||
ot(" add r0,r12,#0xe000*4 ;@ ctrl code pointer\n");
|
ot(" tst r1,r1\n");
|
||||||
ot(" ldr r1,[r0,#-4]\n");
|
ot(" movne pc,lr ;@ already uncompressed\n");
|
||||||
ot(" tst r1,r1\n");
|
ot(" add r3,r12,#0xa000*4 ;@ handler table pointer, r12=dest\n");
|
||||||
ot(" movne pc,lr ;@ already uncompressed\n");
|
ot("unc_loop%s\n", ms?"":":");
|
||||||
ot(" add r3,r12,#0xa000*4 ;@ handler table pointer, r12=dest\n");
|
ot(" ldrh r1,[r0],#2\n");
|
||||||
ot("unc_loop%s\n", ms?"":":");
|
ot(" and r2,r1,#0xf\n");
|
||||||
ot(" ldrh r1,[r0],#2\n");
|
ot(" bic r1,r1,#0xf\n");
|
||||||
ot(" and r2,r1,#0xf\n");
|
ot(" ldr r1,[r3,r1,lsr #2] ;@ r1=handler\n");
|
||||||
ot(" bic r1,r1,#0xf\n");
|
ot(" cmp r2,#0xf\n");
|
||||||
ot(" ldr r1,[r3,r1,lsr #2] ;@ r1=handler\n");
|
ot(" addeq r2,r2,#1 ;@ 0xf is really 0x10\n");
|
||||||
ot(" cmp r2,#0xf\n");
|
ot(" tst r2,r2\n");
|
||||||
ot(" addeq r2,r2,#1 ;@ 0xf is really 0x10\n");
|
ot(" ldreqh r2,[r0],#2 ;@ counter is in next word\n");
|
||||||
ot(" tst r2,r2\n");
|
ot(" tst r2,r2\n");
|
||||||
ot(" ldreqh r2,[r0],#2 ;@ counter is in next word\n");
|
ot(" beq unc_finish ;@ done decompressing\n");
|
||||||
ot(" tst r2,r2\n");
|
ot(" tst r1,r1\n");
|
||||||
ot(" beq unc_finish ;@ done decompressing\n");
|
ot(" addeq r12,r12,r2,lsl #2 ;@ 0 handler means we should skip those bytes\n");
|
||||||
ot(" tst r1,r1\n");
|
ot(" beq unc_loop\n");
|
||||||
ot(" addeq r12,r12,r2,lsl #2 ;@ 0 handler means we should skip those bytes\n");
|
ot("unc_loop_in%s\n", ms?"":":");
|
||||||
ot(" beq unc_loop\n");
|
ot(" subs r2,r2,#1\n");
|
||||||
ot("unc_loop_in%s\n", ms?"":":");
|
ot(" str r1,[r12],#4\n");
|
||||||
ot(" subs r2,r2,#1\n");
|
ot(" bgt unc_loop_in\n");
|
||||||
ot(" str r1,[r12],#4\n");
|
ot(" b unc_loop\n");
|
||||||
ot(" bgt unc_loop_in\n");
|
ot("unc_finish%s\n", ms?"":":");
|
||||||
ot(" b unc_loop\n");
|
ot(" ldr r12,=CycloneJumpTab\n");
|
||||||
ot("unc_finish%s\n", ms?"":":");
|
ot(" ;@ set a-line and f-line handlers\n");
|
||||||
ot(" ldr r12,=CycloneJumpTab\n");
|
ot(" add r0,r12,#0xa000*4\n");
|
||||||
ot(" ;@ set a-line and f-line handlers\n");
|
ot(" ldr r1,[r0,#4] ;@ a-line handler\n");
|
||||||
ot(" add r0,r12,#0xa000*4\n");
|
ot(" ldr r3,[r0,#8] ;@ f-line handler\n");
|
||||||
ot(" ldr r1,[r0,#4] ;@ a-line handler\n");
|
ot(" mov r2,#0x1000\n");
|
||||||
ot(" ldr r3,[r0,#8] ;@ f-line handler\n");
|
ot("unc_fill3%s\n", ms?"":":");
|
||||||
ot(" mov r2,#0x1000\n");
|
ot(" subs r2,r2,#1\n");
|
||||||
ot("unc_fill3%s\n", ms?"":":");
|
ot(" str r1,[r0],#4\n");
|
||||||
ot(" subs r2,r2,#1\n");
|
ot(" bgt unc_fill3\n");
|
||||||
ot(" str r1,[r0],#4\n");
|
ot(" add r0,r12,#0xf000*4\n");
|
||||||
ot(" bgt unc_fill3\n");
|
ot(" mov r2,#0x1000\n");
|
||||||
ot(" add r0,r12,#0xf000*4\n");
|
ot("unc_fill4%s\n", ms?"":":");
|
||||||
ot(" mov r2,#0x1000\n");
|
ot(" subs r2,r2,#1\n");
|
||||||
ot("unc_fill4%s\n", ms?"":":");
|
ot(" str r3,[r0],#4\n");
|
||||||
ot(" subs r2,r2,#1\n");
|
ot(" bgt unc_fill4\n");
|
||||||
ot(" str r3,[r0],#4\n");
|
ot(" bx lr\n");
|
||||||
ot(" bgt unc_fill4\n");
|
ltorg();
|
||||||
ot(" bx lr\n");
|
|
||||||
ltorg();
|
|
||||||
ot("\n");
|
|
||||||
#else
|
#else
|
||||||
ot(";@ do nothing\n");
|
ot(";@ do nothing\n");
|
||||||
if (ms) ot("CycloneInit\n");
|
ot(" bx lr\n");
|
||||||
else ot("CycloneInit:\n");
|
|
||||||
ot(" bx lr\n");
|
|
||||||
ot("\n");
|
|
||||||
#endif
|
#endif
|
||||||
|
ot("\n");
|
||||||
|
|
||||||
|
// --------------
|
||||||
// 68k: XNZVC, ARM: NZCV
|
// 68k: XNZVC, ARM: NZCV
|
||||||
if (ms) ot("CycloneSetSr\n");
|
ot("CycloneSetSr%s\n", ms?"":":");
|
||||||
else ot("CycloneSetSr:\n");
|
|
||||||
ot(" mov r2,r1,lsr #8\n");
|
ot(" mov r2,r1,lsr #8\n");
|
||||||
ot(" ldrb r3,[r0,#0x44] ;@ get SR high\n");
|
// ot(" ldrb r3,[r0,#0x44] ;@ get SR high\n");
|
||||||
ot(" eor r3,r3,r2\n");
|
// ot(" eor r3,r3,r2\n");
|
||||||
ot(" tst r3,#0x20\n");
|
// ot(" tst r3,#0x20\n");
|
||||||
ot(" and r2,r2,#0xa7 ;@ only nonzero bits\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(" 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(" mov r2,r1,lsl #25\n");
|
||||||
ot(" str r2,[r0,#0x4c] ;@ the X flag\n");
|
ot(" str r2,[r0,#0x4c] ;@ the X flag\n");
|
||||||
ot(" bic r2,r1,#0xf3\n");
|
ot(" bic r2,r1,#0xf3\n");
|
||||||
|
@ -286,8 +261,8 @@ static void PrintFramework()
|
||||||
ot(" bx lr\n");
|
ot(" bx lr\n");
|
||||||
ot("\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(" ldrb r1,[r0,#0x46] ;@ flags\n");
|
||||||
ot(" bic r2,r1,#0xf3\n");
|
ot(" bic r2,r1,#0xf3\n");
|
||||||
ot(" tst r1,#1\n");
|
ot(" tst r1,#1\n");
|
||||||
|
@ -302,8 +277,111 @@ static void PrintFramework()
|
||||||
ot(" bx lr\n");
|
ot(" bx lr\n");
|
||||||
ot("\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)
|
#if (CYCLONE_FOR_GENESIS == 2)
|
||||||
ot(" ldr r12,=CycloneJumpTab\n");
|
ot(" ldr r12,=CycloneJumpTab\n");
|
||||||
ot(" tst r0,r0\n");
|
ot(" tst r0,r0\n");
|
||||||
|
@ -316,49 +394,122 @@ static void PrintFramework()
|
||||||
ChangeTAS(0);
|
ChangeTAS(0);
|
||||||
ot(" bx lr\n");
|
ot(" bx lr\n");
|
||||||
ltorg();
|
ltorg();
|
||||||
ot("\n");
|
|
||||||
#else
|
#else
|
||||||
ot(" bx lr\n");
|
ot(" bx lr\n");
|
||||||
ot("\n");
|
|
||||||
#endif
|
#endif
|
||||||
|
ot("\n");
|
||||||
|
|
||||||
ot(";@ DoInterrupt - r0=IRQ number\n");
|
// --------------
|
||||||
|
ot(";@ DoInterrupt - r0=IRQ level\n");
|
||||||
ot("CycloneDoInterruptGoBack%s\n", ms?"":":");
|
ot("CycloneDoInterruptGoBack%s\n", ms?"":":");
|
||||||
ot(" sub r4,r4,#2\n");
|
ot(" sub r4,r4,#2\n");
|
||||||
ot("CycloneDoInterrupt%s\n", ms?"":":");
|
ot("CycloneDoInterrupt%s\n", ms?"":":");
|
||||||
ot(";@ Get IRQ Vector address:\n");
|
ot(" bic r8,r8,#0xff000000\n");
|
||||||
ot(" mov r0,r0,asl #2\n");
|
ot(" orr r8,r8,r0,lsl #29 ;@ abuse r8\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(";@ 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(" 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");
|
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
|
#if USE_INT_ACK_CALLBACK
|
||||||
|
ot(";@ call IrqCallback if it is defined\n");
|
||||||
#if INT_ACK_NEEDS_STUFF
|
#if INT_ACK_NEEDS_STUFF
|
||||||
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
|
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
|
||||||
ot(" mov r1,r9,lsr #28\n");
|
ot(" mov r1,r9,lsr #28\n");
|
||||||
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
|
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
|
||||||
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
|
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
|
||||||
#endif
|
#endif
|
||||||
ot(" ldr r11,[r7,#0x8c] ;@ IrqCallback\n");
|
ot(" ldr r3,[r7,#0x8c] ;@ IrqCallback\n");
|
||||||
ot(" tst r11,r11\n");
|
ot(" add lr,pc,#4*3\n");
|
||||||
ot(" movne lr,pc\n");
|
ot(" tst r3,r3\n");
|
||||||
ot(" movne pc,r11 ;@ call IrqCallback if it is defined\n");
|
ot(" streqb r3,[r7,#0x47] ;@ just clear IRQ if there is no callback\n");
|
||||||
#if INT_ACK_CHANGES_STUFF
|
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(" 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
|
#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
|
#else // !USE_INT_ACK_CALLBACK
|
||||||
ot(";@ Clear irq:\n");
|
ot(";@ Clear irq:\n");
|
||||||
|
ot(" mov r2,#0\n");
|
||||||
ot(" strb r2,[r7,#0x47]\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
|
#endif
|
||||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
||||||
ot(" subs r5,r5,#44 ;@ Subtract cycles\n");
|
ot(" subs r5,r5,#44 ;@ Subtract cycles\n");
|
||||||
|
@ -366,17 +517,276 @@ static void PrintFramework()
|
||||||
ot(" b CycloneEnd\n");
|
ot(" b CycloneEnd\n");
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
|
// --------------
|
||||||
|
// trashes all temp regs
|
||||||
ot("Exception%s\n", ms?"":":");
|
ot("Exception%s\n", ms?"":":");
|
||||||
ot(" stmdb sp!,{lr} ;@ Preserve ARM return address\n");
|
ot(" ;@ Cause an Exception - Vector number in r0\n");
|
||||||
PrintException(0);
|
ot(" mov r11,lr ;@ Preserve ARM return address\n");
|
||||||
ot(" ldmia sp!,{pc} ;@ Return\n");
|
ot(" bic r8,r8,#0xff000000\n");
|
||||||
|
ot(" orr r8,r8,r0,lsl #24 ;@ abuse r8\n");
|
||||||
|
|
||||||
|
// 1. Make a temporary copy of the status register and set the status register for exception processing.
|
||||||
|
ot(" ldr 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");
|
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)
|
// Call Read(r0), Write(r0,r1) or Fetch(r0)
|
||||||
// Trashes r0-r3,r12,lr
|
// 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;
|
int func=0;
|
||||||
func=0x68+type*0xc+(size<<2); // Find correct offset
|
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);
|
ot(" bic r0,r%i,#0x%08x\n", addrreg, MEMHANDLERS_ADDR_MASK & 0x000000ff);
|
||||||
addrreg=0;
|
addrreg=0;
|
||||||
#endif
|
#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)
|
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);
|
ot(" mov r0,r%i\n", addrreg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -442,7 +867,14 @@ static void PrintOpcodes()
|
||||||
|
|
||||||
// Emit null opcode:
|
// Emit null opcode:
|
||||||
ot("Op____%s ;@ Called if an opcode is not recognised\n", ms?"":":");
|
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(" 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
|
#if USE_UNRECOGNIZED_CALLBACK
|
||||||
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
|
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
|
||||||
ot(" mov r1,r9,lsr #28\n");
|
ot(" mov r1,r9,lsr #28\n");
|
||||||
|
@ -457,10 +889,10 @@ static void PrintOpcodes()
|
||||||
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
||||||
ot(" mov r9,r9,lsl #28\n");
|
ot(" mov r9,r9,lsl #28\n");
|
||||||
ot(" tst r0,r0\n");
|
ot(" tst r0,r0\n");
|
||||||
ot(" moveq r0,#0x10\n");
|
ot(" moveq r0,#4\n");
|
||||||
ot(" bleq Exception\n");
|
ot(" bleq Exception\n");
|
||||||
#else
|
#else
|
||||||
ot(" mov r0,#0x10\n");
|
ot(" mov r0,#4\n");
|
||||||
ot(" bl Exception\n");
|
ot(" bl Exception\n");
|
||||||
#endif
|
#endif
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
@ -484,10 +916,10 @@ static void PrintOpcodes()
|
||||||
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
||||||
ot(" mov r9,r9,lsl #28\n");
|
ot(" mov r9,r9,lsl #28\n");
|
||||||
ot(" tst r0,r0\n");
|
ot(" tst r0,r0\n");
|
||||||
ot(" moveq r0,#0x28\n");
|
ot(" moveq r0,#0x0a\n");
|
||||||
ot(" bleq Exception\n");
|
ot(" bleq Exception\n");
|
||||||
#else
|
#else
|
||||||
ot(" mov r0,#0x28\n");
|
ot(" mov r0,#0x0a\n");
|
||||||
ot(" bl Exception\n");
|
ot(" bl Exception\n");
|
||||||
#endif
|
#endif
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
@ -510,10 +942,10 @@ static void PrintOpcodes()
|
||||||
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
|
||||||
ot(" mov r9,r9,lsl #28\n");
|
ot(" mov r9,r9,lsl #28\n");
|
||||||
ot(" tst r0,r0\n");
|
ot(" tst r0,r0\n");
|
||||||
ot(" moveq r0,#0x2c\n");
|
ot(" moveq r0,#0x0b\n");
|
||||||
ot(" bleq Exception\n");
|
ot(" bleq Exception\n");
|
||||||
#else
|
#else
|
||||||
ot(" mov r0,#0x2c\n");
|
ot(" mov r0,#0x0b\n");
|
||||||
ot(" bl Exception\n");
|
ot(" bl Exception\n");
|
||||||
#endif
|
#endif
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
@ -661,6 +1093,7 @@ static int CycloneMake()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
char *name="Cyclone.s";
|
char *name="Cyclone.s";
|
||||||
|
const char *globl=ms?"export":".global";
|
||||||
|
|
||||||
// Open the assembly file
|
// Open the assembly file
|
||||||
if (ms) name="Cyclone.asm";
|
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=0xa000; i<0xb000; i++) CyJump[i] = -2; // a-line emulation
|
||||||
for(i=0xf000; i<0x10000; i++) CyJump[i] = -3; // f-line emulation
|
for(i=0xf000; i<0x10000; i++) CyJump[i] = -3; // f-line emulation
|
||||||
|
|
||||||
if (ms)
|
ot(ms?" area |.text|, code\n":" .text\n .align 4\n\n");
|
||||||
{
|
ot(" %s CycloneInit\n",globl);
|
||||||
ot(" area |.text|, code\n");
|
ot(" %s CycloneRun\n",globl);
|
||||||
ot(" export CycloneInit\n");
|
ot(" %s CycloneSetSr\n",globl);
|
||||||
ot(" export CycloneRun\n");
|
ot(" %s CycloneGetSr\n",globl);
|
||||||
ot(" export CycloneSetSr\n");
|
ot(" %s CycloneFlushIrq\n",globl);
|
||||||
ot(" export CycloneGetSr\n");
|
ot(" %s CyclonePack\n",globl);
|
||||||
ot(" export CycloneSetRealTAS\n");
|
ot(" %s CycloneUnpack\n",globl);
|
||||||
ot(" export CycloneVer\n");
|
ot(" %s CycloneVer\n",globl);
|
||||||
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");
|
|
||||||
#if (CYCLONE_FOR_GENESIS == 2)
|
#if (CYCLONE_FOR_GENESIS == 2)
|
||||||
ot(" .global CycloneSetRealTAS\n");
|
ot(" %s CycloneSetRealTAS\n",globl);
|
||||||
ot(" .global CycloneDoInterrupt\n");
|
ot(" %s CycloneDoInterrupt\n",globl);
|
||||||
ot(" .global CycloneJumpTab\n");
|
ot(" %s CycloneDoTrace\n",globl);
|
||||||
|
ot(" %s CycloneJumpTab\n",globl);
|
||||||
#endif
|
#endif
|
||||||
ot("CycloneVer: .long 0x%.4x\n",CycloneVer);
|
ot("\n");
|
||||||
}
|
ot(ms?"CycloneVer dcd 0x":"CycloneVer: .long 0x");
|
||||||
|
ot("%.4x\n",CycloneVer);
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
PrintFramework();
|
PrintFramework();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
#include "app.h"
|
#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 char OpData[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||||
|
|
||||||
static unsigned short CPU_CALL OpRead16(unsigned int a)
|
static unsigned short 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)
|
if (last_op_count!=arm_op_count)
|
||||||
ot("\n");
|
ot("\n");
|
||||||
pc_dirty = 1;
|
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;
|
int did_fetch=0;
|
||||||
|
opend_check_trace = opend_check_trace && EMULATE_TRACE;
|
||||||
#if MEMHANDLERS_CHANGE_CYCLES
|
#if MEMHANDLERS_CHANGE_CYCLES
|
||||||
if ((sea >= 0x10 && sea != 0x3c) || (tea >= 0x10 && tea != 0x3c))
|
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(" ldr r0,[r7,#0x5c] ;@ Load Cycles\n");
|
||||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\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
|
#endif
|
||||||
if (!did_fetch)
|
if (!did_fetch)
|
||||||
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
|
||||||
|
if (opend_check_trace)
|
||||||
|
ot(" ldr r1,[r7,#0x44]\n");
|
||||||
ot(" subs r5,r5,#%d ;@ Subtract cycles\n",Cycles);
|
ot(" 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(" blt CycloneEnd\n");
|
||||||
ot(";@ CheckInterrupt:\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(" 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(" ldreq pc,[r6,r8,asl #2] ;@ Jump to next opcode handler\n");
|
||||||
ot(" cmp r0,#6 ;@ irq>6 ?\n");
|
ot(" cmp r0,#6 ;@ irq>6 ?\n");
|
||||||
|
|
|
@ -346,7 +346,7 @@ int OpMul(int op)
|
||||||
if (type==0) // div
|
if (type==0) // div
|
||||||
{
|
{
|
||||||
ot("divzero%.4x%s\n",op,ms?"":":");
|
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");
|
ot(" bl Exception\n");
|
||||||
Cycles+=38;
|
Cycles+=38;
|
||||||
OpEnd(ea);
|
OpEnd(ea);
|
||||||
|
@ -553,16 +553,18 @@ int OpAritha(int op)
|
||||||
|
|
||||||
// EA calculation order defines how situations like suba.w (A0)+, A0 get handled.
|
// 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.
|
// 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(-1,0,sea,size,0x003f);
|
||||||
EaCalcReadNoSE(type!=1?10:-1,11,dea,2,0x0e00);
|
EaCalcReadNoSE(type!=1?10:-1,11,dea,2,0x0e00);
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EaCalcReadNoSE(type!=1?10:-1,11,dea,2,0x0e00);
|
EaCalcReadNoSE(type!=1?10:-1,11,dea,2,0x0e00);
|
||||||
EaCalcReadNoSE(-1,0,sea,size,0x003f);
|
EaCalcReadNoSE(-1,0,sea,size,0x003f);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (size<2) ot(" mov r0,r0,asl #%d\n\n",size?16:24);
|
if (size<2) ot(" mov r0,r0,asl #%d\n\n",size?16:24);
|
||||||
if (size<2) asr=(char *)(size?",asr #16":",asr #24");
|
if (size<2) asr=(char *)(size?",asr #16":",asr #24");
|
||||||
|
@ -803,7 +805,7 @@ int OpChk(int op)
|
||||||
OpEnd(ea);
|
OpEnd(ea);
|
||||||
|
|
||||||
ot("chktrap%.4x%s ;@ CHK exception:\n",op,ms?"":":");
|
ot("chktrap%.4x%s ;@ CHK exception:\n",op,ms?"":":");
|
||||||
ot(" mov r0,#0x18\n");
|
ot(" mov r0,#6\n");
|
||||||
ot(" bl Exception\n");
|
ot(" bl Exception\n");
|
||||||
Cycles+=40;
|
Cycles+=40;
|
||||||
OpEnd(ea);
|
OpEnd(ea);
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
|
|
||||||
#include "app.h"
|
#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
|
#if USE_CHECKPC_CALLBACK
|
||||||
ot(";@ Check Memory Base+pc (r%i)\n",reg);
|
ot(";@ Check Memory Base+pc\n");
|
||||||
if (reg != 0)
|
|
||||||
ot(" mov r0,r%i\n", reg);
|
|
||||||
ot(" mov lr,pc\n");
|
ot(" mov lr,pc\n");
|
||||||
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\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");
|
ot("\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push 32-bit value in r1 - trashes r0-r3,r12,lr
|
// Push 32-bit value in r1 - trashes r0-r3,r12,lr
|
||||||
|
@ -61,7 +57,12 @@ static void PopPc()
|
||||||
MemHandler(0,2);
|
MemHandler(0,2);
|
||||||
ot(" add r0,r0,r10 ;@ Memory Base+PC\n");
|
ot(" add r0,r0,r10 ;@ Memory Base+PC\n");
|
||||||
ot("\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)
|
int OpTrap(int op)
|
||||||
|
@ -73,8 +74,7 @@ int OpTrap(int op)
|
||||||
|
|
||||||
OpStart(op,0x10);
|
OpStart(op,0x10);
|
||||||
ot(" and r0,r8,#0xf ;@ Get trap number\n");
|
ot(" and r0,r8,#0xf ;@ Get trap number\n");
|
||||||
ot(" orr r0,r0,#0x20\n");
|
ot(" orr r0,r0,#0x20 ;@ 32+n\n");
|
||||||
ot(" mov r0,r0,asl #2\n");
|
|
||||||
ot(" bl Exception\n");
|
ot(" bl Exception\n");
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
|
@ -177,14 +177,30 @@ int Op4E70(int op)
|
||||||
PopSr(1);
|
PopSr(1);
|
||||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||||
PopPc();
|
PopPc();
|
||||||
SuperChange(op);
|
ot(" ldr r1,[r7,#0x44] ;@ reload SR high\n");
|
||||||
OpEnd(0x10,0,0,1);
|
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;
|
return 0;
|
||||||
|
|
||||||
case 5: // rts
|
case 5: // rts
|
||||||
OpStart(op,0x10); Cycles=16;
|
OpStart(op,0x10); Cycles=16;
|
||||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||||
PopPc();
|
PopPc();
|
||||||
|
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||||
|
ot(" tst r4,#1 ;@ address error?\n");
|
||||||
|
ot(" bne ExceptionAddressError_r_prg_r4\n");
|
||||||
|
#endif
|
||||||
OpEnd(0x10);
|
OpEnd(0x10);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -192,9 +208,10 @@ int Op4E70(int op)
|
||||||
OpStart(op,0x10,0,1); Cycles=4;
|
OpStart(op,0x10,0,1); Cycles=4;
|
||||||
ot(" tst r9,#0x10000000\n");
|
ot(" tst r9,#0x10000000\n");
|
||||||
ot(" subne r5,r5,#%i\n",34);
|
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");
|
ot(" blne Exception\n");
|
||||||
OpEnd(0x10,0,1);
|
opend_op_changes_cycles = 1;
|
||||||
|
OpEnd(0x10,0);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case 7: // rtr
|
case 7: // rtr
|
||||||
|
@ -202,6 +219,10 @@ int Op4E70(int op)
|
||||||
PopSr(0);
|
PopSr(0);
|
||||||
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
|
||||||
PopPc();
|
PopPc();
|
||||||
|
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||||
|
ot(" tst r4,#1 ;@ address error?\n");
|
||||||
|
ot(" bne ExceptionAddressError_r_prg_r4\n");
|
||||||
|
#endif
|
||||||
OpEnd(0x10);
|
OpEnd(0x10);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -231,21 +252,32 @@ int OpJsr(int op)
|
||||||
ot("\n");
|
ot("\n");
|
||||||
EaCalc(11,0x003f,sea,0);
|
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(";@ Jump - Get new PC from r11\n");
|
||||||
ot(" add r0,r11,r10 ;@ Memory Base + New PC\n");
|
ot(" add r0,r11,r10 ;@ Memory Base + New PC\n");
|
||||||
ot("\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=(op&0x40) ? 4 : 12;
|
||||||
Cycles+=Ea_add_ns((op&0x40) ? g_jmp_cycle_table : g_jsr_cycle_table, sea);
|
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(";@ Check if Dn.w is -1\n");
|
||||||
ot(" cmn r0,#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(" beq DbraMin1\n");
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
ot(";@ Get Branch offset:\n");
|
ot(";@ Get Branch offset:\n");
|
||||||
ot(" ldrsh r0,[r4]\n");
|
ot(" ldrsh r0,[r4]\n");
|
||||||
ot(" add r0,r4,r0 ;@ r4 = New PC\n");
|
ot(" add r0,r4,r0 ;@ r0 = New PC\n");
|
||||||
CheckPc(0);
|
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
|
#else
|
||||||
ot("\n");
|
ot("\n");
|
||||||
ot(";@ Get Branch offset:\n");
|
ot(";@ Get Branch offset:\n");
|
||||||
|
@ -343,7 +382,7 @@ int OpDbra(int op)
|
||||||
OpEnd();
|
OpEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA
|
#if (USE_CHECKPC_CALLBACK && USE_CHECKPC_DBRA) || EMULATE_ADDRESS_ERRORS_JUMP
|
||||||
if (op==0x51c8)
|
if (op==0x51c8)
|
||||||
{
|
{
|
||||||
ot(";@ Dn.w is -1:\n");
|
ot(";@ Dn.w is -1:\n");
|
||||||
|
@ -451,15 +490,15 @@ int OpBranch(int op)
|
||||||
#if USE_CHECKPC_CALLBACK
|
#if USE_CHECKPC_CALLBACK
|
||||||
if (offset==-1) checkpc=1;
|
if (offset==-1) checkpc=1;
|
||||||
#endif
|
#endif
|
||||||
if (checkpc)
|
if (checkpc) CheckPc();
|
||||||
{
|
#if EMULATE_ADDRESS_ERRORS_JUMP
|
||||||
CheckPc(0);
|
ot(" mov r4,r0\n");
|
||||||
}
|
ot(" tst r4,#1 ;@ address error?\n");
|
||||||
else
|
ot(" bne ExceptionAddressError_r_prg_r4\n");
|
||||||
{
|
#else
|
||||||
ot(" bic r4,r0,#1\n"); // we do not emulate address errors
|
ot(" bic r4,r0,#1\n");
|
||||||
ot("\n");
|
#endif
|
||||||
}
|
ot("\n");
|
||||||
|
|
||||||
OpEnd(size?0x10:0);
|
OpEnd(size?0x10:0);
|
||||||
|
|
||||||
|
|
|
@ -207,6 +207,7 @@ int OpNeg(int op)
|
||||||
ot("\n");
|
ot("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type==1) eawrite_check_addrerr=1;
|
||||||
EaWrite(10, 1,ea,size,0x003f,0,0);
|
EaWrite(10, 1,ea,size,0x003f,0,0);
|
||||||
|
|
||||||
OpEnd(ea);
|
OpEnd(ea);
|
||||||
|
@ -360,10 +361,12 @@ int OpSet(int op)
|
||||||
|
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
|
eawrite_check_addrerr=1;
|
||||||
EaCalc (0,0x003f, ea,size,0,0);
|
EaCalc (0,0x003f, ea,size,0,0);
|
||||||
EaWrite(0, 1, ea,size,0x003f,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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +617,8 @@ int OpAsr(int op)
|
||||||
|
|
||||||
EaWrite(10, 0, ea,size,0x0007,1);
|
EaWrite(10, 0, ea,size,0x0007,1);
|
||||||
|
|
||||||
OpEnd(ea,0,count<0);
|
opend_op_changes_cycles = (count<0);
|
||||||
|
OpEnd(ea,0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ void OpFlagsToReg(int high)
|
||||||
|
|
||||||
// Convert SR/CRR register in r0 to our flags
|
// Convert SR/CRR register in r0 to our flags
|
||||||
// trashes r0,r1
|
// 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(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n");
|
||||||
ot(" mov r2,r0,lsl #25\n");
|
ot(" mov r2,r0,lsl #25\n");
|
||||||
|
@ -32,9 +32,10 @@ void OpRegToFlags(int high)
|
||||||
|
|
||||||
if (high)
|
if (high)
|
||||||
{
|
{
|
||||||
ot(" mov r0,r0,ror #8\n");
|
int mask=EMULATE_TRACE?0xa7:0x27;
|
||||||
ot(" and r0,r0,#0xa7 ;@ only take defined bits\n");
|
ot(" mov r%i,r0,ror #8\n",srh_reg);
|
||||||
ot(" strb r0,[r7,#0x44] ;@ Store SR high\n");
|
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");
|
ot("\n");
|
||||||
}
|
}
|
||||||
|
@ -44,8 +45,15 @@ void SuperEnd(void)
|
||||||
ot(";@ ----------\n");
|
ot(";@ ----------\n");
|
||||||
ot(";@ tried execute privileged instruction in user mode\n");
|
ot(";@ tried execute privileged instruction in user mode\n");
|
||||||
ot("WrongPrivilegeMode%s\n",ms?"":":");
|
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(" 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");
|
ot(" bl Exception\n");
|
||||||
Cycles=34;
|
Cycles=34;
|
||||||
OpEnd(0);
|
OpEnd(0);
|
||||||
|
@ -53,13 +61,15 @@ void SuperEnd(void)
|
||||||
|
|
||||||
// does OSP and A7 swapping if needed
|
// does OSP and A7 swapping if needed
|
||||||
// new or old SR (not the one already in [r7,#0x44]) should be passed in r11
|
// new or old SR (not the one already in [r7,#0x44]) should be passed in r11
|
||||||
// trashes r0,r11
|
// uses srh from srh_reg (loads if < 0), trashes r0,r11
|
||||||
void SuperChange(int op,int load_srh)
|
void SuperChange(int op,int srh_reg)
|
||||||
{
|
{
|
||||||
ot(";@ A7 <-> OSP?\n");
|
ot(";@ A7 <-> OSP?\n");
|
||||||
if (load_srh)
|
if (srh_reg < 0) {
|
||||||
ot(" ldr r0,[r7,#0x44] ;@ Get other SR high\n");
|
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(" tst r0,#0x20\n");
|
||||||
ot(" beq no_sp_swap%.4x\n",op);
|
ot(" beq no_sp_swap%.4x\n",op);
|
||||||
ot(" ;@ swap OSP and A7:\n");
|
ot(" ;@ swap OSP and A7:\n");
|
||||||
|
@ -122,6 +132,7 @@ int OpMove(int op)
|
||||||
|
|
||||||
if (movea) size=2; // movea always expands to 32-bits
|
if (movea) size=2; // movea always expands to 32-bits
|
||||||
|
|
||||||
|
eawrite_check_addrerr=1;
|
||||||
#if SPLIT_MOVEL_PD
|
#if SPLIT_MOVEL_PD
|
||||||
if ((tea&0x38)==0x20 && size==2) { // -(An)
|
if ((tea&0x38)==0x20 && size==2) { // -(An)
|
||||||
EaCalc (10,0x0e00,tea,size,0,0);
|
EaCalc (10,0x0e00,tea,size,0,0);
|
||||||
|
@ -167,6 +178,7 @@ int OpLea(int op)
|
||||||
|
|
||||||
OpStart(op,sea,tea);
|
OpStart(op,sea,tea);
|
||||||
|
|
||||||
|
eawrite_check_addrerr=1;
|
||||||
EaCalc (1,0x003f,sea,0); // Lea
|
EaCalc (1,0x003f,sea,0); // Lea
|
||||||
EaCalc (0,0x0e00,tea,2);
|
EaCalc (0,0x0e00,tea,2);
|
||||||
EaWrite(0, 1,tea,2,0x0e00);
|
EaWrite(0, 1,tea,2,0x0e00);
|
||||||
|
@ -214,6 +226,7 @@ int OpMoveSr(int op)
|
||||||
|
|
||||||
if (type==0 || type==1)
|
if (type==0 || type==1)
|
||||||
{
|
{
|
||||||
|
eawrite_check_addrerr=1;
|
||||||
OpFlagsToReg(type==0);
|
OpFlagsToReg(type==0);
|
||||||
EaCalc (0,0x003f,ea,size,0,0);
|
EaCalc (0,0x003f,ea,size,0,0);
|
||||||
EaWrite(0, 1,ea,size,0x003f,0,0);
|
EaWrite(0, 1,ea,size,0x003f,0,0);
|
||||||
|
@ -222,13 +235,17 @@ int OpMoveSr(int op)
|
||||||
if (type==2 || type==3)
|
if (type==2 || type==3)
|
||||||
{
|
{
|
||||||
EaCalcReadNoSE(-1,0,ea,size,0x003f);
|
EaCalcReadNoSE(-1,0,ea,size,0x003f);
|
||||||
OpRegToFlags(type==3);
|
OpRegToFlags(type==3,1);
|
||||||
if (type==3) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -239,6 +256,7 @@ int OpArithSr(int op)
|
||||||
{
|
{
|
||||||
int type=0,ea=0;
|
int type=0,ea=0;
|
||||||
int use=0,size=0;
|
int use=0,size=0;
|
||||||
|
int sr_mask=EMULATE_TRACE?0xa7:0x27;
|
||||||
|
|
||||||
type=(op>>9)&5; if (type==4) return 1;
|
type=(op>>9)&5; if (type==4) return 1;
|
||||||
size=(op>>6)&1; // ccr or sr?
|
size=(op>>6)&1; // ccr or sr?
|
||||||
|
@ -249,19 +267,53 @@ int OpArithSr(int op)
|
||||||
|
|
||||||
OpStart(op,ea,0,0,size!=0); Cycles=16;
|
OpStart(op,ea,0,0,size!=0); Cycles=16;
|
||||||
|
|
||||||
EaCalc(10,0x003f,ea,size);
|
EaCalcRead(-1,0,ea,size,0x003f);
|
||||||
EaRead(10, 10,ea,size,0x003f);
|
|
||||||
|
|
||||||
OpFlagsToReg(size);
|
ot(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n");
|
||||||
if (type==0) ot(" orr r0,r1,r10\n");
|
ot(" tst r1,#1 ;@ 1 if C!=V\n");
|
||||||
if (type==1) ot(" and r0,r1,r10\n");
|
ot(" eorne r0,r0,#3 ;@ ___XNZCV\n");
|
||||||
if (type==5) ot(" eor r0,r1,r10\n");
|
ot(" ldr r2,[r7,#0x4c] ;@ Load old X bit\n");
|
||||||
OpRegToFlags(size);
|
|
||||||
if (size && type!=0) { // we can't enter supervisor mode, nor unmask irqs just by using OR
|
// note: old srh is already in r11 (done by OpStart)
|
||||||
SuperChange(op,0);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -338,6 +390,13 @@ int OpMovem(int op)
|
||||||
ot(" tst r11,r11\n"); // sanity check
|
ot(" tst r11,r11\n"); // sanity check
|
||||||
ot(" beq NoRegs%.4x\n",op);
|
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("\n");
|
||||||
ot("Movemloop%.4x%s\n",op, ms?"":":");
|
ot("Movemloop%.4x%s\n",op, ms?"":":");
|
||||||
ot(" add r10,r10,#%d ;@ r10=Next Register\n",decr?-4:4);
|
ot(" add r10,r10,#%d ;@ r10=Next Register\n",decr?-4:4);
|
||||||
|
@ -350,6 +409,7 @@ int OpMovem(int op)
|
||||||
if (dir)
|
if (dir)
|
||||||
{
|
{
|
||||||
ot(" ;@ Copy memory to register:\n",1<<size);
|
ot(" ;@ Copy memory to register:\n",1<<size);
|
||||||
|
earead_check_addrerr=0; // already checked
|
||||||
EaRead (6,0,ea,size,0x003f);
|
EaRead (6,0,ea,size,0x003f);
|
||||||
ot(" str r0,[r7,r10] ;@ Save value into Dn/An\n");
|
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);
|
Cycles+=Ea_add_ns(g_movem_cycle_table,ea);
|
||||||
|
|
||||||
OpEnd(ea,0,1);
|
opend_op_changes_cycles = 1;
|
||||||
|
OpEnd(ea);
|
||||||
ltorg();
|
ltorg();
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
|
@ -413,6 +474,7 @@ int OpMoveUsp(int op)
|
||||||
|
|
||||||
if (dir)
|
if (dir)
|
||||||
{
|
{
|
||||||
|
eawrite_check_addrerr=1;
|
||||||
ot(" ldr r1,[r7,#0x48] ;@ Get from USP\n\n");
|
ot(" ldr r1,[r7,#0x48] ;@ Get from USP\n\n");
|
||||||
EaCalc (0,0x000f,8,2,1);
|
EaCalc (0,0x000f,8,2,1);
|
||||||
EaWrite(0, 1,8,2,0x000f,1);
|
EaWrite(0, 1,8,2,0x000f,1);
|
||||||
|
@ -568,11 +630,12 @@ int OpStopReset(int op)
|
||||||
|
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
ot(" mov r0,#1\n");
|
ot(" ldr r0,[r7,#0x58]\n");
|
||||||
ot(" str r0,[r7,#0x58] ;@ stopped\n");
|
ot(" mov r5,#0 ;@ eat cycles\n");
|
||||||
|
ot(" orr r0,r0,#1 ;@ stopped\n");
|
||||||
|
ot(" str r0,[r7,#0x58]\n");
|
||||||
ot("\n");
|
ot("\n");
|
||||||
|
|
||||||
ot(" mov r5,#0 ;@ eat cycles\n");
|
|
||||||
Cycles = 4;
|
Cycles = 4;
|
||||||
ot("\n");
|
ot("\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,17 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "config.h"
|
#ifndef CONFIG_FILE
|
||||||
|
#define CONFIG_FILE "config.h"
|
||||||
|
#endif
|
||||||
|
#include CONFIG_FILE
|
||||||
|
|
||||||
// Disa.c
|
// Disa.c
|
||||||
#include "Disa/Disa.h"
|
#include "Disa/Disa.h"
|
||||||
|
|
||||||
// Ea.cpp
|
// Ea.cpp
|
||||||
|
extern int earead_check_addrerr;
|
||||||
|
extern int eawrite_check_addrerr;
|
||||||
extern int g_jmp_cycle_table[];
|
extern int g_jmp_cycle_table[];
|
||||||
extern int g_jsr_cycle_table[];
|
extern int g_jsr_cycle_table[];
|
||||||
extern int g_lea_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
|
extern int arm_op_count; // for stats
|
||||||
void ot(const char *format, ...);
|
void ot(const char *format, ...);
|
||||||
void ltorg();
|
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);
|
void FlushPC(void);
|
||||||
|
|
||||||
// OpAny.cpp
|
// OpAny.cpp
|
||||||
extern int g_op;
|
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);
|
int OpGetFlags(int subtract,int xbit,int sprecialz=0);
|
||||||
void OpUse(int op,int use);
|
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 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);
|
int OpBase(int op,int size,int sepa=0);
|
||||||
void OpAny(int op);
|
void OpAny(int op);
|
||||||
|
|
||||||
|
@ -90,7 +96,7 @@ int OpTas(int op, int gen_special=0);
|
||||||
int OpMove(int op);
|
int OpMove(int op);
|
||||||
int OpLea(int op);
|
int OpLea(int op);
|
||||||
void OpFlagsToReg(int high);
|
void OpFlagsToReg(int high);
|
||||||
void OpRegToFlags(int high);
|
void OpRegToFlags(int high,int srh_reg=0);
|
||||||
int OpMoveSr(int op);
|
int OpMoveSr(int op);
|
||||||
int OpArithSr(int op);
|
int OpArithSr(int op);
|
||||||
int OpPea(int op);
|
int OpPea(int op);
|
||||||
|
@ -101,5 +107,5 @@ int OpExg(int op);
|
||||||
int OpMovep(int op);
|
int OpMovep(int op);
|
||||||
int OpStopReset(int op);
|
int OpStopReset(int op);
|
||||||
void SuperEnd(void);
|
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.
|
* If this option is enabled, Microsoft ARMASM compatible output is generated
|
||||||
* Otherwise GNU as syntax is used.
|
* (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 /
|
* 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.
|
* the write-back phase. That will be emulated, if this option is enabled.
|
||||||
* This option also alters timing slightly.
|
* 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
|
* 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
|
* Warning: if you enable this, you MUST call CycloneInit() before calling
|
||||||
* CycloneRun(), or else it will crash.
|
* 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
|
* 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.
|
* 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.
|
* 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
|
* 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,
|
* any reason need to access them in your memory handlers, enable the options below,
|
||||||
* otherwise disable them to improve performance.
|
* otherwise disable them to improve performance.
|
||||||
|
*
|
||||||
* MEMHANDLERS_NEED_PC updates .pc context field with PC value effective at the time
|
* 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
|
* MEMHANDLERS_NEED_PREV_PC updates .prev_pc context field to currently executed
|
||||||
* opcode address + 2.
|
* opcode address + 2.
|
||||||
* Note that .pc and .prev_pc values are always real pointers to memory, so you must
|
* Note that .pc and .prev_pc values are always real pointers to memory, so you must
|
||||||
* subtract .membase to get M68k PC value.
|
* subtract .membase to get M68k PC value.
|
||||||
|
*
|
||||||
* Warning: updating PC in memhandlers is dangerous, as Cyclone may internally
|
* Warning: updating PC in memhandlers is dangerous, as Cyclone may internally
|
||||||
* increment the PC before fetching the next instruction and continue executing
|
* 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_PC 0
|
||||||
#define MEMHANDLERS_NEED_PREV_PC 0
|
#define MEMHANDLERS_NEED_PREV_PC 0
|
||||||
#define MEMHANDLERS_NEED_FLAGS 0
|
#define MEMHANDLERS_NEED_FLAGS 0
|
||||||
#define MEMHANDLERS_NEED_CYCLES 1
|
#define MEMHANDLERS_NEED_CYCLES 0
|
||||||
#define MEMHANDLERS_CHANGE_PC 0
|
#define MEMHANDLERS_CHANGE_PC 0
|
||||||
#define MEMHANDLERS_CHANGE_FLAGS 0
|
#define MEMHANDLERS_CHANGE_FLAGS 0
|
||||||
#define MEMHANDLERS_CHANGE_CYCLES 1
|
#define MEMHANDLERS_CHANGE_CYCLES 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If enabled, Cyclone will call IrqCallback routine from it's context whenever it
|
* 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
|
* acknowledges an IRQ. IRQ level (.irq) is not cleared automatically, do this in your
|
||||||
* hadler if needed. PC, flags and cycles are valid in the context and can be read.
|
* handler if needed.
|
||||||
* If disabled, it simply clears the IRQ level and continues execution.
|
* 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_NEEDS_STUFF 0
|
||||||
#define INT_ACK_CHANGES_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.
|
* encountered. All context members are valid and can be changed.
|
||||||
* If disabled, RESET opcode acts as an NOP.
|
* 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
|
* If enabled, UnrecognizedCallback is called if an invalid opcode is
|
||||||
|
@ -91,34 +102,34 @@
|
||||||
* If disabled, "Illegal Instruction" exception is generated and execution is
|
* If disabled, "Illegal Instruction" exception is generated and execution is
|
||||||
* continued.
|
* continued.
|
||||||
*/
|
*/
|
||||||
#define USE_UNRECOGNIZED_CALLBACK 1
|
#define USE_UNRECOGNIZED_CALLBACK 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This option will also call UnrecognizedCallback for a-line and f-line
|
* This option will also call UnrecognizedCallback for a-line and f-line
|
||||||
* (0xa*** and 0xf***) opcodes the same way as described above, only appropriate
|
* (0xa*** and 0xf***) opcodes the same way as described above, only appropriate
|
||||||
* exceptions will be generated.
|
* 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
|
* 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.
|
* 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.
|
* 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
|
* This determines if checkpc() should be called after jumps when 8 and 16 bit
|
||||||
* displacement values were used.
|
* displacement values were used.
|
||||||
*/
|
*/
|
||||||
#define USE_CHECKPC_OFFSETBITS_16 1
|
#define USE_CHECKPC_OFFSETBITS_16 1
|
||||||
#define USE_CHECKPC_OFFSETBITS_8 0
|
#define USE_CHECKPC_OFFSETBITS_8 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call checkpc() after DBcc jumps (which use 16bit displacement). Cyclone prior to
|
* Call checkpc() after DBcc jumps (which use 16bit displacement). Cyclone prior to
|
||||||
* 0.0087 never did that.
|
* 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
|
* 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).
|
* Bart Trzynadlowski's doc (http://www.trzy.org/files/68knotes.txt).
|
||||||
* Enable this if you are emulating a 16 bit system.
|
* 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
|
CFLAGS = -Wall
|
||||||
|
ifdef CONFIG_FILE
|
||||||
|
CFLAGS += -DCONFIG_FILE=\"$(CONFIG_FILE)\"
|
||||||
|
endif
|
||||||
|
|
||||||
all : cyclone.s
|
all : cyclone.s
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Makefile for MS Visual C
|
# Makefile for MS Visual C
|
||||||
|
|
||||||
CPP=cl.exe
|
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=link.exe
|
||||||
LINK32_FLAGS=user32.lib /nologo /subsystem:console /machine:I386 /out:Cyclone.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
|
$(CPP) $(CPP_PROJ) ..\OpMove.cpp
|
||||||
|
|
||||||
Disa.obj : ..\disa\Disa.c ..\disa\Disa.h
|
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
|
..\app.h : ..\config.h
|
||||||
|
|
||||||
|
@ -56,5 +56,4 @@ CLEAN :
|
||||||
-@erase "Cyclone.exe"
|
-@erase "Cyclone.exe"
|
||||||
-@erase "Cyclone.asm"
|
-@erase "Cyclone.asm"
|
||||||
-@erase "Cyclone.s"
|
-@erase "Cyclone.s"
|
||||||
-@erase "Cyclone.o"
|
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,8 @@ RSC=rc.exe
|
||||||
# PROP Intermediate_Dir "Release"
|
# PROP Intermediate_Dir "Release"
|
||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE 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 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
# ADD CPP /nologo /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
|
||||||
# ADD BASE RSC /l 0x427 /d "NDEBUG"
|
# ADD BASE RSC /l 0x427 /d "NDEBUG"
|
||||||
# ADD RSC /l 0x427 /d "NDEBUG"
|
# ADD RSC /l 0x427 /d "NDEBUG"
|
||||||
BSC32=bscmake.exe
|
BSC32=bscmake.exe
|
||||||
|
@ -65,8 +65,8 @@ LINK32=link.exe
|
||||||
# PROP Intermediate_Dir "Debug"
|
# PROP Intermediate_Dir "Debug"
|
||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# 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 BASE CPP /nologo /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
|
||||||
# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
# ADD CPP /nologo /W4 /O2 /D "_CRT_SECURE_NO_WARNINGS" /c
|
||||||
# ADD BASE RSC /l 0x427 /d "_DEBUG"
|
# ADD BASE RSC /l 0x427 /d "_DEBUG"
|
||||||
# ADD RSC /l 0x427 /d "_DEBUG"
|
# ADD RSC /l 0x427 /d "_DEBUG"
|
||||||
BSC32=bscmake.exe
|
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