From fc7a79b2bbf0e606fc880dfa070fd5bd14cb73d7 Mon Sep 17 00:00:00 2001 From: zones Date: Tue, 19 Apr 2011 23:02:08 +0900 Subject: [PATCH] Add ports list doc (adventure_of_link) / Add APU debugger / Fix the issue that echo breaks the boot ROM --- apu/SNES_SPC.cpp | 2 +- apu/SNES_SPC.h | 20 ++ apu/SNES_SPC_misc.cpp | 442 ++++++++++++++++++++++++++++++++++++++++- apu/SPC_CPU.h | 8 +- apu/SPC_DSP.cpp | 10 + apu/SPC_DSP.h | 8 +- debug.cpp | 24 +-- debug.h | 1 + docs/portsofsnes9x.txt | 57 ++++++ 9 files changed, 548 insertions(+), 24 deletions(-) create mode 100644 docs/portsofsnes9x.txt diff --git a/apu/SNES_SPC.cpp b/apu/SNES_SPC.cpp index d6f6c927..567b7ddc 100644 --- a/apu/SNES_SPC.cpp +++ b/apu/SNES_SPC.cpp @@ -82,7 +82,7 @@ void SNES_SPC::enable_rom( int enable ) { if ( m.rom_enabled != enable ) { - m.rom_enabled = enable; + m.rom_enabled = dsp.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 ); diff --git a/apu/SNES_SPC.h b/apu/SNES_SPC.h index bc69e94d..4b4bd47e 100644 --- a/apu/SNES_SPC.h +++ b/apu/SNES_SPC.h @@ -7,6 +7,12 @@ #include "SPC_DSP.h" #include "blargg_endian.h" +#ifdef DEBUGGER +#include "snes9x.h" +#include "display.h" +#include "debug.h" +#endif + struct SNES_SPC { public: typedef BOOST::uint8_t uint8_t; @@ -114,6 +120,15 @@ public: uint8_t dsp_reg_value( int, int ); int dsp_envx_value( int ); +//// Snes9x Debugger + +#ifdef DEBUGGER + void debug_toggle_trace( void ); + void debug_do_trace( int, int, int, uint8_t const *, uint8_t *, int, int, int, int ); + void debug_op_print( char *, int, int, int, uint8_t const *, uint8_t *, int, int, int, int ); + void debug_io_print( char * ); +#endif + public: BLARGG_DISABLE_NOTHROW @@ -263,6 +278,11 @@ private: // Snes9x timing hack bool allow_time_overflow; +// Snes9x debugger +#ifdef DEBUGGER + FILE *apu_trace; + bool debug_trace; +#endif }; #include diff --git a/apu/SNES_SPC_misc.cpp b/apu/SNES_SPC_misc.cpp index 52899157..50b61a8f 100644 --- a/apu/SNES_SPC_misc.cpp +++ b/apu/SNES_SPC_misc.cpp @@ -70,7 +70,15 @@ blargg_err_t SNES_SPC::init() } allow_time_overflow = false; - + + dsp.rom = m.rom; + dsp.hi_ram = m.hi_ram; + +#ifdef DEBUGGER + apu_trace = NULL; + debug_trace = false; +#endif + #if SPC_LESS_ACCURATE memcpy( reg_times, reg_times_, sizeof reg_times ); #endif @@ -141,7 +149,7 @@ void SNES_SPC::load_regs( uint8_t const in [reg_count] ) // and timer counts. Copies these to proper registers. void SNES_SPC::ram_loaded() { - m.rom_enabled = 0; + m.rom_enabled = dsp.rom_enabled = 0; load_regs( &RAM [0xF0] ); // Put STOP instruction around memory to catch PC underflow/overflow @@ -413,3 +421,433 @@ int SNES_SPC::dsp_envx_value( int ch ) { return dsp.envx_value( ch ); } + +//// Snes9x debugger + +#ifdef DEBUGGER + +void SNES_SPC::debug_toggle_trace( void ) +{ + debug_trace = !debug_trace; + + if (debug_trace) + { + printf("APU tracing enabled.\n"); + ENSURE_TRACE_OPEN(apu_trace, "apu_trace.log", "wb") + } + else + { + printf("APU tracing disabled.\n"); + fclose(apu_trace); + apu_trace = NULL; + } +} + +void SNES_SPC::debug_do_trace( int a, int x, int y, uint8_t const *pc, uint8_t *sp, int psw, int c, int nz, int dp ) +{ + char msg[512]; + + ENSURE_TRACE_OPEN(apu_trace, "apu_trace.log", "a") + + debug_op_print(msg, a, x, y, pc, sp, psw, c, nz, dp); + fprintf(apu_trace, "%s ", msg); + debug_io_print(msg); + fprintf(apu_trace, "%s ", msg); + S9xPrintHVPosition(msg); + fprintf(apu_trace, "%s\n", msg); +} + +void SNES_SPC::debug_op_print( char *buffer, int a, int x, int y, uint8_t const *pc, uint8_t *sp, int psw, int c, int nz, int dp ) +{ + static char mnemonics[256][20] = + { + "NOP", + "TCALL 0", + "SET1 $%02X.0", + "BBS $%02X.0,$%04X", + "OR A,$%02X", + "OR A,!$%04X", + "OR A,(X)", + "OR A,[$%02X+X]", + "OR A,#$%02X", + "OR $%02X,$%02X", + "OR1 C,$%04X.%d", + "ASL $%02X", + "MOV !$%04X,Y", + "PUSH PSW", + "TSET1 !$%04X", + "BRK", + "BPL $%04X", + "TCALL 1", + "CLR1 $%02X.0", + "BBC $%02X.0,$%04X", + "OR A,$%02X+X", + "OR A,!$%04X+X", + "OR A,!$%04X+Y", + "OR A,[$%02X]+Y", + "OR $%02X,#$%02X", + "OR (X),(Y)", + "DECW $%02X", + "ASL $%02X+X", + "ASL A", + "DEC X", + "CMP X,!$%04X", + "JMP [!$%04X+X]", + "CLRP", + "TCALL 2", + "SET1 $%02X.1", + "BBS $%02X.1,$%04X", + "AND A,$%02X", + "AND A,!$%04X", + "AND A,(X)", + "AND A,[$%02X+X]", + "AND A,#$%02X", + "AND $%02X,$%02X", + "OR1 C,/$%04X.%d", + "ROL $%02X", + "ROL !$%04X", + "PUSH A", + "CBNE $%02X,$%04X", + "BRA $%04X", + "BMI $%04X", + "TCALL 3", + "CLR1 $%02X.1", + "BBC $%02X.1,$%04X", + "AND A,$%02X+X", + "AND A,!$%04X+X", + "AND A,!$%04X+Y", + "AND A,[$%02X]+Y", + "AND $%02X,#$%02X", + "AND (X),(Y)", + "INCW $%02X", + "ROL $%02X+X", + "ROL A", + "INC X", + "CMP X,$%02X", + "CALL !$%04X", + "SETP", + "TCALL 4", + "SET1 $%02X.2", + "BBS $%02X.2,$%04X", + "EOR A,$%02X", + "EOR A,!$%04X", + "EOR A,(X)", + "EOR A,[$%02X+X]", + "EOR A,#$%02X", + "EOR $%02X,$%02X", + "AND1 C,$%04X.%d", + "LSR $%02X", + "LSR !$%04X", + "PUSH X", + "TCLR1 !$%04X", + "PCALL $%02X", + "BVC $%04X", + "TCALL 5", + "CLR1 $%02X.2", + "BBC $%02X.2,$%04X", + "EOR A,$%02X+X", + "EOR A,!$%04X+X", + "EOR A,!$%04X+Y", + "EOR A,[$%02X]+Y", + "EOR $%02X,#$%02X", + "EOR (X),(Y)", + "CMPW YA,$%02X", + "LSR $%02X+X", + "LSR A", + "MOV X,A", + "CMP Y,!$%04X", + "JMP !$%04X", + "CLRC", + "TCALL 6", + "SET1 $%02X.3", + "BBS $%02X.3,$%04X", + "CMP A,$%02X", + "CMP A,!$%04X", + "CMP A,(X)", + "CMP A,[$%02X+X]", + "CMP A,#$%02X", + "CMP $%02X,$%02X", + "AND1 C,/$%04X.%d", + "ROR $%02X", + "ROR !$%04X", + "PUSH Y", + "DBNZ $%02X,$%04X", + "RET", + "BVS $%04X", + "TCALL 7", + "CLR1 $%02X.3", + "BBC $%02X.3,$%04X", + "CMP A,$%02X+X", + "CMP A,!$%04X+X", + "CMP A,!$%04X+Y", + "CMP A,[$%02X]+Y", + "CMP $%02X,#$%02X", + "CMP (X),(Y)", + "ADDW YA,$%02X", + "ROR $%02X+X", + "ROR A", + "MOV A,X", + "CMP Y,$%02X", + "RET1", + "SETC", + "TCALL 8", + "SET1 $%02X.4", + "BBS $%02X.4,$%04X", + "ADC A,$%02X", + "ADC A,!$%04X", + "ADC A,(X)", + "ADC A,[$%02X+X]", + "ADC A,#$%02X", + "ADC $%02X,$%02X", + "EOR1 C,$%04X.%d", + "DEC $%02X", + "DEC !$%04X", + "MOV Y,#$%02X", + "POP PSW", + "MOV $%02X,#$%02X", + "BCC $%04X", + "TCALL 9", + "CLR1 $%02X.4", + "BBC $%02X.4,$%04X", + "ADC A,$%02X+X", + "ADC A,!$%04X+X", + "ADC A,!$%04X+Y", + "ADC A,[$%02X]+Y", + "ADC $%02X,#$%02X", + "ADC (X),(Y)", + "SUBW YA,$%02X", + "DEC $%02X+X", + "DEC A", + "MOV X,SP", + "DIV YA,X", + "XCN A", + "EI", + "TCALL 10", + "SET1 $%02X.5", + "BBS $%02X.5,$%04X", + "SBC A,$%02X", + "SBC A,!$%04X", + "SBC A,(X)", + "SBC A,[$%02X+X]", + "SBC A,#$%02X", + "SBC $%02X,$%02X", + "MOV1 C,$%04X.%d", + "INC $%02X", + "INC !$%04X", + "CMP Y,#$%02X", + "POP A", + "MOV (X)+,A", + "BCS $%04X", + "TCALL 11", + "CLR1 $%02X.5", + "BBC $%02X.5,$%04X", + "SBC A,$%02X+X", + "SBC A,!$%04X+X", + "SBC A,!$%04X+Y", + "SBC A,[$%02X]+Y", + "SBC $%02X,#$%02X", + "SBC (X),(Y)", + "MOVW YA,$%02X", + "INC $%02X+X", + "INC A", + "MOV SP,X", + "DAS A", + "MOV A,(X)+", + "DI", + "TCALL 12", + "SET1 $%02X.6", + "BBS $%02X.6,$%04X", + "MOV $%02X,A", + "MOV !$%04X,A", + "MOV (X),A", + "MOV [$%02X+X],A", + "CMP X,#$%02X", + "MOV !$%04X,X", + "MOV1 $%04X.%d,C", + "MOV $%02X,Y", + "ASL !$%04X", + "MOV X,#$%02X", + "POP X", + "MUL YA", + "BNE $%04X", + "TCALL 13", + "CLR1 $%02X.6", + "BBC $%02X.6,$%04X", + "MOV $%02X+X,A", + "MOV !$%04X+X,A", + "MOV !$%04X+Y,A", + "MOV [$%02X]+Y,A", + "MOV $%02X,X", + "MOV $%02X+Y,X", + "MOVW $%02X,YA", + "MOV $%02X+X,Y", + "DEC Y", + "MOV A,Y", + "CBNE $%02X+X,$%04X", + "DAA A", + "CLRV", + "TCALL 14", + "SET1 $%02X.7", + "BBS $%02X.7,$%04X", + "MOV A,$%02X", + "MOV A,!$%04X", + "MOV A,(X)", + "MOV A,[$%02X+X]", + "MOV A,#$%02X", + "MOV X,!$%04X", + "NOT1 $%04X.%d", + "MOV Y,$%02X", + "MOV Y,!$%04X", + "NOTC", + "POP Y", + "SLEEP", + "BEQ $%04X", + "TCALL 15", + "CLR1 $%02X.7", + "BBC $%02X.7,$%04X", + "MOV A,$%02X+X", + "MOV A,!$%04X+X", + "MOV A,!$%04X+Y", + "MOV A,[$%02X]+Y", + "MOV X,$%02X", + "MOV X,$%02X+Y", + "MOV $%02X,$%02X", + "MOV Y,$%02X+X", + "INC Y", + "MOV Y,A", + "DBNZ Y,$%04X", + "STOP" + }; + + static int modes[256] = + { + 2, 2, 0, 5, 0, 1, 2, 0, 0, 3, 6, 0, 1, 2, 1, 2, + 7, 2, 0, 5, 0, 1, 1, 0, 4, 2, 0, 0, 2, 2, 1, 1, + 2, 2, 0, 5, 0, 1, 2, 0, 0, 3, 6, 0, 1, 2, 5, 7, + 7, 2, 0, 5, 0, 1, 1, 0, 4, 2, 0, 0, 2, 2, 0, 1, + 2, 2, 0, 5, 0, 1, 2, 0, 0, 3, 6, 0, 1, 2, 1, 0, + 7, 2, 0, 5, 0, 1, 1, 0, 4, 2, 0, 0, 2, 2, 1, 1, + 2, 2, 0, 5, 0, 1, 2, 0, 0, 3, 6, 0, 1, 2, 5, 2, + 7, 2, 0, 5, 0, 1, 1, 0, 4, 2, 0, 0, 2, 2, 0, 2, + 2, 2, 0, 5, 0, 1, 2, 0, 0, 3, 6, 0, 1, 0, 2, 4, + 7, 2, 0, 5, 0, 1, 1, 0, 4, 2, 0, 0, 2, 2, 2, 2, + 2, 2, 0, 5, 0, 1, 2, 0, 0, 3, 6, 0, 1, 0, 2, 2, + 7, 2, 0, 5, 0, 1, 1, 0, 4, 2, 0, 0, 2, 2, 2, 2, + 2, 2, 0, 5, 0, 1, 2, 0, 0, 1, 6, 0, 1, 0, 2, 2, + 7, 2, 0, 5, 0, 1, 1, 0, 0, 0, 0, 0, 2, 2, 5, 2, + 2, 2, 0, 5, 0, 1, 2, 0, 0, 1, 6, 0, 1, 2, 2, 2, + 7, 2, 0, 5, 0, 1, 1, 0, 0, 0, 3, 0, 2, 2, 7, 2 + }; + + static int modesToBytes[] = + { + 2, 3, 1, 3, 3, 3, 3, 2 + }; + + int const n80 = 0x80; // nz + int const p20 = 0x20; // dp + int const z02 = 0x02; // nz + int const c01 = 0x01; // c + + #define GET_PC() (pc - ram) + #define GET_SP() (sp - 0x101 - ram) + #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;\ + } + + uint8_t const *ram = RAM; + + int addr; + int tsp, tpsw; + uint8_t d0, d1, d2; + + addr = GET_PC(); + tsp = GET_SP(); + GET_PSW(tpsw); + + d0 = *pc; + d1 = (addr < 0xffff) ? *(pc + 1) : 0; + d2 = (addr < 0xfffe) ? *(pc + 2) : 0; + + int mode = modes[d0]; + int bytes = modesToBytes[mode]; + char mnem[100]; + + switch (bytes) + { + case 1: + sprintf(buffer, "%04X %02X ", addr, d0); + break; + + case 2: + sprintf(buffer, "%04X %02X %02X ", addr, d0, d1); + break; + + case 3: + sprintf(buffer, "%04X %02X %02X %02X ", addr, d0, d1, d2); + break; + } + + switch (mode) + { + case 0: + sprintf(mnem, mnemonics[d0], d1); + break; + + case 1: + sprintf(mnem, mnemonics[d0], d1 + (d2 << 8)); + break; + + case 2: + strcpy (mnem, mnemonics[d0]); + break; + + case 3: + sprintf(mnem, mnemonics[d0], d2, d1); + break; + + case 4: + sprintf(mnem, mnemonics[d0], d2, d1); + break; + + case 5: + sprintf(mnem, mnemonics[d0], d1, addr + 3 + (int8_t) d2); + break; + + case 6: + sprintf(mnem, mnemonics[d0], (d1 + (d2 << 8)) & 0x1fff, d2 >> 5); + break; + + case 7: + sprintf(mnem, mnemonics[d0], addr + 2 + (int8_t) d1); + break; + } + + sprintf(buffer, "%s %-20s A:%02X X:%02X Y:%02X S:%02X P:%c%c%c%c%c%c%c%c ROM:%d", + buffer, mnem, a, x, y, tsp, + (tpsw & 0x80) ? 'N' : 'n', + (tpsw & 0x40) ? 'V' : 'v', + (tpsw & 0x20) ? 'P' : 'p', + (tpsw & 0x10) ? 'B' : 'b', + (tpsw & 0x08) ? 'H' : 'h', + (tpsw & 0x04) ? 'I' : 'i', + (tpsw & 0x02) ? 'Z' : 'z', + (tpsw & 0x01) ? 'C' : 'c', + m.rom_enabled ? 1 : 0); +} + +void SNES_SPC::debug_io_print( char *buffer ) +{ + sprintf(buffer, "i/o %02X/%02X %02X/%02X %02X/%02X %02X/%02X", + m.smp_regs[1][r_cpuio0], m.smp_regs[0][r_cpuio0], + m.smp_regs[1][r_cpuio1], m.smp_regs[0][r_cpuio1], + m.smp_regs[1][r_cpuio2], m.smp_regs[0][r_cpuio2], + m.smp_regs[1][r_cpuio3], m.smp_regs[0][r_cpuio3]); +} + +#endif diff --git a/apu/SPC_CPU.h b/apu/SPC_CPU.h index 0bd56fcd..67fee8da 100644 --- a/apu/SPC_CPU.h +++ b/apu/SPC_CPU.h @@ -218,7 +218,13 @@ loop: PROFILE_TIMER_LOOP( 0xEB, 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) data = *++pc; switch ( opcode ) diff --git a/apu/SPC_DSP.cpp b/apu/SPC_DSP.cpp index 8779feba..a05e825e 100644 --- a/apu/SPC_DSP.cpp +++ b/apu/SPC_DSP.cpp @@ -716,7 +716,17 @@ 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_ptr >= 0xffc0 ) + { + SET_LE16A( &hi_ram [m.t_echo_ptr + ch * 2 - 0xffc0], m.t_echo_out [ch] ); + if ( rom_enabled ) + SET_LE16A( ECHO_PTR( ch ), GET_LE16A( &rom [m.t_echo_ptr + ch * 2 - 0xffc0] ) ); + } + } + m.t_echo_out [ch] = 0; } ECHO_CLOCK( 29 ) diff --git a/apu/SPC_DSP.h b/apu/SPC_DSP.h index 6705a490..61d05ab5 100644 --- a/apu/SPC_DSP.h +++ b/apu/SPC_DSP.h @@ -67,9 +67,11 @@ public: // Snes9x Accessor - int stereo_switch; - int take_spc_snapshot; - void (*spc_snapshot_callback) (void); + int stereo_switch; + int take_spc_snapshot; + int rom_enabled; // mirror + uint8_t *rom, *hi_ram; // mirror + void (*spc_snapshot_callback) (void); void set_spc_snapshot_callback( void (*callback) (void) ); void dump_spc_snapshot( void ); diff --git a/debug.cpp b/debug.cpp index f3d7edb3..3cd97b1c 100644 --- a/debug.cpp +++ b/debug.cpp @@ -1566,25 +1566,10 @@ static void debug_process_command (char *Line) printf("HC event tracing %s.\n", Settings.TraceHCEvent ? "enabled" : "disabled"); } -/* if (*Line == 'A') - { - APU.Flags ^= TRACE_FLAG; - - if (APU.Flags & TRACE_FLAG) - { - printf("APU tracing enabled.\n"); - if (apu_trace == NULL) - apu_trace = fopen("aputrace.log", "wb"); - } - else - { - printf("APU tracing disabled.\n"); - fclose(apu_trace); - apu_trace = NULL; - } - } + spc_core->debug_toggle_trace(); +/* if (*Line == 'B') { Settings.TraceSoundDSP = !Settings.TraceSoundDSP; @@ -2623,4 +2608,9 @@ void S9xTraceFormattedMessage (const char *s, ...) } } +void S9xPrintHVPosition (char *s) +{ + sprintf(s, "HC:%04ld VC:%03ld FC:%02d", (long) CPU.Cycles, (long) CPU.V_Counter, IPPU.FrameCount); +} + #endif diff --git a/debug.h b/debug.h index 395304df..cc26133e 100644 --- a/debug.h +++ b/debug.h @@ -205,6 +205,7 @@ void S9xTrace (void); void S9xSA1Trace (void); void S9xTraceMessage (const char *); void S9xTraceFormattedMessage (const char *, ...); +void S9xPrintHVPosition (char *); #endif diff --git a/docs/portsofsnes9x.txt b/docs/portsofsnes9x.txt new file mode 100644 index 00000000..edd775dd --- /dev/null +++ b/docs/portsofsnes9x.txt @@ -0,0 +1,57 @@ +*PSP Version of Snes9X* +Name: Snes9X Euphoria +Latest version: R5 Beta +Homepage/forum: http://www.retroemu.com/forum/ +Maintainer: Zack + +HOW TO GET IT RUNNING: +1. Make sure your PSP is hackable in some way. This means: +* PSP-1000 series and certain PSP-2000 series (Google for compatibility) can use Pandora Battery +* PSP-2000 series that can't be Pandoraized (Google for compatibility), most PSP-3000 series, and PSP Gos are hackable via other means (DaveeFTW Downgrader, etc) + +2. Make sure your PSP has custom firmware or a HEN that's useable (you'll have to upgrade/downgrade the firmware as necessary). Google for these CFWs, HENs, and installations. (Hint: I personally prefer 5.50 GEN-D3 on the Pandora-able PSPs; 6.20 or 6.35 PRO-B4 or better on the non-Pandora-able PSPs.) + +3. When that's done, be sure to put the Snes9X Euphoria in /PSP/GAME on your PSP's memory stick or internal memory (PSP Go only). + +*Wii/Gamecube version of Snes9X* +Name: Snes9X GX +Latest Version: 4.2.7 +Homepage/forum: http://code.google.com/p/snes9x-gx +Maintainer: Tantric + +HOW TO GET IT RUNNING: +Wii: You will need the latest Homebrew Channel installed on your Wii. Use Google for installation instructions and to find the HBC. +In addition, there appears to be a channel for Snes9X GX; again, use Google for installation instructions. +Gamecube: You might need a modchip; search Google. + +*Android and iOS (Apple iPhone/iPod touch) version of Snes9X* +Name: Snes9X EX +Latest Version: 1.3.22 +Homepage/forum: http://www.explusalpha.com/home/snes9x-ex +Maintainer: Rakashazi (on the Snes9X forums) + +HOW TO GET IT RUNNING: +Android: It appears you can just download from the Android app-store thingy and run it from there :) +iOS: You'll have to jailbreak your firmware (use Google) and install the Cydia app installer. Then you'll have to install the BigBoss repository within Cydia and search for Snes9X EX; you may also want to search for the sshd and all needed stuff for that, as it's the only way you can put the ROMs onto your iPhone/iPod Touch. After that you should be able to download and run from there :) + +*Second Android version of Snes9X* +Name: Snesoid +Latest Version: unknown +Homepage/forum: www.snes9x.com/phpbb2 +Maintainer: SparroHawc + +HOW TO GET IT RUNNING: +See this thread: http://www.snes9x.com/phpbb2/viewtopic.php?t=4823 + +*PS3 version of Snes9X* +Name: Snes9X PS3 +Latest Version: 4.4.9 +Homepage/forum: https://code.google.com/p/snes9x-ps3/ (although for some reason, you may have to Google for the latest version) +Maintainer: Squarepusher + +HOW TO GET IT RUNNING: +You'll have to install a HEN/Jailbreaker/CFW/etc on your PS3 (use Google for more information). +After that, it should be as simple as copy the emulator/ROMs over to the PS3 and it should work :) + + +Updated most recently by: 2011/4/19 adventure_of_link