mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
131 lines
4 KiB
C++
131 lines
4 KiB
C++
/*
|
|
Copyright (C) 2006 StrmnNrmn
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include "BuildOptions.h"
|
|
#include "Base/Types.h"
|
|
#include "DynaRec/AssemblyUtils.h"
|
|
|
|
#include "DynarecTargetPSP.h"
|
|
|
|
#include <limits.h>
|
|
|
|
#include <psputilsforkernel.h>
|
|
#include <psputils.h>
|
|
|
|
#include "Base/MathUtil.h"
|
|
|
|
extern "C" { void _DaedalusICacheInvalidate( const void * address, u32 length ); }
|
|
|
|
namespace AssemblyUtils
|
|
{
|
|
|
|
|
|
// Patch a long jump to target the specified location.
|
|
// Return true if the patching succeeded (i.e. within range), false otherwise
|
|
|
|
bool PatchJumpLong( CJumpLocation jump, CCodeLabel target )
|
|
{
|
|
// Get an uncached pointer
|
|
PspOpCode * p_jump_addr( reinterpret_cast< PspOpCode * >( jump.GetWritableU8P() ) );
|
|
PspOpCode & op_code( *p_jump_addr );
|
|
|
|
if( op_code.op == OP_J || op_code.op == OP_JAL )
|
|
{
|
|
op_code.target = reinterpret_cast<u32>(target.GetTargetU8P()) >> 2;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
bool is_standard_branch( op_code.op == OP_BNE || op_code.op == OP_BEQ ||
|
|
op_code.op == OP_BEQL || op_code.op == OP_BNEL ||
|
|
op_code.op == OP_BLEZ || op_code.op == OP_BGTZ );
|
|
bool is_regimm_branch( (op_code.op == OP_REGIMM && (op_code.regimm_op == RegImmOp_BGEZ ||
|
|
op_code.regimm_op == RegImmOp_BLTZ ||
|
|
op_code.regimm_op == RegImmOp_BGEZL ||
|
|
op_code.regimm_op == RegImmOp_BLTZL ) ) );
|
|
bool is_cop1_branch( (op_code.cop1_op == Cop1Op_BCInstr) && ( op_code.cop1_bc == Cop1BCOp_BC1F ||
|
|
op_code.cop1_bc == Cop1BCOp_BC1FL ||
|
|
op_code.cop1_bc == Cop1BCOp_BC1T ||
|
|
op_code.cop1_bc == Cop1BCOp_BC1TL ) );
|
|
DAEDALUS_ASSERT( is_standard_branch || is_regimm_branch || is_cop1_branch, "Unhandled branch type" );
|
|
#endif
|
|
|
|
s32 offset( ( jump.GetOffset( target ) >> 2 ) - 1 );
|
|
|
|
//
|
|
// Check if the branch is within range
|
|
//
|
|
if( offset < SHRT_MIN || offset > SHRT_MAX )
|
|
{
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DAEDALUS_ERROR(" PatchJump out of range!!!");
|
|
#endif
|
|
return false;
|
|
}
|
|
op_code.offset = s16(offset); // Already divided by 4
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// As above but invalidates the instruction cache for the specified address.
|
|
|
|
bool PatchJumpLongAndFlush( CJumpLocation jump, CCodeLabel target )
|
|
{
|
|
if( PatchJumpLong( jump, target ) )
|
|
{
|
|
// sceKernelDcacheWritebackRange( jump.GetTargetU8P(), 4 );
|
|
// sceKernelIcacheInvalidateRange( jump.GetTargetU8P(), 4 );
|
|
|
|
const u8 * p_lower( RoundPointerDown( jump.GetTargetU8P(), 64 ) );
|
|
const u8 * p_upper( RoundPointerUp( jump.GetTargetU8P() + 8, 64 ) );
|
|
const u32 size( p_upper - p_lower);
|
|
|
|
_DaedalusICacheInvalidate( p_lower, size );
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Replace a branch instruction with an unconditional jump
|
|
void ReplaceBranchWithJump( CJumpLocation branch, CCodeLabel target )
|
|
{
|
|
// Get an uncached pointer
|
|
PspOpCode * p_jump_addr( reinterpret_cast< PspOpCode * >( branch.GetWritableU8P() ) );
|
|
PspOpCode & op_code( *p_jump_addr );
|
|
|
|
// Sanity check this is actually a branch?
|
|
op_code.op = OP_J;
|
|
op_code.target = reinterpret_cast<u32>(target.GetTargetU8P()) >> 2;
|
|
|
|
// sceKernelDcacheWritebackRange( branch.GetTargetU8P(), 4 );
|
|
// sceKernelIcacheInvalidateRange( branch.GetTargetU8P(), 4 );
|
|
|
|
const u8 * p_lower( RoundPointerDown( branch.GetTargetU8P(), 64 ) );
|
|
const u8 * p_upper( RoundPointerUp( branch.GetTargetU8P() + 8, 64 ) );
|
|
const u32 size( p_upper - p_lower);
|
|
|
|
_DaedalusICacheInvalidate( p_lower, size );
|
|
}
|
|
|
|
|
|
}
|