daedalus/Source/DynaRec/x64/CodeGeneratorX64.cpp
2024-08-05 21:15:57 +10:00

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
}