From a30bd8631171a5d8ee6c96efafa522bb054874be Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Fri, 14 Nov 2014 22:46:31 +0100 Subject: [PATCH 1/4] pcsx2: interpreter: add an exception for tlb miss When a tlb miss is detected current instruction must be skipped. We need to immediately switch to the handler Typical instruction bug case: lw a0, 0x8(a0) a0 mustn't be loaded if we have a miss v2: create a dedicated exception for tlb miss v3: * rename exception to CancelInstruction * add a basic state machine on the exec loop so we keep same behavior for eeloadReplaceOSDSYS and eeGameStarting v4: remove assert --- pcsx2/Interpreter.cpp | 57 +++++++++++++++++++++++++++++++------------ pcsx2/R5900.h | 6 +++++ pcsx2/vtlb.cpp | 3 +++ 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index 59a90181c1..60f2cb435c 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -479,23 +479,48 @@ static void intEventTest() static void intExecute() { - try { - if (g_SkipBiosHack) { - do - execI(); - while (cpuRegs.pc != EELOAD_START); - eeloadReplaceOSDSYS(); + bool instruction_was_cancelled; + enum ExecuteState { + RESET, + REPLACE_OSDSYS_DONE, + GAME_STARTING_DONE + }; + ExecuteState state = RESET; + do { + instruction_was_cancelled = false; + try { + // The execution was splited in three parts so it is easier to + // resume it after a cancelled instruction. + switch (state) { + case RESET: + if (g_SkipBiosHack) { + do + execI(); + while (cpuRegs.pc != EELOAD_START); + eeloadReplaceOSDSYS(); + } + state = REPLACE_OSDSYS_DONE; + + case REPLACE_OSDSYS_DONE: + if (ElfEntry != 0xFFFFFFFF) { + do + execI(); + while (cpuRegs.pc != ElfEntry); + eeGameStarting(); + } + state = GAME_STARTING_DONE; + + case GAME_STARTING_DONE: + while (true) + execI(); + } } - if (ElfEntry != 0xFFFFFFFF) { - do - execI(); - while (cpuRegs.pc != ElfEntry); - eeGameStarting(); - } else { - while (true) - execI(); - } - } catch( Exception::ExitCpuExecute& ) { } + catch( Exception::ExitCpuExecute& ) { } + catch( Exception::CancelInstruction& ) { instruction_was_cancelled = true; } + + // For example a tlb miss will throw an exception. Cpu must be resumed + // to execute the handler + } while (instruction_was_cancelled); } static void intCheckExecutionState() diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h index 4504a6fed7..cd027895a2 100644 --- a/pcsx2/R5900.h +++ b/pcsx2/R5900.h @@ -37,6 +37,12 @@ namespace Exception public: explicit ExitCpuExecute() { } }; + + class CancelInstruction + { + public: + explicit CancelInstruction() { } + }; } // -------------------------------------------------------------------------------------- diff --git a/pcsx2/vtlb.cpp b/pcsx2/vtlb.cpp index a26ba37bac..da746654b9 100644 --- a/pcsx2/vtlb.cpp +++ b/pcsx2/vtlb.cpp @@ -376,6 +376,9 @@ static __ri void vtlb_Miss(u32 addr,u32 mode) cpuTlbMissW(addr, cpuRegs.branch); else cpuTlbMissR(addr, cpuRegs.branch); + + // Exception handled. Current instruction need to be stopped + throw Exception::CancelInstruction(); } // The exception terminate the program on linux which is very annoying From 8a899c4c7bf6409d9fa8e365096849d80fc3ea1c Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Fri, 14 Nov 2014 22:49:16 +0100 Subject: [PATCH 2/4] pcsx2: interpreter: increment pc before reading pc content memRead32 could throw a TLB miss exception, however TLB handler expects pc counter to be incremented Goemon is now really "playable" with the interpreter (disable automatic gamefix) --- pcsx2/Interpreter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index 60f2cb435c..2706f3f7b0 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -133,8 +133,13 @@ static void execI() intCheckMemcheck(); + u32 pc = cpuRegs.pc; + // We need to increase the pc before executing the memRead32. An exception could appears + // and it expects the PC counter to be pre-incremented + cpuRegs.pc += 4; + // interprete instruction - cpuRegs.code = memRead32( cpuRegs.pc ); + cpuRegs.code = memRead32( pc ); if( IsDebugBuild ) debugI(); @@ -159,7 +164,6 @@ static void execI() cpuBlockCycles += opcode.cycles; - cpuRegs.pc += 4; opcode.interpret(); } From 9c92a30dbbe7b0bafa05ef3ffc9db4e027fe42a4 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Fri, 14 Nov 2014 22:52:27 +0100 Subject: [PATCH 3/4] pcsx2: interpreter: add a typical example to disassemble R5900 Previous examples don't work anymore v2: merge the cpu dump example --- pcsx2/DebugTools/Debug.h | 2 +- pcsx2/Interpreter.cpp | 36 ++++++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/pcsx2/DebugTools/Debug.h b/pcsx2/DebugTools/Debug.h index e5b1950287..9e9e4b0fdb 100644 --- a/pcsx2/DebugTools/Debug.h +++ b/pcsx2/DebugTools/Debug.h @@ -28,7 +28,7 @@ extern char* disVU1MicroLF(u32 code, u32 pc); namespace R5900 { - void disR5900Fasm( std::string& output, u32 code, u32 pc, bool simplify); + void disR5900Fasm( std::string& output, u32 code, u32 pc, bool simplify = false); extern const char * const GPR_REG[32]; extern const char * const COP0_REG[32]; diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index 2706f3f7b0..1beddd1dba 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -45,7 +45,6 @@ static void debugI() if( cpuRegs.GPR.n.r0.UD[0] || cpuRegs.GPR.n.r0.UD[1] ) Console.Error("R0 is not zero!!!!"); } -//long int runs=0; void intBreakpoint(bool memcheck) { @@ -144,23 +143,32 @@ static void execI() debugI(); const OPCODE& opcode = GetCurrentInstruction(); +#if 0 + static long int runs = 0; //use this to find out what opcodes your game uses. very slow! (rama) - //runs++; - //if (runs > 1599999999){ //leave some time to startup the testgame - // if (opcode.Name[0] == 'L') { //find all opcodes beginning with "L" - // Console.WriteLn ("Load %s", opcode.Name); - // } - //} + runs++; + if (runs > 1599999999){ //leave some time to startup the testgame + if (opcode.Name[0] == 'L') { //find all opcodes beginning with "L" + Console.WriteLn ("Load %s", opcode.Name); + } + } +#endif - // Another method of instruction dumping: - /*if( cpuRegs.cycle > 0x4f24d714 ) - { - //CPU_LOG( "%s", disR5900Current.getCString()); +#if 0 + static long int print_me = 0; + // Based on cycle + // if( cpuRegs.cycle > 0x4f24d714 ) + // Or dump from a particular PC (useful to debug handler/syscall) + if (cpuRegs.pc == 0x80000000) { + print_me = 2000; + } + if (print_me) { + print_me--; disOut.clear(); - opcode.disasm( disOut ); - disOut += '\n'; + disR5900Fasm(disOut, cpuRegs.code, pc); CPU_LOG( disOut.c_str() ); - }*/ + } +#endif cpuBlockCycles += opcode.cycles; From c9aa04c679e015d55db0953d46a2b2cf95bac722 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sun, 30 Nov 2014 11:43:24 +0100 Subject: [PATCH 4/4] core: EE interpreter major speed boost Disable the debugger and an useless debugI function * x2 on dbg build :) * x2.5 on dev build :) Note: debugger doesn't work yet with the interpreter so no real drawback. --- pcsx2/Interpreter.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index 1beddd1dba..a4448e8d4c 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -126,11 +126,19 @@ void intCheckMemcheck() static void execI() { + // execI is called for every instruction so it must remains as light as possible. + // If you enable the next define, Interpreter will be much slower (around + // ~4fps on 3.9GHz Haswell vs ~8fps (even 10fps on dev build)) + // Extra note: due to some cycle count issue PCSX2's internal debugger is + // not yet usable with the interpreter +//#define EXTRA_DEBUG +#ifdef EXTRA_DEBUG // check if any breakpoints or memchecks are triggered by this instruction if (isBreakpointNeeded(cpuRegs.pc)) intBreakpoint(false); intCheckMemcheck(); +#endif u32 pc = cpuRegs.pc; // We need to increase the pc before executing the memRead32. An exception could appears @@ -139,8 +147,11 @@ static void execI() // interprete instruction cpuRegs.code = memRead32( pc ); + // Honestly I think this code is useless nowadays. +#ifdef EXTRA_DEBUG if( IsDebugBuild ) debugI(); +#endif const OPCODE& opcode = GetCurrentInstruction(); #if 0