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
This commit is contained in:
Daryl Borth 2018-08-15 12:54:52 -06:00
parent 05a607a8f7
commit 4f2234cbc2
4 changed files with 65 additions and 111 deletions

View File

@ -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 // If write isn't preceded by read, data has this added to it
int const no_read_before_write = 0x2000; 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 ) 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% if ( addr == r_dspdata ) // 99%
dsp_write( data, time ); 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 ) 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;
{
m.hi_ram [i] = (uint8_t) data; if ( m.rom_enabled )
if ( m.rom_enabled ) RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
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 );
}
} }
int const bits_in_int = CHAR_BIT * sizeof (int); 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 ) MEM_ACCESS( time, addr )
@ -463,7 +455,7 @@ inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
return result; 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 ) MEM_ACCESS( time, addr )

View File

@ -4,6 +4,8 @@
#ifndef SNES_SPC_H #ifndef SNES_SPC_H
#define SNES_SPC_H #define SNES_SPC_H
#include <stdint.h>
#include "SPC_DSP.h" #include "SPC_DSP.h"
#include "blargg_endian.h" #include "blargg_endian.h"
@ -171,12 +173,12 @@ private:
struct struct
{ {
int pc; uint16_t pc;
int a; uint8_t a;
int x; uint8_t x;
int y; uint8_t y;
int psw; uint8_t psw;
int sp; uint8_t sp;
} cpu_regs; } cpu_regs;
rel_time_t dsp_time; rel_time_t dsp_time;
@ -202,13 +204,11 @@ private:
struct struct
{ {
// padding to neutralize address overflow // padding to neutralize address overflow -- but this is
union { // still undefined behavior! TODO: remove and instead properly
uint8_t padding1 [0x100]; // guard usage of emulated memory
uint16_t align; // makes compiler align data for 16-bit access uint8_t padding1 [0x100];
} padding1 [1]; alignas(uint16_t) uint8_t ram [0x10000 + 0x100];
uint8_t ram [0x10000];
uint8_t padding2 [0x100];
} ram; } ram;
}; };
state_t m; state_t m;
@ -244,13 +244,13 @@ private:
Timer* run_timer ( Timer* t, rel_time_t ); Timer* run_timer ( Timer* t, rel_time_t );
int dsp_read ( rel_time_t ); int dsp_read ( rel_time_t );
void dsp_write ( int data, 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, uint16_t 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_high ( int data, int i, rel_time_t ); 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_smp_reg ( int i, rel_time_t );
int cpu_read ( int addr, rel_time_t ); int cpu_read ( uint16_t addr, rel_time_t );
unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); unsigned CPU_mem_bit ( uint16_t pc, rel_time_t );
bool check_echo_access ( int addr ); bool check_echo_access ( int addr );
uint8_t* run_until_( time_t end_time ); uint8_t* run_until_( time_t end_time );

View File

@ -154,7 +154,7 @@ void SNES_SPC::ram_loaded()
// Put STOP instruction around memory to catch PC underflow/overflow // Put STOP instruction around memory to catch PC underflow/overflow
memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); 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. // Registers were just loaded. Applies these new values.

View File

@ -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 READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) )
#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) #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 SET_PC( n ) (pc = n)
#define GET_PC() (pc - ram) #define GET_PC() (pc)
#define READ_PC( pc ) (*(pc)) #define READ_PC( pc ) (ram [pc])
#define READ_PC16( pc ) GET_LE16( pc ) #define READ_PC16( pc ) READ_PROG16( pc )
// TODO: remove non-wrapping versions? #define SET_SP( v ) (sp = v)
#define SPC_NO_SP_WRAPAROUND 0 #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 )\ #define PUSH16( data )\
{\ {\
int addr = (sp -= 2) - ram;\ PUSH( (data & 0xff00) >> 8 );\
if ( addr > 0x100 )\ PUSH( data & 0xff );\
{\
SET_LE16( sp, data );\
}\
else\
{\
ram [(uint8_t) addr + 0x100] = (uint8_t) data;\
sp [1] = (uint8_t) (data >> 8);\
sp += 0x100;\
}\
} }
#define PUSH( data )\ #define PUSH( data )\
{\ {\
*--sp = (uint8_t) (data);\ ram [0x100 + sp] = (uint8_t) (data);\
if ( sp - ram == 0x100 )\ --sp;\
sp += 0x100;\
} }
#define POP( out )\ #define POP( out )\
{\ {\
out = *sp++;\ ++sp;\
if ( sp - ram == 0x201 )\ out = ram [0x100 + sp];\
{\
out = sp [-0x101];\
sp -= 0x100;\
}\
} }
#endif
#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) #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 addr = READ_PC16( pc );
unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); 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 SPC_CPU_RUN_FUNC
{ {
uint8_t* const ram = RAM; uint8_t* const ram = RAM;
int a = m.cpu_regs.a; uint8_t a = m.cpu_regs.a;
int x = m.cpu_regs.x; uint8_t x = m.cpu_regs.x;
int y = m.cpu_regs.y; uint8_t y = m.cpu_regs.y;
uint8_t const* pc; uint16_t pc;
uint8_t* sp; uint8_t sp;
int psw; int psw;
int c; int c;
int nz; int nz;
@ -183,7 +158,7 @@ SPC_CPU_RUN_FUNC
// Main loop // Main loop
cbranch_taken_loop: cbranch_taken_loop:
pc += *(BOOST::int8_t const*) pc; pc += (int8_t) ram [pc];
inc_pc_loop: inc_pc_loop:
pc++; pc++;
loop: loop:
@ -195,7 +170,7 @@ loop:
check( (unsigned) x < 0x100 ); check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 ); check( (unsigned) y < 0x100 );
opcode = *pc; opcode = ram [pc];
if (allow_time_overflow && rel_time >= 0 ) if (allow_time_overflow && rel_time >= 0 )
goto stop; goto stop;
if ( (rel_time += m.cycle_table [opcode]) > 0 && !allow_time_overflow) if ( (rel_time += m.cycle_table [opcode]) > 0 && !allow_time_overflow)
@ -219,14 +194,9 @@ loop:
PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); 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) // TODO: if PC is at end of memory, this will get wrong operand (very obscure)
data = *++pc; pc++;
data = ram [pc];
switch ( opcode ) switch ( opcode )
{ {
@ -257,23 +227,12 @@ loop:
} }
case 0x6F:// RET case 0x6F:// RET
#if SPC_NO_SP_WRAPAROUND
{ {
SET_PC( GET_LE16( sp ) ); uint8_t l, h;
sp += 2; 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; goto loop;
case 0xE4: // MOV a,dp case 0xE4: // MOV a,dp
@ -493,7 +452,7 @@ loop:
case 0xAF: // MOV (X)+,A case 0xAF: // MOV (X)+,A
WRITE_DP( 0, x, a + no_read_before_write ); WRITE_DP( 0, x, a + no_read_before_write );
x++; x = (uint8_t) (x + 1);
goto loop; goto loop;
// 5. 8-BIT LOGIC OPERATION COMMANDS // 5. 8-BIT LOGIC OPERATION COMMANDS
@ -512,7 +471,7 @@ loop:
case op + 0x01: /* dp,dp */\ case op + 0x01: /* dp,dp */\
data = READ_DP( -3, data );\ data = READ_DP( -3, data );\
case op + 0x10:{/*dp,imm*/\ case op + 0x10:{/*dp,imm*/\
uint8_t const* addr2 = pc + 1;\ uint16_t addr2 = pc + 1;\
pc += 2;\ pc += 2;\
addr = READ_PC( addr2 ) + dp;\ addr = READ_PC( addr2 ) + dp;\
}\ }\
@ -816,7 +775,7 @@ loop:
unsigned temp = y * a; unsigned temp = y * a;
a = (uint8_t) temp; a = (uint8_t) temp;
nz = ((temp >> 1) | temp) & 0x7F; nz = ((temp >> 1) | temp) & 0x7F;
y = temp >> 8; y = (uint8_t) (temp >> 8);
nz |= y; nz |= y;
goto loop; goto loop;
} }
@ -846,6 +805,7 @@ loop:
nz = (uint8_t) a; nz = (uint8_t) a;
a = (uint8_t) a; a = (uint8_t) a;
y = (uint8_t) y;
goto loop; goto loop;
} }
@ -1009,10 +969,12 @@ loop:
{ {
int temp; int temp;
uint8_t l, h;
case 0x7F: // RET1 case 0x7F: // RET1
temp = *sp; POP (temp);
SET_PC( GET_LE16( sp + 1 ) ); POP (l);
sp += 3; POP (h);
SET_PC( l | (h << 8) );
goto set_psw; goto set_psw;
case 0x8E: // POP PSW case 0x8E: // POP PSW
POP( temp ); POP( temp );
@ -1204,9 +1166,9 @@ loop:
m.cpu_error = "SPC emulation error"; m.cpu_error = "SPC emulation error";
goto stop; goto stop;
} // switch } // switch
} }
out_of_time: 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: stop:
// Uncache registers // Uncache registers