From 66c457771c004e0a68af8925020800a83a1a9c71 Mon Sep 17 00:00:00 2001 From: Sour Date: Fri, 22 Mar 2019 21:29:51 -0400 Subject: [PATCH] SPC: Rewrite SPC core --- Core/Core.vcxproj | 9 +- Core/Core.vcxproj.filters | 49 +- Core/RegisterHandlerB.cpp | 4 +- Core/SNES_SPC.cpp | 565 --------------- Core/SNES_SPC.h | 279 -------- Core/SNES_SPC_misc.cpp | 381 ---------- Core/SNES_SPC_state.cpp | 134 ---- Core/SPC_CPU.h | 1220 ------------------------------- Core/SPC_DSP.cpp | 21 +- Core/SPC_DSP.h | 8 +- Core/Spc.Instructions.cpp | 1426 +++++++++++++++++++++++++++++++++++++ Core/Spc.cpp | 337 ++++++++- Core/Spc.h | 230 +++++- Core/SpcTimer.h | 87 +++ Core/SpcTypes.h | 70 ++ Core/dsp.h | 83 --- 16 files changed, 2143 insertions(+), 2760 deletions(-) delete mode 100644 Core/SNES_SPC.cpp delete mode 100644 Core/SNES_SPC.h delete mode 100644 Core/SNES_SPC_misc.cpp delete mode 100644 Core/SNES_SPC_state.cpp delete mode 100644 Core/SPC_CPU.h create mode 100644 Core/Spc.Instructions.cpp create mode 100644 Core/SpcTimer.h create mode 100644 Core/SpcTypes.h delete mode 100644 Core/dsp.h 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