diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 067cfbc..f2f3d0b 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -86,7 +86,6 @@
-
@@ -115,11 +114,11 @@
-
-
+
+
@@ -169,12 +168,10 @@
-
-
-
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index b54a7aa..b324d4c 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -114,31 +114,22 @@
Misc
- SNES\SPC
+ SNES\DSP
- SNES\SPC
-
-
- SNES\SPC
-
-
- SNES\SPC
+ SNES\DSP
- SNES\SPC
+ SNES\DSP
- SNES\SPC
-
-
- SNES\SPC
+ SNES\DSP
- SNES\SPC
+ SNES\DSP
- SNES\SPC
+ SNES\DSP
SNES
@@ -254,6 +245,12 @@
SNES\Input
+
+ SNES
+
+
+ SNES
+
@@ -311,20 +308,11 @@
Misc
-
- SNES\SPC
-
- SNES\SPC
-
-
- SNES\SPC
-
-
- SNES\SPC
+ SNES\DSP
- SNES\SPC
+ SNES\DSP
SNES
@@ -408,6 +396,9 @@
Misc
+
+ SNES
+
@@ -428,14 +419,14 @@
{0d0cf24e-4126-4bfb-b391-27a015e7722a}
-
- {e0e6a67b-620e-4fe4-9846-26a6c80ca6b6}
-
{3a9ea5fe-e818-4e65-bd50-cb9591de3e0d}
{cfc63477-84ed-48e4-81dc-8f960303b40a}
+
+ {e0e6a67b-620e-4fe4-9846-26a6c80ca6b6}
+
\ No newline at end of file
diff --git a/Core/RegisterHandlerB.cpp b/Core/RegisterHandlerB.cpp
index 3e8d7c8..8b88669 100644
--- a/Core/RegisterHandlerB.cpp
+++ b/Core/RegisterHandlerB.cpp
@@ -19,7 +19,7 @@ uint8_t RegisterHandlerB::Read(uint32_t addr)
{
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
- return _spc->Read(addr & 0x03);
+ return _spc->CpuReadRegister(addr & 0x03);
} else if(addr == 0x2180) {
uint8_t value = _workRam[_wramPosition];
_console->ProcessWorkRamRead(_wramPosition, value);
@@ -40,7 +40,7 @@ void RegisterHandlerB::Write(uint32_t addr, uint8_t value)
{
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
- return _spc->Write(addr & 0x03, value);
+ return _spc->CpuWriteRegister(addr & 0x03, value);
} if(addr >= 0x2180 && addr <= 0x2183) {
switch(addr & 0xFFFF) {
case 0x2180:
diff --git a/Core/SNES_SPC.cpp b/Core/SNES_SPC.cpp
deleted file mode 100644
index 2b0641b..0000000
--- a/Core/SNES_SPC.cpp
+++ /dev/null
@@ -1,565 +0,0 @@
-#include "stdafx.h"
-// Core SPC emulation: CPU, timers, SMP registers, memory
-
-// snes_spc 0.9.0. http://www.slack.net/~ant/
-
-#include "SNES_SPC.h"
-
-#include
-
-/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
-can redistribute it and/or modify it under the terms of the GNU Lesser
-General Public License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version. This
-module is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-details. You should have received a copy of the GNU Lesser General Public
-License along with this module; if not, write to the Free Software Foundation,
-Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include "blargg_source.h"
-
-#define RAM (m.ram.ram)
-#define REGS (m.smp_regs [0])
-#define REGS_IN (m.smp_regs [1])
-
-// (n ? n : 256)
-#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
-
-// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
-// do crazy echo buffer accesses.
-#ifndef SPC_MORE_ACCURACY
- #define SPC_MORE_ACCURACY 0
-#endif
-
-#ifdef BLARGG_ENABLE_OPTIMIZER
- #include BLARGG_ENABLE_OPTIMIZER
-#endif
-
-
-//// Timers
-
-#if SPC_DISABLE_TEMPO
- #define TIMER_DIV( t, n ) ((n) >> t->prescaler)
- #define TIMER_MUL( t, n ) ((n) << t->prescaler)
-#else
- #define TIMER_DIV( t, n ) ((n) / t->prescaler)
- #define TIMER_MUL( t, n ) ((n) * t->prescaler)
-#endif
-
-SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time )
-{
- int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
- t->next_time += TIMER_MUL( t, elapsed );
-
- if ( t->enabled )
- {
- int remain = IF_0_THEN_256( t->period - t->divider );
- int divider = t->divider + elapsed;
- int over = elapsed - remain;
- if ( over >= 0 )
- {
- int n = over / t->period;
- t->counter = (t->counter + 1 + n) & 0x0F;
- divider = over - n * t->period;
- }
- t->divider = (uint8_t) divider;
- }
- return t;
-}
-
-inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time )
-{
- if ( time >= t->next_time )
- t = run_timer_( t, time );
- return t;
-}
-
-
-//// ROM
-
-void SNES_SPC::enable_rom( int enable )
-{
- if ( m.rom_enabled != enable )
- {
- m.rom_enabled = enable;
- if ( enable )
- memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
- memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
- // TODO: ROM can still get overwritten when DSP writes to echo buffer
- }
-}
-
-
-//// DSP
-
-#if SPC_LESS_ACCURATE
- int const max_reg_time = 29;
-
- signed char const SNES_SPC::reg_times_ [256] =
- {
- -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22,
- 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23,
- 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23,
- 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24,
- 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
- 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
- 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
- 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
-
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
- };
-
- #define RUN_DSP( time, offset ) \
- int count = (time) - (offset) - m.dsp_time;\
- if ( count >= 0 )\
- {\
- int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
- m.dsp_time += clock_count;\
- dsp.run( clock_count );\
- }
-#else
- #define RUN_DSP( time, offset ) \
- {\
- int count = (time) - m.dsp_time;\
- if ( !SPC_MORE_ACCURACY || count )\
- {\
- assert( count > 0 );\
- m.dsp_time = (time);\
- dsp.run( count );\
- }\
- }
-#endif
-
-int SNES_SPC::dsp_read( rel_time_t time )
-{
- RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
-
- int result = dsp.read( REGS [r_dspaddr] & 0x7F );
-
- #ifdef SPC_DSP_READ_HOOK
- SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
- #endif
-
- return result;
-}
-
-inline void SNES_SPC::dsp_write( int data, rel_time_t time )
-{
- RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
- #if SPC_LESS_ACCURATE
- else if ( m.dsp_time == skipping_time )
- {
- int r = REGS [r_dspaddr];
- if ( r == SPC_DSP::r_kon )
- m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff );
-
- if ( r == SPC_DSP::r_koff )
- {
- m.skipped_koff |= data;
- m.skipped_kon &= ~data;
- }
- }
- #endif
-
- #ifdef SPC_DSP_WRITE_HOOK
- SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
- #endif
-
- if ( REGS [r_dspaddr] <= 0x7F )
- dsp.write( REGS [r_dspaddr], data );
- else if ( !SPC_MORE_ACCURACY )
- dprintf( "SPC wrote to DSP register > $7F\n" );
-}
-
-
-//// Memory access extras
-
-#if SPC_MORE_ACCURACY
- #define MEM_ACCESS( time, addr ) \
- {\
- if ( time >= m.dsp_time )\
- {\
- RUN_DSP( time, max_reg_time );\
- }\
- }
-#elif !defined (NDEBUG)
- // Debug-only check for read/write within echo buffer, since this might result in
- // inaccurate emulation due to the DSP not being caught up to the present.
-
- bool SNES_SPC::check_echo_access( int addr )
- {
- if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
- {
- int start = 0x100 * dsp.read( SPC_DSP::r_esa );
- int size = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
- int end = start + (size ? size : 4);
- if ( start <= addr && addr < end )
- {
- if ( !m.echo_accessed )
- {
- m.echo_accessed = 1;
- return true;
- }
- }
- }
- return false;
- }
-
- #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
-#else
- #define MEM_ACCESS( time, addr )
-#endif
-
-
-//// CPU write
-
-#if SPC_MORE_ACCURACY
-static unsigned char const glitch_probs [3] [256] =
-{
- 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
- 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
- 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
- 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
- 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
- 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
- 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
- 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
- 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
- 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
- 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
- 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
- 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
- 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
- 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
- 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
-
- 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
- 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
- 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
- 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
- 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
- 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
- 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
- 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
- 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
- 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
- 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
- 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
- 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
- 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
- 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
- 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
-
- 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
- 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
- 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
- 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
- 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
- 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
- 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
- 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
- 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
- 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
- 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
- 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
- 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
- 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
- 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
- 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
-};
-#endif
-
-// divided into multiple functions to keep rarely-used functionality separate
-// so often-used functionality can be optimized better by compiler
-
-// If write isn't preceded by read, data has this added to it
-int const no_read_before_write = 0x2000;
-
-void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
-{
- switch ( addr )
- {
- case r_t0target:
- case r_t1target:
- case r_t2target: {
- Timer* t = &m.timers [addr - r_t0target];
- int period = IF_0_THEN_256( data );
- if ( t->period != period )
- {
- t = run_timer( t, time );
- #if SPC_MORE_ACCURACY
- // Insane behavior when target is written just after counter is
- // clocked and counter matches new period and new period isn't 1, 2, 4, or 8
- if ( t->divider == (period & 0xFF) &&
- t->next_time == time + TIMER_MUL( t, 1 ) &&
- ((period - 1) | ~0x0F) & period )
- {
- //dprintf( "SPC pathological timer target write\n" );
-
- // If the period is 3, 5, or 9, there's a probability this behavior won't occur,
- // based on the previous period
- int prob = 0xFF;
- int old_period = t->period & 0xFF;
- if ( period == 3 ) prob = glitch_probs [0] [old_period];
- if ( period == 5 ) prob = glitch_probs [1] [old_period];
- if ( period == 9 ) prob = glitch_probs [2] [old_period];
-
- // The glitch suppresses incrementing of one of the counter bits, based on
- // the lowest set bit in the new period
- int b = 1;
- while ( !(period & b) )
- b <<= 1;
-
- if ( (rand() >> 4 & 0xFF) <= prob )
- t->divider = (t->divider - b) & 0xFF;
- }
- #endif
- t->period = period;
- }
- break;
- }
-
- case r_t0out:
- case r_t1out:
- case r_t2out:
- if ( !SPC_MORE_ACCURACY )
- dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
-
- if ( data < no_read_before_write / 2 )
- run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
- break;
-
- // Registers that act like RAM
- case 0x8:
- case 0x9:
- REGS_IN [addr] = (uint8_t) data;
- break;
-
- case r_test:
- if ( (uint8_t) data != 0x0A )
- dprintf( "SPC wrote to test register\n" );
- break;
-
- case r_control:
- // port clears
- if ( data & 0x10 )
- {
- REGS_IN [r_cpuio0] = 0;
- REGS_IN [r_cpuio1] = 0;
- }
- if ( data & 0x20 )
- {
- REGS_IN [r_cpuio2] = 0;
- REGS_IN [r_cpuio3] = 0;
- }
-
- // timers
- {
- for ( int i = 0; i < timer_count; i++ )
- {
- Timer* t = &m.timers [i];
- int enabled = data >> i & 1;
- if ( t->enabled != enabled )
- {
- t = run_timer( t, time );
- t->enabled = enabled;
- if ( enabled )
- {
- t->divider = 0;
- t->counter = 0;
- }
- }
- }
- }
- enable_rom( data & 0x80 );
- break;
- }
-}
-
-void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr )
-{
- if ( addr == r_dspdata ) // 99%
- dsp_write( data, time );
- else
- cpu_write_smp_reg_( data, time, addr );
-}
-
-void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time )
-{
- if ( i < rom_size )
- {
- m.hi_ram [i] = (uint8_t) data;
- if ( m.rom_enabled )
- RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
- }
- else
- {
- assert( RAM [i + rom_addr] == (uint8_t) data );
- RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding
- cpu_write( data, i + rom_addr - 0x10000, time );
- }
-}
-
-int const bits_in_int = CHAR_BIT * sizeof (int);
-
-void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
-{
- MEM_ACCESS( time, addr )
-
- // RAM
- RAM [addr] = (uint8_t) data;
- int reg = addr - 0xF0;
- if ( reg >= 0 ) // 64%
- {
- // $F0-$FF
- if ( reg < reg_count ) // 87%
- {
- REGS [reg] = (uint8_t) data;
-
- // Ports
- #ifdef SPC_PORT_WRITE_HOOK
- if ( (unsigned) (reg - r_cpuio0) < port_count )
- SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
- (uint8_t) data, ®S [r_cpuio0] );
- #endif
-
- // Registers other than $F2 and $F4-$F7
- //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
- // TODO: this is a bit on the fragile side
- if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
- cpu_write_smp_reg( data, time, reg );
- }
- // High mem/address wrap-around
- else
- {
- reg -= rom_addr - 0xF0;
- if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
- cpu_write_high( data, reg, time );
- }
- }
-}
-
-
-//// CPU read
-
-inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
-{
- int result = REGS_IN [reg];
- reg -= r_dspaddr;
- // DSP addr and data
- if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
- {
- result = REGS [r_dspaddr];
- if ( (unsigned) reg == 1 )
- result = dsp_read( time ); // 0xF3
- }
- return result;
-}
-
-int SNES_SPC::cpu_read( int addr, rel_time_t time )
-{
- MEM_ACCESS( time, addr )
-
- // RAM
- int result = RAM [addr];
- int reg = addr - 0xF0;
- if ( reg >= 0 ) // 40%
- {
- reg -= 0x10;
- if ( (unsigned) reg >= 0xFF00 ) // 21%
- {
- reg += 0x10 - r_t0out;
-
- // Timers
- if ( (unsigned) reg < timer_count ) // 90%
- {
- Timer* t = &m.timers [reg];
- if ( time >= t->next_time )
- t = run_timer_( t, time );
- result = t->counter;
- t->counter = 0;
- }
- // Other registers
- else if ( reg < 0 ) // 10%
- {
- result = cpu_read_smp_reg( reg + r_t0out, time );
- }
- else // 1%
- {
- assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
- result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
- }
- }
- }
-
- return result;
-}
-
-
-//// Run
-
-// Prefix and suffix for CPU emulator function
-#define SPC_CPU_RUN_FUNC \
-BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\
-{\
- rel_time_t rel_time = m.spc_time - end_time;\
- assert( rel_time <= 0 );\
- m.spc_time = end_time;\
- m.dsp_time += rel_time;\
- m.timers [0].next_time += rel_time;\
- m.timers [1].next_time += rel_time;\
- m.timers [2].next_time += rel_time;
-
-#define SPC_CPU_RUN_FUNC_END \
- m.spc_time += rel_time;\
- m.dsp_time -= rel_time;\
- m.timers [0].next_time -= rel_time;\
- m.timers [1].next_time -= rel_time;\
- m.timers [2].next_time -= rel_time;\
- assert( m.spc_time <= end_time );\
- return ®S [r_cpuio0];\
-}
-
-int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
-
-void SNES_SPC::end_frame( time_t end_time )
-{
- // Catch CPU up to as close to end as possible. If final instruction
- // would exceed end, does NOT execute it and leaves m.spc_time < end.
- if ( end_time > m.spc_time )
- run_until_( end_time );
-
- m.spc_time -= end_time;
- m.extra_clocks += end_time;
-
- // Greatest number of clocks early that emulation can stop early due to
- // not being able to execute current instruction without going over
- // allowed time.
- assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 );
-
- // Catch timers up to CPU
- for ( int i = 0; i < timer_count; i++ )
- run_timer( &m.timers [i], 0 );
-
- // Catch DSP up to CPU
- if ( m.dsp_time < 0 )
- {
- RUN_DSP( 0, max_reg_time );
- }
-
- // Save any extra samples beyond what should be generated
- if ( m.buf_begin )
- save_extra();
-}
-
-// Inclusion here allows static memory access functions and better optimization
-#include "SPC_CPU.h"
diff --git a/Core/SNES_SPC.h b/Core/SNES_SPC.h
deleted file mode 100644
index 8ea0392..0000000
--- a/Core/SNES_SPC.h
+++ /dev/null
@@ -1,279 +0,0 @@
-// SNES SPC-700 APU emulator
-
-// snes_spc 0.9.0
-#ifndef SNES_SPC_H
-#define SNES_SPC_H
-
-#include "SPC_DSP.h"
-#include "blargg_endian.h"
-
-struct SNES_SPC {
-public:
- typedef BOOST::uint8_t uint8_t;
-
- // Must be called once before using
- blargg_err_t init();
-
- // Sample pairs generated per second
- enum { sample_rate = 32000 };
-
-// Emulator use
-
- // Sets IPL ROM data. Library does not include ROM data. Most SPC music files
- // don't need ROM, but a full emulator must provide this.
- enum { rom_size = 0x40 };
- void init_rom( uint8_t const rom [rom_size] );
-
- // Sets destination for output samples
- typedef short sample_t;
- void set_output( sample_t* out, int out_size );
-
- // Number of samples written to output since last set
- int sample_count() const;
-
- // Resets SPC to power-on state. This resets your output buffer, so you must
- // call set_output() after this.
- void reset();
-
- // Emulates pressing reset switch on SNES. This resets your output buffer, so
- // you must call set_output() after this.
- void soft_reset();
-
- // 1024000 SPC clocks per second, sample pair every 32 clocks
- typedef int time_t;
- enum { clock_rate = 1024000 };
- enum { clocks_per_sample = 32 };
-
- // Emulated port read/write at specified time
- enum { port_count = 4 };
- int read_port ( time_t, int port );
- void write_port( time_t, int port, int data );
-
- // Runs SPC to end_time and starts a new time frame at 0
- void end_frame( time_t end_time );
-
-// Sound control
-
- // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
- // Reduces emulation accuracy.
- enum { voice_count = 8 };
- void mute_voices( int mask );
-
- // If true, prevents channels and global volumes from being phase-negated.
- // Only supported by fast DSP.
- void disable_surround( bool disable = true );
-
- // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.
- enum { tempo_unit = 0x100 };
- void set_tempo( int );
-
-// SPC music files
-
- // Loads SPC data into emulator
- enum { spc_min_file_size = 0x10180 };
- enum { spc_file_size = 0x10200 };
- blargg_err_t load_spc( void const* in, long size );
-
- // Clears echo region. Useful after loading an SPC as many have garbage in echo.
- void clear_echo();
-
- // Plays for count samples and write samples to out. Discards samples if out
- // is NULL. Count must be a multiple of 2 since output is stereo.
- blargg_err_t play( int count, sample_t* out );
-
- // Skips count samples. Several times faster than play() when using fast DSP.
- blargg_err_t skip( int count );
-
-// State save/load (only available with accurate DSP)
-
-#if !SPC_NO_COPY_STATE_FUNCS
- // Saves/loads state
- enum { state_size = 67 * 1024L }; // maximum space needed when saving
- typedef SPC_DSP::copy_func_t copy_func_t;
- void copy_state( unsigned char** io, copy_func_t );
-
- // Writes minimal header to spc_out
- static void init_header( void* spc_out );
-
- // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.
- // Does not set up SPC header; use init_header() for that.
- void save_spc( void* spc_out );
-
- // Returns true if new key-on events occurred since last check. Useful for
- // trimming silence while saving an SPC.
- bool check_kon();
-#endif
-
-public:
- BLARGG_DISABLE_NOTHROW
-
- typedef BOOST::uint16_t uint16_t;
-
- // Time relative to m_spc_time. Speeds up code a bit by eliminating need to
- // constantly add m_spc_time to time from CPU. CPU uses time that ends at
- // 0 to eliminate reloading end time every instruction. It pays off.
- typedef int rel_time_t;
-
- struct Timer
- {
- rel_time_t next_time; // time of next event
- int prescaler;
- int period;
- int divider;
- int enabled;
- int counter;
- };
- enum { reg_count = 0x10 };
- enum { timer_count = 3 };
- enum { extra_size = SPC_DSP::extra_size };
-
- enum { signature_size = 35 };
-
-private:
- SPC_DSP dsp;
-
- #if SPC_LESS_ACCURATE
- static signed char const reg_times_ [256];
- signed char reg_times [256];
- #endif
-
- struct state_t
- {
- Timer timers [timer_count];
-
- uint8_t smp_regs [2] [reg_count];
-
- struct
- {
- int pc;
- int a;
- int x;
- int y;
- int psw;
- int sp;
- } cpu_regs;
-
- rel_time_t dsp_time;
- time_t spc_time;
- bool echo_accessed;
-
- int tempo;
- int skipped_kon;
- int skipped_koff;
- const char* cpu_error;
-
- int extra_clocks;
- sample_t* buf_begin;
- sample_t const* buf_end;
- sample_t* extra_pos;
- sample_t extra_buf [extra_size];
-
- int rom_enabled;
- uint8_t rom [rom_size];
- uint8_t hi_ram [rom_size];
-
- unsigned char cycle_table [256];
-
- struct
- {
- // padding to neutralize address overflow
- union {
- uint8_t padding1 [0x100];
- uint16_t align; // makes compiler align data for 16-bit access
- } padding1 [1];
- uint8_t ram [0x10000];
- uint8_t padding2 [0x100];
- } ram;
- };
- state_t m;
-
- enum { rom_addr = 0xFFC0 };
-
- enum { skipping_time = 127 };
-
- // Value that padding should be filled with
- enum { cpu_pad_fill = 0xFF };
-
- enum {
- r_test = 0x0, r_control = 0x1,
- r_dspaddr = 0x2, r_dspdata = 0x3,
- r_cpuio0 = 0x4, r_cpuio1 = 0x5,
- r_cpuio2 = 0x6, r_cpuio3 = 0x7,
- r_f8 = 0x8, r_f9 = 0x9,
- r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,
- r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF
- };
-
- void timers_loaded();
- void enable_rom( int enable );
- void reset_buf();
- void save_extra();
- void load_regs( uint8_t const in [reg_count] );
- void ram_loaded();
- void regs_loaded();
- void reset_time_regs();
- void reset_common( int timer_counter_init );
-
- Timer* run_timer_ ( Timer* t, rel_time_t );
- Timer* run_timer ( Timer* t, rel_time_t );
- int dsp_read ( rel_time_t );
- void dsp_write ( int data, rel_time_t );
- void cpu_write_smp_reg_( int data, rel_time_t, int addr );
- void cpu_write_smp_reg ( int data, rel_time_t, int addr );
- void cpu_write_high ( int data, int i, rel_time_t );
- void cpu_write ( int data, int addr, rel_time_t );
- int cpu_read_smp_reg ( int i, rel_time_t );
- int cpu_read ( int addr, rel_time_t );
- unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t );
-
- bool check_echo_access ( int addr );
- uint8_t* run_until_( time_t end_time );
-
- struct spc_file_t
- {
- char signature [signature_size];
- uint8_t has_id666;
- uint8_t version;
- uint8_t pcl, pch;
- uint8_t a;
- uint8_t x;
- uint8_t y;
- uint8_t psw;
- uint8_t sp;
- char text [212];
- uint8_t ram [0x10000];
- uint8_t dsp [128];
- uint8_t unused [0x40];
- uint8_t ipl_rom [0x40];
- };
-
- static char const signature [signature_size + 1];
-
- void save_regs( uint8_t out [reg_count] );
-};
-
-#include
-
-inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }
-
-inline int SNES_SPC::read_port( time_t t, int port )
-{
- assert( (unsigned) port < port_count );
- return run_until_( t ) [port];
-}
-
-inline void SNES_SPC::write_port( time_t t, int port, int data )
-{
- assert( (unsigned) port < port_count );
- run_until_( t ) [0x10 + port] = data;
-}
-
-inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); }
-
-inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
-
-#if !SPC_NO_COPY_STATE_FUNCS
-inline bool SNES_SPC::check_kon() { return dsp.check_kon(); }
-#endif
-
-#endif
diff --git a/Core/SNES_SPC_misc.cpp b/Core/SNES_SPC_misc.cpp
deleted file mode 100644
index b5ddfa7..0000000
--- a/Core/SNES_SPC_misc.cpp
+++ /dev/null
@@ -1,381 +0,0 @@
-#include "stdafx.h"
-// SPC emulation support: init, sample buffering, reset, SPC loading
-
-// snes_spc 0.9.0. http://www.slack.net/~ant/
-
-#include "SNES_SPC.h"
-
-#include
-
-/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
-can redistribute it and/or modify it under the terms of the GNU Lesser
-General Public License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version. This
-module is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-details. You should have received a copy of the GNU Lesser General Public
-License along with this module; if not, write to the Free Software Foundation,
-Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include "blargg_source.h"
-
-#define RAM (m.ram.ram)
-#define REGS (m.smp_regs [0])
-#define REGS_IN (m.smp_regs [1])
-
-// (n ? n : 256)
-#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
-
-
-//// Init
-
-blargg_err_t SNES_SPC::init()
-{
- memset( &m, 0, sizeof m );
- dsp.init( RAM );
-
- m.tempo = tempo_unit;
-
- // Most SPC music doesn't need ROM, and almost all the rest only rely
- // on these two bytes
- m.rom [0x3E] = 0xFF;
- m.rom [0x3F] = 0xC0;
-
- static unsigned char const cycle_table [128] =
- {// 01 23 45 67 89 AB CD EF
- 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0
- 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1
- 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2
- 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3
- 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4
- 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5
- 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6
- 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7
- 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8
- 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9
- 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A
- 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B
- 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C
- 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D
- 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E
- 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F
- };
-
- // unpack cycle table
- for ( int i = 0; i < 128; i++ )
- {
- int n = cycle_table [i];
- m.cycle_table [i * 2 + 0] = n >> 4;
- m.cycle_table [i * 2 + 1] = n & 0x0F;
- }
-
- #if SPC_LESS_ACCURATE
- memcpy( reg_times, reg_times_, sizeof reg_times );
- #endif
-
- reset();
- return 0;
-}
-
-void SNES_SPC::init_rom( uint8_t const in [rom_size] )
-{
- memcpy( m.rom, in, sizeof m.rom );
-}
-
-void SNES_SPC::set_tempo( int t )
-{
- m.tempo = t;
- int const timer2_shift = 4; // 64 kHz
- int const other_shift = 3; // 8 kHz
-
- #if SPC_DISABLE_TEMPO
- m.timers [2].prescaler = timer2_shift;
- m.timers [1].prescaler = timer2_shift + other_shift;
- m.timers [0].prescaler = timer2_shift + other_shift;
- #else
- if ( !t )
- t = 1;
- int const timer2_rate = 1 << timer2_shift;
- int rate = (timer2_rate * tempo_unit + (t >> 1)) / t;
- if ( rate < timer2_rate / 4 )
- rate = timer2_rate / 4; // max 4x tempo
- m.timers [2].prescaler = rate;
- m.timers [1].prescaler = rate << other_shift;
- m.timers [0].prescaler = rate << other_shift;
- #endif
-}
-
-// Timer registers have been loaded. Applies these to the timers. Does not
-// reset timer prescalers or dividers.
-void SNES_SPC::timers_loaded()
-{
- int i;
- for ( i = 0; i < timer_count; i++ )
- {
- Timer* t = &m.timers [i];
- t->period = IF_0_THEN_256( REGS [r_t0target + i] );
- t->enabled = REGS [r_control] >> i & 1;
- t->counter = REGS_IN [r_t0out + i] & 0x0F;
- }
-
- set_tempo( m.tempo );
-}
-
-// Loads registers from unified 16-byte format
-void SNES_SPC::load_regs( uint8_t const in [reg_count] )
-{
- memcpy( REGS, in, reg_count );
- memcpy( REGS_IN, REGS, reg_count );
-
- // These always read back as 0
- REGS_IN [r_test ] = 0;
- REGS_IN [r_control ] = 0;
- REGS_IN [r_t0target] = 0;
- REGS_IN [r_t1target] = 0;
- REGS_IN [r_t2target] = 0;
-}
-
-// RAM was just loaded from SPC, with $F0-$FF containing SMP registers
-// and timer counts. Copies these to proper registers.
-void SNES_SPC::ram_loaded()
-{
- m.rom_enabled = 0;
- load_regs( &RAM [0xF0] );
-
- // Put STOP instruction around memory to catch PC underflow/overflow
- memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 );
- memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 );
-}
-
-// Registers were just loaded. Applies these new values.
-void SNES_SPC::regs_loaded()
-{
- enable_rom( REGS [r_control] & 0x80 );
- timers_loaded();
-}
-
-void SNES_SPC::reset_time_regs()
-{
- m.cpu_error = 0;
- m.echo_accessed = 0;
- m.spc_time = 0;
- m.dsp_time = 0;
- #if SPC_LESS_ACCURATE
- m.dsp_time = clocks_per_sample + 1;
- #endif
-
- for ( int i = 0; i < timer_count; i++ )
- {
- Timer* t = &m.timers [i];
- t->next_time = 1;
- t->divider = 0;
- }
-
- regs_loaded();
-
- m.extra_clocks = 0;
- reset_buf();
-}
-
-void SNES_SPC::reset_common( int timer_counter_init )
-{
- int i;
- for ( i = 0; i < timer_count; i++ )
- REGS_IN [r_t0out + i] = timer_counter_init;
-
- // Run IPL ROM
- memset( &m.cpu_regs, 0, sizeof m.cpu_regs );
- m.cpu_regs.pc = rom_addr;
-
- REGS [r_test ] = 0x0A;
- REGS [r_control] = 0xB0; // ROM enabled, clear ports
- for ( i = 0; i < port_count; i++ )
- REGS_IN [r_cpuio0 + i] = 0;
-
- reset_time_regs();
-}
-
-void SNES_SPC::soft_reset()
-{
- reset_common( 0 );
- dsp.soft_reset();
-}
-
-void SNES_SPC::reset()
-{
- memset( RAM, 0xFF, 0x10000 );
- ram_loaded();
- reset_common( 0x0F );
- dsp.reset();
-}
-
-char const SNES_SPC::signature [signature_size + 1] =
- "SNES-SPC700 Sound File Data v0.30\x1A\x1A";
-
-blargg_err_t SNES_SPC::load_spc( void const* data, long size )
-{
- spc_file_t const* const spc = (spc_file_t const*) data;
-
- // be sure compiler didn't insert any padding into fle_t
- assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 );
-
- // Check signature and file size
- if ( size < signature_size || memcmp( spc, signature, 27 ) )
- return "Not an SPC file";
-
- if ( size < spc_min_file_size )
- return "Corrupt SPC file";
-
- // CPU registers
- m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl;
- m.cpu_regs.a = spc->a;
- m.cpu_regs.x = spc->x;
- m.cpu_regs.y = spc->y;
- m.cpu_regs.psw = spc->psw;
- m.cpu_regs.sp = spc->sp;
-
- // RAM and registers
- memcpy( RAM, spc->ram, 0x10000 );
- ram_loaded();
-
- // DSP registers
- dsp.load( spc->dsp );
-
- reset_time_regs();
-
- return 0;
-}
-
-void SNES_SPC::clear_echo()
-{
- if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
- {
- int addr = 0x100 * dsp.read( SPC_DSP::r_esa );
- int end = addr + 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
- if ( end > 0x10000 )
- end = 0x10000;
- memset( &RAM [addr], 0xFF, end - addr );
- }
-}
-
-
-//// Sample output
-
-void SNES_SPC::reset_buf()
-{
- // Start with half extra buffer of silence
- sample_t* out = m.extra_buf;
- while ( out < &m.extra_buf [extra_size / 2] )
- *out++ = 0;
-
- m.extra_pos = out;
- m.buf_begin = 0;
-
- dsp.set_output( 0, 0 );
-}
-
-void SNES_SPC::set_output( sample_t* out, int size )
-{
- require( (size & 1) == 0 ); // size must be even
-
- m.extra_clocks &= clocks_per_sample - 1;
- if ( out )
- {
- sample_t const* out_end = out + size;
- m.buf_begin = out;
- m.buf_end = out_end;
-
- // Copy extra to output
- sample_t const* in = m.extra_buf;
- while ( in < m.extra_pos && out < out_end )
- *out++ = *in++;
-
- // Handle output being full already
- if ( out >= out_end )
- {
- // Have DSP write to remaining extra space
- out = dsp.extra();
- out_end = &dsp.extra() [extra_size];
-
- // Copy any remaining extra samples as if DSP wrote them
- while ( in < m.extra_pos )
- *out++ = *in++;
- assert( out <= out_end );
- }
-
- dsp.set_output( out, (int)(out_end - out) );
- }
- else
- {
- reset_buf();
- }
-}
-
-void SNES_SPC::save_extra()
-{
- // Get end pointers
- sample_t const* main_end = m.buf_end; // end of data written to buf
- sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra()
- if ( m.buf_begin <= dsp_end && dsp_end <= main_end )
- {
- main_end = dsp_end;
- dsp_end = dsp.extra(); // nothing in DSP's extra
- }
-
- // Copy any extra samples at these ends into extra_buf
- sample_t* out = m.extra_buf;
- sample_t const* in;
- for ( in = m.buf_begin + sample_count(); in < main_end; in++ )
- *out++ = *in;
- for ( in = dsp.extra(); in < dsp_end ; in++ )
- *out++ = *in;
-
- m.extra_pos = out;
- assert( out <= &m.extra_buf [extra_size] );
-}
-
-blargg_err_t SNES_SPC::play( int count, sample_t* out )
-{
- require( (count & 1) == 0 ); // must be even
- if ( count )
- {
- set_output( out, count );
- end_frame( count * (clocks_per_sample / 2) );
- }
-
- const char* err = m.cpu_error;
- m.cpu_error = 0;
- return err;
-}
-
-blargg_err_t SNES_SPC::skip( int count )
-{
- #if SPC_LESS_ACCURATE
- if ( count > 2 * sample_rate * 2 )
- {
- set_output( 0, 0 );
-
- // Skip a multiple of 4 samples
- time_t end = count;
- count = (count & 3) + 1 * sample_rate * 2;
- end = (end - count) * (clocks_per_sample / 2);
-
- m.skipped_kon = 0;
- m.skipped_koff = 0;
-
- // Preserve DSP and timer synchronization
- // TODO: verify that this really preserves it
- int old_dsp_time = m.dsp_time + m.spc_time;
- m.dsp_time = end - m.spc_time + skipping_time;
- end_frame( end );
- m.dsp_time = m.dsp_time - skipping_time + old_dsp_time;
-
- dsp.write( SPC_DSP::r_koff, m.skipped_koff & ~m.skipped_kon );
- dsp.write( SPC_DSP::r_kon , m.skipped_kon );
- clear_echo();
- }
- #endif
-
- return play( count, 0 );
-}
diff --git a/Core/SNES_SPC_state.cpp b/Core/SNES_SPC_state.cpp
deleted file mode 100644
index 9a6296a..0000000
--- a/Core/SNES_SPC_state.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-#include "stdafx.h"
-// SPC emulation state save/load: copy_state(), save_spc()
-// Separate file to avoid linking in unless needed
-
-// snes_spc 0.9.0. http://www.slack.net/~ant/
-
-#include "SNES_SPC.h"
-
-#if !SPC_NO_COPY_STATE_FUNCS
-
-#include
-
-/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
-can redistribute it and/or modify it under the terms of the GNU Lesser
-General Public License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version. This
-module is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-details. You should have received a copy of the GNU Lesser General Public
-License along with this module; if not, write to the Free Software Foundation,
-Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#include "blargg_source.h"
-
-#define RAM (m.ram.ram)
-#define REGS (m.smp_regs [0])
-#define REGS_IN (m.smp_regs [1])
-
-void SNES_SPC::save_regs( uint8_t out [reg_count*2] )
-{
- // Use current timer counter values
- for ( int i = 0; i < timer_count; i++ )
- out [r_t0out + i] = m.timers [i].counter;
-
- // Last written values
- memcpy( out, REGS, r_t0out );
- memcpy( out + reg_count, REGS_IN, reg_count);
-}
-
-void SNES_SPC::init_header( void* spc_out )
-{
- spc_file_t* const spc = (spc_file_t*) spc_out;
-
- spc->has_id666 = 26; // has none
- spc->version = 30;
- memcpy( spc, signature, sizeof spc->signature );
- memset( spc->text, 0, sizeof spc->text );
-}
-
-void SNES_SPC::save_spc( void* spc_out )
-{
- spc_file_t* const spc = (spc_file_t*) spc_out;
-
- // CPU
- spc->pcl = (uint8_t) (m.cpu_regs.pc >> 0);
- spc->pch = (uint8_t) (m.cpu_regs.pc >> 8);
- spc->a = m.cpu_regs.a;
- spc->x = m.cpu_regs.x;
- spc->y = m.cpu_regs.y;
- spc->psw = m.cpu_regs.psw;
- spc->sp = m.cpu_regs.sp;
-
- // RAM, ROM
- memcpy( spc->ram, RAM, sizeof spc->ram );
- if ( m.rom_enabled )
- memcpy( spc->ram + rom_addr, m.hi_ram, sizeof m.hi_ram );
- memset( spc->unused, 0, sizeof spc->unused );
- memcpy( spc->ipl_rom, m.rom, sizeof spc->ipl_rom );
-
- // SMP registers
- save_regs( &spc->ram [0xF0] );
- int i;
- for ( i = 0; i < port_count; i++ )
- spc->ram [0xF0 + r_cpuio0 + i] = REGS_IN [r_cpuio0 + i];
-
- // DSP registers
- for ( i = 0; i < SPC_DSP::register_count; i++ )
- spc->dsp [i] = dsp.read( i );
-}
-
-void SNES_SPC::copy_state( unsigned char** io, copy_func_t copy )
-{
- SPC_State_Copier copier( io, copy );
-
- // Make state data more readable by putting 64K RAM, 16 SMP registers,
- // then DSP (with its 128 registers) first
-
- // RAM
- enable_rom( 0 ); // will get re-enabled if necessary in regs_loaded() below
- copier.copy( RAM, 0x10000 );
-
- {
- // SMP registers
- uint8_t out_ports [port_count];
- uint8_t regs [reg_count*2];
- memcpy( out_ports, ®S [r_cpuio0], sizeof out_ports );
- save_regs( regs );
- copier.copy( regs, sizeof regs );
- copier.copy( out_ports, sizeof out_ports );
-
- memcpy(REGS, regs, reg_count);
- memcpy(REGS_IN, regs + reg_count, reg_count);
-
- regs_loaded();
- memcpy( ®S [r_cpuio0], out_ports, sizeof out_ports );
- }
-
- // CPU registers
- SPC_COPY( uint16_t, m.cpu_regs.pc );
- SPC_COPY( uint8_t, m.cpu_regs.a );
- SPC_COPY( uint8_t, m.cpu_regs.x );
- SPC_COPY( uint8_t, m.cpu_regs.y );
- SPC_COPY( uint8_t, m.cpu_regs.psw );
- SPC_COPY( uint8_t, m.cpu_regs.sp );
- copier.extra();
-
- SPC_COPY( int16_t, m.spc_time );
- SPC_COPY( int16_t, m.dsp_time );
-
- // DSP
- dsp.copy_state( io, copy );
-
- // Timers
- for ( int i = 0; i < timer_count; i++ )
- {
- Timer* t = &m.timers [i];
- SPC_COPY( int16_t, t->next_time );
- SPC_COPY( uint8_t, t->divider );
- copier.extra();
- }
- copier.extra();
-}
-#endif
diff --git a/Core/SPC_CPU.h b/Core/SPC_CPU.h
deleted file mode 100644
index dcfbc7b..0000000
--- a/Core/SPC_CPU.h
+++ /dev/null
@@ -1,1220 +0,0 @@
-// snes_spc 0.9.0. http://www.slack.net/~ant/
-
-/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
-can redistribute it and/or modify it under the terms of the GNU Lesser
-General Public License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version. This
-module is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-details. You should have received a copy of the GNU Lesser General Public
-License along with this module; if not, write to the Free Software Foundation,
-Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-
-//// Memory access
-
-#if SPC_MORE_ACCURACY
- #define SUSPICIOUS_OPCODE( name ) ((void) 0)
-#else
- #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" )
-#endif
-
-#define CPU_READ( time, offset, addr )\
- cpu_read( addr, time + offset )
-
-#define CPU_WRITE( time, offset, addr, data )\
- cpu_write( data, addr, time + offset )
-
-#if SPC_MORE_ACCURACY
- #define CPU_READ_TIMER( time, offset, addr, out )\
- { out = CPU_READ( time, offset, addr ); }
-
-#else
- // timers are by far the most common thing read from dp
- #define CPU_READ_TIMER( time, offset, addr_, out )\
- {\
- rel_time_t adj_time = time + offset;\
- int dp_addr = addr_;\
- int ti = dp_addr - (r_t0out + 0xF0);\
- if ( (unsigned) ti < timer_count )\
- {\
- Timer* t = &m.timers [ti];\
- if ( adj_time >= t->next_time )\
- t = run_timer_( t, adj_time );\
- out = t->counter;\
- t->counter = 0;\
- }\
- else\
- {\
- out = ram [dp_addr];\
- int i = dp_addr - 0xF0;\
- if ( (unsigned) i < 0x10 )\
- out = cpu_read_smp_reg( i, adj_time );\
- }\
- }
-#endif
-
-#define TIME_ADJ( n ) (n)
-
-#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out )
-#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) )
-#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) )
-
-#define DP_ADDR( addr ) (dp + (addr))
-
-#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out )
-#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) )
-#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data )
-
-#define READ_PROG16( addr ) GET_LE16( ram + (addr) )
-
-#define SET_PC( n ) (pc = ram + (n))
-#define GET_PC() (int)(pc - ram)
-#define READ_PC( pc ) (*(pc))
-#define READ_PC16( pc ) GET_LE16( pc )
-
-// TODO: remove non-wrapping versions?
-#define SPC_NO_SP_WRAPAROUND 0
-
-#define SET_SP( v ) (sp = ram + 0x101 + (v))
-#define GET_SP() (int)(sp - 0x101 - ram)
-
-#if SPC_NO_SP_WRAPAROUND
-#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
-#define PUSH( v ) (void) (*--sp = (uint8_t) (v))
-#define POP( out ) (void) ((out) = *sp++)
-
-#else
-#define PUSH16( data )\
-{\
- int addr = (int)((sp -= 2) - ram);\
- if ( addr > 0x100 )\
- {\
- SET_LE16( sp, data );\
- }\
- else\
- {\
- ram [(uint8_t) addr + 0x100] = (uint8_t) data;\
- sp [1] = (uint8_t) (data >> 8);\
- sp += 0x100;\
- }\
-}
-
-#define PUSH( data )\
-{\
- *--sp = (uint8_t) (data);\
- if ( sp - ram == 0x100 )\
- sp += 0x100;\
-}
-
-#define POP( out )\
-{\
- out = *sp++;\
- if ( sp - ram == 0x201 )\
- {\
- out = sp [-0x101];\
- sp -= 0x100;\
- }\
-}
-
-#endif
-
-#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel )
-
-unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time )
-{
- unsigned addr = READ_PC16( pc );
- unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13);
- return t << 8 & 0x100;
-}
-
-//// Status flag handling
-
-// Hex value in name to clarify code and bit shifting.
-// Flag stored in indicated variable during emulation
-int const n80 = 0x80; // nz
-int const v40 = 0x40; // psw
-int const p20 = 0x20; // dp
-int const b10 = 0x10; // psw
-int const h08 = 0x08; // psw
-int const i04 = 0x04; // psw
-int const z02 = 0x02; // nz
-int const c01 = 0x01; // c
-
-int const nz_neg_mask = 0x880; // either bit set indicates N flag set
-
-#define GET_PSW( out )\
-{\
- out = psw & ~(n80 | p20 | z02 | c01);\
- out |= c >> 8 & c01;\
- out |= dp >> 3 & p20;\
- out |= ((nz >> 4) | nz) & n80;\
- if ( !(uint8_t) nz ) out |= z02;\
-}
-
-#define SET_PSW( in )\
-{\
- psw = in;\
- c = in << 8;\
- dp = in << 3 & 0x100;\
- nz = (in << 4 & 0x800) | (~in & z02);\
-}
-
-SPC_CPU_RUN_FUNC
-{
- uint8_t* const ram = RAM;
- int a = m.cpu_regs.a;
- int x = m.cpu_regs.x;
- int y = m.cpu_regs.y;
- uint8_t const* pc;
- uint8_t* sp;
- int psw;
- int c;
- int nz;
- int dp;
-
- SET_PC( m.cpu_regs.pc );
- SET_SP( m.cpu_regs.sp );
- SET_PSW( m.cpu_regs.psw );
-
- goto loop;
-
-
- // Main loop
-
-cbranch_taken_loop:
- pc += *(BOOST::int8_t const*) pc;
-inc_pc_loop:
- pc++;
-loop:
-{
- unsigned opcode;
- unsigned data;
-
- check( (unsigned) a < 0x100 );
- check( (unsigned) x < 0x100 );
- check( (unsigned) y < 0x100 );
-
- opcode = *pc;
- if ( (rel_time += m.cycle_table [opcode]) > 0 )
- goto out_of_time;
-
- #ifdef SPC_CPU_OPCODE_HOOK
- SPC_CPU_OPCODE_HOOK( GET_PC(), opcode );
- #endif
- /*
- //SUB_CASE_COUNTER( 1 );
- #define PROFILE_TIMER_LOOP( op, addr, len )\
- if ( opcode == op )\
- {\
- int cond = (unsigned) ((addr) - 0xFD) < 3 &&\
- pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\
- SUB_CASE_COUNTER( op && cond );\
- }
-
- PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 );
- PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 );
- PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 );
- */
-
- // TODO: if PC is at end of memory, this will get wrong operand (very obscure)
- data = *++pc;
- switch ( opcode )
- {
-
-// Common instructions
-
-#define BRANCH( cond )\
-{\
- pc++;\
- pc += (BOOST::int8_t) data;\
- if ( cond )\
- goto loop;\
- pc -= (BOOST::int8_t) data;\
- rel_time -= 2;\
- goto loop;\
-}
-
- case 0xF0: // BEQ
- BRANCH( !(uint8_t) nz ) // 89% taken
-
- case 0xD0: // BNE
- BRANCH( (uint8_t) nz )
-
- case 0x3F:{// CALL
- int old_addr = GET_PC() + 2;
- SET_PC( READ_PC16( pc ) );
- PUSH16( old_addr );
- goto loop;
- }
-
- case 0x6F:// RET
- #if SPC_NO_SP_WRAPAROUND
- {
- SET_PC( GET_LE16( sp ) );
- sp += 2;
- }
- #else
- {
- int addr = (int)(sp - ram);
- SET_PC( GET_LE16( sp ) );
- sp += 2;
- if ( addr < 0x1FF )
- goto loop;
-
- SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] );
- sp -= 0x100;
- }
- #endif
- goto loop;
-
- case 0xE4: // MOV a,dp
- ++pc;
- // 80% from timer
- READ_DP_TIMER( 0, data, a = nz );
- goto loop;
-
- case 0xFA:{// MOV dp,dp
- int temp;
- READ_DP_TIMER( -2, data, temp );
- data = temp + no_read_before_write ;
- }
- // fall through
- case 0x8F:{// MOV dp,#imm
- int temp = READ_PC( pc + 1 );
- pc += 2;
-
- #if !SPC_MORE_ACCURACY
- {
- int i = dp + temp;
- ram [i] = (uint8_t) data;
- i -= 0xF0;
- if ( (unsigned) i < 0x10 ) // 76%
- {
- REGS [i] = (uint8_t) data;
-
- // Registers other than $F2 and $F4-$F7
- //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 )
- if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12%
- cpu_write_smp_reg( data, rel_time, i );
- }
- }
- #else
- WRITE_DP( 0, temp, data );
- #endif
- goto loop;
- }
-
- case 0xC4: // MOV dp,a
- ++pc;
- #if !SPC_MORE_ACCURACY
- {
- int i = dp + data;
- ram [i] = (uint8_t) a;
- i -= 0xF0;
- if ( (unsigned) i < 0x10 ) // 39%
- {
- unsigned sel = i - 2;
- REGS [i] = (uint8_t) a;
-
- if ( sel == 1 ) // 51% $F3
- dsp_write( a, rel_time );
- else if ( sel > 1 ) // 1% not $F2 or $F3
- cpu_write_smp_reg_( a, rel_time, i );
- }
- }
- #else
- WRITE_DP( 0, data, a );
- #endif
- goto loop;
-
-#define CASE( n ) case n:
-
-// Define common address modes based on opcode for immediate mode. Execution
-// ends with data set to the address of the operand.
-#define ADDR_MODES_( op )\
- CASE( op - 0x02 ) /* (X) */\
- data = x + dp;\
- pc--;\
- goto end_##op;\
- CASE( op + 0x0F ) /* (dp)+Y */\
- data = READ_PROG16( data + dp ) + y;\
- goto end_##op;\
- CASE( op - 0x01 ) /* (dp+X) */\
- data = READ_PROG16( ((uint8_t) (data + x)) + dp );\
- goto end_##op;\
- CASE( op + 0x0E ) /* abs+Y */\
- data += y;\
- goto abs_##op;\
- CASE( op + 0x0D ) /* abs+X */\
- data += x;\
- CASE( op - 0x03 ) /* abs */\
- abs_##op:\
- data += 0x100 * READ_PC( ++pc );\
- goto end_##op;\
- CASE( op + 0x0C ) /* dp+X */\
- data = (uint8_t) (data + x);
-
-#define ADDR_MODES_NO_DP( op )\
- ADDR_MODES_( op )\
- data += dp;\
- end_##op:
-
-#define ADDR_MODES( op )\
- ADDR_MODES_( op )\
- CASE( op - 0x04 ) /* dp */\
- data += dp;\
- end_##op:
-
-// 1. 8-bit Data Transmission Commands. Group I
-
- ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr
- a = nz = READ( 0, data );
- goto inc_pc_loop;
-
- case 0xBF:{// MOV A,(X)+
- int temp = x + dp;
- x = (uint8_t) (x + 1);
- a = nz = READ( -1, temp );
- goto loop;
- }
-
- case 0xE8: // MOV A,imm
- a = data;
- nz = data;
- goto inc_pc_loop;
-
- case 0xF9: // MOV X,dp+Y
- data = (uint8_t) (data + y);
- case 0xF8: // MOV X,dp
- READ_DP_TIMER( 0, data, x = nz );
- goto inc_pc_loop;
-
- case 0xE9: // MOV X,abs
- data = READ_PC16( pc );
- ++pc;
- data = READ( 0, data );
- case 0xCD: // MOV X,imm
- x = data;
- nz = data;
- goto inc_pc_loop;
-
- case 0xFB: // MOV Y,dp+X
- data = (uint8_t) (data + x);
- case 0xEB: // MOV Y,dp
- // 70% from timer
- pc++;
- READ_DP_TIMER( 0, data, y = nz );
- goto loop;
-
- case 0xEC:{// MOV Y,abs
- int temp = READ_PC16( pc );
- pc += 2;
- READ_TIMER( 0, temp, y = nz );
- //y = nz = READ( 0, temp );
- goto loop;
- }
-
- case 0x8D: // MOV Y,imm
- y = data;
- nz = data;
- goto inc_pc_loop;
-
-// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2
-
- ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A
- WRITE( 0, data, a );
- goto inc_pc_loop;
-
- {
- int temp;
- case 0xCC: // MOV abs,Y
- temp = y;
- goto mov_abs_temp;
- case 0xC9: // MOV abs,X
- temp = x;
- mov_abs_temp:
- WRITE( 0, READ_PC16( pc ), temp );
- pc += 2;
- goto loop;
- }
-
- case 0xD9: // MOV dp+Y,X
- data = (uint8_t) (data + y);
- case 0xD8: // MOV dp,X
- WRITE( 0, data + dp, x );
- goto inc_pc_loop;
-
- case 0xDB: // MOV dp+X,Y
- data = (uint8_t) (data + x);
- case 0xCB: // MOV dp,Y
- WRITE( 0, data + dp, y );
- goto inc_pc_loop;
-
-// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.
-
- case 0x7D: // MOV A,X
- a = x;
- nz = x;
- goto loop;
-
- case 0xDD: // MOV A,Y
- a = y;
- nz = y;
- goto loop;
-
- case 0x5D: // MOV X,A
- x = a;
- nz = a;
- goto loop;
-
- case 0xFD: // MOV Y,A
- y = a;
- nz = a;
- goto loop;
-
- case 0x9D: // MOV X,SP
- x = nz = GET_SP();
- goto loop;
-
- case 0xBD: // MOV SP,X
- SET_SP( x );
- goto loop;
-
- //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)
-
- case 0xAF: // MOV (X)+,A
- WRITE_DP( 0, x, a + no_read_before_write );
- x++;
- goto loop;
-
-// 5. 8-BIT LOGIC OPERATION COMMANDS
-
-#define LOGICAL_OP( op, func )\
- ADDR_MODES( op ) /* addr */\
- data = READ( 0, data );\
- case op: /* imm */\
- nz = a func##= data;\
- goto inc_pc_loop;\
- { unsigned addr;\
- case op + 0x11: /* X,Y */\
- data = READ_DP( -2, y );\
- addr = x + dp;\
- goto addr_##op;\
- case op + 0x01: /* dp,dp */\
- data = READ_DP( -3, data );\
- case op + 0x10:{/*dp,imm*/\
- uint8_t const* addr2 = pc + 1;\
- pc += 2;\
- addr = READ_PC( addr2 ) + dp;\
- }\
- addr_##op:\
- nz = data func READ( -1, addr );\
- WRITE( 0, addr, nz );\
- goto loop;\
- }
-
- LOGICAL_OP( 0x28, & ); // AND
-
- LOGICAL_OP( 0x08, | ); // OR
-
- LOGICAL_OP( 0x48, ^ ); // EOR
-
-// 4. 8-BIT ARITHMETIC OPERATION COMMANDS
-
- ADDR_MODES( 0x68 ) // CMP addr
- data = READ( 0, data );
- case 0x68: // CMP imm
- nz = a - data;
- c = ~nz;
- nz &= 0xFF;
- goto inc_pc_loop;
-
- case 0x79: // CMP (X),(Y)
- data = READ_DP( -2, y );
- nz = READ_DP( -1, x ) - data;
- c = ~nz;
- nz &= 0xFF;
- goto loop;
-
- case 0x69: // CMP dp,dp
- data = READ_DP( -3, data );
- case 0x78: // CMP dp,imm
- nz = READ_DP( -1, READ_PC( ++pc ) ) - data;
- c = ~nz;
- nz &= 0xFF;
- goto inc_pc_loop;
-
- case 0x3E: // CMP X,dp
- data += dp;
- goto cmp_x_addr;
- case 0x1E: // CMP X,abs
- data = READ_PC16( pc );
- pc++;
- cmp_x_addr:
- data = READ( 0, data );
- case 0xC8: // CMP X,imm
- nz = x - data;
- c = ~nz;
- nz &= 0xFF;
- goto inc_pc_loop;
-
- case 0x7E: // CMP Y,dp
- data += dp;
- goto cmp_y_addr;
- case 0x5E: // CMP Y,abs
- data = READ_PC16( pc );
- pc++;
- cmp_y_addr:
- data = READ( 0, data );
- case 0xAD: // CMP Y,imm
- nz = y - data;
- c = ~nz;
- nz &= 0xFF;
- goto inc_pc_loop;
-
- {
- int addr;
- case 0xB9: // SBC (x),(y)
- case 0x99: // ADC (x),(y)
- pc--; // compensate for inc later
- data = READ_DP( -2, y );
- addr = x + dp;
- goto adc_addr;
- case 0xA9: // SBC dp,dp
- case 0x89: // ADC dp,dp
- data = READ_DP( -3, data );
- case 0xB8: // SBC dp,imm
- case 0x98: // ADC dp,imm
- addr = READ_PC( ++pc ) + dp;
- adc_addr:
- nz = READ( -1, addr );
- goto adc_data;
-
-// catch ADC and SBC together, then decode later based on operand
-#undef CASE
-#define CASE( n ) case n: case (n) + 0x20:
- ADDR_MODES( 0x88 ) // ADC/SBC addr
- data = READ( 0, data );
- case 0xA8: // SBC imm
- case 0x88: // ADC imm
- addr = -1; // A
- nz = a;
- adc_data: {
- int flags;
- if ( opcode >= 0xA0 ) // SBC
- data ^= 0xFF;
-
- flags = data ^ nz;
- nz += data + (c >> 8 & 1);
- flags ^= nz;
-
- psw = (psw & ~(v40 | h08)) |
- (flags >> 1 & h08) |
- ((flags + 0x80) >> 2 & v40);
- c = nz;
- if ( addr < 0 )
- {
- a = (uint8_t) nz;
- goto inc_pc_loop;
- }
- WRITE( 0, addr, /*(uint8_t)*/ nz );
- goto inc_pc_loop;
- }
-
- }
-
-// 6. ADDITION & SUBTRACTION COMMANDS
-
-#define INC_DEC_REG( reg, op )\
- nz = reg op;\
- reg = (uint8_t) nz;\
- goto loop;
-
- case 0xBC: INC_DEC_REG( a, + 1 ) // INC A
- case 0x3D: INC_DEC_REG( x, + 1 ) // INC X
- case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y
-
- case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A
- case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X
- case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y
-
- case 0x9B: // DEC dp+X
- case 0xBB: // INC dp+X
- data = (uint8_t) (data + x);
- case 0x8B: // DEC dp
- case 0xAB: // INC dp
- data += dp;
- goto inc_abs;
- case 0x8C: // DEC abs
- case 0xAC: // INC abs
- data = READ_PC16( pc );
- pc++;
- inc_abs:
- nz = (opcode >> 4 & 2) - 1;
- nz += READ( -1, data );
- WRITE( 0, data, /*(uint8_t)*/ nz );
- goto inc_pc_loop;
-
-// 7. SHIFT, ROTATION COMMANDS
-
- case 0x5C: // LSR A
- c = 0;
- case 0x7C:{// ROR A
- nz = (c >> 1 & 0x80) | (a >> 1);
- c = a << 8;
- a = nz;
- goto loop;
- }
-
- case 0x1C: // ASL A
- c = 0;
- case 0x3C:{// ROL A
- int temp = c >> 8 & 1;
- c = a << 1;
- nz = c | temp;
- a = (uint8_t) nz;
- goto loop;
- }
-
- case 0x0B: // ASL dp
- c = 0;
- data += dp;
- goto rol_mem;
- case 0x1B: // ASL dp+X
- c = 0;
- case 0x3B: // ROL dp+X
- data = (uint8_t) (data + x);
- case 0x2B: // ROL dp
- data += dp;
- goto rol_mem;
- case 0x0C: // ASL abs
- c = 0;
- case 0x2C: // ROL abs
- data = READ_PC16( pc );
- pc++;
- rol_mem:
- nz = c >> 8 & 1;
- nz |= (c = READ( -1, data ) << 1);
- WRITE( 0, data, /*(uint8_t)*/ nz );
- goto inc_pc_loop;
-
- case 0x4B: // LSR dp
- c = 0;
- data += dp;
- goto ror_mem;
- case 0x5B: // LSR dp+X
- c = 0;
- case 0x7B: // ROR dp+X
- data = (uint8_t) (data + x);
- case 0x6B: // ROR dp
- data += dp;
- goto ror_mem;
- case 0x4C: // LSR abs
- c = 0;
- case 0x6C: // ROR abs
- data = READ_PC16( pc );
- pc++;
- ror_mem: {
- int temp = READ( -1, data );
- nz = (c >> 1 & 0x80) | (temp >> 1);
- c = temp << 8;
- WRITE( 0, data, nz );
- goto inc_pc_loop;
- }
-
- case 0x9F: // XCN
- nz = a = (a >> 4) | (uint8_t) (a << 4);
- goto loop;
-
-// 8. 16-BIT TRANSMISION COMMANDS
-
- case 0xBA: // MOVW YA,dp
- a = READ_DP( -2, data );
- nz = (a & 0x7F) | (a >> 1);
- y = READ_DP( 0, (uint8_t) (data + 1) );
- nz |= y;
- goto inc_pc_loop;
-
- case 0xDA: // MOVW dp,YA
- WRITE_DP( -1, data, a );
- WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write );
- goto inc_pc_loop;
-
-// 9. 16-BIT OPERATION COMMANDS
-
- case 0x3A: // INCW dp
- case 0x1A:{// DECW dp
- int temp;
- // low byte
- data += dp;
- temp = READ( -3, data );
- temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW
- nz = ((temp >> 1) | temp) & 0x7F;
- WRITE( -2, data, /*(uint8_t)*/ temp );
-
- // high byte
- data = (uint8_t) (data + 1) + dp;
- temp = (uint8_t) ((temp >> 8) + READ( -1, data ));
- nz |= temp;
- WRITE( 0, data, temp );
-
- goto inc_pc_loop;
- }
-
- case 0x7A: // ADDW YA,dp
- case 0x9A:{// SUBW YA,dp
- int lo = READ_DP( -2, data );
- int hi = READ_DP( 0, (uint8_t) (data + 1) );
- int result;
- int flags;
-
- if ( opcode == 0x9A ) // SUBW
- {
- lo = (lo ^ 0xFF) + 1;
- hi ^= 0xFF;
- }
-
- lo += a;
- result = y + hi + (lo >> 8);
- flags = hi ^ y ^ result;
-
- psw = (psw & ~(v40 | h08)) |
- (flags >> 1 & h08) |
- ((flags + 0x80) >> 2 & v40);
- c = result;
- a = (uint8_t) lo;
- result = (uint8_t) result;
- y = result;
- nz = (((lo >> 1) | lo) & 0x7F) | result;
-
- goto inc_pc_loop;
- }
-
- case 0x5A: { // CMPW YA,dp
- int temp = a - READ_DP( -1, data );
- nz = ((temp >> 1) | temp) & 0x7F;
- temp = y + (temp >> 8);
- temp -= READ_DP( 0, (uint8_t) (data + 1) );
- nz |= temp;
- c = ~temp;
- nz &= 0xFF;
- goto inc_pc_loop;
- }
-
-// 10. MULTIPLICATION & DIVISON COMMANDS
-
- case 0xCF: { // MUL YA
- unsigned temp = y * a;
- a = (uint8_t) temp;
- nz = ((temp >> 1) | temp) & 0x7F;
- y = temp >> 8;
- nz |= y;
- goto loop;
- }
-
- case 0x9E: // DIV YA,X
- {
- unsigned ya = y * 0x100 + a;
-
- psw &= ~(h08 | v40);
-
- if ( y >= x )
- psw |= v40;
-
- if ( (y & 15) >= (x & 15) )
- psw |= h08;
-
- if ( y < x * 2 )
- {
- a = ya / x;
- y = ya - a * x;
- }
- else
- {
- a = 255 - (ya - x * 0x200) / (256 - x);
- y = x + (ya - x * 0x200) % (256 - x);
- }
-
- nz = (uint8_t) a;
- a = (uint8_t) a;
-
- goto loop;
- }
-
-// 11. DECIMAL COMPENSATION COMMANDS
-
- case 0xDF: // DAA
- SUSPICIOUS_OPCODE( "DAA" );
- if ( a > 0x99 || c & 0x100 )
- {
- a += 0x60;
- c = 0x100;
- }
-
- if ( (a & 0x0F) > 9 || psw & h08 )
- a += 0x06;
-
- nz = a;
- a = (uint8_t) a;
- goto loop;
-
- case 0xBE: // DAS
- SUSPICIOUS_OPCODE( "DAS" );
- if ( a > 0x99 || !(c & 0x100) )
- {
- a -= 0x60;
- c = 0;
- }
-
- if ( (a & 0x0F) > 9 || !(psw & h08) )
- a -= 0x06;
-
- nz = a;
- a = (uint8_t) a;
- goto loop;
-
-// 12. BRANCHING COMMANDS
-
- case 0x2F: // BRA rel
- pc += (BOOST::int8_t) data;
- goto inc_pc_loop;
-
- case 0x30: // BMI
- BRANCH( (nz & nz_neg_mask) )
-
- case 0x10: // BPL
- BRANCH( !(nz & nz_neg_mask) )
-
- case 0xB0: // BCS
- BRANCH( c & 0x100 )
-
- case 0x90: // BCC
- BRANCH( !(c & 0x100) )
-
- case 0x70: // BVS
- BRANCH( psw & v40 )
-
- case 0x50: // BVC
- BRANCH( !(psw & v40) )
-
- #define CBRANCH( cond )\
- {\
- pc++;\
- if ( cond )\
- goto cbranch_taken_loop;\
- rel_time -= 2;\
- goto inc_pc_loop;\
- }
-
- case 0x03: // BBS dp.bit,rel
- case 0x23:
- case 0x43:
- case 0x63:
- case 0x83:
- case 0xA3:
- case 0xC3:
- case 0xE3:
- CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 )
-
- case 0x13: // BBC dp.bit,rel
- case 0x33:
- case 0x53:
- case 0x73:
- case 0x93:
- case 0xB3:
- case 0xD3:
- case 0xF3:
- CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) )
-
- case 0xDE: // CBNE dp+X,rel
- data = (uint8_t) (data + x);
- // fall through
- case 0x2E:{// CBNE dp,rel
- int temp;
- // 61% from timer
- READ_DP_TIMER( -4, data, temp );
- CBRANCH( temp != a )
- }
-
- case 0x6E: { // DBNZ dp,rel
- unsigned temp = READ_DP( -4, data ) - 1;
- WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write );
- CBRANCH( temp )
- }
-
- case 0xFE: // DBNZ Y,rel
- y = (uint8_t) (y - 1);
- BRANCH( y )
-
- case 0x1F: // JMP [abs+X]
- SET_PC( READ_PC16( pc ) + x );
- // fall through
- case 0x5F: // JMP abs
- SET_PC( READ_PC16( pc ) );
- goto loop;
-
-// 13. SUB-ROUTINE CALL RETURN COMMANDS
-
- case 0x0F:{// BRK
- int temp;
- int ret_addr = GET_PC();
- SUSPICIOUS_OPCODE( "BRK" );
- SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified
- PUSH16( ret_addr );
- GET_PSW( temp );
- psw = (psw | b10) & ~i04;
- PUSH( temp );
- goto loop;
- }
-
- case 0x4F:{// PCALL offset
- int ret_addr = GET_PC() + 1;
- SET_PC( 0xFF00 | data );
- PUSH16( ret_addr );
- goto loop;
- }
-
- case 0x01: // TCALL n
- case 0x11:
- case 0x21:
- case 0x31:
- case 0x41:
- case 0x51:
- case 0x61:
- case 0x71:
- case 0x81:
- case 0x91:
- case 0xA1:
- case 0xB1:
- case 0xC1:
- case 0xD1:
- case 0xE1:
- case 0xF1: {
- int ret_addr = GET_PC();
- SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
- PUSH16( ret_addr );
- goto loop;
- }
-
-// 14. STACK OPERATION COMMANDS
-
- {
- int temp;
- case 0x7F: // RET1
- temp = *sp;
- SET_PC( GET_LE16( sp + 1 ) );
- sp += 3;
- goto set_psw;
- case 0x8E: // POP PSW
- POP( temp );
- set_psw:
- SET_PSW( temp );
- goto loop;
- }
-
- case 0x0D: { // PUSH PSW
- int temp;
- GET_PSW( temp );
- PUSH( temp );
- goto loop;
- }
-
- case 0x2D: // PUSH A
- PUSH( a );
- goto loop;
-
- case 0x4D: // PUSH X
- PUSH( x );
- goto loop;
-
- case 0x6D: // PUSH Y
- PUSH( y );
- goto loop;
-
- case 0xAE: // POP A
- POP( a );
- goto loop;
-
- case 0xCE: // POP X
- POP( x );
- goto loop;
-
- case 0xEE: // POP Y
- POP( y );
- goto loop;
-
-// 15. BIT OPERATION COMMANDS
-
- case 0x02: // SET1
- case 0x22:
- case 0x42:
- case 0x62:
- case 0x82:
- case 0xA2:
- case 0xC2:
- case 0xE2:
- case 0x12: // CLR1
- case 0x32:
- case 0x52:
- case 0x72:
- case 0x92:
- case 0xB2:
- case 0xD2:
- case 0xF2: {
- int bit = 1 << (opcode >> 5);
- int mask = ~bit;
- if ( opcode & 0x10 )
- bit = 0;
- data += dp;
- WRITE( 0, data, (READ( -1, data ) & mask) | bit );
- goto inc_pc_loop;
- }
-
- case 0x0E: // TSET1 abs
- case 0x4E: // TCLR1 abs
- data = READ_PC16( pc );
- pc += 2;
- {
- unsigned temp = READ( -2, data );
- nz = (uint8_t) (a - temp);
- temp &= ~a;
- if ( opcode == 0x0E )
- temp |= a;
- WRITE( 0, data, temp );
- }
- goto loop;
-
- case 0x4A: // AND1 C,mem.bit
- c &= MEM_BIT( 0 );
- pc += 2;
- goto loop;
-
- case 0x6A: // AND1 C,/mem.bit
- c &= ~MEM_BIT( 0 );
- pc += 2;
- goto loop;
-
- case 0x0A: // OR1 C,mem.bit
- c |= MEM_BIT( -1 );
- pc += 2;
- goto loop;
-
- case 0x2A: // OR1 C,/mem.bit
- c |= ~MEM_BIT( -1 );
- pc += 2;
- goto loop;
-
- case 0x8A: // EOR1 C,mem.bit
- c ^= MEM_BIT( -1 );
- pc += 2;
- goto loop;
-
- case 0xEA: // NOT1 mem.bit
- data = READ_PC16( pc );
- pc += 2;
- {
- unsigned temp = READ( -1, data & 0x1FFF );
- temp ^= 1 << (data >> 13);
- WRITE( 0, data & 0x1FFF, temp );
- }
- goto loop;
-
- case 0xCA: // MOV1 mem.bit,C
- data = READ_PC16( pc );
- pc += 2;
- {
- unsigned temp = READ( -2, data & 0x1FFF );
- unsigned bit = data >> 13;
- temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit);
- WRITE( 0, data & 0x1FFF, temp + no_read_before_write );
- }
- goto loop;
-
- case 0xAA: // MOV1 C,mem.bit
- c = MEM_BIT( 0 );
- pc += 2;
- goto loop;
-
-// 16. PROGRAM PSW FLAG OPERATION COMMANDS
-
- case 0x60: // CLRC
- c = 0;
- goto loop;
-
- case 0x80: // SETC
- c = ~0;
- goto loop;
-
- case 0xED: // NOTC
- c ^= 0x100;
- goto loop;
-
- case 0xE0: // CLRV
- psw &= ~(v40 | h08);
- goto loop;
-
- case 0x20: // CLRP
- dp = 0;
- goto loop;
-
- case 0x40: // SETP
- dp = 0x100;
- goto loop;
-
- case 0xA0: // EI
- SUSPICIOUS_OPCODE( "EI" );
- psw |= i04;
- goto loop;
-
- case 0xC0: // DI
- SUSPICIOUS_OPCODE( "DI" );
- psw &= ~i04;
- goto loop;
-
-// 17. OTHER COMMANDS
-
- case 0x00: // NOP
- goto loop;
-
- case 0xFF:{// STOP
- // handle PC wrap-around
- unsigned addr = GET_PC() - 1;
- if ( addr >= 0x10000 )
- {
- addr &= 0xFFFF;
- SET_PC( addr );
- dprintf( "SPC: PC wrapped around\n" );
- goto loop;
- }
- }
- // fall through
- case 0xEF: // SLEEP
- SUSPICIOUS_OPCODE( "STOP/SLEEP" );
- --pc;
- rel_time = 0;
- m.cpu_error = "SPC emulation error";
- goto stop;
- } // switch
-
- assert( 0 ); // catch any unhandled instructions
-}
-out_of_time:
- rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode
-stop:
-
- // Uncache registers
- if ( GET_PC() >= 0x10000 )
- dprintf( "SPC: PC wrapped around\n" );
- m.cpu_regs.pc = (uint16_t) GET_PC();
- m.cpu_regs.sp = ( uint8_t) GET_SP();
- m.cpu_regs.a = ( uint8_t) a;
- m.cpu_regs.x = ( uint8_t) x;
- m.cpu_regs.y = ( uint8_t) y;
- {
- int temp;
- GET_PSW( temp );
- m.cpu_regs.psw = (uint8_t) temp;
- }
-}
-SPC_CPU_RUN_FUNC_END
diff --git a/Core/SPC_DSP.cpp b/Core/SPC_DSP.cpp
index 679a415..e40e1e8 100644
--- a/Core/SPC_DSP.cpp
+++ b/Core/SPC_DSP.cpp
@@ -708,8 +708,9 @@ ECHO_CLOCK( 28 )
}
inline void SPC_DSP::echo_write( int ch )
{
- if ( !(m.t_echo_enabled & 0x20) )
- SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] );
+ if(!(m.t_echo_enabled & 0x20) && _echoWriteEnabled) {
+ SET_LE16A(ECHO_PTR(ch), m.t_echo_out[ch]);
+ }
m.t_echo_out [ch] = 0;
}
ECHO_CLOCK( 29 )
@@ -785,22 +786,14 @@ PHASE(31) V(V4,0) V(V1,2)\
#if !SPC_DSP_CUSTOM_RUN
-void SPC_DSP::run( int clocks_remain )
+void SPC_DSP::run()
{
- require( clocks_remain > 0 );
-
- int const phase = m.phase;
- m.phase = (phase + clocks_remain) & 31;
- switch ( phase )
+ m.phase = (m.phase + 1) & 31;
+ switch (m.phase)
{
- loop:
-
- #define PHASE( n ) if ( n && !--clocks_remain ) break; case n:
+ #define PHASE( n ) if ( n ) break; case n:
GEN_DSP_TIMING
#undef PHASE
-
- if ( --clocks_remain )
- goto loop;
}
}
diff --git a/Core/SPC_DSP.h b/Core/SPC_DSP.h
index ee42ae0..c9b312f 100644
--- a/Core/SPC_DSP.h
+++ b/Core/SPC_DSP.h
@@ -42,8 +42,10 @@ public:
// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
// a pair of samples is be generated.
- void run( int clock_count );
+ void run();
+ void setEchoWriteEnabled(bool enabled);
+
// Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
@@ -187,6 +189,8 @@ private:
};
state_t m;
+ bool _echoWriteEnabled = true;
+
void init_counter();
void run_counters();
unsigned read_counter( int rate );
@@ -271,6 +275,8 @@ inline void SPC_DSP::write( int addr, int data )
}
}
+inline void SPC_DSP::setEchoWriteEnabled(bool enabled) { _echoWriteEnabled = enabled; }
+
inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
inline bool SPC_DSP::check_kon()
diff --git a/Core/Spc.Instructions.cpp b/Core/Spc.Instructions.cpp
new file mode 100644
index 0000000..05e8f7b
--- /dev/null
+++ b/Core/Spc.Instructions.cpp
@@ -0,0 +1,1426 @@
+#include "stdafx.h"
+#include "Spc.h"
+#include "../Utilities/HexUtilities.h"
+
+void Spc::Exec()
+{
+ uint8_t opCode = GetOpCode();
+ switch(opCode) {
+ case 0x00: NOP(); break;
+ case 0x01: TCALL<0>(); break;
+ case 0x02: Addr_Dir(); SET1<0>(); break;
+ case 0x03: Addr_Dir(); BBS<0>(); break;
+ case 0x04: Addr_Dir(); OR_Acc(); break;
+ case 0x05: Addr_Abs(); OR_Acc(); break;
+ case 0x06: Addr_IndX(); OR_Acc(); break;
+ case 0x07: Addr_DirIdxXInd(); OR_Acc(); break;
+ case 0x08: Addr_Imm(); OR_Acc(); _immediateMode = false; break;
+ case 0x09: Addr_DirToDir(); OR(); break;
+ case 0x0A: Addr_AbsBit(); OR1(); break;
+ case 0x0B: Addr_Dir(); ASL(); break;
+ case 0x0C: Addr_Abs(); ASL(); break;
+ case 0x0D: PHP(); break;
+ case 0x0E: Addr_Abs(); TSET1(); break;
+ case 0x0F: BRK(); break;
+ case 0x10: Addr_Rel(); BPL(); break;
+ case 0x11: TCALL<1>(); break;
+ case 0x12: Addr_Dir(); CLR1<0>(); break;
+ case 0x13: Addr_Dir(); BBC<0>(); break;
+ case 0x14: Addr_DirIdxX(); OR_Acc(); break;
+ case 0x15: Addr_AbsIdxX(); OR_Acc(); break;
+ case 0x16: Addr_AbsIdxY(); OR_Acc(); break;
+ case 0x17: Addr_DirIndIdxY(); OR_Acc(); break;
+ case 0x18: Addr_DirImm(); OR(); break;
+ case 0x19: Addr_IndXToIndY(); OR(); break;
+ case 0x1A: Addr_Dir(); DECW(); break;
+ case 0x1B: Addr_DirIdxX(); ASL(); break;
+ case 0x1C: ASL_Acc(); break;
+ case 0x1D: DEX(); break;
+ case 0x1E: Addr_Abs(); CPX(); break;
+ case 0x1F: Addr_AbsIdxXInd(); JMP(); break;
+ case 0x20: CLRP(); break;
+ case 0x21: TCALL<2>(); break;
+ case 0x22: Addr_Dir(); SET1<1>(); break;
+ case 0x23: Addr_Dir(); BBS<1>(); break;
+ case 0x24: Addr_Dir(); AND_Acc(); break;
+ case 0x25: Addr_Abs(); AND_Acc(); break;
+ case 0x26: Addr_IndX(); AND_Acc(); break;
+ case 0x27: Addr_DirIdxXInd(); AND_Acc(); break;
+ case 0x28: Addr_Imm(); AND_Acc(); _immediateMode = false; break;
+ case 0x29: Addr_DirToDir(); AND(); break;
+ case 0x2A: Addr_AbsBit(); NOR1(); break;
+ case 0x2B: Addr_Dir(); ROL(); break;
+ case 0x2C: Addr_Abs(); ROL(); break;
+ case 0x2D: PHA(); break;
+ case 0x2E: Addr_Dir(); CBNE(); break;
+ case 0x2F: Addr_Rel(); BRA(); break;
+ case 0x30: Addr_Rel(); BMI(); break;
+ case 0x31: TCALL<3>(); break;
+ case 0x32: Addr_Dir(); CLR1<1>(); break;
+ case 0x33: Addr_Dir(); BBC<1>(); break;
+ case 0x34: Addr_DirIdxX(); AND_Acc(); break;
+ case 0x35: Addr_AbsIdxX(); AND_Acc(); break;
+ case 0x36: Addr_AbsIdxY(); AND_Acc(); break;
+ case 0x37: Addr_DirIndIdxY(); AND_Acc(); break;
+ case 0x38: Addr_DirImm(); AND(); break;
+ case 0x39: Addr_IndXToIndY(); AND(); break;
+ case 0x3A: Addr_Dir(); INCW(); break;
+ case 0x3B: Addr_DirIdxX(); ROL(); break;
+ case 0x3C: ROL_Acc(); break;
+ case 0x3D: INX(); break;
+ case 0x3E: Addr_Dir(); CPX(); break;
+ case 0x3F: Addr_Abs(); JSR(); break;
+ case 0x40: SETP(); break;
+ case 0x41: TCALL<4>(); break;
+ case 0x42: Addr_Dir(); SET1<2>(); break;
+ case 0x43: Addr_Dir(); BBS<2>(); break;
+ case 0x44: Addr_Dir(); EOR_Acc(); break;
+ case 0x45: Addr_Abs(); EOR_Acc(); break;
+ case 0x46: Addr_IndX(); EOR_Acc(); break;
+ case 0x47: Addr_DirIdxXInd(); EOR_Acc(); break;
+ case 0x48: Addr_Imm(); EOR_Acc(); _immediateMode = false; break;
+ case 0x49: Addr_DirToDir(); EOR(); break;
+ case 0x4A: Addr_AbsBit(); AND1(); break;
+ case 0x4B: Addr_Dir(); LSR(); break;
+ case 0x4C: Addr_Abs(); LSR(); break;
+ case 0x4D: PHX(); break;
+ case 0x4E: Addr_Abs(); TCLR1(); break;
+ case 0x4F: PCALL(); break;
+ case 0x50: Addr_Rel(); BVC(); break;
+ case 0x51: TCALL<5>(); break;
+ case 0x52: Addr_Dir(); CLR1<2>(); break;
+ case 0x53: Addr_Dir(); BBC<2>(); break;
+ case 0x54: Addr_DirIdxX(); EOR_Acc(); break;
+ case 0x55: Addr_AbsIdxX(); EOR_Acc(); break;
+ case 0x56: Addr_AbsIdxY(); EOR_Acc(); break;
+ case 0x57: Addr_DirIndIdxY(); EOR_Acc(); break;
+ case 0x58: Addr_DirImm(); EOR(); break;
+ case 0x59: Addr_IndXToIndY(); EOR(); break;
+ case 0x5A: Addr_Dir(); CMPW(); break;
+ case 0x5B: Addr_DirIdxX(); LSR(); break;
+ case 0x5C: LSR_Acc(); break;
+ case 0x5D: TAX(); break;
+ case 0x5E: Addr_Abs(); CPY(); break;
+ case 0x5F: Addr_Abs(); JMP(); break;
+ case 0x60: CLRC(); break;
+ case 0x61: TCALL<6>(); break;
+ case 0x62: Addr_Dir(); SET1<3>(); break;
+ case 0x63: Addr_Dir(); BBS<3>(); break;
+ case 0x64: Addr_Dir(); CMP_Acc(); break;
+ case 0x65: Addr_Abs(); CMP_Acc(); break;
+ case 0x66: Addr_IndX(); CMP_Acc(); break;
+ case 0x67: Addr_DirIdxXInd(); CMP_Acc(); break;
+ case 0x68: Addr_Imm(); CMP_Acc(); _immediateMode = false; break;
+ case 0x69: Addr_DirToDir(); CMP(); break;
+ case 0x6A: Addr_AbsBit(); NAND1(); break;
+ case 0x6B: Addr_Dir(); ROR(); break;
+ case 0x6C: Addr_Abs(); ROR(); break;
+ case 0x6D: PHY(); break;
+ case 0x6E: Addr_Dir(); DBNZ(); break;
+ case 0x6F: RTS(); break;
+ case 0x70: Addr_Rel(); BVS(); break;
+ case 0x71: TCALL<7>(); break;
+ case 0x72: Addr_Dir(); CLR1<3>(); break;
+ case 0x73: Addr_Dir(); BBC<3>(); break;
+ case 0x74: Addr_DirIdxX(); CMP_Acc(); break;
+ case 0x75: Addr_AbsIdxX(); CMP_Acc(); break;
+ case 0x76: Addr_AbsIdxY(); CMP_Acc(); break;
+ case 0x77: Addr_DirIndIdxY(); CMP_Acc(); break;
+ case 0x78: Addr_DirImm(); CMP(); break;
+ case 0x79: Addr_IndXToIndY(); CMP(); break;
+ case 0x7A: Addr_Dir(); ADDW(); break;
+ case 0x7B: Addr_DirIdxX(); ROR(); break;
+ case 0x7C: ROR_Acc(); break;
+ case 0x7D: TXA(); break;
+ case 0x7E: Addr_Dir(); CPY(); break;
+ case 0x7F: RTI(); break;
+ case 0x80: SETC(); break;
+ case 0x81: TCALL<8>(); break;
+ case 0x82: Addr_Dir(); SET1<4>(); break;
+ case 0x83: Addr_Dir(); BBS<4>(); break;
+ case 0x84: Addr_Dir(); ADC_Acc(); break;
+ case 0x85: Addr_Abs(); ADC_Acc(); break;
+ case 0x86: Addr_IndX(); ADC_Acc(); break;
+ case 0x87: Addr_DirIdxXInd(); ADC_Acc(); break;
+ case 0x88: Addr_Imm(); ADC_Acc(); _immediateMode = false; break;
+ case 0x89: Addr_DirToDir(); ADC(); break;
+ case 0x8A: Addr_AbsBit(); EOR1(); break;
+ case 0x8B: Addr_Dir(); DEC(); break;
+ case 0x8C: Addr_Abs(); DEC(); break;
+ case 0x8D: Addr_Imm(); LDY(); _immediateMode = false; break;
+ case 0x8E: PLP(); break;
+ case 0x8F: Addr_DirImm(); DummyRead(_operandB); MOV(); break;
+ case 0x90: Addr_Rel(); BCC(); break;
+ case 0x91: TCALL<9>(); break;
+ case 0x92: Addr_Dir(); CLR1<4>(); break;
+ case 0x93: Addr_Dir(); BBC<4>(); break;
+ case 0x94: Addr_DirIdxX(); ADC_Acc(); break;
+ case 0x95: Addr_AbsIdxX(); ADC_Acc(); break;
+ case 0x96: Addr_AbsIdxY(); ADC_Acc(); break;
+ case 0x97: Addr_DirIndIdxY(); ADC_Acc(); break;
+ case 0x98: Addr_DirImm(); ADC(); break;
+ case 0x99: Addr_IndXToIndY(); ADC(); break;
+ case 0x9A: Addr_Dir(); SUBW(); break;
+ case 0x9B: Addr_DirIdxX(); DEC(); break;
+ case 0x9C: DEC_Acc(); break;
+ case 0x9D: TSX(); break;
+ case 0x9E: DIV(); break;
+ case 0x9F: XCN(); break;
+ case 0xA0: EI(); break;
+ case 0xA1: TCALL<10>(); break;
+ case 0xA2: Addr_Dir(); SET1<5>(); break;
+ case 0xA3: Addr_Dir(); BBS<5>(); break;
+ case 0xA4: Addr_Dir(); SBC_Acc(); break;
+ case 0xA5: Addr_Abs(); SBC_Acc(); break;
+ case 0xA6: Addr_IndX(); SBC_Acc(); break;
+ case 0xA7: Addr_DirIdxXInd(); SBC_Acc(); break;
+ case 0xA8: Addr_Imm(); SBC_Acc(); _immediateMode = false; break;
+ case 0xA9: Addr_DirToDir(); SBC(); break;
+ case 0xAA: Addr_AbsBit(); LDC(); break;
+ case 0xAB: Addr_Dir(); INC(); break;
+ case 0xAC: Addr_Abs(); INC(); break;
+ case 0xAD: Addr_Imm(); CPY(); _immediateMode = false; break;
+ case 0xAE: PLA(); break;
+ case 0xAF: Addr_IndX(); STA_AutoIncX(); break;
+ case 0xB0: Addr_Rel(); BCS(); break;
+ case 0xB1: TCALL<11>(); break;
+ case 0xB2: Addr_Dir(); CLR1<5>(); break;
+ case 0xB3: Addr_Dir(); BBC<5>(); break;
+ case 0xB4: Addr_DirIdxX(); SBC_Acc(); break;
+ case 0xB5: Addr_AbsIdxX(); SBC_Acc(); break;
+ case 0xB6: Addr_AbsIdxY(); SBC_Acc(); break;
+ case 0xB7: Addr_DirIndIdxY(); SBC_Acc(); break;
+ case 0xB8: Addr_DirImm(); SBC(); break;
+ case 0xB9: Addr_IndXToIndY(); SBC(); break;
+ case 0xBA: Addr_Dir(); LDW(); break;
+ case 0xBB: Addr_DirIdxX(); INC(); break;
+ case 0xBC: INC_Acc(); break;
+ case 0xBD: TXS(); break;
+ case 0xBE: DAS(); break;
+ case 0xBF: Addr_IndX(); LDA_AutoIncX(); break;
+ case 0xC0: DI(); break;
+ case 0xC1: TCALL<12>(); break;
+ case 0xC2: Addr_Dir(); SET1<6>(); break;
+ case 0xC3: Addr_Dir(); BBS<6>(); break;
+ case 0xC4: Addr_Dir(); STA(); break;
+ case 0xC5: Addr_Abs(); STA(); break;
+ case 0xC6: Addr_IndX(); STA(); break;
+ case 0xC7: Addr_DirIdxXInd(); STA(); break;
+ case 0xC8: Addr_Imm(); CPX(); _immediateMode = false; break;
+ case 0xC9: Addr_Abs(); STX(); break;
+ case 0xCA: Addr_AbsBit(); STC(); break;
+ case 0xCB: Addr_Dir(); STY(); break;
+ case 0xCC: Addr_Abs(); STY(); break;
+ case 0xCD: Addr_Imm(); LDX(); _immediateMode = false; break;
+ case 0xCE: PLX(); break;
+ case 0xCF: MUL(); break;
+ case 0xD0: Addr_Rel(); BNE(); break;
+ case 0xD1: TCALL<13>(); break;
+ case 0xD2: Addr_Dir(); CLR1<6>(); break;
+ case 0xD3: Addr_Dir(); BBC<6>(); break;
+ case 0xD4: Addr_DirIdxX(); STA(); break;
+ case 0xD5: Addr_AbsIdxX(); STA(); break;
+ case 0xD6: Addr_AbsIdxY(); STA(); break;
+ case 0xD7: Addr_DirIndIdxY(); STA(); break;
+ case 0xD8: Addr_Dir(); STX(); break;
+ case 0xD9: Addr_DirIdxY(); STX(); break;
+ case 0xDA: Addr_Dir(); STW(); break;
+ case 0xDB: Addr_DirIdxX(); STY(); break;
+ case 0xDC: DEY(); break;
+ case 0xDD: TYA(); break;
+ case 0xDE: Addr_DirIdxX(); CBNE(); break;
+ case 0xDF: DAA(); break;
+ case 0xE0: CLRV(); break;
+ case 0xE1: TCALL<14>(); break;
+ case 0xE2: Addr_Dir(); SET1<7>(); break;
+ case 0xE3: Addr_Dir(); BBS<7>(); break;
+ case 0xE4: Addr_Dir(); LDA(); break;
+ case 0xE5: Addr_Abs(); LDA(); break;
+ case 0xE6: Addr_IndX(); LDA(); break;
+ case 0xE7: Addr_DirIdxXInd(); LDA(); break;
+ case 0xE8: Addr_Imm(); LDA(); _immediateMode = false; break;
+ case 0xE9: Addr_Abs(); LDX(); break;
+ case 0xEA: Addr_AbsBit(); NOT1(); break;
+ case 0xEB: Addr_Dir(); LDY(); break;
+ case 0xEC: Addr_Abs(); LDY(); break;
+ case 0xED: NOTC(); break;
+ case 0xEE: PLY(); break;
+ case 0xEF: SLEEP(); break;
+ case 0xF0: Addr_Rel(); BEQ(); break;
+ case 0xF1: TCALL<15>(); break;
+ case 0xF2: Addr_Dir(); CLR1<7>(); break;
+ case 0xF3: Addr_Dir(); BBC<7>(); break;
+ case 0xF4: Addr_DirIdxX(); LDA(); break;
+ case 0xF5: Addr_AbsIdxX(); LDA(); break;
+ case 0xF6: Addr_AbsIdxY(); LDA(); break;
+ case 0xF7: Addr_DirIndIdxY(); LDA(); break;
+ case 0xF8: Addr_Dir(); LDX(); break;
+ case 0xF9: Addr_DirIdxY(); LDX(); break;
+ case 0xFA: Addr_DirToDir(); MOV(); break;
+ case 0xFB: Addr_DirIdxX(); LDY(); break;
+ case 0xFC: INY(); break;
+ case 0xFD: TAY(); break;
+ case 0xFE: DBNZ_Y(); break;
+ case 0xFF: STOP(); break;
+ }
+}
+
+//*****************
+// ADDRESSING MODES
+//*****************
+void Spc::Addr_Dir()
+{
+ _operandA = GetDirectAddress(ReadOperandByte());
+}
+
+void Spc::Addr_DirIdxX()
+{
+ _operandA = GetDirectAddress(ReadOperandByte() + _state.X);
+ Idle();
+}
+
+void Spc::Addr_DirIdxY()
+{
+ _operandA = GetDirectAddress(ReadOperandByte() + _state.Y);
+ Idle();
+}
+
+void Spc::Addr_DirToDir()
+{
+ _operandA = Read(GetDirectAddress(ReadOperandByte()));
+ _operandB = GetDirectAddress(ReadOperandByte());
+}
+
+void Spc::Addr_DirImm()
+{
+ _operandA = ReadOperandByte();
+ _operandB = GetDirectAddress(ReadOperandByte());
+}
+
+void Spc::Addr_DirIdxXInd()
+{
+ uint16_t addr = GetDirectAddress(ReadOperandByte() + _state.X);
+ Idle();
+ uint8_t lsb = Read(addr);
+ uint8_t msb = Read(GetDirectAddress(addr + 1));
+ _operandA = (msb << 8) | lsb;
+}
+
+void Spc::Addr_DirIndIdxY()
+{
+ uint16_t addr = GetDirectAddress(ReadOperandByte());
+ uint8_t lsb = Read(addr);
+ uint8_t msb = Read(GetDirectAddress(addr + 1));
+ Idle();
+ _operandA = ((msb << 8) | lsb) + _state.Y;
+}
+
+void Spc::Addr_IndX()
+{
+ DummyRead();
+ _operandA = GetDirectAddress(_state.X);
+}
+
+void Spc::Addr_IndXToIndY()
+{
+ DummyRead();
+ _operandA = Read(GetDirectAddress(_state.Y));
+ _operandB = GetDirectAddress(_state.X);
+}
+
+void Spc::Addr_Abs()
+{
+ uint8_t lsb = ReadOperandByte();
+ uint8_t msb = ReadOperandByte();
+ _operandA = ((msb << 8) | lsb);
+}
+
+void Spc::Addr_AbsBit()
+{
+ Addr_Abs();
+ _operandB = _operandA >> 13;
+ _operandA = _operandA & 0x1FFF;
+}
+
+void Spc::Addr_AbsIdxX()
+{
+ Addr_Abs();
+ Idle();
+ _operandA += _state.X;
+}
+
+void Spc::Addr_AbsIdxY()
+{
+ Addr_Abs();
+ Idle();
+ _operandA += _state.Y;
+}
+
+void Spc::Addr_AbsIdxXInd()
+{
+ //Used by JMP only
+ uint8_t lsb = ReadOperandByte();
+ uint8_t msb = ReadOperandByte();
+ Idle();
+ uint16_t addr = ((msb << 8) | lsb);
+ uint8_t addrLsb = Read(addr + _state.X);
+ uint8_t addrMsb = Read(addr + _state.X + 1);
+ _operandA = (addrMsb << 8) | addrLsb;
+}
+
+void Spc::Addr_Rel()
+{
+ _operandA = ReadOperandByte();
+}
+
+void Spc::Addr_Imm()
+{
+ _immediateMode = true;
+ _operandA = ReadOperandByte();
+}
+
+//*****************
+// INSTRUCTIONS
+//*****************
+void Spc::STA()
+{
+ Read(_operandA);
+ Write(_operandA, _state.A);
+}
+
+void Spc::STX()
+{
+ Read(_operandA);
+ Write(_operandA, _state.X);
+}
+
+void Spc::STY()
+{
+ Read(_operandA);
+ Write(_operandA, _state.Y);
+}
+
+void Spc::STW()
+{
+ DummyRead(_operandA);
+
+ Write(_operandA, _state.A);
+
+ uint16_t msbAddress = GetDirectAddress(_operandA + 1);
+ Write(msbAddress, _state.Y);
+}
+
+void Spc::STA_AutoIncX()
+{
+ Idle();
+ Write(_operandA, _state.A);
+ _state.X++;
+}
+
+void Spc::LDA_AutoIncX()
+{
+ _state.A = Read(_operandA);
+ SetZeroNegativeFlags(_state.A);
+ Idle();
+ _state.X++;
+}
+
+void Spc::LDA()
+{
+ _state.A = GetByteValue();
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::LDX()
+{
+ _state.X = GetByteValue();
+ SetZeroNegativeFlags(_state.X);
+}
+
+void Spc::LDY()
+{
+ _state.Y = GetByteValue();
+ SetZeroNegativeFlags(_state.Y);
+}
+
+void Spc::LDW()
+{
+ uint8_t lsb = Read(_operandA);
+ Idle();
+ uint8_t msb = Read(GetDirectAddress(_operandA + 1));
+
+ uint16_t value = (msb << 8) | lsb;
+ _state.A = (uint8_t)value;
+ _state.Y = (value >> 8);
+ SetZeroNegativeFlags16(value);
+}
+
+void Spc::TXA()
+{
+ DummyRead();
+ _state.A = _state.X;
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::TYA()
+{
+ DummyRead();
+ _state.A = _state.Y;
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::TAX()
+{
+ DummyRead();
+ _state.X = _state.A;
+ SetZeroNegativeFlags(_state.X);
+}
+
+void Spc::TAY()
+{
+ DummyRead();
+ _state.Y = _state.A;
+ SetZeroNegativeFlags(_state.Y);
+}
+
+void Spc::TSX()
+{
+ DummyRead();
+ _state.X = _state.SP;
+ SetZeroNegativeFlags(_state.X);
+}
+
+void Spc::TXS()
+{
+ DummyRead();
+ _state.SP = _state.X;
+}
+
+void Spc::MOV()
+{
+ Write(_operandB, (uint8_t)_operandA);
+}
+
+uint8_t Spc::Add(uint8_t a, uint8_t b)
+{
+ uint32_t result = a + b + (_state.PS & SpcFlags::Carry);
+ uint8_t subResult = (a & 0x0F) + (_state.PS & SpcFlags::Carry);
+
+ ClearFlags(SpcFlags::Carry | SpcFlags::Negative | SpcFlags::Zero | SpcFlags::Overflow | SpcFlags::HalfCarry);
+
+ if(~(a ^ b) & (a ^ result) & 0x80) {
+ SetFlags(SpcFlags::Overflow);
+ }
+ if(result > 0xFF) {
+ SetFlags(SpcFlags::Carry);
+ }
+ if(((result & 0x0F) - subResult) & 0x10) {
+ SetFlags(SpcFlags::HalfCarry);
+ }
+ SetZeroNegativeFlags((uint8_t)result);
+
+ return (uint8_t)result;
+}
+
+uint8_t Spc::Sub(uint8_t a, uint8_t b)
+{
+ uint32_t carryCalc = a - b - ((_state.PS & SpcFlags::Carry) ^ 0x01);
+ uint8_t result = Add(a, ~b);
+
+ if(carryCalc <= 0xFF) {
+ SetFlags(SpcFlags::Carry);
+ } else {
+ ClearFlags(SpcFlags::Carry);
+ }
+
+ return (uint8_t)result;
+}
+
+void Spc::ADC()
+{
+ uint8_t val1 = (uint8_t)_operandA;
+ uint8_t val2 = Read(_operandB);
+ Write(_operandB, Add(val2, val1));
+}
+
+void Spc::ADC_Acc()
+{
+ _state.A = Add(_state.A, GetByteValue());
+}
+
+void Spc::ADDW()
+{
+ uint8_t lsb = Read(_operandA);
+ Idle();
+ uint8_t msb = Read(GetDirectAddress(_operandA + 1));
+ uint16_t value = ((msb << 8) | lsb);
+
+ uint8_t lowCarry = (lsb + _state.A) > 0xFF ? 1 : 0;
+ ClearFlags(SpcFlags::Carry | SpcFlags::HalfCarry | SpcFlags::Overflow);
+ if(((_state.Y & 0x0F) + (msb & 0x0F) + lowCarry) & 0x10) {
+ SetFlags(SpcFlags::HalfCarry);
+ }
+
+ uint16_t ya = (_state.Y << 8) | _state.A;
+ uint32_t result = ya + value;
+ if(result > 0xFFFF) {
+ SetFlags(SpcFlags::Carry);
+ }
+ SetZeroNegativeFlags16(result);
+
+ if(~(ya ^ value) & (ya ^ result) & 0x8000) {
+ SetFlags(SpcFlags::Overflow);
+ }
+
+ _state.Y = result >> 8;
+ _state.A = (uint8_t)result;
+}
+
+void Spc::SBC()
+{
+ uint8_t val1 = (uint8_t)_operandA;
+ uint8_t val2 = Read(_operandB);
+ Write(_operandB, Sub(val2, val1));
+}
+
+void Spc::SBC_Acc()
+{
+ _state.A = Sub(_state.A, GetByteValue());
+}
+
+void Spc::SUBW()
+{
+ uint8_t lsb = Read(_operandA);
+ Idle();
+ uint8_t msb = Read(GetDirectAddress(_operandA + 1));
+ uint16_t value = ((msb << 8) | lsb);
+ uint16_t ya = (_state.Y << 8) | _state.A;
+
+ uint32_t l = _state.A - lsb;
+ uint8_t carry = l > 0xFF ? 1 : 0;
+ uint32_t h = _state.Y - msb - carry;
+
+ ClearFlags(SpcFlags::Carry | SpcFlags::HalfCarry | SpcFlags::Overflow);
+ if(h <= 0xFF) {
+ SetFlags(SpcFlags::Carry);
+ }
+
+ if((((_state.Y & 0x0F) - (msb & 0x0F) - carry) & 0x10) == 0) {
+ SetFlags(SpcFlags::HalfCarry);
+ }
+
+ _state.Y = h;
+ _state.A = l;
+
+ uint16_t result = (_state.Y << 8) | _state.A;
+
+ if((ya ^ value) & (ya ^ result) & 0x8000) {
+ SetFlags(SpcFlags::Overflow);
+ }
+
+ SetZeroNegativeFlags16(result);
+}
+
+void Spc::Compare(uint8_t a, uint8_t b)
+{
+ if(a >= b) {
+ SetFlags(SpcFlags::Carry);
+ } else {
+ ClearFlags(SpcFlags::Carry);
+ }
+
+ uint8_t result = a - b;
+ SetZeroNegativeFlags(result);
+}
+
+void Spc::CMP()
+{
+ Compare(Read(_operandB), (uint8_t)_operandA);
+ Idle();
+}
+
+void Spc::CMP_Acc()
+{
+ Compare(_state.A, GetByteValue());
+}
+
+void Spc::CPX()
+{
+ Compare(_state.X, GetByteValue());
+}
+
+void Spc::CPY()
+{
+ Compare(_state.Y, GetByteValue());
+}
+
+void Spc::CMPW()
+{
+ uint8_t lsb = Read(_operandA);
+ uint8_t msb = Read(GetDirectAddress(_operandA + 1));
+ uint16_t value = ((msb << 8) | lsb);
+
+ uint16_t ya = (_state.Y << 8) | _state.A;
+
+ if(ya >= value) {
+ SetFlags(SpcFlags::Carry);
+ } else {
+ ClearFlags(SpcFlags::Carry);
+ }
+
+ uint16_t result = ya - value;
+ SetZeroNegativeFlags16(result);
+}
+
+void Spc::INC()
+{
+ uint8_t result = Read(_operandA) + 1;
+ Write(_operandA, result);
+ SetZeroNegativeFlags(result);
+}
+
+void Spc::INC_Acc()
+{
+ DummyRead();
+ _state.A++;
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::INX()
+{
+ DummyRead();
+ _state.X++;
+ SetZeroNegativeFlags(_state.X);
+}
+
+void Spc::INY()
+{
+ DummyRead();
+ _state.Y++;
+ SetZeroNegativeFlags(_state.Y);
+}
+
+void Spc::INCW()
+{
+ uint8_t lsb = Read(_operandA);
+ Write(_operandA, lsb + 1);
+
+ uint16_t msbAddress = GetDirectAddress(_operandA + 1);
+ uint8_t msb = Read(msbAddress);
+ uint16_t value = ((msb << 8) | lsb) + 1;
+ Write(msbAddress, value >> 8);
+ SetZeroNegativeFlags16(value);
+}
+
+void Spc::DEC()
+{
+ uint8_t result = Read(_operandA) - 1;
+ Write(_operandA, result);
+ SetZeroNegativeFlags(result);
+}
+
+void Spc::DEC_Acc()
+{
+ DummyRead();
+ _state.A--;
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::DEX()
+{
+ DummyRead();
+ _state.X--;
+ SetZeroNegativeFlags(_state.X);
+}
+
+void Spc::DEY()
+{
+ DummyRead();
+ _state.Y--;
+ SetZeroNegativeFlags(_state.Y);
+}
+
+void Spc::DECW()
+{
+ uint8_t lsb = Read(_operandA);
+ Write(_operandA, lsb - 1);
+
+ uint16_t msbAddress = GetDirectAddress(_operandA + 1);
+ uint8_t msb = Read(msbAddress);
+ uint16_t value = ((msb << 8) | lsb) - 1;
+ Write(msbAddress, value >> 8);
+ SetZeroNegativeFlags16(value);
+}
+
+void Spc::MUL()
+{
+ uint16_t result = _state.Y * _state.A;
+ _state.Y = result >> 8;
+ _state.A = (uint8_t)result;
+
+ DummyRead();
+ Idle();
+ Idle();
+ Idle();
+ Idle();
+ Idle();
+ Idle();
+ Idle();
+
+ SetZeroNegativeFlags(_state.Y);
+}
+
+void Spc::DIV()
+{
+ uint32_t ya = (_state.Y << 8) | _state.A;
+ uint32_t sub = _state.X << 9;
+
+ DummyRead();
+
+ for(int i = 0; i < 9; i++) {
+ if(ya & 0x10000) {
+ ya = ((ya << 1) | 0x01) & 0x1FFFF;
+ } else {
+ ya = (ya << 1) & 0x1FFFF;
+ }
+
+ if(ya >= sub) {
+ ya ^= 0x01;
+ }
+
+ if(ya & 0x01) {
+ ya = (ya - sub) & 0x1FFFF;
+ }
+
+ Idle();
+ }
+
+ //12 cycles total
+ Idle();
+
+ if((_state.Y & 0x0F) >= (_state.X & 0x0F)) {
+ SetFlags(SpcFlags::HalfCarry);
+ } else {
+ ClearFlags(SpcFlags::HalfCarry);
+ }
+
+ _state.A = (uint8_t)ya;
+ _state.Y = ya >> 9;
+
+ if(ya & 0x100) {
+ SetFlags(SpcFlags::Overflow);
+ } else {
+ ClearFlags(SpcFlags::Overflow);
+ }
+
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::DAA()
+{
+ Idle();
+ Idle();
+
+ if(CheckFlag(SpcFlags::Carry) || _state.A > 0x99) {
+ _state.A += 0x60;
+ SetFlags(SpcFlags::Carry);
+ }
+
+ if(CheckFlag(SpcFlags::HalfCarry) || ((_state.A & 0x0F) > 9)) {
+ _state.A += 6;
+ }
+
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::DAS()
+{
+ Idle();
+ Idle();
+
+ if(!CheckFlag(SpcFlags::Carry) || _state.A > 0x99) {
+ _state.A -= 0x60;
+ ClearFlags(SpcFlags::Carry);
+ }
+
+ if(!CheckFlag(SpcFlags::HalfCarry) || ((_state.A & 0x0F) > 9)) {
+ _state.A -= 6;
+ }
+
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::AND()
+{
+ uint8_t result = _operandA & Read(_operandB);
+ Write(_operandB, result);
+ SetZeroNegativeFlags(result);
+}
+
+void Spc::AND_Acc()
+{
+ _state.A &= GetByteValue();
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::OR()
+{
+ uint8_t result = _operandA | Read(_operandB);
+ Write(_operandB, result);
+ SetZeroNegativeFlags(result);
+}
+
+void Spc::OR_Acc()
+{
+ _state.A |= GetByteValue();
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::EOR()
+{
+ uint8_t result = _operandA ^ Read(_operandB);
+ Write(_operandB, result);
+ SetZeroNegativeFlags(result);
+}
+
+void Spc::EOR_Acc()
+{
+ _state.A ^= GetByteValue();
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::SetCarry(uint8_t carry)
+{
+ _state.PS = (_state.PS & ~SpcFlags::Carry) | carry;
+}
+
+void Spc::OR1()
+{
+ uint8_t carry = _state.PS & SpcFlags::Carry;
+ carry |= (Read(_operandA) >> _operandB) & 0x01;
+ SetCarry(carry);
+ Idle();
+}
+
+void Spc::NOR1()
+{
+ uint8_t carry = _state.PS & SpcFlags::Carry;
+ carry |= ~((Read(_operandA) >> _operandB)) & 0x01;
+ SetCarry(carry);
+ Idle();
+}
+
+void Spc::AND1()
+{
+ uint8_t carry = _state.PS & SpcFlags::Carry;
+ carry &= (Read(_operandA) >> _operandB) & 0x01;
+ SetCarry(carry);
+}
+
+void Spc::NAND1()
+{
+ uint8_t carry = _state.PS & SpcFlags::Carry;
+ carry &= ~((Read(_operandA) >> _operandB)) & 0x01;
+ SetCarry(carry);
+}
+
+void Spc::EOR1()
+{
+ uint8_t carry = _state.PS & SpcFlags::Carry;
+ carry ^= (Read(_operandA) >> _operandB) & 0x01;
+ SetCarry(carry);
+ Idle();
+}
+
+void Spc::NOT1()
+{
+ uint8_t value = Read(_operandA);
+ uint8_t mask = (1 << _operandB);
+ Write(_operandA, value ^ mask);
+}
+
+void Spc::STC()
+{
+ uint8_t value = Read(_operandA);
+ uint8_t mask = (1 << _operandB);
+ uint8_t carry = (_state.PS & SpcFlags::Carry) << _operandB;
+ Idle();
+ Write(_operandA, (value & ~mask) | carry);
+}
+
+void Spc::LDC()
+{
+ uint8_t carry = (Read(_operandA) >> _operandB) & 0x01;
+ SetCarry(carry);
+}
+
+uint8_t Spc::ShiftLeft(uint8_t value)
+{
+ uint8_t result = value << 1;
+ if(value & (1 << 7)) {
+ SetFlags(SpcFlags::Carry);
+ } else {
+ ClearFlags(SpcFlags::Carry);
+ }
+ SetZeroNegativeFlags(result);
+ return result;
+}
+
+uint8_t Spc::RollLeft(uint8_t value)
+{
+ uint8_t result = value << 1 | (_state.PS & SpcFlags::Carry);
+ if(value & (1 << 7)) {
+ SetFlags(SpcFlags::Carry);
+ } else {
+ ClearFlags(SpcFlags::Carry);
+ }
+ SetZeroNegativeFlags(result);
+ return result;
+}
+
+uint8_t Spc::ShiftRight(uint8_t value)
+{
+ uint8_t result = value >> 1;
+ if(value & 0x01) {
+ SetFlags(SpcFlags::Carry);
+ } else {
+ ClearFlags(SpcFlags::Carry);
+ }
+ SetZeroNegativeFlags(result);
+ return result;
+}
+
+uint8_t Spc::RollRight(uint8_t value)
+{
+ uint8_t result = value >> 1 | ((_state.PS & 0x01) << 7);
+ if(value & 0x01) {
+ SetFlags(SpcFlags::Carry);
+ } else {
+ ClearFlags(SpcFlags::Carry);
+ }
+ SetZeroNegativeFlags(result);
+ return result;
+}
+
+void Spc::ASL()
+{
+ uint8_t result = ShiftLeft(Read(_operandA));
+ Write(_operandA, result);
+}
+
+void Spc::ASL_Acc()
+{
+ DummyRead();
+ _state.A = ShiftLeft(_state.A);
+}
+
+void Spc::LSR()
+{
+ uint8_t result = ShiftRight(Read(_operandA));
+ Write(_operandA, result);
+}
+
+void Spc::LSR_Acc()
+{
+ DummyRead();
+ _state.A = ShiftRight(_state.A);
+}
+
+void Spc::ROL()
+{
+ uint8_t result = RollLeft(Read(_operandA));
+ Write(_operandA, result);
+}
+
+void Spc::ROL_Acc()
+{
+ DummyRead();
+ _state.A = RollLeft(_state.A);
+}
+
+void Spc::ROR()
+{
+ uint8_t result = RollRight(Read(_operandA));
+ Write(_operandA, result);
+}
+
+void Spc::ROR_Acc()
+{
+ DummyRead();
+ _state.A = RollRight(_state.A);
+}
+
+void Spc::XCN()
+{
+ DummyRead();
+ Idle();
+ Idle();
+ Idle();
+ _state.A = (_state.A >> 4) | (_state.A << 4);
+ SetZeroNegativeFlags(_state.A);
+}
+
+void Spc::Branch()
+{
+ Idle();
+ Idle();
+ int8_t offset = (int8_t)_operandA;
+ _state.PC = _state.PC + offset;
+}
+
+void Spc::BRA()
+{
+ Branch();
+}
+
+void Spc::BEQ()
+{
+ if(CheckFlag(SpcFlags::Zero)) {
+ Branch();
+ }
+}
+
+void Spc::BNE()
+{
+ if(!CheckFlag(SpcFlags::Zero)) {
+ Branch();
+ }
+}
+
+void Spc::BCS()
+{
+ if(CheckFlag(SpcFlags::Carry)) {
+ Branch();
+ }
+}
+
+void Spc::BCC()
+{
+ if(!CheckFlag(SpcFlags::Carry)) {
+ Branch();
+ }
+}
+
+void Spc::BVS()
+{
+ if(CheckFlag(SpcFlags::Overflow)) {
+ Branch();
+ }
+}
+
+void Spc::BVC()
+{
+ if(!CheckFlag(SpcFlags::Overflow)) {
+ Branch();
+ }
+}
+
+void Spc::BMI()
+{
+ if(CheckFlag(SpcFlags::Negative)) {
+ Branch();
+ }
+}
+
+void Spc::BPL()
+{
+ if(!CheckFlag(SpcFlags::Negative)) {
+ Branch();
+ }
+}
+
+template
+void Spc::SET1()
+{
+ uint8_t value = Read(_operandA);
+ Write(_operandA, value | (1 << bit));
+}
+
+template
+void Spc::CLR1()
+{
+ uint8_t value = Read(_operandA);
+ Write(_operandA, value & ~(1 << bit));
+}
+
+template
+void Spc::BBS()
+{
+ uint8_t compareValue = Read(_operandA);
+ Idle();
+
+ int8_t offset = ReadOperandByte();
+ if(compareValue & (1 << bit)) {
+ Idle();
+ Idle();
+ _state.PC += offset;
+ }
+}
+
+template
+void Spc::BBC()
+{
+ uint8_t compareValue = Read(_operandA);
+ Idle();
+
+ int8_t offset = ReadOperandByte();
+ if((compareValue & (1 << bit)) == 0) {
+ Idle();
+ Idle();
+ _state.PC += offset;
+ }
+}
+
+void Spc::CBNE()
+{
+ uint8_t compareValue = Read(_operandA);
+ Idle();
+
+ int8_t offset = ReadOperandByte();
+ if(_state.A != compareValue) {
+ Idle();
+ Idle();
+ _state.PC = _state.PC + offset;
+ }
+}
+
+void Spc::DBNZ()
+{
+ uint8_t value = Read(_operandA);
+
+ value--;
+ Write(_operandA, value);
+
+ int8_t offset = ReadOperandByte();
+ if(value) {
+ Idle();
+ Idle();
+ _state.PC = _state.PC + offset;
+ }
+}
+
+void Spc::DBNZ_Y()
+{
+ DummyRead();
+ Idle();
+
+ _state.Y--;
+
+ int8_t offset = ReadOperandByte();
+ if(_state.Y) {
+ Idle();
+ Idle();
+ _state.PC = _state.PC + offset;
+ }
+}
+
+
+void Spc::JMP()
+{
+ _state.PC = _operandA;
+}
+
+void Spc::NOTC()
+{
+ DummyRead();
+ Idle();
+ if(CheckFlag(SpcFlags::Carry)) {
+ ClearFlags(SpcFlags::Carry);
+ } else {
+ SetFlags(SpcFlags::Carry);
+ }
+}
+
+void Spc::CLRC()
+{
+ DummyRead();
+ ClearFlags(SpcFlags::Carry);
+}
+
+void Spc::CLRP()
+{
+ DummyRead();
+ ClearFlags(SpcFlags::DirectPage);
+}
+
+void Spc::CLRV()
+{
+ DummyRead();
+ ClearFlags(SpcFlags::Overflow | SpcFlags::HalfCarry);
+}
+
+void Spc::SETC()
+{
+ DummyRead();
+ SetFlags(SpcFlags::Carry);
+}
+
+void Spc::SETP()
+{
+ DummyRead();
+ SetFlags(SpcFlags::DirectPage);
+}
+
+void Spc::EI()
+{
+ DummyRead();
+ SetFlags(SpcFlags::IrqEnable);
+ Idle();
+}
+
+void Spc::DI()
+{
+ DummyRead();
+ ClearFlags(SpcFlags::IrqEnable);
+ Idle();
+}
+
+void Spc::TSET1()
+{
+ uint8_t value = Read(_operandA);
+ DummyRead(_operandA);
+
+ Write(_operandA, value | _state.A);
+ SetZeroNegativeFlags(_state.A - value);
+}
+
+void Spc::TCLR1()
+{
+ uint8_t value = Read(_operandA);
+ DummyRead(_operandA);
+
+ Write(_operandA, value & ~_state.A);
+ SetZeroNegativeFlags(_state.A - value);
+}
+
+template
+void Spc::TCALL()
+{
+ DummyRead();
+ Idle();
+ PushWord(_state.PC);
+ Idle();
+
+ constexpr uint16_t vectorAddr = 0xFFDE - (offset * 2);
+ _state.PC = ReadWord(vectorAddr);
+}
+
+void Spc::PCALL()
+{
+ uint8_t offset = ReadOperandByte();
+ Idle();
+ PushWord(_state.PC);
+
+ Idle();
+ _state.PC = 0xFF00 | offset;
+}
+
+void Spc::JSR()
+{
+ Idle();
+ PushWord(_state.PC);
+ Idle();
+ Idle();
+ _state.PC = _operandA;
+}
+
+void Spc::RTS()
+{
+ DummyRead();
+ Idle();
+ _state.PC = PopWord();
+}
+
+void Spc::RTI()
+{
+ DummyRead();
+ Idle();
+ _state.PS = Pop();
+ _state.PC = PopWord();
+}
+
+void Spc::BRK()
+{
+ DummyRead();
+ PushWord(_state.PC);
+ Push(_state.PS);
+ Idle();
+
+ uint8_t lsb = Read(0xFFDE);
+ uint8_t msb = Read(0xFFDF);
+ _state.PC = (msb << 8) | lsb;
+
+ SetFlags(SpcFlags::Break);
+ ClearFlags(SpcFlags::IrqEnable);
+}
+
+void Spc::PHA()
+{
+ DummyRead();
+ Push(_state.A);
+ Idle();
+}
+
+void Spc::PHX()
+{
+ DummyRead();
+ Push(_state.X);
+ Idle();
+}
+
+void Spc::PHY()
+{
+ DummyRead();
+ Push(_state.Y);
+ Idle();
+}
+
+void Spc::PHP()
+{
+ DummyRead();
+ Push(_state.PS);
+ Idle();
+}
+
+void Spc::PLA()
+{
+ DummyRead();
+ Idle();
+ _state.A = Pop();
+}
+
+void Spc::PLX()
+{
+ DummyRead();
+ Idle();
+ _state.X = Pop();
+}
+
+void Spc::PLY()
+{
+ DummyRead();
+ Idle();
+ _state.Y = Pop();
+}
+
+void Spc::PLP()
+{
+ DummyRead();
+ Idle();
+ _state.PS = Pop();
+}
+
+void Spc::NOP()
+{
+ DummyRead();
+}
+
+void Spc::SLEEP()
+{
+ //WAI
+}
+
+void Spc::STOP()
+{
+ //STP
+}
diff --git a/Core/Spc.cpp b/Core/Spc.cpp
index 92a159b..c48dddb 100644
--- a/Core/Spc.cpp
+++ b/Core/Spc.cpp
@@ -1,92 +1,353 @@
#include "stdafx.h"
#include "Spc.h"
-#include "SNES_SPC.h"
#include "Console.h"
#include "MemoryManager.h"
#include "SoundMixer.h"
+#include "SPC_DSP.h"
#include "../Utilities/Serializer.h"
Spc::Spc(shared_ptr console, vector &spcRomData)
{
_console = console;
-
- memcpy(_spcBios, spcRomData.data(), 64);
-
_soundBuffer = new int16_t[Spc::SampleBufferSize];
+ _immediateMode = false;
+ _operandA = 0;
+ _operandB = 0;
- _spc = new SNES_SPC();
- _spc->init();
- _spc->init_rom(_spcBios);
- _spc->reset();
- _spc->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
+ _dsp.reset(new SPC_DSP());
+ _dsp->init(_ram);
+ _dsp->reset();
+ _dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
- _startFrameMasterClock = 0;
+ memcpy(_spcBios, spcRomData.data(), 64);
+ memset(_ram, 0, sizeof(_ram));
+
+ _state = {};
+ _state.WriteEnabled = true;
+ _state.TimersEnabled = true;
+ _state.RomEnabled = true;
+ _state.SP = 0xFF;
+ _state.PC = ReadWord(Spc::ResetVector);
}
Spc::~Spc()
{
- delete _spc;
+ delete[] _soundBuffer;
}
void Spc::Reset()
{
- _spc->soft_reset();
- _spc->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
- _startFrameMasterClock = _console->GetMemoryManager()->GetMasterClock();
+ _state.Timer0.Reset();
+ _state.Timer1.Reset();
+ _state.Timer2.Reset();
+ _state.PC = ReadWord(Spc::ResetVector);
+
+ _dsp->soft_reset();
+ _dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
}
-int Spc::GetSpcTime()
+void Spc::Idle()
{
- uint64_t currentClock = _console->GetMemoryManager()->GetMasterClock();
- uint64_t elapsedClocks = currentClock - _startFrameMasterClock;
- return (int)(elapsedClocks * 1024000 / _console->GetMasterClockRate());
+ IncCycleCount(-1);
}
-uint8_t Spc::Read(uint16_t addr)
+void Spc::DummyRead()
{
- return _spc->read_port(GetSpcTime(), addr & 0x03);
+ Read(_state.PC);
}
-void Spc::Write(uint32_t addr, uint8_t value)
+void Spc::DummyRead(uint16_t addr)
{
- _spc->write_port(GetSpcTime(), addr & 0x03, value);
+ Read(addr);
+}
+
+void Spc::IncCycleCount(int32_t addr)
+{
+ static constexpr uint8_t cpuWait[4] = { 2, 4, 10, 20 };
+ static constexpr uint8_t timerMultiplier[4] = { 2, 4, 8, 16 };
+
+ uint8_t speedSelect;
+ if(addr < 0 || ((addr & 0xFFF0) == 0x00F0) || (addr >= 0xFFC0 && _state.RomEnabled)) {
+ //Use internal speed (bits 4-5) for idle cycles, register access or IPL rom access
+ speedSelect = _state.InternalSpeed;
+ } else {
+ speedSelect = _state.ExternalSpeed;
+ }
+
+ _state.Cycle += cpuWait[speedSelect];
+ _dsp->run();
+
+ uint8_t timerInc = timerMultiplier[speedSelect];
+ _state.Timer0.Run(timerInc);
+ _state.Timer1.Run(timerInc);
+ _state.Timer2.Run(timerInc);
+}
+
+uint8_t Spc::Read(uint16_t addr, MemoryOperationType type)
+{
+ IncCycleCount(addr);
+
+ if(addr >= 0xFFC0 && _state.RomEnabled) {
+ return _spcBios[addr & 0x3F];
+ }
+
+ switch(addr) {
+ case 0xF0: return 0;
+ case 0xF1: return 0;
+
+ case 0xF2: return _state.DspReg;
+ case 0xF3: return _dsp->read(_state.DspReg & 0x7F);
+
+ case 0xF4: return _state.CpuRegs[0];
+ case 0xF5: return _state.CpuRegs[1];
+ case 0xF6: return _state.CpuRegs[2];
+ case 0xF7: return _state.CpuRegs[3];
+
+ case 0xF8: return _state.RamReg[0];
+ case 0xF9: return _state.RamReg[1];
+
+ case 0xFA: return 0;
+ case 0xFB: return 0;
+ case 0xFC: return 0;
+
+ case 0xFD: return _state.Timer0.GetOutput();
+ case 0xFE: return _state.Timer1.GetOutput();
+ case 0xFF: return _state.Timer2.GetOutput();
+ }
+
+ return _ram[addr];
+}
+
+void Spc::Write(uint16_t addr, uint8_t value, MemoryOperationType type)
+{
+ IncCycleCount(addr);
+
+ //Writes always affect the underlying RAM
+ if(_state.WriteEnabled) {
+ _ram[addr] = value;
+ }
+
+ switch(addr) {
+ case 0xF0:
+ if(!CheckFlag(SpcFlags::DirectPage)) {
+ _state.InternalSpeed = (value >> 6) & 0x03;
+ _state.ExternalSpeed = (value >> 4) & 0x03;
+ _state.TimersEnabled = (value & 0x09) == 0x08;
+ _state.WriteEnabled = value & 0x02;
+
+ _dsp->setEchoWriteEnabled(_state.WriteEnabled);
+
+ _state.Timer0.SetGlobalEnabled(_state.TimersEnabled);
+ _state.Timer1.SetGlobalEnabled(_state.TimersEnabled);
+ _state.Timer2.SetGlobalEnabled(_state.TimersEnabled);
+ }
+ break;
+
+ case 0xF1:
+ if(value & SpcControlFlags::ClearPortsA) {
+ _state.CpuRegs[0] = _state.CpuRegs[1] = 0;
+ }
+ if(value & SpcControlFlags::ClearPortsB) {
+ _state.CpuRegs[2] = _state.CpuRegs[3] = 0;
+ }
+
+ _state.Timer0.SetEnabled((value & SpcControlFlags::Timer0) != 0);
+ _state.Timer1.SetEnabled((value & SpcControlFlags::Timer1) != 0);
+ _state.Timer2.SetEnabled((value & SpcControlFlags::Timer2) != 0);
+
+ _state.RomEnabled = (value & SpcControlFlags::EnableRom) != 0;
+ break;
+
+ case 0xF2: _state.DspReg = value; break;
+ case 0xF3:
+ if(_state.DspReg < 128) {
+ _dsp->write(_state.DspReg, value);
+ }
+ break;
+
+ case 0xF4: _state.OutputReg[0] = value; break;
+ case 0xF5: _state.OutputReg[1] = value; break;
+ case 0xF6: _state.OutputReg[2] = value; break;
+ case 0xF7: _state.OutputReg[3] = value; break;
+ case 0xF8: _state.RamReg[0] = value; break;
+ case 0xF9: _state.RamReg[1] = value; break;
+
+ case 0xFA: _state.Timer0.SetTarget(value); break;
+ case 0xFB: _state.Timer1.SetTarget(value); break;
+ case 0xFC: _state.Timer2.SetTarget(value); break;
+
+ case 0xFD: break;
+ case 0xFE: break;
+ case 0xFF: break;
+ }
+}
+
+uint8_t Spc::CpuReadRegister(uint16_t addr)
+{
+ Run();
+ return _state.OutputReg[addr & 0x03];
+}
+
+void Spc::CpuWriteRegister(uint32_t addr, uint8_t value)
+{
+ Run();
+ _state.CpuRegs[addr & 0x03] = value;
+}
+
+void Spc::Run()
+{
+ int64_t masterClock = _console->GetMemoryManager()->GetMasterClock();
+ //TODO: This will overflow after 100+ hours, needs to be fixed
+ uint64_t targetCycle = (masterClock * (uint64_t)1024000 / (uint64_t)_console->GetMasterClockRate()) * 2;
+ while(_state.Cycle < targetCycle) {
+ Exec();
+ }
}
void Spc::ProcessEndFrame()
{
- _spc->end_frame(GetSpcTime());
+ Run();
- int sampleCount = _spc->sample_count();
+ int sampleCount = _dsp->sample_count();
_console->GetSoundMixer()->PlayAudioBuffer(_soundBuffer, sampleCount / 2);
- _spc->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
-
- uint64_t remainder = (_console->GetMemoryManager()->GetMasterClock() - _startFrameMasterClock) * 1024000 % _console->GetMasterClockRate() / 1024000;
- _startFrameMasterClock = _console->GetMemoryManager()->GetMasterClock() - remainder;
+ _dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
}
void Spc::Serialize(Serializer &s)
{
- s.Stream(_startFrameMasterClock);
+ s.Stream(_state.A, _state.Cycle, _state.PC, _state.PS, _state.SP, _state.X, _state.Y);
+ s.Stream(_state.CpuRegs[0], _state.CpuRegs[1], _state.CpuRegs[2], _state.CpuRegs[3]);
+ s.Stream(_state.OutputReg[0], _state.OutputReg[1], _state.OutputReg[2], _state.OutputReg[3]);
+ s.Stream(_state.RamReg[0], _state.RamReg[1]);
+ s.Stream(_state.ExternalSpeed, _state.InternalSpeed, _state.WriteEnabled, _state.TimersEnabled);
+ s.Stream(_state.DspReg, _state.RomEnabled);
- uint8_t state[SNES_SPC::state_size];
- memset(state, 0, SNES_SPC::state_size);
+ _state.Timer0.Serialize(s);
+ _state.Timer1.Serialize(s);
+ _state.Timer2.Serialize(s);
+
+ ArrayInfo ram { _ram, Spc::SpcRamSize };
+ s.Stream(ram, _state.DspReg, _state.RomEnabled);
+
+ uint8_t dspState[SPC_DSP::state_size];
+ memset(dspState, 0, SPC_DSP::state_size);
if(s.IsSaving()) {
- uint8_t *out = state;
- _spc->copy_state(&out, [](uint8_t** output, void* in, size_t size) {
+ uint8_t *out = dspState;
+ _dsp->copy_state(&out, [](uint8_t** output, void* in, size_t size) {
memcpy(*output, in, size);
*output += size;
});
- s.StreamArray(state, SNES_SPC::state_size);
+ s.StreamArray(dspState, SPC_DSP::state_size);
} else {
- s.StreamArray(state, SNES_SPC::state_size);
+ s.StreamArray(dspState, SPC_DSP::state_size);
- uint8_t *in = state;
- _spc->copy_state(&in, [](uint8_t** input, void* output, size_t size) {
+ uint8_t *in = dspState;
+ _dsp->copy_state(&in, [](uint8_t** input, void* output, size_t size) {
memcpy(output, *input, size);
*input += size;
});
- _spc->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
+ _dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
}
}
+
+uint8_t Spc::GetOpCode()
+{
+ return Read(_state.PC++, MemoryOperationType::ExecOpCode);
+}
+
+uint8_t Spc::ReadOperandByte()
+{
+ return Read(_state.PC++, MemoryOperationType::ExecOperand);
+}
+
+uint16_t Spc::ReadWord(uint16_t addr, MemoryOperationType type)
+{
+ uint8_t lsb = Read(addr, type);
+ uint8_t msb = Read(addr + 1, type);
+ return (msb << 8) | lsb;
+}
+
+void Spc::ClearFlags(uint8_t flags)
+{
+ _state.PS &= ~flags;
+}
+
+void Spc::SetFlags(uint8_t flags)
+{
+ _state.PS |= flags;
+}
+
+bool Spc::CheckFlag(uint8_t flag)
+{
+ return (_state.PS & flag) == flag;
+}
+
+void Spc::SetZeroNegativeFlags(uint8_t value)
+{
+ ClearFlags(SpcFlags::Zero | SpcFlags::Negative);
+ if(value == 0) {
+ SetFlags(SpcFlags::Zero);
+ } else if(value & 0x80) {
+ SetFlags(SpcFlags::Negative);
+ }
+}
+
+void Spc::SetZeroNegativeFlags16(uint16_t value)
+{
+ ClearFlags(SpcFlags::Zero | SpcFlags::Negative);
+ if(value == 0) {
+ SetFlags(SpcFlags::Zero);
+ } else if(value & 0x8000) {
+ SetFlags(SpcFlags::Negative);
+ }
+}
+
+uint8_t Spc::GetByteValue()
+{
+ if(_immediateMode) {
+ return (uint8_t)_operandA;
+ } else {
+ return Read(_operandA);
+ }
+}
+
+uint16_t Spc::GetWordValue()
+{
+ if(_immediateMode) {
+ return (uint16_t)_operandA;
+ } else {
+ return ReadWord(_operandA);
+ }
+}
+
+void Spc::Push(uint8_t value)
+{
+ Write(0x100 | _state.SP, value);
+ _state.SP--;
+}
+
+uint8_t Spc::Pop()
+{
+ _state.SP++;
+ return Read(0x100 | _state.SP);
+}
+
+void Spc::PushWord(uint16_t value)
+{
+ Push(value >> 8);
+ Push((uint8_t)value);
+}
+
+uint16_t Spc::PopWord()
+{
+ uint8_t lo = Pop();
+ uint8_t hi = Pop();
+ return lo | hi << 8;
+}
+
+uint16_t Spc::GetDirectAddress(uint8_t offset)
+{
+ return (CheckFlag(SpcFlags::DirectPage) ? 0x100 : 0) + offset;
+}
diff --git a/Core/Spc.h b/Core/Spc.h
index 2528c2b..2122a56 100644
--- a/Core/Spc.h
+++ b/Core/Spc.h
@@ -1,31 +1,245 @@
#pragma once
#include "stdafx.h"
+#include "SpcTypes.h"
+#include "CpuTypes.h"
+#include "SpcTimer.h"
#include "../Utilities/ISerializable.h"
class Console;
-struct SNES_SPC;
+class SPC_DSP;
class Spc : public ISerializable
{
private:
static constexpr int SampleBufferSize = 0x100000;
+ static constexpr int SpcRamSize = 0x10000;
+ static constexpr uint16_t ResetVector = 0xFFFE;
shared_ptr _console;
- uint8_t _spcBios[64];
- SNES_SPC* _spc;
- int16_t *_soundBuffer;
- uint64_t _startFrameMasterClock = 0;
+ unique_ptr _dsp;
- int GetSpcTime();
+ bool _immediateMode;
+ uint16_t _operandA;
+ uint16_t _operandB;
+
+ SpcState _state;
+ uint8_t _ram[Spc::SpcRamSize];
+ uint8_t _spcBios[64];
+
+ int16_t *_soundBuffer;
+ //Store operations
+ void STA();
+ void STX();
+ void STY();
+ void STW();
+
+ void STA_AutoIncX();
+ void LDA_AutoIncX();
+
+ //Load operations
+ void LDA();
+ void LDX();
+ void LDY();
+ void LDW();
+
+ //Transfer
+ void TXA();
+ void TYA();
+ void TAX();
+ void TAY();
+ void TSX();
+ void TXS();
+
+ void STC();
+ void LDC();
+ void MOV();
+
+ //Arithmetic
+ uint8_t Add(uint8_t a, uint8_t b);
+ uint8_t Sub(uint8_t a, uint8_t b);
+
+ void ADC();
+ void ADC_Acc();
+ void ADDW();
+ void SBC();
+ void SBC_Acc();
+ void SUBW();
+
+ void Compare(uint8_t a, uint8_t b);
+ void CMP();
+ void CMP_Acc();
+ void CPX();
+ void CPY();
+ void CMPW();
+
+ //Increment/decrement
+ void INC();
+ void INC_Acc();
+ void INX();
+ void INY();
+ void INCW();
+
+ void DEC();
+ void DEC_Acc();
+ void DEX();
+ void DEY();
+ void DECW();
+
+ void MUL();
+ void DIV();
+
+ //Decimal
+ void DAA();
+ void DAS();
+
+ //Logical
+ void AND();
+ void AND_Acc();
+ void OR();
+ void OR_Acc();
+ void EOR();
+ void EOR_Acc();
+
+ void SetCarry(uint8_t carry);
+ void OR1();
+ void NOR1();
+ void AND1();
+ void NAND1();
+ void EOR1();
+ void NOT1();
+
+ //Shift/rotate
+ uint8_t ShiftLeft(uint8_t value);
+ uint8_t RollLeft(uint8_t value);
+ uint8_t ShiftRight(uint8_t value);
+ uint8_t RollRight(uint8_t value);
+
+ void ASL();
+ void ASL_Acc();
+ void LSR();
+ void LSR_Acc();
+ void ROL();
+ void ROL_Acc();
+ void ROR();
+ void ROR_Acc();
+ void XCN();
+
+ //Branch operations
+ void Branch();
+ void BRA();
+ void BEQ();
+ void BNE();
+ void BCS();
+ void BCC();
+ void BVS();
+ void BVC();
+ void BMI();
+ void BPL();
+ void BBS();
+ void BBC();
+ void CBNE();
+ void DBNZ();
+ void DBNZ_Y();
+ void JMP();
+
+ //Flag operations
+ void CLRC();
+ void SETC();
+ void NOTC();
+ void CLRV();
+ void CLRP();
+ void SETP();
+ void EI();
+ void DI();
+
+ void TSET1();
+ void TCLR1();
+
+ template void SET1();
+ template void CLR1();
+ template void BBS();
+ template void BBC();
+
+ //Subroutine operations
+ void PCALL();
+ void JSR();
+ void RTS();
+ void BRK();
+ void RTI();
+
+ template void TCALL();
+
+ //Stack operations
+ void PHA();
+ void PHX();
+ void PHY();
+ void PHP();
+
+ void PLA();
+ void PLX();
+ void PLY();
+ void PLP();
+
+ //Other operations
+ void NOP();
+ void SLEEP();
+ void STOP();
+
+ void Idle();
+ void DummyRead();
+ void DummyRead(uint16_t addr);
+ uint8_t Read(uint16_t addr, MemoryOperationType type = MemoryOperationType::Read);
+ uint16_t ReadWord(uint16_t addr, MemoryOperationType type = MemoryOperationType::Read);
+ void Write(uint16_t addr, uint8_t value, MemoryOperationType type = MemoryOperationType::Write);
+
+ void Addr_Dir();
+ void Addr_DirIdxX();
+ void Addr_DirIdxY();
+ void Addr_DirToDir();
+ void Addr_DirImm();
+ void Addr_IndX();
+ void Addr_IndXToIndY();
+ void Addr_Abs();
+ void Addr_AbsBit();
+ void Addr_AbsIdxX();
+ void Addr_AbsIdxY();
+ void Addr_AbsIdxXInd();
+ void Addr_DirIdxXInd();
+ void Addr_DirIndIdxY();
+ void Addr_Rel();
+ void Addr_Imm();
+
+ void ClearFlags(uint8_t flags);
+ void SetFlags(uint8_t flags);
+ bool CheckFlag(uint8_t flag);
+ void SetZeroNegativeFlags(uint8_t value);
+ void SetZeroNegativeFlags16(uint16_t value);
+
+ uint8_t GetByteValue();
+ uint16_t GetWordValue();
+
+ void Push(uint8_t value);
+ uint8_t Pop();
+ void PushWord(uint16_t value);
+ uint16_t PopWord();
+
+ uint16_t GetDirectAddress(uint8_t offset);
+
+ uint8_t GetOpCode();
+ uint8_t ReadOperandByte();
+
+ void IncCycleCount(int32_t addr);
+ void Exec();
public:
Spc(shared_ptr console, vector &spcRomData);
~Spc();
+ void Run();
void Reset();
- uint8_t Read(uint16_t addr);
- void Write(uint32_t addr, uint8_t value);
+ uint8_t CpuReadRegister(uint16_t addr);
+ void CpuWriteRegister(uint32_t addr, uint8_t value);
void ProcessEndFrame();
diff --git a/Core/SpcTimer.h b/Core/SpcTimer.h
new file mode 100644
index 0000000..144211f
--- /dev/null
+++ b/Core/SpcTimer.h
@@ -0,0 +1,87 @@
+#pragma once
+#include "stdafx.h"
+#include "../Utilities/Serializer.h"
+
+template
+class SpcTimer
+{
+private:
+ bool _enabled = false;
+ bool _timersEnabled = true;
+ uint8_t _output = 0x0F; //"On power on, all three TnOUT have the value $F. On reset, they are $0."
+ uint8_t _stage0 = 0;
+ uint8_t _stage1 = 0;
+ uint8_t _prevStage1 = 0;
+ uint8_t _stage2 = 0;
+ uint8_t _target = 0;
+
+ void ClockTimer()
+ {
+ uint8_t currentState = _stage1;
+ if(!_timersEnabled) {
+ //All timers are disabled
+ currentState = 0;
+ }
+
+ uint8_t prevState = _prevStage1;
+ _prevStage1 = currentState;
+ if(!_enabled || !prevState || currentState) {
+ //Only clock on 1->0 transitions, when the timer is enabled
+ return;
+ }
+
+ if(++_stage2 == _target) {
+ _stage2 = 0;
+ _output++;
+ }
+ }
+
+public:
+ void Reset()
+ {
+ _output = 0;
+ }
+
+ void SetEnabled(bool enabled)
+ {
+ if(!_enabled && enabled) {
+ _stage2 = 0;
+ _output = 0;
+ }
+ _enabled = enabled;
+ }
+
+ void SetGlobalEnabled(bool enabled)
+ {
+ _timersEnabled = enabled;
+ ClockTimer();
+ }
+
+ void Run(uint8_t step)
+ {
+ _stage0 += step;
+ if(_stage0 >= rate) {
+ _stage1 ^= 0x01;
+ _stage0 -= rate;
+
+ ClockTimer();
+ }
+ }
+
+ void SetTarget(uint8_t target)
+ {
+ _target = target;
+ }
+
+ uint8_t GetOutput()
+ {
+ uint8_t value = _output & 0x0F;
+ _output = 0;
+ return value;
+ }
+
+ void Serialize(Serializer &s)
+ {
+ s.Stream(_stage0, _stage1, _stage2, _output, _target, _enabled, _timersEnabled, _prevStage1);
+ }
+};
\ No newline at end of file
diff --git a/Core/SpcTypes.h b/Core/SpcTypes.h
new file mode 100644
index 0000000..38c4e9c
--- /dev/null
+++ b/Core/SpcTypes.h
@@ -0,0 +1,70 @@
+#pragma once
+#include "stdafx.h"
+#include "SpcTimer.h"
+
+struct SpcState
+{
+ uint64_t Cycle;
+ uint16_t PC;
+ uint8_t A;
+ uint8_t X;
+ uint8_t Y;
+ uint8_t SP;
+ uint8_t PS;
+
+ bool WriteEnabled;
+ bool RomEnabled;
+ uint8_t InternalSpeed;
+ uint8_t ExternalSpeed;
+ bool TimersEnabled;
+
+ uint8_t DspReg;
+ uint8_t OutputReg[4];
+ uint8_t RamReg[2];
+ uint8_t CpuRegs[4];
+
+ SpcTimer<128> Timer0;
+ SpcTimer<128> Timer1;
+ SpcTimer<16> Timer2;
+};
+
+namespace SpcFlags {
+ enum SpcFlags : uint8_t
+ {
+ Carry = 0x01,
+ Zero = 0x02,
+ IrqEnable = 0x04, //unused
+ HalfCarry = 0x08,
+ Break = 0x10, /* Set by the BRK instruction */
+ DirectPage = 0x20, /* Selects page 0 or 1 for direct mode addressing */
+ Overflow = 0x40,
+ Negative = 0x80
+ };
+}
+
+namespace SpcControlFlags {
+ enum SpcControlFlags : uint8_t
+ {
+ Timer0 = 0x01,
+ Timer1 = 0x02,
+ Timer2 = 0x04,
+
+ ClearPortsA = 0x10,
+ ClearPortsB = 0x20,
+
+ EnableRom = 0x80
+ };
+}
+
+namespace SpcTestFlags {
+ enum SpcTestFlags : uint8_t
+ {
+ TimersDisabled = 0x01,
+ WriteEnabled = 0x02,
+ Unknown = 0x04,
+ TimersEnabled = 0x08,
+
+ ExternalSpeed = 0x30,
+ InternalSpeed = 0xC0,
+ };
+}
\ No newline at end of file
diff --git a/Core/dsp.h b/Core/dsp.h
deleted file mode 100644
index 59867d9..0000000
--- a/Core/dsp.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* SNES SPC-700 DSP emulator C interface (also usable from C++) */
-
-/* snes_spc 0.9.0 */
-#ifndef DSP_H
-#define DSP_H
-
-#include
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-typedef struct SPC_DSP SPC_DSP;
-
-/* Creates new DSP emulator. NULL if out of memory. */
-SPC_DSP* spc_dsp_new( void );
-
-/* Frees DSP emulator */
-void spc_dsp_delete( SPC_DSP* );
-
-/* Initializes DSP and has it use the 64K RAM provided */
-void spc_dsp_init( SPC_DSP*, void* ram_64k );
-
-/* Sets destination for output samples. If out is NULL or out_size is 0,
-doesn't generate any. */
-typedef short spc_dsp_sample_t;
-void spc_dsp_set_output( SPC_DSP*, spc_dsp_sample_t* out, int out_size );
-
-/* Number of samples written to output since it was last set, always
-a multiple of 2. Undefined if more samples were generated than
-output buffer could hold. */
-int spc_dsp_sample_count( SPC_DSP const* );
-
-
-/**** Emulation *****/
-
-/* Resets DSP to power-on state */
-void spc_dsp_reset( SPC_DSP* );
-
-/* Emulates pressing reset switch on SNES */
-void spc_dsp_soft_reset( SPC_DSP* );
-
-/* Reads/writes DSP registers. For accuracy, you must first call spc_dsp_run() */
-/* to catch the DSP up to present. */
-int spc_dsp_read ( SPC_DSP const*, int addr );
-void spc_dsp_write( SPC_DSP*, int addr, int data );
-
-/* Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks */
-/* a pair of samples is be generated. */
-void spc_dsp_run( SPC_DSP*, int clock_count );
-
-
-/**** Sound control *****/
-
-/* Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */
-enum { spc_dsp_voice_count = 8 };
-void spc_dsp_mute_voices( SPC_DSP*, int mask );
-
-/* If true, prevents channels and global volumes from being phase-negated.
-Only supported by fast DSP; has no effect on accurate DSP. */
-void spc_dsp_disable_surround( SPC_DSP*, int disable );
-
-
-/**** State save/load *****/
-
-/* Resets DSP and uses supplied values to initialize registers */
-enum { spc_dsp_register_count = 128 };
-void spc_dsp_load( SPC_DSP*, unsigned char const regs [spc_dsp_register_count] );
-
-/* Saves/loads exact emulator state (accurate DSP only) */
-enum { spc_dsp_state_size = 640 }; /* maximum space needed when saving */
-typedef void (*spc_dsp_copy_func_t)( unsigned char** io, void* state, size_t );
-void spc_dsp_copy_state( SPC_DSP*, unsigned char** io, spc_dsp_copy_func_t );
-
-/* Returns non-zero if new key-on events occurred since last call (accurate DSP only) */
-int spc_dsp_check_kon( SPC_DSP* );
-
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif