mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
1061 lines
31 KiB
C++
1061 lines
31 KiB
C++
/*
|
|
Copyright (C) 2001,2005 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 "Base/Types.h"
|
|
|
|
|
|
#include "Interface/ConfigOptions.h"
|
|
#include "Core/CPU.h"
|
|
#include "Core/R4300.h"
|
|
#include "Debug/Registers.h"
|
|
#include "Debug/DBGConsole.h"
|
|
#include "Debug/DebugLog.h"
|
|
#include "DynaRec/AssemblyUtils.h"
|
|
#include "DynaRec/IndirectExitMap.h"
|
|
#include "DynaRec/StaticAnalysis.h"
|
|
#include "DynaRec/Trace.h"
|
|
#include "Ultra/ultra_R4300.h"
|
|
|
|
#include "CodeGeneratorX64.h"
|
|
|
|
using namespace AssemblyUtils;
|
|
|
|
static const u32 NUM_MIPS_REGISTERS( 32 );
|
|
static const u32 INTEL_REG_UNUSED( ~0 );
|
|
// XX this optimisation works very well on the PSP, option to disable it was removed
|
|
static const bool gDynarecStackOptimisation = true;
|
|
//*****************************************************************************
|
|
// XXXX
|
|
//*****************************************************************************
|
|
void Dynarec_ClearedCPUStuffToDo()
|
|
{
|
|
}
|
|
void Dynarec_SetCPUStuffToDo()
|
|
{
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// Register Caching
|
|
//*****************************************************************************
|
|
class CRegisterStatus
|
|
{
|
|
struct CachedRegInfo
|
|
{
|
|
EIntelReg iCachedIReg; // INVALID_CODE if uncached, else ???_CODE of intel reg we're cached in
|
|
bool BLoValid; // If cached, true if value in intel register is valid
|
|
bool BLoDirty; // If cached, true if value has been modified
|
|
|
|
u32 HiValue; // If BHiUnknown is false, this is the value of the register (through setmipshi..)
|
|
bool BHiDirty; // Do we need to write information about this register back to memory?
|
|
bool BHiUnknown; // If true, we don't know the value of this register
|
|
};
|
|
public:
|
|
|
|
void Reset()
|
|
{
|
|
for (u32 i = 0; i < NUM_REGISTERS; i++)
|
|
{
|
|
SetCachedReg( i, INVALID_CODE );
|
|
MarkAsValid( i, false );
|
|
MarkAsDirty( i, false );
|
|
|
|
MarkHiAsDirty( i, false );
|
|
MarkHiAsUnknown( i, true );
|
|
SetHiValue( i, 0 ); // Ignored
|
|
}
|
|
}
|
|
|
|
inline void MarkAsValid( u32 mreg, bool valid )
|
|
{
|
|
mCachedRegisterInfo[mreg].BLoValid = valid;
|
|
}
|
|
|
|
inline bool IsValid( u32 mreg ) const
|
|
{
|
|
return mCachedRegisterInfo[mreg].BLoValid;
|
|
}
|
|
|
|
inline bool IsDirty( u32 mreg ) const
|
|
{
|
|
return mCachedRegisterInfo[mreg].BLoDirty;
|
|
}
|
|
|
|
inline void MarkAsDirty( u32 mreg, bool dirty )
|
|
{
|
|
mCachedRegisterInfo[mreg].BLoDirty = dirty;
|
|
}
|
|
|
|
inline EIntelReg GetCachedReg( u32 mreg ) const
|
|
{
|
|
return mCachedRegisterInfo[mreg].iCachedIReg;
|
|
}
|
|
|
|
inline void SetCachedReg( u32 mreg, EIntelReg reg )
|
|
{
|
|
mCachedRegisterInfo[mreg].iCachedIReg = reg;
|
|
}
|
|
|
|
inline void MarkHiAsUnknown( u32 mreg, bool unk )
|
|
{
|
|
mCachedRegisterInfo[mreg].BHiUnknown = unk;
|
|
}
|
|
|
|
inline void MarkHiAsDirty( u32 mreg, bool dirty )
|
|
{
|
|
mCachedRegisterInfo[mreg].BHiDirty = dirty;
|
|
}
|
|
|
|
inline bool IsHiDirty( u32 mreg ) const
|
|
{
|
|
return mCachedRegisterInfo[mreg].BHiDirty;
|
|
}
|
|
|
|
inline bool IsHiUnknown( u32 mreg ) const
|
|
{
|
|
return mCachedRegisterInfo[mreg].BHiUnknown;
|
|
}
|
|
|
|
inline void SetHiValue( u32 mreg, u32 value )
|
|
{
|
|
mCachedRegisterInfo[mreg].HiValue = value;
|
|
}
|
|
|
|
inline u32 GetHiValue( u32 mreg ) const
|
|
{
|
|
return mCachedRegisterInfo[mreg].HiValue;
|
|
}
|
|
|
|
private:
|
|
static const u32 NUM_REGISTERS = 32;
|
|
CachedRegInfo mCachedRegisterInfo[NUM_REGISTERS];
|
|
};
|
|
|
|
|
|
static CRegisterStatus gRegisterStatus;
|
|
static u32 gIntelRegUsageMap[NUM_X64_REGISTERS];
|
|
static u32 gWriteCheck[NUM_MIPS_REGISTERS];
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CCodeGeneratorX64::CCodeGeneratorX64( CAssemblyBuffer * p_primary, CAssemblyBuffer * p_secondary )
|
|
: CCodeGenerator( )
|
|
, CAssemblyWriterX64( p_primary )
|
|
, mSpCachedInESI( false )
|
|
, mSetSpPostUpdate( 0 )
|
|
, mpPrimary( p_primary )
|
|
, mpSecondary( p_secondary )
|
|
{
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::Finalise( ExceptionHandlerFn p_exception_handler_fn, const std::vector< CJumpLocation > & exception_handler_jumps, const std::vector< RegisterSnapshotHandle >& exception_handler_snapshots )
|
|
{
|
|
if( !exception_handler_jumps.empty() )
|
|
{
|
|
GenerateExceptionHander( p_exception_handler_fn, exception_handler_jumps, exception_handler_snapshots );
|
|
}
|
|
|
|
SetAssemblyBuffer( NULL );
|
|
mpPrimary = NULL;
|
|
mpSecondary = NULL;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
#if DAEDALUS_DEBUG_DYNAREC
|
|
u32 gNumFragmentsExecuted = 0;
|
|
extern "C"
|
|
{
|
|
|
|
void LogFragmentEntry( u32 entry_address )
|
|
{
|
|
gNumFragmentsExecuted++;
|
|
if(gNumFragmentsExecuted >= 0x99990)
|
|
{
|
|
printf("Address %08x\n", entry_address );
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::Initialise( u32 entry_address, u32 exit_address, u32 * hit_counter, const void * p_base, const SRegisterUsageInfo & register_usage )
|
|
{
|
|
#if DAEDALUS_DEBUG_DYNAREC
|
|
MOVI(FIRST_PARAM_REG_CODE, entry_address);
|
|
CALL( CCodeLabel( (void*)LogFragmentEntry ) );
|
|
#endif
|
|
// if( hit_counter != NULL )
|
|
// {
|
|
// MOV_REG_MEM( RAX_CODE, hit_counter );
|
|
// ADDI( RAX_CODE, 1 );
|
|
// MOV_MEM_REG( hit_counter, RAX_CODE );
|
|
// }
|
|
|
|
// p_base/span_list ignored for now
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::UpdateRegisterCaching( u32 instruction_idx )
|
|
{
|
|
// This is ignored for now
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
RegisterSnapshotHandle CCodeGeneratorX64::GetRegisterSnapshot()
|
|
{
|
|
// This doesn't do anything useful yet.
|
|
return RegisterSnapshotHandle( 0 );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CCodeLabel CCodeGeneratorX64::GetEntryPoint() const
|
|
{
|
|
return mpPrimary->GetStartAddress();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CCodeLabel CCodeGeneratorX64::GetCurrentLocation() const
|
|
{
|
|
return mpPrimary->GetLabel();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
u32 CCodeGeneratorX64::GetCompiledCodeSize() const
|
|
{
|
|
return mpPrimary->GetSize() + mpSecondary->GetSize();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateExitCode( u32 exit_address, u32 jump_address, u32 num_instructions, CCodeLabel next_fragment )
|
|
{
|
|
//DAEDALUS_ASSERT( exit_address != u32( ~0 ), "Invalid exit address" );
|
|
DAEDALUS_ASSERT( !next_fragment.IsSet() || jump_address == 0, "Shouldn't be specifying a jump address if we have a next fragment?" );
|
|
|
|
#ifdef _DEBUG
|
|
if(exit_address == u32(~0))
|
|
{
|
|
INT3();
|
|
}
|
|
#endif
|
|
|
|
MOVI(FIRST_PARAM_REG_CODE, num_instructions);
|
|
CALL( CCodeLabel( (void*)CPU_UpdateCounter ) );
|
|
|
|
// This jump may be NULL, in which case we patch it below
|
|
// This gets patched with a jump to the next fragment if the target is later found
|
|
CJumpLocation jump_to_next_fragment( GenerateBranchIfNotSet( const_cast< u32 * >( &gCPUState.StuffToDo ), next_fragment ) );
|
|
|
|
// If the flag was set, we need in initialise the pc/delay to exit with
|
|
CCodeLabel interpret_next_fragment( GetAssemblyBuffer()->GetLabel() );
|
|
|
|
u8 exit_delay;
|
|
|
|
if( jump_address != 0 )
|
|
{
|
|
SetVar( &gCPUState.TargetPC, jump_address );
|
|
exit_delay = EXEC_DELAY;
|
|
}
|
|
else
|
|
{
|
|
exit_delay = NO_DELAY;
|
|
}
|
|
|
|
SetVar8( &gCPUState.Delay, exit_delay );
|
|
SetVar( &gCPUState.CurrentPC, exit_address );
|
|
|
|
// No need to call CPU_SetPC(), as this is handled by CFragment when we exit
|
|
RET();
|
|
|
|
// Patch up the exit jump
|
|
if( !next_fragment.IsSet() )
|
|
{
|
|
PatchJumpLong( jump_to_next_fragment, interpret_next_fragment );
|
|
}
|
|
|
|
return jump_to_next_fragment;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// Handle branching back to the interpreter after an ERET
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::GenerateEretExitCode( u32 num_instructions, CIndirectExitMap * p_map )
|
|
{
|
|
MOVI(FIRST_PARAM_REG_CODE, num_instructions);
|
|
CALL( CCodeLabel( (void*)CPU_UpdateCounter ) );
|
|
|
|
// We always exit to the interpreter, regardless of the state of gCPUState.StuffToDo
|
|
|
|
// Eret is a bit bodged so we exit at PC + 4
|
|
MOV_REG_MEM( RAX_CODE, &gCPUState.CurrentPC );
|
|
ADDI( RAX_CODE, 4 );
|
|
MOV_MEM_REG( &gCPUState.CurrentPC, RAX_CODE );
|
|
SetVar8( &gCPUState.Delay, NO_DELAY );
|
|
|
|
// No need to call CPU_SetPC(), as this is handled by CFragment when we exit
|
|
|
|
RET();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// Handle branching back to the interpreter after an indirect jump
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::GenerateIndirectExitCode( u32 num_instructions, CIndirectExitMap * p_map )
|
|
{
|
|
MOVI(FIRST_PARAM_REG_CODE, num_instructions);
|
|
CALL( CCodeLabel( (void*) CPU_UpdateCounter ) );
|
|
|
|
CCodeLabel no_target( NULL );
|
|
CJumpLocation jump_to_next_fragment( GenerateBranchIfNotSet( const_cast< u32 * >( &gCPUState.StuffToDo ), no_target ) );
|
|
|
|
CCodeLabel exit_dynarec( GetAssemblyBuffer()->GetLabel() );
|
|
// New return address is in gCPUState.TargetPC
|
|
MOV_REG_MEM( RAX_CODE, &gCPUState.TargetPC );
|
|
MOV_MEM_REG( &gCPUState.CurrentPC, RAX_CODE );
|
|
SetVar8( &gCPUState.Delay, NO_DELAY );
|
|
|
|
// No need to call CPU_SetPC(), as this is handled by CFragment when we exit
|
|
|
|
RET();
|
|
|
|
// gCPUState.StuffToDo == 0, try to jump to the indirect target
|
|
PatchJumpLong( jump_to_next_fragment, GetAssemblyBuffer()->GetLabel() );
|
|
|
|
MOVI_64(FIRST_PARAM_REG_CODE, (uintptr_t)p_map);
|
|
MOV_REG_MEM( SECOND_PARAM_REG_CODE, &gCPUState.TargetPC );
|
|
CALL( CCodeLabel( (void*)IndirectExitMap_Lookup ) );
|
|
|
|
// If the target was not found, exit
|
|
TEST( RAX_CODE, RAX_CODE );
|
|
JELong( exit_dynarec );
|
|
|
|
JMP_REG( RAX_CODE );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::GenerateExceptionHander( ExceptionHandlerFn p_exception_handler_fn, const std::vector< CJumpLocation > & exception_handler_jumps, const std::vector< RegisterSnapshotHandle>& exception_handler_snapshots )
|
|
{
|
|
CCodeLabel exception_handler( GetAssemblyBuffer()->GetLabel() );
|
|
|
|
CALL( CCodeLabel( (void*)p_exception_handler_fn ) );
|
|
RET();
|
|
|
|
for( std::vector< CJumpLocation >::const_iterator it = exception_handler_jumps.begin(); it != exception_handler_jumps.end(); ++it )
|
|
{
|
|
CJumpLocation jump( *it );
|
|
PatchJumpLong( jump, exception_handler );
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::SetVar( u32 * p_var, u32 value )
|
|
{
|
|
MOVI_MEM( p_var, value );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::SetVar8( u32 * p_var, u8 value )
|
|
{
|
|
MOVI_MEM8( p_var, value );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::GenerateBranchHandler( CJumpLocation branch_handler_jump, RegisterSnapshotHandle snapshot )
|
|
{
|
|
PatchJumpLong( branch_handler_jump, GetAssemblyBuffer()->GetLabel() );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateBranchAlways( CCodeLabel target )
|
|
{
|
|
return JMPLong( target );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateBranchIfSet( const u32 * p_var, CCodeLabel target )
|
|
{
|
|
MOV_REG_MEM( RAX_CODE, p_var );
|
|
TEST( RAX_CODE, RAX_CODE );
|
|
|
|
return JNELong( target );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateBranchIfNotSet( const u32 * p_var, CCodeLabel target )
|
|
{
|
|
MOV_REG_MEM( RAX_CODE, p_var );
|
|
TEST( RAX_CODE, RAX_CODE );
|
|
|
|
return JELong( target );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateBranchIfEqual32( const u32 * p_var, u32 value, CCodeLabel target )
|
|
{
|
|
CMP_MEM32_I32( p_var, value );
|
|
|
|
return JELong( target );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateBranchIfEqual8( const u32 * p_var, u8 value, CCodeLabel target )
|
|
{
|
|
CMP_MEM32_I8( p_var, value );
|
|
|
|
return JELong( target );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateBranchIfNotEqual32( const u32 * p_var, u32 value, CCodeLabel target )
|
|
{
|
|
CMP_MEM32_I32( p_var, value );
|
|
|
|
return JNELong( target );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateBranchIfNotEqual8( const u32 * p_var, u8 value, CCodeLabel target )
|
|
{
|
|
CMP_MEM32_I8( p_var, value );
|
|
|
|
return JNELong( target );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// Generates instruction handler for the specified op code.
|
|
// Returns a jump location if an exception handler is required
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::GenerateOpCode( const STraceEntry& ti, bool branch_delay_slot, const SBranchDetails * p_branch, CJumpLocation * p_branch_jump)
|
|
{
|
|
u32 address = ti.Address;
|
|
bool exception = false;
|
|
OpCode op_code = ti.OpCode;
|
|
|
|
if (op_code._u32 == 0)
|
|
{
|
|
if( branch_delay_slot )
|
|
{
|
|
SetVar8( &gCPUState.Delay, NO_DELAY );
|
|
}
|
|
return CJumpLocation();
|
|
}
|
|
|
|
if( branch_delay_slot )
|
|
{
|
|
SetVar8( &gCPUState.Delay, EXEC_DELAY );
|
|
}
|
|
|
|
const EN64Reg rs = EN64Reg( op_code.rs );
|
|
const EN64Reg rt = EN64Reg( op_code.rt );
|
|
const EN64Reg rd = EN64Reg( op_code.rd );
|
|
const u32 sa = op_code.sa;
|
|
const EN64Reg base = EN64Reg( op_code.base );
|
|
//const u32 jump_target( (address&0xF0000000) | (op_code.target<<2) );
|
|
//const u32 branch_target( address + ( ((s32)(s16)op_code.immediate)<<2 ) + 4);
|
|
const u32 ft = op_code.ft;
|
|
|
|
|
|
bool handled = false;
|
|
switch(op_code.op)
|
|
{
|
|
case OP_J: handled = true; break;
|
|
case OP_JAL: GenerateJAL( address ); handled = true; break;
|
|
case OP_CACHE: GenerateCACHE( base, op_code.immediate, rt ); handled = true; break;
|
|
|
|
case OP_DADDI: GenerateDADDIU( rt, rs, s16( op_code.immediate ) ); handled = true; break;
|
|
case OP_DADDIU: GenerateDADDIU( rt, rs, s16( op_code.immediate ) ); handled = true; break;
|
|
|
|
// For LW, SW, SWC1, LB etc, only generate an exception handler if access wasn't done through the stack (handle = false)
|
|
// This will have to be reworked once we handle accesses other than the stack!
|
|
case OP_SW:
|
|
handled = GenerateSW(rt, base, s16(op_code.immediate));
|
|
exception = !handled;
|
|
break;
|
|
case OP_SWC1:
|
|
handled = GenerateSWC1(ft, base, s16(op_code.immediate));
|
|
exception = !handled;
|
|
break;
|
|
case OP_LW:
|
|
handled = GenerateLW(rt, base, s16(op_code.immediate));
|
|
exception = !handled;
|
|
break;
|
|
case OP_LB:
|
|
handled = GenerateLB(rt, base, s16(op_code.immediate));
|
|
exception = !handled;
|
|
break;
|
|
case OP_LBU:
|
|
handled = GenerateLBU(rt, base, s16(op_code.immediate));
|
|
exception = !handled;
|
|
break;
|
|
case OP_LH:
|
|
handled = GenerateLH(rt, base, s16(op_code.immediate));
|
|
exception = !handled;
|
|
break;
|
|
case OP_LWC1:
|
|
handled = GenerateLWC1(ft, base, s16(op_code.immediate));
|
|
exception = !handled;
|
|
break;
|
|
case OP_LUI:
|
|
GenerateLUI(rt, s16(op_code.immediate));
|
|
exception = true;
|
|
break;
|
|
|
|
case OP_ADDIU:
|
|
case OP_ADDI:
|
|
GenerateADDIU(rt, rs, s16(op_code.immediate)); handled = true; break;
|
|
break;
|
|
case OP_ANDI:
|
|
GenerateANDI(rt, rs, op_code.immediate);
|
|
handled = true;
|
|
break;
|
|
case OP_ORI:
|
|
GenerateORI(rt, rs, op_code.immediate);
|
|
handled = true;
|
|
break;
|
|
case OP_XORI:
|
|
GenerateXORI(rt, rs, op_code.immediate);
|
|
handled = true;
|
|
break;
|
|
case OP_SPECOP:
|
|
{
|
|
switch(op_code.spec_op)
|
|
{
|
|
// case SpecOp_JR: handled = GenerateJR(rs); break;
|
|
|
|
case SpecOp_SLL: GenerateSLL( rd, rt, sa ); handled = true; break;
|
|
case SpecOp_SRA: GenerateSRA( rd, rt, sa ); handled = true; break;
|
|
case SpecOp_SRL: GenerateSRL( rd, rt, sa ); handled = true; break;
|
|
|
|
case SpecOp_OR: GenerateOR( rd, rs, rt ); handled = true; break;
|
|
case SpecOp_AND: GenerateAND( rd, rs, rt ); handled = true; break;
|
|
case SpecOp_XOR: GenerateXOR( rd, rs, rt ); handled = true; break;
|
|
case SpecOp_NOR: GenerateNOR( rd, rs, rt ); handled = true; break;
|
|
|
|
case SpecOp_ADD:
|
|
case SpecOp_ADDU: GenerateADDU( rd, rs, rt ); handled = true; break;
|
|
|
|
case SpecOp_DADD:
|
|
case SpecOp_DADDU: GenerateDADDU( rd, rs, rt ); handled = true; break;
|
|
|
|
case SpecOp_SUB:
|
|
case SpecOp_SUBU: GenerateSUBU( rd, rs, rt ); handled = true; break;
|
|
|
|
case SpecOp_DSUB:
|
|
case SpecOp_DSUBU: GenerateDSUBU( rd, rs, rt ); handled = true; break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!handled)
|
|
{
|
|
if( R4300_InstructionHandlerNeedsPC( op_code ) )
|
|
{
|
|
SetVar( &gCPUState.CurrentPC, address );
|
|
exception = true;
|
|
}
|
|
GenerateGenericR4300( op_code, R4300_GetInstructionHandler( op_code ) );
|
|
}
|
|
CJumpLocation exception_handler;
|
|
CCodeLabel no_target( NULL );
|
|
|
|
if( exception )
|
|
{
|
|
exception_handler = GenerateBranchIfSet( const_cast< u32 * >( &gCPUState.StuffToDo ), no_target );
|
|
}
|
|
|
|
// Check whether we want to invert the status of this branch
|
|
if( p_branch != NULL )
|
|
{
|
|
//
|
|
// Check if the branch has been taken
|
|
//
|
|
if( p_branch->Direct )
|
|
{
|
|
if( p_branch->ConditionalBranchTaken )
|
|
{
|
|
*p_branch_jump = GenerateBranchIfNotEqual8( &gCPUState.Delay, DO_DELAY, no_target );
|
|
}
|
|
else
|
|
{
|
|
*p_branch_jump = GenerateBranchIfEqual8( &gCPUState.Delay, DO_DELAY, no_target );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// XXXX eventually just exit here, and skip default exit code below
|
|
if( p_branch->Eret )
|
|
{
|
|
*p_branch_jump = GenerateBranchAlways( no_target );
|
|
}
|
|
else
|
|
{
|
|
*p_branch_jump = GenerateBranchIfNotEqual32( &gCPUState.TargetPC, p_branch->TargetAddress, no_target );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( branch_delay_slot )
|
|
{
|
|
SetVar8( &gCPUState.Delay, NO_DELAY );
|
|
}
|
|
}
|
|
|
|
|
|
return exception_handler;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
void CCodeGeneratorX64::GenerateGenericR4300( OpCode op_code, CPU_Instruction p_instruction )
|
|
{
|
|
// XXXX Flush all fp registers before a generic call
|
|
|
|
// Call function - __fastcall
|
|
MOVI(FIRST_PARAM_REG_CODE, op_code._u32);
|
|
CALL( CCodeLabel( (void*)p_instruction ) );
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
CJumpLocation CCodeGeneratorX64::ExecuteNativeFunction( CCodeLabel speed_hack, bool check_return )
|
|
{
|
|
CALL( speed_hack );
|
|
if( check_return )
|
|
{
|
|
TEST( RAX_CODE, RAX_CODE );
|
|
|
|
return JELong( CCodeLabel(NULL) );
|
|
}
|
|
else
|
|
{
|
|
return CJumpLocation(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void CCodeGeneratorX64::GenerateCACHE( EN64Reg base, s16 offset, u32 cache_op )
|
|
{
|
|
u32 dwCache = cache_op & 0x3;
|
|
u32 dwAction = (cache_op >> 2) & 0x7;
|
|
|
|
// For instruction cache invalidation, make sure we let the CPU know so the whole
|
|
// dynarec system can be invalidated
|
|
if(dwCache == 0 && (dwAction == 0 || dwAction == 4))
|
|
{
|
|
MOV_REG_MEM(FIRST_PARAM_REG_CODE, &gCPUState.CPU[base]._u32_0);
|
|
MOVI(SECOND_PARAM_REG_CODE, 0x20);
|
|
ADDI(FIRST_PARAM_REG_CODE, offset);
|
|
CALL( CCodeLabel( reinterpret_cast< const void * >( CPU_InvalidateICacheRange ) ));
|
|
}
|
|
else
|
|
{
|
|
// We don't care about data cache etc
|
|
}
|
|
}
|
|
|
|
void CCodeGeneratorX64::GenerateLoad(EN64Reg base, s16 offset, u8 twiddle, u8 bits)
|
|
{
|
|
MOV_REG_MEM(RCX_CODE, &gCPUState.CPU[base]._u32_0);
|
|
|
|
if (twiddle == 0)
|
|
{
|
|
DAEDALUS_ASSERT_Q(bits == 32);
|
|
ADD(RCX_CODE, R15_CODE, true);
|
|
MOV_REG_MEM_BASE_OFFSET(RAX_CODE, RCX_CODE, offset);
|
|
}
|
|
else
|
|
{
|
|
ADDI(RCX_CODE, offset, true);
|
|
XORI(RCX_CODE, twiddle, true);
|
|
ADD(RCX_CODE, R15_CODE, true);
|
|
switch(bits)
|
|
{
|
|
case 32:
|
|
MOV_REG_MEM_BASE(RAX_CODE, RCX_CODE);
|
|
break;
|
|
case 16:
|
|
MOV16_REG_MEM_BASE(RAX_CODE, RCX_CODE);
|
|
break;
|
|
case 8:
|
|
MOV8_REG_MEM_BASE(RAX_CODE, RCX_CODE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CCodeGeneratorX64::GenerateLW( EN64Reg rt, EN64Reg base, s16 offset )
|
|
{
|
|
if (gDynarecStackOptimisation && base == N64Reg_SP)
|
|
{
|
|
GenerateLoad(base, offset, 0, 32);
|
|
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_0, RAX_CODE);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_1, RDX_CODE);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CCodeGeneratorX64::GenerateSWC1( u32 ft, EN64Reg base, s16 offset )
|
|
{
|
|
if (gDynarecStackOptimisation && base == N64Reg_SP)
|
|
{
|
|
MOV_REG_MEM(RCX_CODE, &gCPUState.CPU[base]._u32_0);
|
|
ADD(RCX_CODE, R15_CODE, true);
|
|
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.FPU[ft]._u32);
|
|
MOV_MEM_BASE_OFFSET_REG(RCX_CODE, offset, RAX_CODE);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//u32 address = (u32)( gGPR[op_code.base]._s32_0 + (s32)(s16)op_code.immediate );
|
|
// Write32Bits(address, gGPR[op_code.rt]._u32_0);
|
|
bool CCodeGeneratorX64::GenerateSW( EN64Reg rt, EN64Reg base, s16 offset )
|
|
{
|
|
if (gDynarecStackOptimisation && base == N64Reg_SP)
|
|
{
|
|
MOV_REG_MEM(RCX_CODE, &gCPUState.CPU[base]._u32_0);
|
|
ADD(RCX_CODE, R15_CODE, true);
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.CPU[rt]._u32_0);
|
|
MOV_MEM_BASE_OFFSET_REG(RCX_CODE, offset, RAX_CODE);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CCodeGeneratorX64::GenerateLB( EN64Reg rt, EN64Reg base, s16 offset )
|
|
{
|
|
if (gDynarecStackOptimisation && base == N64Reg_SP)
|
|
{
|
|
GenerateLoad(base, offset, U8_TWIDDLE, 8);
|
|
MOVSX(RAX_CODE, RAX_CODE, true);
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_0, RAX_CODE);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_1, RDX_CODE);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CCodeGeneratorX64::GenerateLBU( EN64Reg rt, EN64Reg base, s16 offset )
|
|
{
|
|
if (gDynarecStackOptimisation && base == N64Reg_SP)
|
|
{
|
|
GenerateLoad(base, offset, U8_TWIDDLE, 8);
|
|
MOVZX(RAX_CODE, RAX_CODE, true);
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_0, RAX_CODE);
|
|
XOR(RDX_CODE, RDX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_1, RDX_CODE);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CCodeGeneratorX64::GenerateLH( EN64Reg rt, EN64Reg base, s16 offset )
|
|
{
|
|
if (gDynarecStackOptimisation && base == N64Reg_SP)
|
|
{
|
|
GenerateLoad(base, offset, U16_TWIDDLE, 16);
|
|
|
|
MOVSX(RAX_CODE, RAX_CODE, false);
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_0, RAX_CODE);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_1, RDX_CODE);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//gGPR[op_code.rt]._s64 = (s64)(s32)((s32)(s16)op_code.immediate<<16);
|
|
void CCodeGeneratorX64::GenerateLUI( EN64Reg rt, s16 immediate )
|
|
{
|
|
if (rt == 0) return;
|
|
|
|
MOVI(RAX_CODE, s32(immediate) << 16);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
//gGPR[op_code.rt]._s64 = gGPR[op_code.rs]._s64 + (s32)(s16)op_code.immediate;
|
|
void CCodeGeneratorX64::GenerateDADDIU( EN64Reg rt, EN64Reg rs, s16 immediate )
|
|
{
|
|
if (rt == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
ADDI(RAX_CODE, immediate, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rt]._u64, RAX_CODE);
|
|
}
|
|
|
|
// gGPR[op_code.rt]._s64 = (s64)(s32)(gGPR[op_code.rs]._s32_0 + (s32)(s16)op_code.immediate);
|
|
void CCodeGeneratorX64::GenerateADDIU( EN64Reg rt, EN64Reg rs, s16 immediate )
|
|
{
|
|
if (rt == 0) return;
|
|
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u32_0);
|
|
ADDI(RAX_CODE, immediate);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rt]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
//gGPR[op_code.rt]._u64 = gGPR[op_code.rs]._u64 & (u64)(u16)op_code.immediate;
|
|
void CCodeGeneratorX64::GenerateANDI( EN64Reg rt, EN64Reg rs, u16 immediate )
|
|
{
|
|
if (rt == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
ANDI(RAX_CODE, immediate, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rt]._u64, RAX_CODE);
|
|
}
|
|
|
|
//gGPR[op_code.rt]._u64 = gGPR[op_code.rs]._u64 | (u64)(u16)op_code.immediate;
|
|
void CCodeGeneratorX64::GenerateORI( EN64Reg rt, EN64Reg rs, u16 immediate )
|
|
{
|
|
if (rt == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
ORI(RAX_CODE, immediate, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rt]._u64, RAX_CODE);
|
|
}
|
|
|
|
// gGPR[op_code.rt]._u64 = gGPR[op_code.rs]._u64 ^ (u64)(u16)op_code.immediate;
|
|
void CCodeGeneratorX64::GenerateXORI( EN64Reg rt, EN64Reg rs, u16 immediate )
|
|
{
|
|
if (rt == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
XORI(RAX_CODE, immediate, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rt]._u64, RAX_CODE);
|
|
}
|
|
|
|
// gGPR[ op_code.rd ]._s64 = (s64)(s32)( (gGPR[ op_code.rt ]._u32_0 << op_code.sa) & 0xFFFFFFFF );
|
|
void CCodeGeneratorX64::GenerateSLL( EN64Reg rd, EN64Reg rt, u32 sa )
|
|
{
|
|
// NOP
|
|
if (rd == 0) return;
|
|
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.CPU[rt]._u32_0);
|
|
SHLI(RAX_CODE, sa);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
// gGPR[ op_code.rd ]._s64 = (s64)(s32)( gGPR[ op_code.rt ]._u32_0 >> op_code.sa );
|
|
void CCodeGeneratorX64::GenerateSRL( EN64Reg rd, EN64Reg rt, u32 sa )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.CPU[rt]._u32_0);
|
|
SHRI(RAX_CODE, sa);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
//gGPR[ op_code.rd ]._s64 = (s64)(s32)( gGPR[ op_code.rt ]._s32_0 >> op_code.sa );
|
|
void CCodeGeneratorX64::GenerateSRA( EN64Reg rd, EN64Reg rt, u32 sa )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.CPU[rt]._u32_0);
|
|
SARI(RAX_CODE, sa);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
//gGPR[ op_code.rd ]._u64 = gGPR[ op_code.rs ]._u64 | gGPR[ op_code.rt ]._u64;
|
|
void CCodeGeneratorX64::GenerateOR( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
MOV64_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u64);
|
|
OR(RAX_CODE, RCX_CODE, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rd]._u64, RAX_CODE);
|
|
}
|
|
|
|
//gGPR[ op_code.rd ]._u64 = gGPR[ op_code.rs ]._u64 & gGPR[ op_code.rt ]._u64;
|
|
void CCodeGeneratorX64::GenerateAND( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
MOV64_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u64);
|
|
AND(RAX_CODE, RCX_CODE, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rd]._u64, RAX_CODE);
|
|
}
|
|
|
|
//gGPR[ op_code.rd ]._u64 = gGPR[ op_code.rs ]._u64 ^ gGPR[ op_code.rt ]._u64;
|
|
void CCodeGeneratorX64::GenerateXOR( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
MOV64_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u64);
|
|
XOR(RAX_CODE, RCX_CODE, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rd]._u64, RAX_CODE);
|
|
}
|
|
|
|
//gGPR[ op_code.rd ]._u64 = ~(gGPR[ op_code.rs ]._u64 | gGPR[ op_code.rt ]._u64);
|
|
void CCodeGeneratorX64::GenerateNOR( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
MOV64_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u64);
|
|
OR(RAX_CODE, RCX_CODE, true);
|
|
NOT(RAX_CODE, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rd]._u64, RAX_CODE);
|
|
}
|
|
|
|
// gGPR[ op_code.rd ]._s64 = (s64)(s32)( gGPR[ op_code.rs ]._s32_0 + gGPR[ op_code.rt ]._s32_0 );
|
|
void CCodeGeneratorX64::GenerateADDU( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u32_0);
|
|
MOV_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u32_0);
|
|
ADD(RAX_CODE, RCX_CODE);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
// gGPR[ op_code.rd ]._u64 = gGPR[ op_code.rs ]._u64 + gGPR[ op_code.rt ]._u64;
|
|
void CCodeGeneratorX64::GenerateDADDU( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
MOV64_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u64);
|
|
ADD(RAX_CODE, RCX_CODE, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rd]._u64, RAX_CODE);
|
|
}
|
|
|
|
// gGPR[ op_code.rd ]._s64 = (s64)(s32)( gGPR[ op_code.rs ]._s32_0 - gGPR[ op_code.rt ]._s32_0 );
|
|
void CCodeGeneratorX64::GenerateSUBU( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u32_0);
|
|
MOV_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u32_0);
|
|
SUB(RAX_CODE, RCX_CODE);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[rd]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
//gGPR[ op_code.rd ]._u64 = gGPR[ op_code.rs ]._u64 - gGPR[ op_code.rt ]._u64;
|
|
void CCodeGeneratorX64::GenerateDSUBU( EN64Reg rd, EN64Reg rs, EN64Reg rt )
|
|
{
|
|
if (rd == 0) return;
|
|
|
|
MOV64_REG_MEM(RAX_CODE, &gCPUState.CPU[rs]._u64);
|
|
MOV64_REG_MEM(RCX_CODE, &gCPUState.CPU[rt]._u64);
|
|
SUB(RAX_CODE, RCX_CODE, true);
|
|
MOV64_MEM_REG(&gCPUState.CPU[rd]._u64, RAX_CODE);
|
|
}
|
|
|
|
bool CCodeGeneratorX64::GenerateLWC1( u32 ft, EN64Reg base, s16 offset )
|
|
{
|
|
if (gDynarecStackOptimisation && base == N64Reg_SP)
|
|
{
|
|
GenerateLoad(base, offset, 0, 32);
|
|
|
|
MOV_MEM_REG(&gCPUState.FPU[ft]._u32, RAX_CODE);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CCodeGeneratorX64::GenerateJAL( u32 address )
|
|
{
|
|
MOVI(RAX_CODE, address + 8);
|
|
CDQ();
|
|
MOV_MEM_REG(&gCPUState.CPU[N64Reg_RA]._u32_0, RAX_CODE);
|
|
MOV_MEM_REG(&gCPUState.CPU[N64Reg_RA]._u32_1, RDX_CODE);
|
|
}
|
|
|
|
void CCodeGeneratorX64::GenerateJR( EN64Reg rs)
|
|
{
|
|
//TODO
|
|
}
|