From 4f2234cbc2dafd03e18c73469948b40adf361756 Mon Sep 17 00:00:00 2001 From: Daryl Borth Date: Wed, 15 Aug 2018 12:54:52 -0600 Subject: [PATCH] fix SPC crash that happens on some games, eg: chrono trigger (http://www.snes9x.com/phpbb3/viewtopic.php?f=6&t=6881) the snes9x team gave up and replaced the core, but others have fixed it https://bitbucket.org/mpyne/game-music-emu/issues/18/spc_cpucpp-492-always-assert-s --- source/snes9x/apu/SNES_SPC.cpp | 24 ++---- source/snes9x/apu/SNES_SPC.h | 36 ++++----- source/snes9x/apu/SNES_SPC_misc.cpp | 2 +- source/snes9x/apu/SPC_CPU.h | 114 ++++++++++------------------ 4 files changed, 65 insertions(+), 111 deletions(-) diff --git a/source/snes9x/apu/SNES_SPC.cpp b/source/snes9x/apu/SNES_SPC.cpp index c207963..38b181b 100644 --- a/source/snes9x/apu/SNES_SPC.cpp +++ b/source/snes9x/apu/SNES_SPC.cpp @@ -282,7 +282,7 @@ static unsigned char const glitch_probs [3] [256] = // 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 ) +void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) { switch ( addr ) { @@ -383,7 +383,7 @@ void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr ) } } -void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr ) +void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, uint16_t addr ) { if ( addr == r_dspdata ) // 99% dsp_write( data, time ); @@ -393,23 +393,15 @@ void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int 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 [0]) + i + rom_addr) == (uint8_t) data ); - *(&(RAM [0]) + i + rom_addr) = cpu_pad_fill; // restore overwritten padding - cpu_write( data, i + rom_addr - 0x10000, time ); - } + m.hi_ram [i] = (uint8_t) data; + + if ( m.rom_enabled ) + RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM } int const bits_in_int = CHAR_BIT * sizeof (int); -void SNES_SPC::cpu_write( int data, int addr, rel_time_t time ) +void SNES_SPC::cpu_write( int data, uint16_t addr, rel_time_t time ) { MEM_ACCESS( time, addr ) @@ -463,7 +455,7 @@ inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time ) return result; } -int SNES_SPC::cpu_read( int addr, rel_time_t time ) +int SNES_SPC::cpu_read( uint16_t addr, rel_time_t time ) { MEM_ACCESS( time, addr ) diff --git a/source/snes9x/apu/SNES_SPC.h b/source/snes9x/apu/SNES_SPC.h index 8beff6e..9013eb1 100644 --- a/source/snes9x/apu/SNES_SPC.h +++ b/source/snes9x/apu/SNES_SPC.h @@ -4,6 +4,8 @@ #ifndef SNES_SPC_H #define SNES_SPC_H +#include + #include "SPC_DSP.h" #include "blargg_endian.h" @@ -171,12 +173,12 @@ private: struct { - int pc; - int a; - int x; - int y; - int psw; - int sp; + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t psw; + uint8_t sp; } cpu_regs; rel_time_t dsp_time; @@ -202,13 +204,11 @@ private: 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]; + // padding to neutralize address overflow -- but this is + // still undefined behavior! TODO: remove and instead properly + // guard usage of emulated memory + uint8_t padding1 [0x100]; + alignas(uint16_t) uint8_t ram [0x10000 + 0x100]; } ram; }; state_t m; @@ -244,13 +244,13 @@ private: 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_smp_reg_( int data, rel_time_t, uint16_t addr ); + void cpu_write_smp_reg ( int data, rel_time_t, uint16_t addr ); void cpu_write_high ( int data, int i, rel_time_t ); - void cpu_write ( int data, int addr, rel_time_t ); + void cpu_write ( int data, uint16_t 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 ); + int cpu_read ( uint16_t addr, rel_time_t ); + unsigned CPU_mem_bit ( uint16_t pc, rel_time_t ); bool check_echo_access ( int addr ); uint8_t* run_until_( time_t end_time ); diff --git a/source/snes9x/apu/SNES_SPC_misc.cpp b/source/snes9x/apu/SNES_SPC_misc.cpp index 07d720b..0071362 100644 --- a/source/snes9x/apu/SNES_SPC_misc.cpp +++ b/source/snes9x/apu/SNES_SPC_misc.cpp @@ -154,7 +154,7 @@ void SNES_SPC::ram_loaded() // 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 ); + memset( m.ram.ram + 0x10000, cpu_pad_fill, sizeof m.ram.padding1 ); } // Registers were just loaded. Applies these new values. diff --git a/source/snes9x/apu/SPC_CPU.h b/source/snes9x/apu/SPC_CPU.h index 784a149..9fbc101 100644 --- a/source/snes9x/apu/SPC_CPU.h +++ b/source/snes9x/apu/SPC_CPU.h @@ -66,62 +66,37 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #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 READ_PROG16( addr ) (RAM [addr & 0xffff] | (RAM [(addr + 1) & 0xffff] << 8)) -#define SET_PC( n ) (pc = ram + (n)) -#define GET_PC() (pc - ram) -#define READ_PC( pc ) (*(pc)) -#define READ_PC16( pc ) GET_LE16( pc ) +#define SET_PC( n ) (pc = n) +#define GET_PC() (pc) +#define READ_PC( pc ) (ram [pc]) +#define READ_PC16( pc ) READ_PROG16( pc ) -// TODO: remove non-wrapping versions? -#define SPC_NO_SP_WRAPAROUND 0 +#define SET_SP( v ) (sp = v) +#define GET_SP() ((uint8_t) (sp)) -#define SET_SP( v ) (sp = ram + 0x101 + (v)) -#define GET_SP() (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 = (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;\ - }\ + PUSH( (data & 0xff00) >> 8 );\ + PUSH( data & 0xff );\ } #define PUSH( data )\ {\ - *--sp = (uint8_t) (data);\ - if ( sp - ram == 0x100 )\ - sp += 0x100;\ + ram [0x100 + sp] = (uint8_t) (data);\ + --sp;\ } #define POP( out )\ {\ - out = *sp++;\ - if ( sp - ram == 0x201 )\ - {\ - out = sp [-0x101];\ - sp -= 0x100;\ - }\ + ++sp;\ + out = ram [0x100 + sp];\ } -#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 SNES_SPC::CPU_mem_bit( uint16_t pc, rel_time_t rel_time ) { unsigned addr = READ_PC16( pc ); unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); @@ -163,11 +138,11 @@ int const nz_neg_mask = 0x880; // either bit set indicates N flag set 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; + uint8_t a = m.cpu_regs.a; + uint8_t x = m.cpu_regs.x; + uint8_t y = m.cpu_regs.y; + uint16_t pc; + uint8_t sp; int psw; int c; int nz; @@ -183,7 +158,7 @@ SPC_CPU_RUN_FUNC // Main loop cbranch_taken_loop: - pc += *(BOOST::int8_t const*) pc; + pc += (int8_t) ram [pc]; inc_pc_loop: pc++; loop: @@ -195,7 +170,7 @@ loop: check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); - opcode = *pc; + opcode = ram [pc]; if (allow_time_overflow && rel_time >= 0 ) goto stop; if ( (rel_time += m.cycle_table [opcode]) > 0 && !allow_time_overflow) @@ -219,14 +194,9 @@ loop: PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); */ -#ifdef DEBUGGER - if (debug_trace) - debug_do_trace(a, x, y, pc, sp, psw, c, nz, dp); -#endif - - // TODO: if PC is at end of memory, this will get wrong operand (very obscure) - data = *++pc; + pc++; + data = ram [pc]; switch ( opcode ) { @@ -257,23 +227,12 @@ loop: } case 0x6F:// RET - #if SPC_NO_SP_WRAPAROUND { - SET_PC( GET_LE16( sp ) ); - sp += 2; + uint8_t l, h; + POP( l ); + POP( h ); + SET_PC( l | (h << 8) ); } - #else - { - int addr = 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 @@ -493,7 +452,7 @@ loop: case 0xAF: // MOV (X)+,A WRITE_DP( 0, x, a + no_read_before_write ); - x++; + x = (uint8_t) (x + 1); goto loop; // 5. 8-BIT LOGIC OPERATION COMMANDS @@ -512,7 +471,7 @@ loop: case op + 0x01: /* dp,dp */\ data = READ_DP( -3, data );\ case op + 0x10:{/*dp,imm*/\ - uint8_t const* addr2 = pc + 1;\ + uint16_t addr2 = pc + 1;\ pc += 2;\ addr = READ_PC( addr2 ) + dp;\ }\ @@ -816,7 +775,7 @@ loop: unsigned temp = y * a; a = (uint8_t) temp; nz = ((temp >> 1) | temp) & 0x7F; - y = temp >> 8; + y = (uint8_t) (temp >> 8); nz |= y; goto loop; } @@ -846,6 +805,7 @@ loop: nz = (uint8_t) a; a = (uint8_t) a; + y = (uint8_t) y; goto loop; } @@ -1009,10 +969,12 @@ loop: { int temp; + uint8_t l, h; case 0x7F: // RET1 - temp = *sp; - SET_PC( GET_LE16( sp + 1 ) ); - sp += 3; + POP (temp); + POP (l); + POP (h); + SET_PC( l | (h << 8) ); goto set_psw; case 0x8E: // POP PSW POP( temp ); @@ -1204,9 +1166,9 @@ loop: m.cpu_error = "SPC emulation error"; goto stop; } // switch -} +} out_of_time: - rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode + rel_time -= m.cycle_table [ ram [pc] ]; // undo partial execution of opcode stop: // Uncache registers