1
0
mirror of https://github.com/ScrelliCopter/VGM-Tools synced 2025-02-21 04:09:25 +11:00
Files
VGM-Tools/spctools/spc2it/spc700.c

3527 lines
129 KiB
C

/****************************************************
*Part of SPC2IT, read readme.md for more information*
****************************************************/
/*
SNEeSe, an Open Source Super NES emulator.
Copyright (c) 1998-2006, Charles Bilyue'.
Portions copyright (c) 1998-2003, Brad Martin.
Portions copyright (c) 2003-2004, Daniel Horchner.
Portions copyright (c) 2004-2005, Nach. ( http://nsrt.edgeemu.com/ )
Unzip Technology, copyright (c) 1998 Gilles Vollant.
zlib Technology ( www.gzip.org/zlib/ ), Copyright (c) 1995-2003,
Jean-loup Gailly ( jloup* *at* *gzip.org ) and Mark Adler
( madler* *at* *alumni.caltech.edu ).
JMA Technology, copyright (c) 2004-2005 NSRT Team. ( http://nsrt.edgeemu.com/ )
LZMA Technology, copyright (c) 2001-4 Igor Pavlov. ( http://www.7-zip.org )
Portions copyright (c) 2002 Andrea Mazzoleni. ( http://advancemame.sf.net )
This is free software. See './doc/LICENSE_SNEESE' for details.
You must read and accept the license prior to use.
*/
#define SNEeSe_apu_spc700_c
#include "sneese_spc.h"
SPC700_CONTEXT primary_context;
SPC700_CONTEXT *active_context = &primary_context;
/*
SNEeSe SPC700 CPU emulation core
Originally written by Lee Hammerton in AT&T assembly
Maintained/rewritten/ported to NASM by Charles Bilyue'
Maintained/ported to C by Charles Bilyue'
This file contains:
CPU core info
Reset
Execution Loop
Invalid Opcode Handler or Dispatcher
Variable definitions (registers, cycle counters, etc.)
CPU opcode emulation handlers
CPU opcode handler table
CPU opcode timing table
CPU core info:
A register - active_context->YA.b.A
Y register - active_context->YA.b.Y
YA register pair - active_context->YA.w
X register - active_context->X
Stack pointer - active_context->SP
Program Counter - active_context->PC
Processor status word - active_context->PSW
Individual flags - N_flag, V_flag, P_flag, B_flag,
H_flag, I_flag, Z_flag, C_flag
SPC timers
SPC700 timing is not directly related to 65c816 timing, but for
simplicity in emulation we act as if it is. SPC700 gets 5632
cycles for every 118125 (21.47727..MHz) 5A22 cycles. Since the
timers run at ~8KHz and ~64KHz and the CPU core runs at
1.024Mhz, the timers are clocked as follows:
1.024MHz / 8KHz = 128 cycles (Timers 0 and 1)
1.024MHz / 64KHz = 16 cycles (Timer 2)
*/
/* lots of #define's! */
/* These are the bits for flag set/clr operations */
#define SPC_FLAG_C 1 /* Carry */
#define SPC_FLAG_Z 2 /* Zero result */
#define SPC_FLAG_I 4 /* Interrupt Disable */
#define SPC_FLAG_H 8 /* Half-carry */
#define SPC_FLAG_B 0x10 /* Break */
#define SPC_FLAG_P 0x20 /* Page (direct page) */
#define SPC_FLAG_V 0x40 /* Overflow */
#define SPC_FLAG_N 0x80 /* Negative result */
#define SPC_FLAG_NZ (SPC_FLAG_N | SPC_FLAG_Z)
#define SPC_FLAG_NZC (SPC_FLAG_NZ | SPC_FLAG_C)
#define SPC_FLAG_NHZC (SPC_FLAG_NZC | SPC_FLAG_H)
#define _Cycles (active_context->Cycles)
#define _last_cycles (active_context->last_cycles)
#define _TotalCycles (active_context->TotalCycles)
#define _WorkCycles (active_context->WorkCycles)
#define _PORT_R (active_context->PORT_R)
#define _PORT0R (_PORT_R[0])
#define _PORT1R (_PORT_R[1])
#define _PORT2R (_PORT_R[2])
#define _PORT3R (_PORT_R[3])
#define _PORT_W (active_context->PORT_W)
#define _PORT0W (_PORT_W[0])
#define _PORT1W (_PORT_W[1])
#define _PORT2W (_PORT_W[2])
#define _PORT3W (_PORT_W[3])
#define _FFC0_Address (active_context->FFC0_Address)
#define _PC (active_context->PC.w)
#define _PCL (active_context->PC.b.l)
#define _PCH (active_context->PC.b.h)
#define _YA (active_context->YA.w)
#define _A (active_context->YA.b.l)
#define _Y (active_context->YA.b.h)
#define _dp (active_context->direct_page.w)
#define _direct_page (active_context->direct_page.b.h)
#define _SP (active_context->SP)
#define _X (active_context->X)
#define _PSW (active_context->PSW)
#define _cycle (active_context->cycle)
#define _opcode (active_context->opcode)
#define _data (active_context->data)
#define _data2 (active_context->data2)
#define _data16 (active_context->data16.w)
#define _offset (active_context->offset)
#define _address (active_context->address.w)
#define _address_l (active_context->address.b.l)
#define _address_h (active_context->address.b.h)
#define _address2 (active_context->address2.w)
#define _address2_l (active_context->address2.b.l)
#define _address2_h (active_context->address2.b.h)
#define _N_flag (active_context->N_flag)
#define _V_flag (active_context->V_flag)
#define _P_flag (active_context->P_flag)
#define _B_flag (active_context->B_flag)
#define _H_flag (active_context->H_flag)
#define _I_flag (active_context->I_flag)
#define _Z_flag (active_context->Z_flag)
#define _C_flag (active_context->C_flag)
#define _timers (active_context->timers)
/* bits used all over the core */
void set_flag_spc(u8 flag)
{
if (flag & SPC_FLAG_N)
{
_N_flag = 0x80;
}
if (flag & SPC_FLAG_V)
{
_V_flag = 1;
}
if (flag & SPC_FLAG_P)
{
_P_flag = 1;
_direct_page = 0x01;
}
if (flag & SPC_FLAG_B)
{
_B_flag = 1;
}
if (flag & SPC_FLAG_H)
{
_H_flag = 1;
}
if (flag & SPC_FLAG_I)
{
_I_flag = 1;
}
if (flag & SPC_FLAG_Z)
{
_Z_flag = 0;
}
if (flag & SPC_FLAG_C)
{
_C_flag = 1;
}
}
void clr_flag_spc(u8 flag)
{
if (flag & SPC_FLAG_N)
{
_N_flag = 0;
}
if (flag & SPC_FLAG_V)
{
_V_flag = 0;
}
if (flag & SPC_FLAG_P)
{
_P_flag = 0;
_direct_page = 0x00;
}
if (flag & SPC_FLAG_B)
{
_B_flag = 0;
}
if (flag & SPC_FLAG_H)
{
_H_flag = 0;
}
if (flag & SPC_FLAG_I)
{
_I_flag = 0;
}
if (flag & SPC_FLAG_Z)
{
_Z_flag = 1;
}
if (flag & SPC_FLAG_C)
{
_C_flag = 0;
}
}
void complement_carry_spc(void)
{
_C_flag = !_C_flag;
}
u8 flag_state_spc(u8 flag)
{
if (flag == SPC_FLAG_N)
{
return _N_flag & 0x80;
}
else if (flag == SPC_FLAG_V)
{
return _V_flag;
}
else if (flag == SPC_FLAG_P)
{
return _P_flag;
}
else if (flag == SPC_FLAG_B)
{
return _B_flag;
}
else if (flag == SPC_FLAG_H)
{
return _H_flag;
}
else if (flag == SPC_FLAG_I)
{
return _I_flag;
}
else if (flag == SPC_FLAG_Z)
{
return !_Z_flag;
}
else if (flag == SPC_FLAG_C)
{
return _C_flag;
}
return 0;
}
void load_cycles_spc(void)
{
_WorkCycles = _TotalCycles - _Cycles;
}
u32 get_cycles_spc(void)
{
return _WorkCycles + _Cycles;
}
void save_cycles_spc(void)
{
_TotalCycles = _WorkCycles + _Cycles;
}
/* Set up the flags from our flag format to SPC flag format */
void spc_setup_flags(s32 B_flag)
{
u8 PSW = 0;
PSW += _N_flag & 0x80;
PSW += _V_flag ? 0x40 : 0;
PSW += _P_flag ? 0x20 : 0;
PSW += B_flag ? 0x10 : 0;
PSW += _H_flag ? 0x08 : 0;
PSW += _I_flag ? 0x04 : 0;
PSW += !_Z_flag ? 0x02 : 0;
PSW += _C_flag ? 0x01 : 0;
_PSW = PSW;
}
/* Restore the flags from SPC flag format to our flag format */
void spc_restore_flags(void)
{
u8 PSW = _PSW;
_N_flag = PSW;
_V_flag = PSW & SPC_FLAG_V;
if (PSW & SPC_FLAG_P)
set_flag_spc(SPC_FLAG_P);
else
clr_flag_spc(SPC_FLAG_P);
_B_flag = PSW & SPC_FLAG_B;
_H_flag = PSW & SPC_FLAG_H;
_I_flag = PSW & SPC_FLAG_I;
_Z_flag = ~PSW & SPC_FLAG_Z;
_C_flag = PSW & SPC_FLAG_C;
}
void store_flag_n(u8 value)
{
_N_flag = value;
}
void store_flag_v(u8 value)
{
_V_flag = value;
}
void store_flag_p(u8 value)
{
_P_flag = value;
_direct_page = value ? 0x01 : 0x00;
}
void store_flag_h(u8 value)
{
_H_flag = value;
}
void store_flag_i(u8 value)
{
_I_flag = value;
}
void store_flag_z(u8 value)
{
_Z_flag = value;
}
void store_flag_c(u8 value)
{
_C_flag = value;
}
void store_flags_nz(u8 value)
{
store_flag_n(value);
store_flag_z(value);
}
void store_flags_nzc(u8 nz, u8 c)
{
store_flag_n(nz);
store_flag_z(nz);
store_flag_c(c);
}
/* bits for external access by the 5A22 core */
u8 SPC_READ_PORT_W(u16 address)
{
return _PORT_W[address & 3];
}
void SPC_WRITE_PORT_R(u16 address, u8 data)
{
_PORT_R[address & 3] = data;
}
/* bits for handling cycle counter overflows */
void Wrap_SPC_Cyclecounter()
{
_TotalCycles -= 0xF0000000;
_Cycles -= 0xF0000000;
_timers[0].cycle_latch -= 0xF0000000;
_timers[1].cycle_latch -= 0xF0000000;
_timers[2].cycle_latch -= 0xF0000000;
Wrap_SDSP_Cyclecounter();
}
/* This code should be mapped into the top of the address space */
static u8 SPC_ROM_CODE[64] = {0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0, 0xFC, 0x8F, 0xAA, 0xF4, 0x8F,
0xBB, 0xF5, 0x78, 0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4, 0xD0, 0xFC,
0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5, 0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3,
0xAB, 0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA, 0xF6, 0xDA, 0x00, 0xBA,
0xF4, 0xC4, 0xF4, 0xDD, 0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF};
static u8 SPC_READ_INVALID(u16 address)
{
#ifdef TRAP_INVALID_READ
#ifdef DEBUG
/* Set up address so message works */
Map_Address = address;
Map_Byte = 0;
InvalidSPCHWRead(); /* Display read from invalid HW warning */
#endif
#endif
return 0;
}
static u8 SPC_READ_RAM(u16 address)
{
return SPCRAM[address];
}
static u8 SPC_READ_DSP_DATA(u16 address)
{
SPC_READ_DSP();
/* read from DSP register */
/* DSP address bit 7 ignored during reads only! */
return SPC_DSP[SPC_DSP_ADDR & 0x7F];
}
u8 SPC_READ_PORT_R(u16 address)
{
return _PORT_R[address & 3];
}
/* timer registers are write-only, actual timer clock is internal and */
/* not accessible! */
/* counters are 4-bit, upon read/write they reset to 0 */
void Update_SPC_Timer(s32 timer)
{
u32 shift, mask, cycles, position;
if (timer != 2)
{
shift = 7;
}
else
{
shift = 4;
}
mask = -BIT(shift);
cycles = _TotalCycles - _timers[timer].cycle_latch;
_timers[timer].cycle_latch += cycles & mask;
/* nothing to do if timer turned off */
if (!(SPC_CTRL & BIT(timer)))
return;
position = _timers[timer].position + (cycles >> shift);
_timers[timer].position = position;
if (position < _timers[timer].target)
{
return;
}
_timers[timer].counter += position / _timers[timer].target;
/* 4-bit counter without saturation */
_timers[timer].counter &= 0x0F;
_timers[timer].position = position % _timers[timer].target;
}
static u8 SPC_READ_COUNTER(u16 address)
{
/* 0xFD = read address for first timer's counter */
s32 timer = address - 0xFD;
u8 counter;
Update_SPC_Timer(timer);
counter = _timers[timer].counter;
_timers[timer].counter = 0;
return counter;
}
/*
| ROMEN | ----- | PC32 | PC10 | ----- | ST2 | ST1 | ST0 |
ROMEN - enable mask ROM in top 64-bytes of address space for CPU read
PC32 - clear SPC read ports 2 & 3
PC10 - clear SPC read ports 0 & 1
ST2 - start timer 2 (64kHz)
ST1 - start timer 1 (8kHz)
ST0 - start timer 0 (8kHz)
*/
void spc_start_timer(s32 timer)
{
u32 shift, mask;
if (timer != 2)
{
shift = 7;
}
else
{
shift = 4;
}
mask = -BIT(shift);
_timers[timer].cycle_latch = _TotalCycles & mask;
_timers[timer].position = 0;
_timers[timer].counter = 0;
}
static void SPC_WRITE_INVALID(u16 address, u8 data)
{
#ifdef TRAP_INVALID_WRITE
#ifdef DEBUG
/* Set up address so message works */
Map_Address = address;
Map_Byte = data;
InvalidSPCHWWrite(); /* Display write to invalid HW warning */
#endif
#endif
}
static void SPC_WRITE_CTRL(u16 address, u8 data)
{
/* IPL ROM enable */
_FFC0_Address = data & 0x80 ? SPC_ROM_CODE - 0xFFC0 : SPCRAM;
/* read ports 0/1 reset */
if (data & 0x10)
{
_PORT_R[0] = 0;
_PORT_R[1] = 0;
}
/* read ports 2/3 reset */
if (data & 0x20)
{
_PORT_R[2] = 0;
_PORT_R[3] = 0;
}
/* timer 0 control */
if (!(SPCRAM[address] & 1) && (data & 1))
{
spc_start_timer(0);
}
/* timer 0 control */
if (!(SPCRAM[address] & 2) && (data & 2))
{
spc_start_timer(1);
}
/* timer 2 control */
if (!(SPCRAM[address] & 4) && (data & 4))
{
spc_start_timer(2);
}
SPC_CTRL = data;
}
static void SPC_WRITE_RAM(u16 address, u8 data)
{
SPCRAM[address] = data;
}
static void SPC_WRITE_DSP_DATA(u16 address, u8 data)
{
SPC_DSP_DATA = data;
/* write to DSP register */
SPC_WRITE_DSP();
}
void SPC_WRITE_PORT_W(u16 address, u8 data)
{
_PORT_W[address & 3] = data;
}
static void SPC_WRITE_TIMER(u16 address, u8 data)
{
/* 0xFA = write address for first timer's target */
s32 timer = address - 0xFA;
s32 target;
if ((_timers[timer].target & 0xFF) == data)
{
return;
}
target = data ? data : 256;
/* Timer must catch up before changing target */
Update_SPC_Timer(timer);
_timers[timer].target = target;
/* does setting target for current position raise counter? assuming not */
if (target <= _timers[timer].position)
/* handle 'delay' where new target is set below position */
{
_timers[timer].position -= 256;
}
}
/* Mappings for SPC Registers */
static u8 (*Read_Func_Map[16])(u16 address) = {SPC_READ_INVALID, SPC_READ_INVALID, SPC_READ_RAM, SPC_READ_DSP_DATA,
SPC_READ_PORT_R, SPC_READ_PORT_R, SPC_READ_PORT_R, SPC_READ_PORT_R,
SPC_READ_RAM, SPC_READ_RAM, SPC_READ_INVALID, SPC_READ_INVALID,
SPC_READ_INVALID, SPC_READ_COUNTER, SPC_READ_COUNTER, SPC_READ_COUNTER};
static void (*Write_Func_Map[16])(u16 address, u8 data) = {
SPC_WRITE_INVALID, SPC_WRITE_CTRL, SPC_WRITE_RAM, SPC_WRITE_DSP_DATA, SPC_WRITE_PORT_W, SPC_WRITE_PORT_W,
SPC_WRITE_PORT_W, SPC_WRITE_PORT_W, SPC_WRITE_RAM, SPC_WRITE_RAM, SPC_WRITE_TIMER, SPC_WRITE_TIMER,
SPC_WRITE_TIMER, SPC_WRITE_RAM, SPC_WRITE_RAM, SPC_WRITE_RAM};
static u8 offset_to_bit[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
static u8 offset_to_not[8] = {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F};
u8 get_byte_spc(u16 address)
{
/* Note: need to update sound if echo write enabled and accessing echo */
/* region */
if (address >= 0x0100)
/* not zero page */
{
if (address >= 0xFFC0)
/* return ROM if it's mapped in, else RAM */
{
return ((u8 *)_FFC0_Address)[address];
}
/* return RAM */
return SPCRAM[address];
}
/* zero page */
if (address < 0xF0)
/* RAM */
{
return SPCRAM[address];
}
save_cycles_spc(); /* Set cycle counter */
return Read_Func_Map[address - 0xF0](address);
}
/* -------- */
void set_byte_spc(u16 address, u8 data)
{
/* Note: need to update sound always, since all (?) writes affect RAM */
if (address >= 0x0100 || address < 0xF0)
/* write to RAM */
{
save_cycles_spc(); /* Set cycle counter */
update_sound();
SPCRAM[address] = data;
}
else
{
save_cycles_spc(); /* Set cycle counter */
Write_Func_Map[address - 0xF0](address, data);
}
}
void Reset_SPC(void)
{
s32 i;
/* Get ROM reset vector and setup Program Counter */
_PC = SPC_ROM_CODE[0xFFFE - 0xFFC0] + (SPC_ROM_CODE[0xFFFF - 0xFFC0] << 8);
/* Reset cycle counts */
_TotalCycles = 6; /* 5-7 cycles before execution begins */
_Cycles = 0;
_last_cycles = 0;
_cycle = 0;
/* Reset SSMP registers */
_dp = 0; /* Used to save P flag check for dp addressing */
_SP = 0xEF;
_YA = 0;
_X = 0;
/* Clear flags register */
_PSW = 0;
clr_flag_spc(SPC_FLAG_N);
clr_flag_spc(SPC_FLAG_V);
clr_flag_spc(SPC_FLAG_P);
clr_flag_spc(SPC_FLAG_B);
clr_flag_spc(SPC_FLAG_H);
clr_flag_spc(SPC_FLAG_I);
clr_flag_spc(SPC_FLAG_Z);
clr_flag_spc(SPC_FLAG_C);
SPC_CTRL = 0x80;
_FFC0_Address = SPC_ROM_CODE - 0xFFC0;
/* Reset timers */
for (i = 0; i < 3; i++)
{
_timers[i].cycle_latch = 0;
_timers[i].position = 0;
_timers[i].target = 256;
_timers[i].counter = 0;
}
sound_cycle_latch = 0;
/* Reset SPC700 input ports */
_PORT_R[0] = 0;
_PORT_R[1] = 0;
_PORT_R[2] = 0;
_PORT_R[3] = 0;
/* Reset SPC700 output ports */
_PORT_W[0] = 0;
_PORT_W[1] = 0;
_PORT_W[2] = 0;
_PORT_W[3] = 0;
/* Reset sound DSP port address */
SPC_DSP_ADDR = 0;
SPC_DSP_DATA = 0;
}
void SPC_SHOW_REGISTERS(void)
{
DisplaySPC();
}
u8 get_SPC_PSW(void)
{
spc_setup_flags(_B_flag);
return _PSW;
}
#ifdef OPCODE_TRACE_LOG
#define SS_WAIT_FOR_KEY /*if ((readkey() & 0xFF) == 'g') { s32 i = 0; while (i++ < 49) simulate_keypress(' ' + \
(KEY_SPACE << 8)); }*/
#else
#define SS_WAIT_FOR_KEY
void dummy_fprintf()
{
}
#define fprintf dummy_fprintf
#endif
#ifdef OPCODE_TRACE_LOG
/* cycle #, PC, TotalCycles */
#define SINGLE_STEP_START(c) \
if (dump_flag && debug_log_file) \
{ \
fprintf(debug_log_file, "START_CYCLE(%u) PC:%04X %u\n", c, _PC & 0xFFFF, get_cycles_spc()); \
if ((c == 1) && (_PC == 0x02C4)) \
exit(0); \
}
void single_step_end(void)
{
if (!dump_flag || !debug_log_file)
return;
fprintf(debug_log_file, "NVPBHIZC R:%02X %02X %02X %02X X:%02X Y:%02X A:%02X SP:%02X dp:%02X Op:%02X\n",
_PORT0R & 0xFF, _PORT1R & 0xFF, _PORT2R & 0xFF, _PORT3R & 0xFF, _X & 0xFF, _Y & 0xFF, _A & 0xFF, _SP & 0xFF,
_direct_page & 0xFF, _opcode & 0xFF);
fprintf(debug_log_file, "%c%c%c%c%c%c%c%c W:%02X %02X %02X %02X Ad%04X %04X Off%02X D%02X %02X D16 %04X",
flag_state_spc(SPC_FLAG_N) ? '1' : '0', flag_state_spc(SPC_FLAG_V) ? '1' : '0',
flag_state_spc(SPC_FLAG_P) ? '1' : '0', flag_state_spc(SPC_FLAG_B) ? '1' : '0',
flag_state_spc(SPC_FLAG_H) ? '1' : '0', flag_state_spc(SPC_FLAG_I) ? '1' : '0',
flag_state_spc(SPC_FLAG_Z) ? '1' : '0', flag_state_spc(SPC_FLAG_C) ? '1' : '0', _PORT0W & 0xFF,
_PORT1W & 0xFF, _PORT2W & 0xFF, _PORT3W & 0xFF, _address & 0xFFFF, _address2 & 0xFFFF, _offset & 0xFF,
_data & 0xFF, _data2 & 0xFF, _data16 & 0xFFFF);
if (_cycle == 0)
fprintf(debug_log_file, " %s\n", SPC_OpID[_opcode]);
else
fprintf(debug_log_file, "\n");
}
/* op, R ports, W ports, X Y A */
/* address1 address2 offset data1 data2 data16 */
#define SINGLE_STEP_END single_step_end();
#else
#define SINGLE_STEP_START(c)
#define SINGLE_STEP_END
#endif
#define START_CYCLE(c) \
if (_cycle <= ((c)-1)) \
{ \
SINGLE_STEP_START(c)
#define END_FETCH_CYCLE() \
_WorkCycles++; \
SINGLE_STEP_END if (_WorkCycles >= 0) \
{ \
_cycle = 1; \
opcode_done = 0; \
break; \
} \
}
#define END_CYCLE(c, n) \
_WorkCycles += n; \
SINGLE_STEP_END if (_WorkCycles >= 0) \
{ \
_cycle = c; \
opcode_done = 0; \
break; \
} \
}
#define EXIT_OPCODE(n) \
{ \
_WorkCycles += n; \
SINGLE_STEP_END break; \
}
#define END_OPCODE(n) \
EXIT_OPCODE(n) \
}
#define END_BRANCH_OPCODE(cycle, TEST) \
TEST END_CYCLE((cycle), 1) \
\
/* sign extend offset and add to PC */ \
START_CYCLE((cycle)+1) _address = _PC + (((s32)_offset ^ 0x80) - 0x80); \
END_CYCLE((cycle)+1, 1) \
\
START_CYCLE((cycle)+2) \
_PC = _address; \
END_OPCODE(1)
#define REL_TEST_BRA ;
#define REL_TEST_BPL \
if (flag_state_spc(SPC_FLAG_N)) \
EXIT_OPCODE(1)
#define REL_TEST_BMI \
if (!flag_state_spc(SPC_FLAG_N)) \
EXIT_OPCODE(1)
#define REL_TEST_BVC \
if (flag_state_spc(SPC_FLAG_V)) \
EXIT_OPCODE(1)
#define REL_TEST_BVS \
if (!flag_state_spc(SPC_FLAG_V)) \
EXIT_OPCODE(1)
#define REL_TEST_BCC \
if (flag_state_spc(SPC_FLAG_C)) \
EXIT_OPCODE(1)
#define REL_TEST_BCS \
if (!flag_state_spc(SPC_FLAG_C)) \
EXIT_OPCODE(1)
#define REL_TEST_BNE \
if (flag_state_spc(SPC_FLAG_Z)) \
EXIT_OPCODE(1)
#define REL_TEST_BEQ \
if (!flag_state_spc(SPC_FLAG_Z)) \
EXIT_OPCODE(1)
#define OP_TCALL(vector) \
/* 8 cycles - opcode, new PCL, new PCH, stack address load, PCH */ \
/* write, PCL write, dummy cycle (PSW write in BRK?) */ \
/* SP decrement */ \
/* fetch address for PC */ \
START_CYCLE(2) \
_address = 0xFFC0 + ((15 - (vector)) * 2); \
_address2 = get_byte_spc(_address); \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address2 += (get_byte_spc(_address + 1) << 8); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_address = 0x0100 + _SP; \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
set_byte_spc(_address, _PC >> 8); \
_SP--; \
_address = 0x0100 + _SP; \
END_CYCLE(5, 1) \
\
START_CYCLE(6) \
set_byte_spc(_address, _PC); \
_SP--; \
_address = 0x0100 + _SP; \
END_CYCLE(6, 1) \
\
START_CYCLE(7) \
/* should we write PSW to stack here? */ \
END_CYCLE(7, 1) \
\
START_CYCLE(8) \
_PC = _address2; \
END_OPCODE(1)
#define COND_REL(TEST) \
/* 2 cycles - opcode, branch logic + offset; */ \
/* +2 cycles (taken branch) add PC to offset, reload PC */ \
\
START_CYCLE(2) \
_offset = get_byte_spc(_PC); \
_PC++; \
END_BRANCH_OPCODE(2, TEST)
#define DP_REL_TEST_BBS \
if (!(_data & offset_to_bit[_opcode >> 5])) \
EXIT_OPCODE(1)
#define DP_REL_TEST_BBC \
if ((_data & offset_to_bit[_opcode >> 5])) \
EXIT_OPCODE(1)
#define TEST_CBNE \
if (_data == _A) \
EXIT_OPCODE(1)
#define DP_REL_TEST_DBNZ \
--_data; \
set_byte_spc(_address, _data); \
if (!_data) \
EXIT_OPCODE(1)
#define COND_DP_REL(TEST) \
/* 5 cycles - opcode, address, branch offset, data read, branch logic */ \
/* and data write (DBNZ only); +2 cycles (taken branch) add PC to */ \
/* offset, reload PC */ \
START_CYCLE(2) \
_address = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_offset = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data = get_byte_spc(_address); \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
END_BRANCH_OPCODE(5, TEST)
#define OP_READ_DP(OP, dest) \
/* 3 cycles - opcode, address, data read + op */ \
START_CYCLE(2) \
_address = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_data = get_byte_spc(_address); \
OP_##OP((dest), _data) END_OPCODE(1)
/* xxx00100 */
#define OP_READ_DP_A(OP) OP_READ_DP(OP, _A)
#define OP_READ_ABS(OP, dest) \
/* 4 cycles - opcode, address low, address high, data read + op */ \
START_CYCLE(2) \
_address = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address += get_byte_spc(_PC) << 8; \
_PC++; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data = get_byte_spc(_address); \
OP_##OP((dest), _data) END_OPCODE(1)
/* xxx00101 */
#define OP_READ_ABS_A(OP) OP_READ_ABS(OP, _A)
#define OP_READ_INDIRECT(OP, dest) \
/* 3 cycles - opcode, address calc, data read + op */ \
START_CYCLE(2) \
_address = _dp + _X; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_data = get_byte_spc(_address); \
OP_##OP(_A, _data) END_OPCODE(1)
#define OP_RMW_INDIRECT(OP, NEED_OLD_DATA) \
/* 4 cycles - opcode, address calc, data read, data write */ \
START_CYCLE(2) \
_address = _dp + _X; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
if (NEED_OLD_DATA) \
_data = get_byte_spc(_address); \
else \
get_byte_spc(_address); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
OP END_OPCODE(1)
/* xxx00110 */
#define OP_READ_INDIRECT_A(OP) OP_READ_INDIRECT(OP, _A)
#define OP_READ_INDEXED_INDIRECT(OP, dest) \
/* 6 cycles - opcode, offset, address calc, address low, */ \
/* address high, data read + op */ \
START_CYCLE(2) \
_address2 = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address2 = _dp + ((_address2 + _X) & 0xFF); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_address = get_byte_spc(_address2); \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
_address += get_byte_spc(_address2 + 1) << 8; \
END_CYCLE(5, 1) \
\
START_CYCLE(6) \
_data = get_byte_spc(_address); \
OP_##OP((dest), _data) END_OPCODE(1)
#define OP_RMW_INDEXED_INDIRECT(OP, NEED_OLD_DATA) \
/* 7 cycles - opcode, offset, address calc, address low, */ \
/* address high, data read, data read + op */ \
START_CYCLE(2) \
_address2 = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address2 = _dp + ((_address2 + _X) & 0xFF); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_address = get_byte_spc(_address2); \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
_address += get_byte_spc(_address2 + 1) << 8; \
END_CYCLE(5, 1) \
\
START_CYCLE(6) \
if (NEED_OLD_DATA) \
_data = get_byte_spc(_address); \
else \
get_byte_spc(_address); \
END_CYCLE(6, 1) \
\
START_CYCLE(7) \
OP END_OPCODE(1)
/* xxx00111 */
#define OP_READ_INDEXED_INDIRECT_A(OP) OP_READ_INDEXED_INDIRECT(OP, _A)
#define OP_READ_IMM(OP, dest) \
/* 2 cycles - opcode, data read + op */ \
START_CYCLE(2) \
_data = get_byte_spc(_PC); \
_PC++; \
OP_##OP((dest), _data) END_OPCODE(1)
/* xxx01000 */
#define OP_READ_IMM_A(OP) OP_READ_IMM(OP, _A)
#define OP_OR(dest, src) \
{ \
(dest) |= (src); \
store_flags_nz(dest); \
}
#define OP_AND(dest, src) \
{ \
(dest) &= (src); \
store_flags_nz(dest); \
}
#define OP_EOR(dest, src) \
{ \
(dest) ^= (src); \
store_flags_nz(dest); \
}
#define OP_CMP(dest, src) \
{ \
u32 temp = (dest) - (src); \
\
store_flag_c(temp <= 0xFF); \
store_flags_nz(temp); \
}
#define OP_ADC(dest, src) \
{ \
u32 result = (dest) + (src) + (flag_state_spc(SPC_FLAG_C) ? 1 : 0); \
\
store_flag_h(((u32)(((dest)&0x0F) + ((src)&0x0F) + (flag_state_spc(SPC_FLAG_C) ? 1 : 0))) > 0x0F ? 1 : 0); \
store_flag_c(result > 0xFF); \
store_flag_v((~((dest) ^ (src))) & (((dest) ^ result) & 0x80)); \
store_flags_nz(result); \
(dest) = result; \
}
#define OP_SBC(dest, src) \
{ \
u32 result = (dest) - (src) - (flag_state_spc(SPC_FLAG_C) ? 0 : 1); \
\
store_flag_h(((u32)(((dest)&0x0F) - ((src)&0x0F) - (flag_state_spc(SPC_FLAG_C) ? 0 : 1))) > 0x0F ? 0 : 1); \
store_flag_c(result <= 0xFF); \
store_flag_v(((dest) ^ (src)) & (((dest) ^ result) & 0x80)); \
store_flags_nz(result); \
(dest) = result; \
}
#define OP_MOV_READ_NOFLAGS(dest, src) \
{ \
(dest) = (src); \
}
#define OP_MOV_READ(dest, src) \
{ \
(dest) = (src); \
store_flags_nz(src); \
}
#define OP_ADDW(dest, src) \
{ \
u32 temp_low, carry_low, temp_high, result; \
\
temp_low = ((dest)&0xFF) + ((src)&0xFF); \
carry_low = temp_low > 0xFF ? 1 : 0; \
\
store_flag_h(((u32)((((dest) >> 8) & 0x0F) + (((src) >> 8) & 0x0F) + carry_low)) > 0x0F ? 1 : 0); \
\
temp_high = ((dest) >> 8) + ((src) >> 8) + carry_low; \
store_flag_c(temp_high > 0xFF); \
result = ((temp_low & 0xFF) + (temp_high << 8)) & 0xFFFF; \
\
store_flag_v(((~((dest) ^ (src))) & (((dest) ^ result) & 0x8000)) >> 8); \
store_flag_n(result >> 8); \
store_flag_z(result != 0); \
(dest) = result; \
}
#define OP_SUBW(dest, src) \
{ \
u32 temp_low, carry_low, temp_high, result; \
\
temp_low = ((dest)&0xFF) - ((src)&0xFF); \
carry_low = temp_low > 0xFF ? 1 : 0; \
\
store_flag_h(((u32)((((dest) >> 8) & 0x0F) - (((src) >> 8) & 0x0F) - carry_low)) > 0x0F ? 0 : 1); \
\
temp_high = ((dest) >> 8) - ((src) >> 8) - carry_low; \
store_flag_c(temp_high <= 0xFF); \
result = ((temp_low & 0xFF) + (temp_high << 8)) & 0xFFFF; \
\
store_flag_v((((dest) ^ (src)) & (((dest) ^ result) & 0x8000)) >> 8); \
store_flag_n(result >> 8); \
store_flag_z(result != 0); \
(dest) = result; \
}
#define OP_MOVW_READ(dest, src) \
{ \
(dest) = (src); \
store_flag_n((dest) >> 8); \
store_flag_z((dest) != 0); \
}
#define OP_ASL(var) \
{ \
store_flag_c((var)&0x80); \
(var) <<= 1; \
store_flags_nz(var); \
}
#define OP_ROL(var) \
{ \
s32 c = flag_state_spc(SPC_FLAG_C) ? 1 : 0; \
\
store_flag_c((var)&0x80); \
(var) = ((var) << 1) + c; \
store_flags_nz(var); \
}
#define OP_LSR(var) \
{ \
store_flag_c((var)&1); \
(var) >>= 1; \
store_flags_nz(var); \
}
#define OP_ROR(var) \
{ \
s32 c = flag_state_spc(SPC_FLAG_C) ? 0x80 : 0; \
\
store_flag_c((var)&1); \
(var) = ((var) >> 1) + c; \
store_flags_nz(var); \
}
#define OP_DECW(var) \
{ \
(var)--; \
store_flag_n(var >> 8); \
store_flag_z((var & 0xFFFF) != 0); \
}
#define OP_INCW(var) \
{ \
(var)++; \
store_flag_n(var >> 8); \
store_flag_z((var & 0xFFFF) != 0); \
}
#define OP_DEC(var) \
{ \
(var)--; \
store_flags_nz(var); \
}
#define OP_INC(var) \
{ \
(var)++; \
store_flags_nz(var); \
}
#define OP_SET1(var) \
{ \
(var) |= offset_to_bit[_opcode >> 5]; \
}
#define OP_CLR1(var) \
{ \
(var) &= offset_to_not[_opcode >> 5]; \
}
#define WRITE_OP(OP) \
OP_##OP(_data); \
set_byte_spc(_address, _data);
#define WRITE_MOV(var) set_byte_spc(_address, (var));
#define WRITE_ASL \
OP_ASL(_data); \
set_byte_spc(_address, _data);
#define WRITE_LSR \
OP_LSR(_data); \
set_byte_spc(_address, _data);
#define WRITE_ROL \
OP_ROL(_data); \
set_byte_spc(_address, _data);
#define WRITE_ROR \
OP_ROR(_data); \
set_byte_spc(_address, _data);
#define WRITE_DEC \
OP_DEC(_data); \
set_byte_spc(_address, _data);
#define WRITE_INC \
OP_INC(_data); \
set_byte_spc(_address, _data);
/* xxx01011 */
#define OP_RMW_DP(OP, NEED_OLD_DATA) \
/* 4 cycles - opcode, address, data read, op + data write */ \
START_CYCLE(2) \
_address = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
if (NEED_OLD_DATA) \
_data = get_byte_spc(_address); \
else \
get_byte_spc(_address); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
OP END_OPCODE(1)
/* xxx01001 */
#define OP_RMW_DP_DP(OP) \
/* 6 cycles - opcode, src address, dest address, src read, */ \
/* dest read + op, dest write */ \
START_CYCLE(2) \
_address2 = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data2 = get_byte_spc(_address2); \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
_data = get_byte_spc(_address); \
OP_##OP(_data, _data2) END_CYCLE(5, 1) \
\
START_CYCLE(6) set_byte_spc(_address, _data); \
END_OPCODE(1)
/* xxx01100 */
#define OP_RMW_ABS(OP, NEED_OLD_DATA) \
/* 5 cycles - opcode, address low, address high, data read, */ \
/* op + data write */ \
START_CYCLE(2) \
_address = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address += get_byte_spc(_PC) << 8; \
_PC++; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
if (NEED_OLD_DATA) \
_data = get_byte_spc(_address); \
else \
get_byte_spc(_address); \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
OP END_OPCODE(1)
/* 0xx01101 */
#define OP_PUSH(src) \
{ \
/* 4 cycles - opcode, address load, data write, SP decrement */ \
START_CYCLE(2) \
_address = 0x0100 + _SP; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
set_byte_spc(_address, src); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_SP--; \
END_OPCODE(1) \
}
/* 1xx01110 */
#define OP_POP(dest) \
{ \
/* 4 cycles - opcode, SP increment, address load, data read */ \
START_CYCLE(2) \
_SP++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address = 0x0100 + _SP; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
(dest) = get_byte_spc(_address); \
END_OPCODE(1) \
}
#define OP_READ_DP_reg_INDEXED(OP, dest, index) \
/* 4 cycles - opcode, address, address calc, data read + op */ \
START_CYCLE(2) \
_address = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address = _dp + ((_address + index) & 0xFF); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data = get_byte_spc(_address); \
OP_##OP((dest), _data) END_OPCODE(1)
#define OP_RMW_DP_reg_INDEXED(OP, NEED_OLD_DATA, index) \
/* 5 cycles - opcode, address, address calc, data read, */ \
/* data write */ \
START_CYCLE(2) \
_address = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address = _dp + ((_address + index) & 0xFF); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
if (NEED_OLD_DATA) \
_data = get_byte_spc(_address); \
else \
get_byte_spc(_address); \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
OP END_OPCODE(1)
/* xxx10100 */
#define OP_READ_DP_X_INDEXED(OP, dest) OP_READ_DP_reg_INDEXED(OP, dest, _X)
#define OP_READ_DP_X_INDEXED_A(OP) OP_READ_DP_X_INDEXED(OP, _A)
/* xxx11011 */
#define OP_RMW_DP_X_INDEXED(OP, NEED_OLD_DATA) OP_RMW_DP_reg_INDEXED(OP, NEED_OLD_DATA, _X)
/* xxx11001 */
#define OP_READ_DP_Y_INDEXED(OP, dest) OP_READ_DP_reg_INDEXED(OP, dest, _Y)
#define OP_RMW_DP_Y_INDEXED(OP, NEED_OLD_DATA) OP_RMW_DP_reg_INDEXED(OP, NEED_OLD_DATA, _Y)
#define OP_READ_ABS_reg_INDEXED(OP, dest, index) \
/* 5 cycles - opcode, address low, address high, address calc, */ \
/* data read + op */ \
START_CYCLE(2) \
_address = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address += get_byte_spc(_PC) << 8; \
_PC++; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_address += index; \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
_data = get_byte_spc(_address); \
OP_##OP((dest), _data) END_OPCODE(1)
#define OP_RMW_ABS_reg_INDEXED(OP, NEED_OLD_DATA, index) \
/* 6 cycles - opcode, address low, address high, address calc, */ \
/* data read, data write */ \
START_CYCLE(2) \
_address = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address += get_byte_spc(_PC) << 8; \
_PC++; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_address += index; \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
if (NEED_OLD_DATA) \
_data = get_byte_spc(_address); \
else \
get_byte_spc(_address); \
END_CYCLE(5, 1) \
\
START_CYCLE(6) \
OP END_OPCODE(1)
/* xxx10101 */
#define OP_READ_ABS_X_INDEXED_A(OP) OP_READ_ABS_reg_INDEXED(OP, _A, _X)
#define OP_RMW_ABS_X_INDEXED(OP, NEED_OLD_DATA) OP_RMW_ABS_reg_INDEXED(OP, NEED_OLD_DATA, _X)
/* xxx10110 */
#define OP_READ_ABS_Y_INDEXED_A(OP) OP_READ_ABS_reg_INDEXED(OP, _A, _Y)
#define OP_RMW_ABS_Y_INDEXED(OP, NEED_OLD_DATA) OP_RMW_ABS_reg_INDEXED(OP, NEED_OLD_DATA, _Y)
#define OP_READ_INDIRECT_INDEXED(OP, dest) \
/* 6 cycles - opcode, offset, address low, address high, */ \
/* address calc, data read + op */ \
START_CYCLE(2) \
_address2 = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address = get_byte_spc(_address2); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_address += get_byte_spc(_address2 + 1) << 8; \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
_address += _Y; \
END_CYCLE(5, 1) \
\
START_CYCLE(6) \
_data = get_byte_spc(_address); \
OP_##OP((dest), _data) END_OPCODE(1)
#define OP_RMW_INDIRECT_INDEXED(OP, NEED_OLD_DATA) \
/* 7 cycles - opcode, offset, address low, address high, */ \
/* address calc, data read + op, data write */ \
START_CYCLE(2) \
_address2 = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address = get_byte_spc(_address2); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_address += get_byte_spc(_address2 + 1) << 8; \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
_address += _Y; \
END_CYCLE(5, 1) \
\
START_CYCLE(6) \
if (NEED_OLD_DATA) \
_data = get_byte_spc(_address); \
else \
get_byte_spc(_address); \
END_CYCLE(6, 1) \
\
START_CYCLE(7) \
OP END_OPCODE(1)
/* xxx10111 */
#define OP_READ_INDIRECT_INDEXED_A(OP) OP_READ_INDIRECT_INDEXED(OP, _A)
/* xxx11000 */
#define OP_RMW_DP_IMM(OP) \
/* 5 cycles - opcode, src data, dest address, dest read + op, */ \
/* dest write */ \
START_CYCLE(2) \
_data2 = get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_address = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data = get_byte_spc(_address); \
OP_##OP(_data, _data2) END_CYCLE(4, 1) \
\
START_CYCLE(5) set_byte_spc(_address, _data); \
END_OPCODE(1)
/* xxx11001 */
#define OP_RMW_INDIRECT_INDIRECT(OP) \
/* 5 cycles - opcode, address calc, src read, dest read + op, */ \
/* dest write */ \
START_CYCLE(2) \
_address = _dp + _Y; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_data2 = get_byte_spc(_address); \
_address = _dp + _X; \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data = get_byte_spc(_address); \
OP_##OP(_data, _data2) END_CYCLE(4, 1) \
\
START_CYCLE(5) set_byte_spc(_address, _data); \
END_OPCODE(1)
/* note - timing on all 16-bit opcodes may be off: RMW could be instead: */
/* data low read, write, data high read, write; or data low read, */
/* data high read, data low write, data high write */
/* xxx11010 */
#define OP_READ16_YA_DP(OP) \
/* 5 cycles - opcode, address, data low read, data high read + op, */ \
/* (?) */ \
START_CYCLE(2) \
_address = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_data16 = get_byte_spc(_address); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data16 += get_byte_spc(_address + 1) << 8; \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
OP_##OP(_YA, _data16) END_OPCODE(1)
#define OP_RMW16_DP(OP) \
/* 6 cycles - opcode, address, data low read, data high read + op, */ \
/* data low (?) write, data high write */ \
START_CYCLE(2) \
_address = _dp + get_byte_spc(_PC); \
_PC++; \
END_CYCLE(2, 1) \
\
START_CYCLE(3) \
_data16 = get_byte_spc(_address); \
END_CYCLE(3, 1) \
\
START_CYCLE(4) \
_data16 += get_byte_spc(_address + 1) << 8; \
END_CYCLE(4, 1) \
\
START_CYCLE(5) \
OP_##OP(_data16) set_byte_spc(_address, _data16); \
END_CYCLE(5, 1) \
\
START_CYCLE(6) \
set_byte_spc(_address + 1, _data16 >> 8); \
END_OPCODE(1)
/* xxx11100 */
#define OP_RMW_IMPLIED(OP, reg) \
/* 2 cycles - opcode, op */ \
START_CYCLE(2) \
OP_##OP(reg); \
END_OPCODE(1)
/* xxx11101 */
#define OP_MOV_IMPLIED(dest, src) \
/* 2 cycles - opcode, op */ \
START_CYCLE(2) \
OP_MOV_READ(dest, src) \
END_OPCODE(1)
#define OP_MOV_IMPLIED_NO_FLAGS(dest, src) \
/* 2 cycles - opcode, op */ \
START_CYCLE(2) \
(dest) = (src); \
END_OPCODE(1)
static void Execute_SPC(void)
{
u8 was_in_cpu = In_CPU;
In_CPU = 0;
load_cycles_spc();
while (_WorkCycles < 0)
{
s32 opcode_done = 1;
START_CYCLE(1)
/* fetch opcode */
_opcode = get_byte_spc(_PC);
_PC++;
END_FETCH_CYCLE()
switch (_opcode)
{
/* xxx00000 */
case 0x00: /* NOP */
{
START_CYCLE(2)
END_OPCODE(1)
}
case 0x20: /* CLRP */
{
START_CYCLE(2)
clr_flag_spc(SPC_FLAG_P);
END_OPCODE(1)
}
case 0x40: /* SETP */
{
START_CYCLE(2)
set_flag_spc(SPC_FLAG_P);
END_OPCODE(1)
}
case 0x60: /* CLRC */
{
START_CYCLE(2)
clr_flag_spc(SPC_FLAG_C);
END_OPCODE(1)
}
case 0x80: /* SETC */
{
START_CYCLE(2)
set_flag_spc(SPC_FLAG_C);
END_OPCODE(1)
}
case 0xA0: /* EI */
{
START_CYCLE(2)
set_flag_spc(SPC_FLAG_I);
END_OPCODE(1)
}
case 0xC0: /* DI */
{
START_CYCLE(2)
clr_flag_spc(SPC_FLAG_I);
END_OPCODE(1)
}
case 0xE0: /* CLRV */
{
START_CYCLE(2)
clr_flag_spc(SPC_FLAG_H | SPC_FLAG_V);
END_OPCODE(1)
}
/* xxxx0001 */
#define opcode_TCALL(vector) (((vector) << 4) + 0x01)
case opcode_TCALL(0):
case opcode_TCALL(1):
case opcode_TCALL(2):
case opcode_TCALL(3):
case opcode_TCALL(4):
case opcode_TCALL(5):
case opcode_TCALL(6):
case opcode_TCALL(7):
case opcode_TCALL(8):
case opcode_TCALL(9):
case opcode_TCALL(10):
case opcode_TCALL(11):
case opcode_TCALL(12):
case opcode_TCALL(13):
case opcode_TCALL(14):
case opcode_TCALL(15):
{
OP_TCALL(_opcode >> 4)
}
/* xxx00010 */
#define opcode_SET1(bit) (((bit) << 5) + 0x02)
case opcode_SET1(0):
case opcode_SET1(1):
case opcode_SET1(2):
case opcode_SET1(3):
case opcode_SET1(4):
case opcode_SET1(5):
case opcode_SET1(6):
case opcode_SET1(7):
{
OP_RMW_DP(WRITE_OP(SET1), 1)
}
/* xxx00011 */
#define opcode_BBS(bit) (((bit) << 5) + 0x03)
case opcode_BBS(0):
case opcode_BBS(1):
case opcode_BBS(2):
case opcode_BBS(3):
case opcode_BBS(4):
case opcode_BBS(5):
case opcode_BBS(6):
case opcode_BBS(7):
{
COND_DP_REL(DP_REL_TEST_BBS)
}
/* xxx00100 */
case 0x04: /* OR A,dp */
{
OP_READ_DP_A(OR)
}
case 0x24: /* AND A,dp */
{
OP_READ_DP_A(AND)
}
case 0x44: /* EOR A,dp */
{
OP_READ_DP_A(EOR)
}
case 0x64: /* CMP A,dp */
{
OP_READ_DP_A(CMP)
}
case 0x84: /* ADC A,dp */
{
OP_READ_DP_A(ADC)
}
case 0xA4: /* SBC A,dp */
{
OP_READ_DP_A(SBC)
}
case 0xC4: /* MOV dp,A */
{
OP_RMW_DP(WRITE_MOV(_A), 0)
}
case 0xE4: /* MOV A,dp */
{
OP_READ_DP_A(MOV_READ)
}
/* xxx00101 */
case 0x05: /* OR A,abs */
{
OP_READ_ABS_A(OR)
}
case 0x25: /* AND A,abs */
{
OP_READ_ABS_A(AND)
}
case 0x45: /* EOR A,abs */
{
OP_READ_ABS_A(EOR)
}
case 0x65: /* CMP A,abs */
{
OP_READ_ABS_A(CMP)
}
case 0x85: /* ADC A,abs */
{
OP_READ_ABS_A(ADC)
}
case 0xA5: /* SBC A,abs */
{
OP_READ_ABS_A(SBC)
}
case 0xC5: /* MOV abs,A */
{
OP_RMW_ABS(WRITE_MOV(_A), 0)
}
case 0xE5: /* MOV A,abs */
{
OP_READ_ABS_A(MOV_READ)
}
/* xxx00110 */
case 0x06: /* OR A,(X) */
{
OP_READ_INDIRECT_A(OR)
}
case 0x26: /* AND A,(X) */
{
OP_READ_INDIRECT_A(AND)
}
case 0x46: /* EOR A,(X) */
{
OP_READ_INDIRECT_A(EOR)
}
case 0x66: /* CMP A,(X) */
{
OP_READ_INDIRECT_A(CMP)
}
case 0x86: /* ADC A,(X) */
{
OP_READ_INDIRECT_A(ADC)
}
case 0xA6: /* SBC A,(X) */
{
OP_READ_INDIRECT_A(SBC)
}
case 0xC6: /* MOV (X),A */
{
OP_RMW_INDIRECT(WRITE_MOV(_A), 0)
}
case 0xE6: /* MOV A,(X) */
{
OP_READ_INDIRECT_A(MOV_READ)
}
/* xxx00111 */
case 0x07: /* OR A,(dp+X) */
{
OP_READ_INDEXED_INDIRECT_A(OR)
}
case 0x27: /* AND A,(dp+X) */
{
OP_READ_INDEXED_INDIRECT_A(AND)
}
case 0x47: /* EOR A,(dp+X) */
{
OP_READ_INDEXED_INDIRECT_A(EOR)
}
case 0x67: /* CMP A,(dp+X) */
{
OP_READ_INDEXED_INDIRECT_A(CMP)
}
case 0x87: /* ADC A,(dp+X) */
{
OP_READ_INDEXED_INDIRECT_A(ADC)
}
case 0xA7: /* SBC A,(dp+X) */
{
OP_READ_INDEXED_INDIRECT_A(SBC)
}
case 0xC7: /* MOV (dp+X),A */
{
OP_RMW_INDEXED_INDIRECT(WRITE_MOV(_A), 0)
}
case 0xE7: /* MOV A,(dp+X) */
{
OP_READ_INDEXED_INDIRECT_A(MOV_READ)
}
/* xxx01000 */
case 0x08: /* OR A,#imm */
{
OP_READ_IMM_A(OR)
}
case 0x28: /* AND A,#imm */
{
OP_READ_IMM_A(AND)
}
case 0x48: /* EOR A,#imm */
{
OP_READ_IMM_A(EOR)
}
case 0x68: /* CMP A,#imm */
{
OP_READ_IMM_A(CMP)
}
case 0x88: /* ADC A,#imm */
{
OP_READ_IMM_A(ADC)
}
case 0xA8: /* SBC A,#imm */
{
OP_READ_IMM_A(SBC)
}
case 0xC8: /* CMP X,#imm */
{
OP_READ_IMM(CMP, _X)
}
case 0xE8: /* MOV A,#imm */
{
OP_READ_IMM_A(MOV_READ)
}
/* xxx01001 */
case 0x09: /* OR dp(d),dp(s) */
{
OP_RMW_DP_DP(OR)
}
case 0x29: /* AND dp(d),dp(s) */
{
OP_RMW_DP_DP(AND)
}
case 0x49: /* EOR dp(d),dp(s) */
{
OP_RMW_DP_DP(EOR)
}
case 0x69: /* CMP dp(d),dp(s) */
{
/* 6 cycles - opcode, src address, dest address, src read, */
/* dest read + op, dummy cycle */
START_CYCLE(2)
_address2 = _dp + get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address = _dp + get_byte_spc(_PC);
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data2 = get_byte_spc(_address2);
END_CYCLE(4, 1)
START_CYCLE(5)
_data = get_byte_spc(_address);
OP_CMP(_data, _data2)
END_OPCODE(2)
}
case 0x89: /* ADC dp(d),dp(s) */
{
OP_RMW_DP_DP(ADC)
}
case 0xA9: /* SBC dp(d),dp(s) */
{
OP_RMW_DP_DP(SBC)
}
case 0xC9: /* MOV abs,X */
{
OP_RMW_ABS(WRITE_MOV(_X), 0)
}
case 0xE9: /* MOV X,abs */
{
OP_READ_ABS(MOV_READ, _X)
}
/* xxx01010 */
case 0x0A: /* OR1 C,mem.bit */
{
/* 5 cycles - opcode, address low, address high, data read, op */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
END_CYCLE(4, 1)
START_CYCLE(5)
if (_data & offset_to_bit[_offset])
set_flag_spc(SPC_FLAG_C);
END_OPCODE(1)
}
case 0x2A: /* OR1 C,/mem.bit */
{
/* 5 cycles - opcode, address low, address high, data read, op */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
END_CYCLE(4, 1)
START_CYCLE(5)
if (!(_data & offset_to_bit[_offset]))
set_flag_spc(SPC_FLAG_C);
END_OPCODE(1)
}
case 0x4A: /* AND1 C,mem.bit */
{
/* 4 cycles - opcode, address low, address high, data read + op */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
if (!(_data & offset_to_bit[_offset]))
clr_flag_spc(SPC_FLAG_C);
END_OPCODE(1)
}
case 0x6A: /* AND1 C,/mem.bit */
{
/* 4 cycles - opcode, address low, address high, data read + op */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
if (_data & offset_to_bit[_offset])
clr_flag_spc(SPC_FLAG_C);
END_OPCODE(1)
}
case 0x8A: /* EOR1 C,mem.bit */
{
/* 5 cycles - opcode, address low, address high, data read, op */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
END_CYCLE(4, 1)
START_CYCLE(5)
if (_data & offset_to_bit[_offset])
complement_carry_spc();
END_OPCODE(1)
}
case 0xAA: /* MOV1 C,mem.bit */
{
/* 4 cycles - opcode, address low, address high, data read */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
store_flag_c(_data & offset_to_bit[_offset]);
END_OPCODE(1)
}
case 0xCA: /* MOV1 mem.bit,C */
{
/* 6 cycles - opcode, address low, address high, data read, op, */
/* data write */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
END_CYCLE(4, 1)
START_CYCLE(5)
if (flag_state_spc(SPC_FLAG_C))
_data |= offset_to_bit[_offset];
else
_data &= offset_to_not[_offset];
END_CYCLE(5, 1)
START_CYCLE(6)
set_byte_spc(_address, _data);
END_OPCODE(1)
}
case 0xEA: /* NOT1 mem.bit */
{
/* 5 cycles - opcode, address low, address high, data read, */
/* op + data write */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
/* separate bit number, address */
_offset = _address >> 13;
_address &= 0x1FFF;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
END_CYCLE(4, 1)
START_CYCLE(5)
_data ^= offset_to_bit[_offset];
set_byte_spc(_address, _data);
END_OPCODE(1)
}
/* xxx01011 */
case 0x0B: /* ASL dp */
{
OP_RMW_DP(WRITE_OP(ASL), 1)
}
case 0x2B: /* ROL dp */
{
OP_RMW_DP(WRITE_OP(ROL), 1)
}
case 0x4B: /* LSR dp */
{
OP_RMW_DP(WRITE_OP(LSR), 1)
}
case 0x6B: /* ROR dp */
{
OP_RMW_DP(WRITE_OP(ROR), 1)
}
case 0x8B: /* DEC dp */
{
OP_RMW_DP(WRITE_OP(DEC), 1)
}
case 0xAB: /* INC dp */
{
OP_RMW_DP(WRITE_OP(INC), 1)
}
case 0xCB: /* MOV dp,Y */
{
OP_RMW_DP(WRITE_MOV(_Y), 0)
}
case 0xEB: /* MOV Y,dp */
{
OP_READ_DP(MOV_READ, _Y)
}
/* xxx01100 */
case 0x0C: /* ASL abs */
{
OP_RMW_ABS(WRITE_OP(ASL), 1)
}
case 0x2C: /* ROL abs */
{
OP_RMW_ABS(WRITE_OP(ROL), 1)
}
case 0x4C: /* LSR abs */
{
OP_RMW_ABS(WRITE_OP(LSR), 1)
}
case 0x6C: /* ROR abs */
{
OP_RMW_ABS(WRITE_OP(ROR), 1)
}
case 0x8C: /* DEC abs */
{
OP_RMW_ABS(WRITE_OP(DEC), 1)
}
case 0xAC: /* INC abs */
{
OP_RMW_ABS(WRITE_OP(INC), 1)
}
case 0xCC: /* MOV abs,Y */
{
OP_RMW_ABS(WRITE_MOV(_Y), 0)
}
case 0xEC: /* MOV Y,abs */
{
OP_READ_ABS(MOV_READ, _Y)
}
/* xxx01101 */
case 0x0D: /* PUSH PSW */
{
/* 4 cycles - opcode, address load, data write, SP decrement */
START_CYCLE(2)
_address = 0x0100 + _SP;
END_CYCLE(2, 1)
START_CYCLE(3)
spc_setup_flags(_B_flag);
set_byte_spc(_address, _PSW);
END_CYCLE(3, 1)
START_CYCLE(4)
_SP--;
END_OPCODE(1)
}
case 0x2D: /* PUSH A */
{
OP_PUSH(_A)
}
case 0x4D: /* PUSH X */
{
OP_PUSH(_X)
}
case 0x6D: /* PUSH Y */
{
OP_PUSH(_Y)
}
case 0x8D: /* MOV Y,#imm */
{
OP_READ_IMM(MOV_READ, _Y)
}
case 0xAD: /* CMP Y,#imm */
{
OP_READ_IMM(CMP, _Y)
}
case 0xCD: /* MOV X,#imm */
{
OP_READ_IMM(MOV_READ, _X)
}
case 0xED: /* NOTC */
{
/* 2 cycles - opcode, op */
START_CYCLE(2)
complement_carry_spc();
END_OPCODE(1)
}
/* xxx01110 */
case 0x0E: /* TSET1 abs */
{
/* 6 cycles - opcode, address low, address high, data read, */
/* test for flags, op + data write */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
END_CYCLE(4, 1)
START_CYCLE(5)
store_flags_nz(_data & _A);
END_CYCLE(5, 1)
START_CYCLE(6)
_data |= _A;
set_byte_spc(_address, _data);
END_OPCODE(1)
}
case 0x2E: /* CBNE dp,rel */
{
COND_DP_REL(TEST_CBNE)
}
case 0x4E: /* TCLR1 abs */
{
/* 6 cycles - opcode, address low, address high, data read, */
/* test for flags, op + data write */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
END_CYCLE(4, 1)
START_CYCLE(5)
store_flags_nz(_data & _A);
END_CYCLE(5, 1)
START_CYCLE(6)
_data &= ~_A;
set_byte_spc(_address, _data);
END_OPCODE(1)
}
case 0x6E: /* DBNZ dp,rel */
{
COND_DP_REL(DP_REL_TEST_DBNZ)
}
case 0x8E: /* POP PSW */
{
/* 4 cycles - opcode, SP increment, address load, data read */
START_CYCLE(2)
_SP++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address = 0x0100 + _SP;
END_CYCLE(3, 1)
START_CYCLE(4)
_PSW = get_byte_spc(_address);
spc_restore_flags();
END_OPCODE(1)
}
case 0xAE: /* POP A */
{
OP_POP(_A)
}
case 0xCE: /* POP X */
{
OP_POP(_X)
}
case 0xEE: /* POP Y */
{
OP_POP(_Y)
}
/* xxx01111 */
case 0x0F: /* BRK */
{
/* 8 cycles - opcode, new PCL, new PCH, stack address load, */
/* PSW write, PCH write, PCL write, SP decrement */
/* fetch address for PC */
START_CYCLE(2)
/* same vector as TCALL 0 */
_address = 0xFFC0 + ((15 - (0)) * 2);
_address2 = get_byte_spc(_address);
END_CYCLE(2, 1)
START_CYCLE(3)
_address2 += (get_byte_spc(_address + 1) << 8);
END_CYCLE(3, 1)
START_CYCLE(4)
_address = 0x0100 + _SP;
END_CYCLE(4, 1)
START_CYCLE(5)
set_byte_spc(_address, _PC >> 8);
_SP--;
_address = 0x0100 + _SP;
END_CYCLE(5, 1)
START_CYCLE(6)
set_byte_spc(_address, _PC);
_SP--;
_address = 0x0100 + _SP;
END_CYCLE(6, 1)
START_CYCLE(7)
spc_setup_flags(_B_flag);
set_byte_spc(_address, _PSW);
set_flag_spc(SPC_FLAG_B);
clr_flag_spc(SPC_FLAG_I);
END_CYCLE(7, 1)
START_CYCLE(8)
_PC = _address2;
_SP--;
END_OPCODE(1)
}
case 0x2F: /* BRA rel */
{
COND_REL(REL_TEST_BRA)
}
case 0x4F: /* PCALL upage */
{
/* 6 cycles - opcode, new PCL, stack address load, PCH write, */
/* PCL write, SP decrement */
/* fetch address for PC */
START_CYCLE(2)
_address2 = 0xFF00 + get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address = 0x0100 + _SP;
END_CYCLE(3, 1)
START_CYCLE(4)
set_byte_spc(_address, _PC >> 8);
_SP--;
_address = 0x0100 + _SP;
END_CYCLE(4, 1)
START_CYCLE(5)
set_byte_spc(_address, _PC);
END_CYCLE(5, 1)
START_CYCLE(6)
_PC = _address2;
_SP--;
END_OPCODE(1)
}
case 0x6F: /* RET */
{
/* 5 cycles - opcode, SP increment, address load, new PCL, new PCH */
/* pop address to PC */
START_CYCLE(2)
_SP++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address = 0x0100 + _SP;
END_CYCLE(3, 1)
START_CYCLE(4)
_address2 = get_byte_spc(_address);
_SP++;
_address = 0x0100 + _SP;
END_CYCLE(4, 1)
START_CYCLE(5)
_PC = (get_byte_spc(_address) << 8) + _address2;
END_OPCODE(1)
}
case 0x8F: /* MOV dp,#imm */
{
OP_RMW_DP_IMM(MOV_READ_NOFLAGS)
}
case 0xAF: /* MOV (X)+,A */
{
/* 4 cycles - opcode, address load, data write, X increment */
START_CYCLE(2)
_address = _dp + _X;
END_CYCLE(2, 1)
START_CYCLE(3)
set_byte_spc(_address, _A);
END_CYCLE(3, 1)
START_CYCLE(4)
_X++;
END_OPCODE(1)
}
case 0xCF: /* MUL YA */
{
/* 9 cycles - opcode, 8(op) */
START_CYCLE(2)
_YA = (u32)_Y * _A;
store_flags_nz(_Y);
END_OPCODE(8)
}
/* xxx10000 */
case 0x10: /* BPL rel */
{
COND_REL(REL_TEST_BPL)
}
case 0x30: /* BMI rel */
{
COND_REL(REL_TEST_BMI)
}
case 0x50: /* BVC rel */
{
COND_REL(REL_TEST_BVC)
}
case 0x70: /* BVS rel */
{
COND_REL(REL_TEST_BVS)
}
case 0x90: /* BCC rel */
{
COND_REL(REL_TEST_BCC)
}
case 0xB0: /* BCS rel */
{
COND_REL(REL_TEST_BCS)
}
case 0xD0: /* BNE rel */
{
COND_REL(REL_TEST_BNE)
}
case 0xF0: /* BEQ rel */
{
COND_REL(REL_TEST_BEQ)
}
/* xxx10010 */
#define opcode_CLR1(bit) (((bit) << 5) + 0x12)
case opcode_CLR1(0):
case opcode_CLR1(1):
case opcode_CLR1(2):
case opcode_CLR1(3):
case opcode_CLR1(4):
case opcode_CLR1(5):
case opcode_CLR1(6):
case opcode_CLR1(7):
{
OP_RMW_DP(WRITE_OP(CLR1), 1)
}
/* xxx10011 */
#define opcode_BBC(bit) (((bit) << 5) + 0x13)
case opcode_BBC(0):
case opcode_BBC(1):
case opcode_BBC(2):
case opcode_BBC(3):
case opcode_BBC(4):
case opcode_BBC(5):
case opcode_BBC(6):
case opcode_BBC(7):
{
COND_DP_REL(DP_REL_TEST_BBC)
}
/* xxx10100 */
case 0x14: /* OR A,dp+X */
{
OP_READ_DP_X_INDEXED_A(OR)
}
case 0x34: /* AND A,dp+X */
{
OP_READ_DP_X_INDEXED_A(AND)
}
case 0x54: /* EOR A,dp+X */
{
OP_READ_DP_X_INDEXED_A(EOR)
}
case 0x74: /* CMP A,dp+X */
{
OP_READ_DP_X_INDEXED_A(CMP)
}
case 0x94: /* ADC A,dp+X */
{
OP_READ_DP_X_INDEXED_A(ADC)
}
case 0xB4: /* SBC A,dp+X */
{
OP_READ_DP_X_INDEXED_A(SBC)
}
case 0xD4: /* MOV dp+X,A */
{
OP_RMW_DP_X_INDEXED(WRITE_MOV(_A), 0)
}
case 0xF4: /* MOV A,dp+X */
{
OP_READ_DP_X_INDEXED_A(MOV_READ)
}
/* xxx10101 */
case 0x15: /* OR A,abs+X */
{
OP_READ_ABS_X_INDEXED_A(OR)
}
case 0x35: /* AND A,abs+X */
{
OP_READ_ABS_X_INDEXED_A(AND)
}
case 0x55: /* EOR A,abs+X */
{
OP_READ_ABS_X_INDEXED_A(EOR)
}
case 0x75: /* CMP A,abs+X */
{
OP_READ_ABS_X_INDEXED_A(CMP)
}
case 0x95: /* ADC A,abs+X */
{
OP_READ_ABS_X_INDEXED_A(ADC)
}
case 0xB5: /* SBC A,abs+X */
{
OP_READ_ABS_X_INDEXED_A(SBC)
}
case 0xD5: /* MOV abs+X,A */
{
OP_RMW_ABS_X_INDEXED(WRITE_MOV(_A), 0)
}
case 0xF5: /* MOV A,abs+X */
{
OP_READ_ABS_X_INDEXED_A(MOV_READ)
}
/* xxx10110 */
case 0x16: /* OR A,abs+Y */
{
OP_READ_ABS_Y_INDEXED_A(OR)
}
case 0x36: /* AND A,abs+Y */
{
OP_READ_ABS_Y_INDEXED_A(AND)
}
case 0x56: /* EOR A,abs+Y */
{
OP_READ_ABS_Y_INDEXED_A(EOR)
}
case 0x76: /* CMP A,abs+Y */
{
OP_READ_ABS_Y_INDEXED_A(CMP)
}
case 0x96: /* ADC A,abs+Y */
{
OP_READ_ABS_Y_INDEXED_A(ADC)
}
case 0xB6: /* SBC A,abs+Y */
{
OP_READ_ABS_Y_INDEXED_A(SBC)
}
case 0xD6: /* MOV abs+Y,A */
{
OP_RMW_ABS_Y_INDEXED(WRITE_MOV(_A), 0)
}
case 0xF6: /* MOV A,abs+Y */
{
OP_READ_ABS_Y_INDEXED_A(MOV_READ)
}
/* xxx10111 */
case 0x17: /* OR A,(dp)+Y */
{
OP_READ_INDIRECT_INDEXED_A(OR)
}
case 0x37: /* AND A,(dp)+Y */
{
OP_READ_INDIRECT_INDEXED_A(AND)
}
case 0x57: /* EOR A,(dp)+Y */
{
OP_READ_INDIRECT_INDEXED_A(EOR)
}
case 0x77: /* CMP A,(dp)+Y */
{
OP_READ_INDIRECT_INDEXED_A(CMP)
}
case 0x97: /* ADC A,(dp)+Y */
{
OP_READ_INDIRECT_INDEXED_A(ADC)
}
case 0xB7: /* SBC A,(dp)+Y */
{
OP_READ_INDIRECT_INDEXED_A(SBC)
}
case 0xD7: /* MOV (dp)+Y,A */
{
OP_RMW_INDIRECT_INDEXED(WRITE_MOV(_A), 0)
}
case 0xF7: /* MOV A,(dp)+Y */
{
OP_READ_INDIRECT_INDEXED_A(MOV_READ)
}
/* xxx11000 */
case 0x18: /* OR dp,#imm */
{
OP_RMW_DP_IMM(OR)
}
case 0x38: /* AND dp,#imm */
{
OP_RMW_DP_IMM(AND)
}
case 0x58: /* EOR dp,#imm */
{
OP_RMW_DP_IMM(EOR)
}
case 0x78: /* CMP dp,#imm */
{
/* 5 cycles - opcode, src data, dest address, dest read + op, */
/* dummy cycle */
START_CYCLE(2)
_data2 = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address = _dp + get_byte_spc(_PC);
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
OP_CMP(_data, _data2)
END_OPCODE(2)
}
case 0x98: /* ADC dp,#imm */
{
OP_RMW_DP_IMM(ADC)
}
case 0xB8: /* SBC dp,#imm */
{
OP_RMW_DP_IMM(SBC)
}
case 0xD8: /* MOV dp,X */
{
OP_RMW_DP(WRITE_MOV(_X), 0)
}
case 0xF8: /* MOV X,dp */
{
OP_READ_DP(MOV_READ, _X)
}
/* xxx11001 */
case 0x19: /* OR (X),(Y) */
{
OP_RMW_INDIRECT_INDIRECT(OR)
}
case 0x39: /* AND (X),(Y) */
{
OP_RMW_INDIRECT_INDIRECT(AND)
}
case 0x59: /* EOR (X),(Y) */
{
OP_RMW_INDIRECT_INDIRECT(EOR)
}
case 0x79: /* CMP (X),(Y) */
{
/* 5 cycles - opcode, address calc, src read, dest read + op, */
/* dummy cycle */
START_CYCLE(2)
_address = _dp + _Y;
END_CYCLE(2, 1)
START_CYCLE(3)
_data2 = get_byte_spc(_address);
_address = _dp + _X;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address);
OP_CMP(_data, _data2)
END_OPCODE(2)
}
case 0x99: /* ADC (X),(Y) */
{
OP_RMW_INDIRECT_INDIRECT(ADC)
}
case 0xB9: /* SBC (X),(Y) */
{
OP_RMW_INDIRECT_INDIRECT(SBC)
}
case 0xD9: /* MOV dp+Y,X */
{
OP_RMW_DP_Y_INDEXED(WRITE_MOV(_X), 0)
}
case 0xF9: /* MOV X,dp+Y */
{
OP_READ_DP_Y_INDEXED(MOV_READ, _X)
}
/* xxx11010 */
case 0x1A: /* DECW dp */
{
OP_RMW16_DP(DECW)
}
case 0x3A: /* INCW dp */
{
OP_RMW16_DP(INCW)
}
case 0x5A: /* CMPW YA,dp */
{
u32 temp;
/* 4 cycles - opcode, address, data low read, data high read + op */
START_CYCLE(2)
_address = _dp + get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_data16 = get_byte_spc(_address);
END_CYCLE(3, 1)
START_CYCLE(4)
_data16 += get_byte_spc(_address + 1) << 8;
temp = _YA - _data16;
store_flag_c(temp <= 0xFFFF);
store_flag_n(temp >> 8);
store_flag_z(temp != 0);
END_OPCODE(1)
}
case 0x7A: /* ADDW YA,dp */
{
OP_READ16_YA_DP(ADDW)
}
case 0x9A: /* SUBW YA,dp */
{
OP_READ16_YA_DP(SUBW)
}
case 0xBA: /* MOVW YA,dp */
{
OP_READ16_YA_DP(MOVW_READ)
}
case 0xDA: /* MOVW dp,YA */
{
/* 5 cycles - opcode, address, (?), data low write, data high write, */
START_CYCLE(2)
_address = _dp + get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
get_byte_spc(_address);
END_CYCLE(3, 1)
START_CYCLE(4)
set_byte_spc(_address, _A);
END_CYCLE(4, 1)
START_CYCLE(5)
set_byte_spc(_address + 1, _Y);
END_OPCODE(1)
}
case 0xFA: /* MOV dp(d),dp(s) */
{
/* 5 cycles - opcode, src address, dest address, src read, */
/* dest write */
START_CYCLE(2)
_address2 = _dp + get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address = _dp + get_byte_spc(_PC);
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_data = get_byte_spc(_address2);
END_CYCLE(4, 1)
START_CYCLE(5)
set_byte_spc(_address, _data);
END_OPCODE(1)
}
/* xxx11011 */
case 0x1B: /* ASL dp+X */
{
OP_RMW_DP_X_INDEXED(WRITE_OP(ASL), 1)
}
case 0x3B: /* ROL dp+X */
{
OP_RMW_DP_X_INDEXED(WRITE_OP(ROL), 1)
}
case 0x5B: /* LSR dp+X */
{
OP_RMW_DP_X_INDEXED(WRITE_OP(LSR), 1)
}
case 0x7B: /* ROR dp+X */
{
OP_RMW_DP_X_INDEXED(WRITE_OP(ROR), 1)
}
case 0x9B: /* DEC dp+X */
{
OP_RMW_DP_X_INDEXED(WRITE_OP(DEC), 1)
}
case 0xBB: /* INC dp+X */
{
OP_RMW_DP_X_INDEXED(WRITE_OP(INC), 1)
}
case 0xDB: /* MOV dp+X,Y */
{
OP_RMW_DP_X_INDEXED(WRITE_MOV(_Y), 0)
}
case 0xFB: /* MOV Y,dp+X */
{
OP_READ_DP_X_INDEXED(MOV_READ, _Y)
}
/* xxx11100 */
case 0x1C: /* ASL A */
{
OP_RMW_IMPLIED(ASL, _A)
}
case 0x3C: /* ROL A */
{
OP_RMW_IMPLIED(ROL, _A)
}
case 0x5C: /* LSR A */
{
OP_RMW_IMPLIED(LSR, _A)
}
case 0x7C: /* ROR A */
{
OP_RMW_IMPLIED(ROR, _A)
}
case 0x9C: /* DEC A */
{
OP_RMW_IMPLIED(DEC, _A)
}
case 0xBC: /* INC A */
{
OP_RMW_IMPLIED(INC, _A)
}
case 0xDC: /* DEC Y */
{
OP_RMW_IMPLIED(DEC, _Y)
}
case 0xFC: /* INC Y */
{
OP_RMW_IMPLIED(INC, _Y)
}
/* xxx11101 */
case 0x1D: /* DEC X */
{
OP_RMW_IMPLIED(DEC, _X)
}
case 0x3D: /* INC X */
{
OP_RMW_IMPLIED(INC, _X)
}
case 0x5D: /* MOV X,A */
{
OP_MOV_IMPLIED(_X, _A)
}
case 0x7D: /* MOV A,X */
{
OP_MOV_IMPLIED(_A, _X)
}
case 0x9D: /* MOV X,SP */
{
OP_MOV_IMPLIED(_X, _SP)
}
case 0xBD: /* MOV SP,X */
{
OP_MOV_IMPLIED_NO_FLAGS(_SP, _X)
}
case 0xDD: /* MOV A,Y */
{
OP_MOV_IMPLIED(_A, _Y)
}
case 0xFD: /* MOV Y,A */
{
OP_MOV_IMPLIED(_Y, _A)
}
/* xxx11110 */
case 0x1E: /* CMP X,abs */
{
OP_READ_ABS(CMP, _X)
}
case 0x3E: /* CMP X,dp */
{
OP_READ_DP(CMP, _X)
}
case 0x5E: /* CMP Y,abs */
{
OP_READ_ABS(CMP, _Y)
}
case 0x7E: /* CMP Y,dp */
{
OP_READ_DP(CMP, _Y)
}
case 0x9E: /* DIV YA,X */
{
/* 12 cycles - opcode, 11(op) */
/* timing of operations completely wrong here, at least */
START_CYCLE(2)
u32 yva, work_x, i;
yva = _YA;
work_x = (u32)_X << 9;
if ((_X & 0xF) <= (_Y & 0xF))
set_flag_spc(SPC_FLAG_H);
else
clr_flag_spc(SPC_FLAG_H);
for (i = 0; i < 9; i++)
{
yva <<= 1;
if (yva & 0x20000)
yva = (yva & 0x1FFFF) | 1; /* 17-bit ROL */
if (yva >= work_x)
yva ^= 1; /* Why XOR i don't know, but it's what works */
/* and I guess this was easier than a compound if */
if (yva & 1)
yva = (yva - work_x) & 0x1FFFF; /* enforce 17-bit register limit! */
}
if (yva & 0x100)
set_flag_spc(SPC_FLAG_V);
else
clr_flag_spc(SPC_FLAG_V);
_YA = (((yva >> 9) & 0xFF) << 8) + (yva & 0xFF);
store_flags_nz(_YA);
END_OPCODE(11)
}
case 0xBE: /* DAS */
{
/* 3 cycles - opcode, 2(op) */
START_CYCLE(2)
_data = _A;
if ((_data & 0x0F) > 9 || !flag_state_spc(SPC_FLAG_H))
{
_A -= 6;
}
END_CYCLE(2, 1)
START_CYCLE(3)
if (_data > 0x99 || !flag_state_spc(SPC_FLAG_C))
{
_A -= 0x60;
clr_flag_spc(SPC_FLAG_C);
}
store_flags_nz(_A);
END_OPCODE(1)
}
case 0xDE: /* CBNE dp+X,rel */
{
/* 6 cycles - opcode, address, branch offset, address index */
/* (add X), data read, branch logic; */
/* +2 cycles (taken branch) add PC to offset, reload PC */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_offset = get_byte_spc(_PC);
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_address = _dp + ((_address + _X) & 0xFF);
END_CYCLE(4, 1)
START_CYCLE(5)
_data = get_byte_spc(_address);
END_CYCLE(5, 1)
START_CYCLE(6)
END_BRANCH_OPCODE(6, TEST_CBNE)
}
case 0xFE: /* DBNZ Y,rel */
{
/* 4 cycles - opcode, branch offset, decrement Y, branch logic; */
/* +2 cycles (taken branch) add PC to offset, reload PC */
START_CYCLE(2)
_offset = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_Y--;
END_CYCLE(3, 1)
START_CYCLE(4)
END_BRANCH_OPCODE(4, if (!_Y) EXIT_OPCODE(1))
}
/* xxx11111 */
case 0x1F: /* JMP (abs+X) */
{
/* 6 cycles - opcode, address low, address high */
/* address index (add X), new PCL, new PCH */
/* fetch base adderss */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address += get_byte_spc(_PC) << 8;
END_CYCLE(3, 1)
START_CYCLE(4)
_address += _X;
END_CYCLE(4, 1)
START_CYCLE(5)
_offset = get_byte_spc(_address);
END_CYCLE(5, 1)
START_CYCLE(6)
_PC = (get_byte_spc(_address + 1) << 8) + _offset;
END_OPCODE(1)
}
case 0x3F: /* CALL abs */
{
/* 8 cycles - opcode, new PCL, new PCH, stack address load, PCH */
/* write, PCL write, dummy cycle (PSW write in BRK?) */
/* SP decrement */
/* fetch address for PC */
START_CYCLE(2)
_address2 = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address2 += (get_byte_spc(_PC) << 8);
_PC++;
END_CYCLE(3, 1)
START_CYCLE(4)
_address = 0x0100 + _SP;
END_CYCLE(4, 1)
START_CYCLE(5)
set_byte_spc(_address, _PC >> 8);
_SP--;
_address = 0x0100 + _SP;
END_CYCLE(5, 1)
START_CYCLE(6)
set_byte_spc(_address, _PC);
_SP--;
_address = 0x0100 + _SP;
END_CYCLE(6, 1)
START_CYCLE(7)
/* should we write PSW to stack here? */
END_CYCLE(7, 1)
START_CYCLE(8)
_PC = _address2;
END_OPCODE(1)
}
case 0x5F: /* JMP abs */
{
/* 3 cycles - opcode, new PCL, new PCH */
/* fetch address to PC */
START_CYCLE(2)
_address = get_byte_spc(_PC);
_PC++;
END_CYCLE(2, 1)
START_CYCLE(3)
_PC = (get_byte_spc(_PC) << 8) + _address;
END_OPCODE(1)
}
case 0x7F: /* RETI */
{
/* 6 cycles - opcode, SP increment, address load, new PSW, new PCL, */
/* new PCH, pop address to PC */
START_CYCLE(2)
_SP++;
END_CYCLE(2, 1)
START_CYCLE(3)
_address = 0x0100 + _SP;
END_CYCLE(3, 1)
START_CYCLE(4)
_PSW = get_byte_spc(_address);
spc_restore_flags();
_SP++;
_address = 0x0100 + _SP;
END_CYCLE(4, 1)
START_CYCLE(5)
_address2 = get_byte_spc(_address);
_SP++;
_address = 0x0100 + _SP;
END_CYCLE(5, 1)
START_CYCLE(6)
_PC = (get_byte_spc(_address) << 8) + _address2;
END_OPCODE(1)
}
case 0x9F: /* XCN A */
{
/* 5 cycles - opcode, 4(op) */
/* timing of operations may be off here */
START_CYCLE(2)
_data = _A;
END_CYCLE(2, 1)
START_CYCLE(3)
_A <<= 4;
END_CYCLE(3, 1)
START_CYCLE(4)
_data >>= 4;
END_CYCLE(4, 1)
START_CYCLE(5)
OP_OR(_A, _data);
END_OPCODE(1)
}
case 0xBF: /* MOV A,(X)+ */
{
/* 4 cycles - opcode, address load, data read, X increment */
START_CYCLE(2)
_address = _dp + _X;
END_CYCLE(2, 1)
START_CYCLE(3)
_data = get_byte_spc(_address);
OP_MOV_READ(_A, _data)
END_CYCLE(3, 1)
START_CYCLE(4)
_X++;
END_OPCODE(1)
}
case 0xDF: /* DAA */
{
/* 3 cycles - opcode, 2(op) */
START_CYCLE(2)
_data = _A;
if ((_data & 0x0F) > 9 || flag_state_spc(SPC_FLAG_H))
{
_A += 6;
if (_A < 6)
set_flag_spc(SPC_FLAG_C);
}
END_CYCLE(2, 1)
START_CYCLE(3)
if (_data > 0x99 || flag_state_spc(SPC_FLAG_C))
{
_A += 0x60;
set_flag_spc(SPC_FLAG_C);
}
store_flags_nz(_A);
END_OPCODE(1)
}
/* handle unhandled or invalid opcodes */
case 0xEF: /* SLEEP */
case 0xFF: /* STOP */
default:
{
/* set up address (PC) and opcode for display */
Map_Byte = _opcode;
/* Adjust address to correct for increment */
Map_Address = (_PC - 1) & 0xFFFF;
save_cycles_spc(); /* Set cycle counter */
InvalidSPCOpcode(); /* This exits.. aviods conflict with other things! */
load_cycles_spc();
break;
}
}
if (opcode_done)
_cycle = 0;
}
save_cycles_spc(); /* Set cycle counter */
#ifdef INDEPENDENT_SPC
/* update SPC700 timers to prevent overflow */
Update_SPC_Timer(0);
Update_SPC_Timer(1);
Update_SPC_Timer(2);
#endif
In_CPU = was_in_cpu;
}
void SPC_START(u32 cycles)
{
u64 temp = cycles;
temp = (temp * SPC_CPU_cycle_multiplicand) + SPC_CPU_cycles_mul;
/* save remainder */
SPC_CPU_cycles_mul = temp % SPC_CPU_cycle_divisor;
cycles = temp / SPC_CPU_cycle_divisor;
SPC_CPU_cycles = 0;
/* Add new balance of SPC cycles */
_Cycles += cycles;
if (_Cycles <= _TotalCycles)
{
if ((s32)_Cycles < 0)
return;
if ((s32)_TotalCycles >= 0)
return;
Wrap_SPC_Cyclecounter();
}
Execute_SPC();
}