2026-01-12 05:18:12 -05:00
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
2024-07-30 13:42:36 +02:00
// SPDX-License-Identifier: GPL-3.0+
2022-12-28 17:44:34 +10:00
2009-02-09 21:15:56 +00:00
#include "Common.h"
#include "R5900OpcodeTables.h"
2021-12-19 01:11:52 +10:00
#include "VMManager.h"
2010-04-22 00:48:06 +00:00
#include "Elfheader.h"
2024-12-08 14:05:03 -05:00
#include "Cache.h"
2026-06-20 20:27:56 -07:00
#include "ee_divtrace.h"
2026-07-31 04:00:14 -06:00
#include "no-jit-improvements.h"
2010-04-19 23:46:12 +00:00
2021-05-14 01:42:47 +02:00
#include "DebugTools/Breakpoints.h"
2014-08-21 13:01:07 +02:00
2022-12-28 17:44:34 +10:00
#include "common/FastJmp.h"
2009-02-09 21:15:56 +00:00
#include <float.h>
using namespace R5900 ; // for OPCODE and OpcodeImpl
extern int vu0branch , vu1branch ;
static int branch2 = 0 ;
static u32 cpuBlockCycles = 0 ; // 3 bit fixed point version of cycle count
static std :: string disOut ;
2022-04-04 22:19:39 +10:00
static bool intExitExecution = false ;
2022-12-28 17:44:34 +10:00
static fastjmp_buf intJmpBuf ;
2023-11-02 16:52:40 +00:00
static u32 intLastBranchTo ;
2009-02-09 21:15:56 +00:00
2025-11-22 07:39:17 +00:00
void intEventTest ();
2009-10-23 20:24:59 +00:00
2026-06-20 20:27:56 -07:00
// Charge raw block cycles for a syscall handler the interpreter
// SKIPPED (FlushCache/iFlushCache under g_skip_flushcache_syscall), mirroring the
// JIT's recSYSCALL `s_nBlockCycles += 5650`. cpuBlockCycles is the same 3-bit
// fixed-point accumulator as the JIT's s_nBlockCycles with identical scaling
// (intUpdateCPUCycles == scaleblockcycles_calculation), so adding the same raw
// constant keeps the cycle-derived hardware (EE timers) in lockstep across the
// skip. Gated entirely by the caller; no effect in production.
void intChargeSkippedHandlerCycles ( u32 raw_block_cycles )
{
cpuBlockCycles += raw_block_cycles ;
}
2023-11-02 15:19:38 +00:00
void intUpdateCPUCycles ()
2023-11-02 14:33:24 +00:00
{
2023-11-02 15:19:38 +00:00
const bool lowcycles = ( cpuBlockCycles <= 40 );
const s8 cyclerate = EmuConfig . Speedhacks . EECycleRate ;
u32 scale_cycles = 0 ;
2023-11-02 14:33:24 +00:00
2023-11-02 15:19:38 +00:00
if ( cyclerate == 0 || lowcycles || cyclerate < - 99 || cyclerate > 3 )
scale_cycles = cpuBlockCycles >> 3 ;
else if ( cyclerate > 1 )
scale_cycles = cpuBlockCycles >> ( 2 + cyclerate );
else if ( cyclerate == 1 )
scale_cycles = ( cpuBlockCycles >> 3 ) / 1.3f ; // Adds a mild 30% increase in clockspeed for value 1.
2024-02-11 15:24:16 +00:00
else if ( cyclerate == - 1 ) // the mildest value.
2023-11-02 15:19:38 +00:00
// These values were manually tuned to yield mild speedup with high compatibility
scale_cycles = ( cpuBlockCycles <= 80 || cpuBlockCycles > 168 ? 5 : 7 ) * cpuBlockCycles / 32 ;
else
scale_cycles = (( 5 + ( - 2 * ( cyclerate + 1 ))) * cpuBlockCycles ) >> 5 ;
// Ensure block cycle count is never less than 1.
cpuRegs . cycle += ( scale_cycles < 1 ) ? 1 : scale_cycles ;
2023-11-02 16:52:40 +00:00
if ( cyclerate > 1 )
{
cpuBlockCycles &= ( 0x1 << ( cyclerate + 2 )) - 1 ;
}
else
{
cpuBlockCycles &= 0x7 ;
}
2023-11-02 14:33:24 +00:00
}
2009-02-09 21:15:56 +00:00
// These macros are used to assemble the repassembler functions
2014-08-21 13:01:07 +02:00
void intBreakpoint ( bool memcheck )
{
2023-06-27 11:28:12 +02:00
const u32 pc = cpuRegs . pc ;
2021-01-08 18:34:08 -05:00
if ( CBreakPoints :: CheckSkipFirst ( BREAKPOINT_EE , pc ) != 0 )
2026-01-10 15:39:18 -05:00
{
CBreakPoints :: ClearSkipFirst ( BREAKPOINT_EE );
2014-08-21 13:01:07 +02:00
return ;
2026-01-10 15:39:18 -05:00
}
2014-08-21 13:01:07 +02:00
if ( ! memcheck )
{
2021-01-08 18:34:08 -05:00
auto cond = CBreakPoints :: GetBreakPointCondition ( BREAKPOINT_EE , pc );
2014-08-21 13:01:07 +02:00
if ( cond && ! cond -> Evaluate ())
return ;
}
2023-10-15 12:44:37 -04:00
CBreakPoints :: SetBreakpointTriggered ( true , BREAKPOINT_EE );
2022-02-02 23:05:38 -05:00
VMManager :: SetPaused ( true );
2023-04-27 21:47:52 -04:00
Cpu -> ExitExecution ();
2014-08-21 13:01:07 +02:00
}
void intMemcheck ( u32 op , u32 bits , bool store )
{
// compute accessed address
2025-10-25 12:56:29 -04:00
u32 start = cpuRegs . GPR . r [( op >> 21 ) & 0x1F ]. UL [ 0 ];
2023-06-27 11:28:12 +02:00
if ( static_cast < s16 > ( op ) != 0 )
start += static_cast < s16 > ( op );
2014-08-21 13:01:07 +02:00
if ( bits == 128 )
start &= ~ 0x0F ;
2022-01-20 23:59:48 -05:00
start = standardizeBreakpointAddress ( start );
2023-06-27 11:28:12 +02:00
const u32 end = start + bits / 8 ;
2022-09-13 21:20:25 -05:00
2023-01-03 00:27:05 -05:00
auto checks = CBreakPoints :: GetMemChecks ( BREAKPOINT_EE );
2014-08-21 13:01:07 +02:00
for ( size_t i = 0 ; i < checks . size (); i ++ )
{
auto & check = checks [ i ];
if ( check . result == 0 )
continue ;
2024-07-07 21:41:00 -04:00
if (( check . memCond & MEMCHECK_WRITE ) == 0 && store )
2014-08-21 13:01:07 +02:00
continue ;
2024-07-07 21:41:00 -04:00
if (( check . memCond & MEMCHECK_READ ) == 0 && ! store )
2014-08-21 13:01:07 +02:00
continue ;
2024-07-07 21:41:00 -04:00
if ( check . hasCond )
{
if ( ! check . cond . Evaluate ())
continue ;
}
2014-08-21 13:01:07 +02:00
if ( start < check . end && check . start < end )
intBreakpoint ( true );
}
}
void intCheckMemcheck ()
{
2023-06-27 11:28:12 +02:00
const u32 pc = cpuRegs . pc ;
const int needed = isMemcheckNeeded ( pc );
2014-08-21 13:01:07 +02:00
if ( needed == 0 )
return ;
2023-06-27 11:28:12 +02:00
const u32 op = memRead32 ( needed == 2 ? pc + 4 : pc );
2014-08-21 13:01:07 +02:00
const OPCODE & opcode = GetInstruction ( op );
2023-06-27 11:28:12 +02:00
const bool store = ( opcode . flags & IS_STORE ) != 0 ;
2014-08-21 13:01:07 +02:00
switch ( opcode . flags & MEMTYPE_MASK )
{
2023-06-27 11:28:12 +02:00
case MEMTYPE_BYTE :
intMemcheck ( op , 8 , store );
break ;
case MEMTYPE_HALF :
intMemcheck ( op , 16 , store );
break ;
case MEMTYPE_WORD :
intMemcheck ( op , 32 , store );
break ;
case MEMTYPE_DWORD :
intMemcheck ( op , 64 , store );
break ;
case MEMTYPE_QWORD :
intMemcheck ( op , 128 , store );
break ;
2014-08-21 13:01:07 +02:00
}
}
2009-02-09 21:15:56 +00:00
static void execI ()
{
2014-11-30 11:43:24 +01:00
// 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
2022-01-20 23:59:48 -05:00
#if defined(EXTRA_DEBUG) || defined(PCSX2_DEVBUILD)
2014-08-21 13:01:07 +02:00
// check if any breakpoints or memchecks are triggered by this instruction
if ( isBreakpointNeeded ( cpuRegs . pc ))
intBreakpoint ( false );
intCheckMemcheck ();
2026-01-10 15:39:18 -05:00
CBreakPoints :: CommitClearSkipFirst ( BREAKPOINT_EE );
2014-11-30 11:43:24 +01:00
#endif
2014-08-21 13:01:07 +02:00
2023-06-27 11:28:12 +02:00
const u32 pc = cpuRegs . pc ;
2014-11-14 22:49:16 +01:00
// 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 ;
2014-08-21 13:01:07 +02:00
// interprete instruction
2014-11-14 22:49:16 +01:00
cpuRegs . code = memRead32 ( pc );
2009-02-09 21:15:56 +00:00
const OPCODE & opcode = GetCurrentInstruction ();
2014-11-14 22:52:27 +01:00
#if 0
static long int runs = 0;
2009-02-09 21:15:56 +00:00
//use this to find out what opcodes your game uses. very slow! (rama)
2014-11-14 22:52:27 +01:00
runs++;
2023-06-27 11:28:12 +02:00
//leave some time to startup the testgame
if (runs > 1599999999)
{
//find all opcodes beginning with "L"
if (opcode.Name[0] == 'L')
{
2014-11-14 22:52:27 +01:00
Console.WriteLn ("Load %s", opcode.Name);
}
}
#endif
2009-02-09 21:15:56 +00:00
2014-11-14 22:52:27 +01:00
#if 0
static long int print_me = 0;
// Based on cycle
2025-05-17 02:58:07 +01:00
// if ( cpuRegs.cycle > 0x4f24d714 )
2014-11-14 22:52:27 +01:00
// Or dump from a particular PC (useful to debug handler/syscall)
2023-06-27 11:28:12 +02:00
if (pc == 0x80000000)
{
2014-11-14 22:52:27 +01:00
print_me = 2000;
}
2023-06-27 11:28:12 +02:00
if (print_me)
{
2014-11-14 22:52:27 +01:00
print_me--;
2009-02-27 06:40:05 +00:00
disOut.clear();
2014-11-14 22:52:27 +01:00
disR5900Fasm(disOut, cpuRegs.code, pc);
2009-02-27 06:40:05 +00:00
CPU_LOG( disOut.c_str() );
2014-11-14 22:52:27 +01:00
}
#endif
2009-02-27 06:40:05 +00:00
2023-11-02 14:33:24 +00:00
cpuBlockCycles += opcode . cycles * ( 2 - (( cpuRegs . CP0 . n . Config >> 18 ) & 0x1 ));
2009-02-09 21:15:56 +00:00
opcode . interpret ();
2026-06-20 20:27:56 -07:00
#ifdef PCSX2_RECOMPILER_TESTS
// One sample per retired instruction (including branch delay slots, which also
// flow through execI). cpuRegs.pc now points at the next instruction to execute,
// so a sample with pc=X is "architectural state just before executing X" — the
// same point the JIT block hook captures for block entry X. Off unless
// ee_divtrace::g_enabled was set for this frame (single relaxed load otherwise).
// Test-hook only — release builds drop the per-instruction probe entirely.
if ( ee_divtrace :: g_enabled . load ( std :: memory_order_relaxed ))
ee_divtrace :: RecordSample ( cpuRegs . pc );
#endif
2009-02-09 21:15:56 +00:00
}
2026-07-31 04:00:14 -06:00
static bool execCachedI ( u32 pc , u32 code , const OPCODE & opcode )
{
// A previous instruction can redirect execution without using an opcode
// marked as a branch (for example, an exception). Stop at that boundary.
if ( cpuRegs . pc != pc )
return false ;
cpuRegs . pc = pc + 4 ;
cpuRegs . code = code ;
cpuBlockCycles += opcode . cycles * ( 2 - (( cpuRegs . CP0 . n . Config >> 18 ) & 0x1 ));
opcode . interpret ();
#ifdef PCSX2_RECOMPILER_TESTS
if ( ee_divtrace :: g_enabled . load ( std :: memory_order_relaxed ))
ee_divtrace :: RecordSample ( cpuRegs . pc );
#endif
return ( opcode . flags & IS_BRANCH ) == 0 && cpuRegs . pc == ( pc + 4 );
}
2010-08-09 04:10:38 +00:00
static __fi void _doBranch_shared ( u32 tar )
2009-02-09 21:15:56 +00:00
{
branch2 = cpuRegs . branch = 1 ;
execI ();
// branch being 0 means an exception was thrown, since only the exception
// handler should ever clear it.
if ( cpuRegs . branch != 0 )
{
2023-11-02 16:52:40 +00:00
if ( Cpu == & intCpu )
{
if ( intLastBranchTo == tar && EmuConfig . Speedhacks . WaitLoop )
{
intUpdateCPUCycles ();
bool can_skip = true ;
if ( tar != 0x81fc0 )
{
if (( cpuRegs . pc - tar ) < ( 4 * 10 ))
{
2023-11-07 14:14:51 +01:00
for ( u32 i = tar ; i < cpuRegs . pc ; i += 4 )
2023-11-02 16:52:40 +00:00
{
if ( PSM ( i ) != 0 )
{
can_skip = false ;
break ;
}
}
}
else
can_skip = false ;
}
if ( can_skip )
{
2026-03-06 21:55:35 +01:00
if ( static_cast < s64 > ( cpuRegs . nextEventCycle - cpuRegs . cycle ) > 0 )
2023-11-02 16:52:40 +00:00
cpuRegs . cycle = cpuRegs . nextEventCycle ;
else
cpuRegs . nextEventCycle = cpuRegs . cycle ;
}
}
}
intLastBranchTo = tar ;
2009-02-09 21:15:56 +00:00
cpuRegs . pc = tar ;
cpuRegs . branch = 0 ;
}
}
2022-05-12 22:34:42 +10:00
static void doBranch ( u32 target )
2009-02-09 21:15:56 +00:00
{
_doBranch_shared ( target );
2023-11-02 15:19:38 +00:00
intUpdateCPUCycles ();
2009-08-25 15:38:48 +00:00
intEventTest ();
2009-02-09 21:15:56 +00:00
}
2022-05-12 22:34:42 +10:00
void intDoBranch ( u32 target )
2009-02-09 21:15:56 +00:00
{
2009-10-04 08:27:27 +00:00
//Console.WriteLn("Interpreter Branch ");
2009-02-09 21:15:56 +00:00
_doBranch_shared ( target );
if ( Cpu == & intCpu )
{
2023-11-02 15:19:38 +00:00
intUpdateCPUCycles ();
2009-08-25 15:38:48 +00:00
intEventTest ();
2009-02-09 21:15:56 +00:00
}
}
2010-04-25 00:31:27 +00:00
void intSetBranch ()
2009-04-24 02:28:14 +00:00
{
2009-02-09 21:15:56 +00:00
branch2 = /*cpuRegs.branch =*/ 1 ;
}
////////////////////////////////////////////////////////////////////
// R5900 Branching Instructions!
// These are the interpreter versions of the branch instructions. Unlike other
// types of interpreter instructions which can be called safely from the recompilers,
// these instructions are not "recSafe" because they may not invoke the
// necessary branch test logic that the recs need to maintain sync with the
// cpuRegs.pc and delaySlot instruction and such.
namespace R5900 {
namespace Interpreter {
namespace OpcodeImpl {
/*********************************************************
* Jump to target *
* Format: OP target *
*********************************************************/
2009-05-01 01:13:37 +00:00
// fixme: looking at the other branching code, shouldn't those _SetLinks in BGEZAL and such only be set
// if the condition is true? --arcum42
2010-04-25 00:31:27 +00:00
void J ()
2009-04-24 02:28:14 +00:00
{
2009-02-09 21:15:56 +00:00
doBranch ( _JumpTarget_ );
}
2010-04-25 00:31:27 +00:00
void JAL ()
2009-04-24 02:28:14 +00:00
{
2014-12-07 17:25:56 +01:00
// 0x3563b8 is the start address of the function that invalidate entry in TLB cache
if ( EmuConfig . Gamefixes . GoemonTlbHack ) {
if ( _JumpTarget_ == 0x3563b8 )
GoemonUnloadTlb ( cpuRegs . GPR . n . a0 . UL [ 0 ]);
}
2010-04-25 00:31:27 +00:00
_SetLink ( 31 );
2009-04-24 02:28:14 +00:00
doBranch ( _JumpTarget_ );
2009-02-09 21:15:56 +00:00
}
/*********************************************************
* Register branch logic *
* Format: OP rs, rt, offset *
*********************************************************/
2009-04-24 02:28:14 +00:00
void BEQ () // Branch if Rs == Rt
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] == cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ])
doBranch ( _BranchTarget_ );
else
2009-04-24 02:28:14 +00:00
intEventTest ();
2010-04-25 00:31:27 +00:00
}
2009-02-09 21:15:56 +00:00
2009-04-24 02:28:14 +00:00
void BNE () // Branch if Rs != Rt
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] != cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ])
doBranch ( _BranchTarget_ );
else
2009-04-24 02:28:14 +00:00
intEventTest ();
2010-04-25 00:31:27 +00:00
}
2009-02-09 21:15:56 +00:00
/*********************************************************
* Register branch logic *
* Format: OP rs, offset *
*********************************************************/
2009-04-24 02:28:14 +00:00
void BGEZ () // Branch if Rs >= 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] >= 0 )
{
doBranch ( _BranchTarget_ );
2009-04-24 02:28:14 +00:00
}
2010-04-25 00:31:27 +00:00
}
2009-04-24 02:28:14 +00:00
void BGEZAL () // Branch if Rs >= 0 and link
2010-04-25 00:31:27 +00:00
{
2016-09-16 03:58:37 -05:00
_SetLink ( 31 );
2009-04-24 02:28:14 +00:00
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] >= 0 )
2010-04-25 00:31:27 +00:00
{
doBranch ( _BranchTarget_ );
2009-04-24 02:28:14 +00:00
}
2010-04-25 00:31:27 +00:00
}
2009-04-24 02:28:14 +00:00
void BGTZ () // Branch if Rs > 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] > 0 )
{
doBranch ( _BranchTarget_ );
2009-04-24 02:28:14 +00:00
}
2010-04-25 00:31:27 +00:00
}
2009-04-24 02:28:14 +00:00
void BLEZ () // Branch if Rs <= 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] <= 0 )
{
doBranch ( _BranchTarget_ );
2009-04-24 02:28:14 +00:00
}
2010-04-25 00:31:27 +00:00
}
2009-04-24 02:28:14 +00:00
void BLTZ () // Branch if Rs < 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < 0 )
{
doBranch ( _BranchTarget_ );
2009-04-24 02:28:14 +00:00
}
2010-04-25 00:31:27 +00:00
}
2009-04-24 02:28:14 +00:00
void BLTZAL () // Branch if Rs < 0 and link
2010-04-25 00:31:27 +00:00
{
2016-09-16 03:58:37 -05:00
_SetLink ( 31 );
2010-04-25 00:31:27 +00:00
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < 0 )
{
doBranch ( _BranchTarget_ );
2009-02-09 21:15:56 +00:00
}
2010-04-25 00:31:27 +00:00
}
2009-02-09 21:15:56 +00:00
/*********************************************************
* Register branch logic Likely *
* Format: OP rs, offset *
*********************************************************/
2009-04-24 02:28:14 +00:00
void BEQL () // Branch if Rs == Rt
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] == cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ])
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
intEventTest ();
}
2009-04-24 02:28:14 +00:00
}
void BNEL () // Branch if Rs != Rt
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] != cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ])
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
intEventTest ();
}
2009-04-24 02:28:14 +00:00
}
void BLEZL () // Branch if Rs <= 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] <= 0 )
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
2009-04-24 02:28:14 +00:00
intEventTest ();
}
}
void BGTZL () // Branch if Rs > 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] > 0 )
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
2009-04-24 02:28:14 +00:00
intEventTest ();
}
}
void BLTZL () // Branch if Rs < 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < 0 )
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
2009-04-24 02:28:14 +00:00
intEventTest ();
}
}
void BGEZL () // Branch if Rs >= 0
2010-04-25 00:31:27 +00:00
{
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] >= 0 )
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
2009-04-24 02:28:14 +00:00
intEventTest ();
}
}
void BLTZALL () // Branch if Rs < 0 and link
2010-04-25 00:31:27 +00:00
{
2016-09-16 03:58:37 -05:00
_SetLink ( 31 );
2010-04-25 00:31:27 +00:00
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < 0 )
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
intEventTest ();
2009-04-24 02:28:14 +00:00
}
}
void BGEZALL () // Branch if Rs >= 0 and link
2010-04-25 00:31:27 +00:00
{
2016-09-16 03:58:37 -05:00
_SetLink ( 31 );
2010-04-25 00:31:27 +00:00
if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] >= 0 )
{
doBranch ( _BranchTarget_ );
}
else
{
cpuRegs . pc += 4 ;
intEventTest ();
2009-04-24 02:28:14 +00:00
}
}
2009-02-09 21:15:56 +00:00
/*********************************************************
* Register jump *
* Format: OP rs, rd *
*********************************************************/
2010-04-25 00:31:27 +00:00
void JR ()
{
2014-12-07 19:25:14 +01:00
// 0x33ad48 and 0x35060c are the return address of the function (0x356250) that populate the TLB cache
2014-12-07 17:25:56 +01:00
if ( EmuConfig . Gamefixes . GoemonTlbHack ) {
2023-06-27 11:28:12 +02:00
const u32 add = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ];
2014-12-07 19:25:14 +01:00
if ( add == 0x33ad48 || add == 0x35060c )
2014-12-07 17:25:56 +01:00
GoemonPreloadTlb ();
2014-06-12 00:35:28 +02:00
}
2010-04-25 00:31:27 +00:00
doBranch ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ]);
2009-02-09 21:15:56 +00:00
}
2009-04-24 02:28:14 +00:00
void JALR ()
2010-04-25 00:31:27 +00:00
{
2023-06-27 11:28:12 +02:00
const u32 temp = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ];
2010-04-25 00:31:27 +00:00
if ( _Rd_ ) _SetLink ( _Rd_ );
2009-02-09 21:15:56 +00:00
doBranch ( temp );
}
} } } // end namespace R5900::Interpreter::OpcodeImpl
2010-10-22 16:23:52 +00:00
// --------------------------------------------------------------------------------------
// R5900cpu/intCpu interface (implementations)
// --------------------------------------------------------------------------------------
static void intReserve ()
{
// fixme : detect cpu for use the optimize asm code
}
2009-02-09 21:15:56 +00:00
2009-10-23 20:24:59 +00:00
static void intReset ()
2009-02-09 21:15:56 +00:00
{
cpuRegs . branch = 0 ;
branch2 = 0 ;
2026-07-31 04:00:14 -06:00
NoJITImprovements :: ResetEEBlockCache ();
2009-02-09 21:15:56 +00:00
}
2025-11-22 07:39:17 +00:00
void intEventTest ()
2009-02-09 21:15:56 +00:00
{
// Perform counters, ints, and IOP updates:
2010-09-05 15:38:14 +00:00
_cpuEventTest_Shared ();
2022-09-13 21:20:25 -05:00
2022-04-04 22:19:39 +10:00
if ( intExitExecution )
{
intExitExecution = false ;
2024-12-08 14:05:03 -05:00
if ( CHECK_EEREC )
writebackCache ();
2022-12-28 17:44:34 +10:00
fastjmp_jmp ( & intJmpBuf , 1 );
2022-04-04 22:19:39 +10:00
}
2009-02-09 21:15:56 +00:00
}
2022-04-05 21:53:49 +10:00
static void intSafeExitExecution ()
2009-10-23 20:24:59 +00:00
{
2022-04-05 21:53:49 +10:00
// If we're currently processing events, we can't safely jump out of the interpreter here, because we'll
// leave things in an inconsistent state. So instead, we flag it for exiting once cpuEventTest() returns.
if ( eeEventTestIsActive )
intExitExecution = true ;
else
2024-12-08 14:05:03 -05:00
{
if ( CHECK_EEREC )
writebackCache ();
2022-12-28 17:44:34 +10:00
fastjmp_jmp ( & intJmpBuf , 1 );
2024-12-08 14:05:03 -05:00
}
2022-12-28 17:44:34 +10:00
}
static void intCancelInstruction ()
{
// See execute function.
fastjmp_jmp ( & intJmpBuf , 0 );
}
static void intExecute ()
{
// This will come back as zero the first time it runs, or on instruction cancel.
// It will come back as nonzero when we exit execution.
if ( fastjmp_set ( & intJmpBuf ) != 0 )
return ;
for (;;)
{
2023-06-13 22:43:11 +10:00
if ( ! VMManager :: Internal :: HasBootedELF ())
{
// Avoid reloading every instruction.
u32 elf_entry_point = VMManager :: Internal :: GetCurrentELFEntryPoint ();
u32 eeload_main = g_eeloadMain ;
2023-06-24 22:16:19 +10:00
u32 eeload_exec = g_eeloadExec ;
2023-06-13 22:43:11 +10:00
while ( true )
2022-12-28 17:44:34 +10:00
{
2023-06-13 22:43:11 +10:00
execI ();
2022-12-28 17:44:34 +10:00
if ( cpuRegs . pc == EELOAD_START )
{
// The EELOAD _start function is the same across all BIOS versions afaik
2023-06-27 11:28:12 +02:00
const u32 mainjump = memRead32 ( EELOAD_START + 0x9c );
2022-12-28 17:44:34 +10:00
if ( mainjump >> 26 == 3 ) // JAL
g_eeloadMain = (( EELOAD_START + 0xa0 ) & 0xf0000000U ) | ( mainjump << 2 & 0x0fffffffU );
2023-06-13 22:43:11 +10:00
eeload_main = g_eeloadMain ;
2022-12-28 17:44:34 +10:00
}
2023-06-24 22:16:19 +10:00
else if ( cpuRegs . pc == eeload_main )
2022-12-28 17:44:34 +10:00
{
eeloadHook ();
2023-06-13 22:43:11 +10:00
if ( VMManager :: Internal :: IsFastBootInProgress ())
2022-12-28 17:44:34 +10:00
{
2023-08-01 11:41:23 +01:00
// See comments on this code in iR5900.cpp's recRecompile()
2023-06-27 11:28:12 +02:00
const u32 typeAexecjump = memRead32 ( EELOAD_START + 0x470 );
const u32 typeBexecjump = memRead32 ( EELOAD_START + 0x5B0 );
const u32 typeCexecjump = memRead32 ( EELOAD_START + 0x618 );
const u32 typeDexecjump = memRead32 ( EELOAD_START + 0x600 );
2022-12-28 17:44:34 +10:00
if (( typeBexecjump >> 26 == 3 ) || ( typeCexecjump >> 26 == 3 ) || ( typeDexecjump >> 26 == 3 )) // JAL to 0x822B8
g_eeloadExec = EELOAD_START + 0x2B8 ;
else if ( typeAexecjump >> 26 == 3 ) // JAL to 0x82170
g_eeloadExec = EELOAD_START + 0x170 ;
else
Console . WriteLn ( "intExecute: Could not enable launch arguments for fast boot mode; unidentified BIOS version! Please report this to the PCSX2 developers." );
2023-06-24 22:16:19 +10:00
eeload_exec = g_eeloadExec ;
2022-12-28 17:44:34 +10:00
}
2023-06-13 22:43:11 +10:00
elf_entry_point = VMManager :: Internal :: GetCurrentELFEntryPoint ();
2022-12-28 17:44:34 +10:00
}
2023-06-24 22:16:19 +10:00
else if ( cpuRegs . pc == eeload_exec )
2022-12-28 17:44:34 +10:00
{
eeloadHook2 ();
}
2023-06-13 22:43:11 +10:00
else if ( cpuRegs . pc == elf_entry_point )
2022-12-28 17:44:34 +10:00
{
2023-06-13 22:43:11 +10:00
VMManager :: Internal :: EntryPointCompilingOnCPUThread ();
break ;
2022-12-28 17:44:34 +10:00
}
}
2023-06-13 22:43:11 +10:00
}
else
{
while ( true )
2026-07-31 04:00:14 -06:00
{
if ( ! NoJITImprovements :: ExecuteEEBlock ( cpuRegs . pc , execCachedI ))
execI ();
}
2022-12-28 17:44:34 +10:00
}
}
2009-10-23 20:24:59 +00:00
}
2009-02-09 21:15:56 +00:00
static void intStep ()
{
2026-07-07 06:44:20 -07:00
// Arm the cancel target: intCancelInstruction (TLB miss / vtlb_Miss path)
// longjmps to intJmpBuf, which only intExecute used to arm — a cancel
// during a single Step (debugger stepping, recompiler_tests interp
// oracle) jumped through an unarmed buffer straight to PC=0. A cancelled
// instruction has already vectored via cpuException, so returning here
// with pc on the exception vector is exactly one completed "step".
if ( fastjmp_set ( & intJmpBuf ) != 0 )
return ;
2009-02-09 21:15:56 +00:00
execI ();
}
static void intClear ( u32 Addr , u32 Size )
{
2026-07-31 04:00:14 -06:00
NoJITImprovements :: InvalidateEEBlockCache ( Addr , Size );
2009-02-09 21:15:56 +00:00
}
static void intShutdown () {
2026-07-31 04:00:14 -06:00
NoJITImprovements :: ResetEEBlockCache ();
2009-02-09 21:15:56 +00:00
}
2009-12-06 15:31:11 +00:00
R5900cpu intCpu =
{
2010-10-22 16:23:52 +00:00
intReserve ,
2009-12-06 15:31:11 +00:00
intShutdown ,
2009-02-09 21:15:56 +00:00
intReset ,
intStep ,
intExecute ,
2009-12-06 15:31:11 +00:00
2022-04-05 21:53:49 +10:00
intSafeExitExecution ,
2022-12-28 17:44:34 +10:00
intCancelInstruction ,
2022-10-12 23:57:53 +10:00
intClear
2009-02-09 21:15:56 +00:00
};