413 lines
7.2 KiB
C++
413 lines
7.2 KiB
C++
#include "xbyak/xbyak.h"
|
|
|
|
#include "types.h"
|
|
|
|
#if HOST_CPU == CPU_X64
|
|
#include "hw/sh4/sh4_opcode_list.h"
|
|
#include "hw/sh4/modules/ccn.h"
|
|
#include "hw/sh4/sh4_interrupts.h"
|
|
|
|
#include "hw/sh4/sh4_core.h"
|
|
#include "hw/sh4/dyna/ngen.h"
|
|
#include "hw/sh4/sh4_mem.h"
|
|
#include "hw/sh4/dyna/regalloc.h"
|
|
#include "emitter/x86_emitter.h"
|
|
#include "profiler/profiler.h"
|
|
#include "oslib/oslib.h"
|
|
|
|
|
|
struct DynaRBI : RuntimeBlockInfo
|
|
{
|
|
virtual u32 Relink() {
|
|
//verify(false);
|
|
return 0;
|
|
}
|
|
|
|
virtual void Relocate(void* dst) {
|
|
verify(false);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
int cycle_counter;
|
|
|
|
void ngen_FailedToFindBlock_internal() {
|
|
rdv_FailedToFindBlock(Sh4cntx.pc);
|
|
}
|
|
|
|
void(*ngen_FailedToFindBlock)() = &ngen_FailedToFindBlock_internal;
|
|
|
|
void ngen_mainloop(void* v_cntx)
|
|
{
|
|
Sh4RCB* ctx = (Sh4RCB*)((u8*)v_cntx - sizeof(Sh4RCB));
|
|
|
|
cycle_counter = 0;
|
|
|
|
for (;;) {
|
|
cycle_counter = SH4_TIMESLICE;
|
|
do {
|
|
DynarecCodeEntryPtr rcb = bm_GetCode(ctx->cntx.pc);
|
|
rcb();
|
|
} while (cycle_counter > 0);
|
|
|
|
if (UpdateSystem()) {
|
|
rdv_DoInterrupts_pc(ctx->cntx.pc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ngen_init()
|
|
{
|
|
}
|
|
|
|
void ngen_ResetBlocks()
|
|
{
|
|
}
|
|
|
|
void ngen_GetFeatures(ngen_features* dst)
|
|
{
|
|
dst->InterpreterFallback = false;
|
|
dst->OnlyDynamicEnds = true;
|
|
}
|
|
|
|
RuntimeBlockInfo* ngen_AllocateBlock()
|
|
{
|
|
return new DynaRBI();
|
|
}
|
|
|
|
u32* GetRegPtr(u32 reg)
|
|
{
|
|
return Sh4_int_GetRegisterPtr((Sh4RegType)reg);
|
|
}
|
|
|
|
class BlockCompiler : public Xbyak::CodeGenerator{
|
|
public:
|
|
|
|
vector<Xbyak::Reg32> call_regs;
|
|
vector<Xbyak::Reg64> call_regs64;
|
|
vector<Xbyak::Xmm> call_regsxmm;
|
|
|
|
BlockCompiler() : Xbyak::CodeGenerator(64 * 1024, emit_GetCCPtr()) {
|
|
#if HOST_OS == OS_WINDOWS
|
|
call_regs.push_back(ecx);
|
|
call_regs.push_back(edx);
|
|
call_regs.push_back(r8d);
|
|
call_regs.push_back(r9d);
|
|
|
|
call_regs64.push_back(rcx);
|
|
call_regs64.push_back(rdx);
|
|
call_regs64.push_back(r8);
|
|
call_regs64.push_back(r9);
|
|
#else
|
|
call_regs.push_back(edi);
|
|
call_regs.push_back(esi);
|
|
call_regs.push_back(edx);
|
|
call_regs.push_back(ecx);
|
|
|
|
call_regs64.push_back(rdi);
|
|
call_regs64.push_back(rsi);
|
|
call_regs64.push_back(rdx);
|
|
call_regs64.push_back(rcx);
|
|
#endif
|
|
|
|
call_regsxmm.push_back(xmm0);
|
|
call_regsxmm.push_back(xmm1);
|
|
call_regsxmm.push_back(xmm2);
|
|
call_regsxmm.push_back(xmm3);
|
|
}
|
|
|
|
#define sh_to_reg(prm, op, rd) \
|
|
do { \
|
|
if (prm.is_imm()) { \
|
|
op(rd, prm._imm); \
|
|
} \
|
|
else if (prm.is_reg()) { \
|
|
mov(rax, (size_t)prm.reg_ptr()); \
|
|
op(rd, dword[rax]); \
|
|
} \
|
|
else { \
|
|
verify(prm.is_null()); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define sh_to_reg_noimm(prm, op, rd) \
|
|
do { \
|
|
if (prm.is_reg()) { \
|
|
mov(rax, (size_t)prm.reg_ptr()); \
|
|
op(rd, dword[rax]); \
|
|
} \
|
|
else { \
|
|
verify(prm.is_null()); \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define reg_to_sh(prm, rs) \
|
|
do { \
|
|
mov(rax, (size_t)prm.reg_ptr()); \
|
|
mov(dword[rax], rs); \
|
|
} while (0)
|
|
|
|
#define reg_to_sh_ss(prm, rs) \
|
|
do { \
|
|
mov(rax, (size_t)prm.reg_ptr()); \
|
|
movss(dword[rax], rs); \
|
|
} while (0)
|
|
|
|
void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) {
|
|
mov(rax, (size_t)&cycle_counter);
|
|
|
|
sub(dword[rax], block->guest_cycles);
|
|
|
|
sub(rsp, 0x28);
|
|
|
|
for (size_t i = 0; i < block->oplist.size(); i++) {
|
|
shil_opcode& op = block->oplist[i];
|
|
switch (op.op) {
|
|
|
|
case shop_ifb:
|
|
if (op.rs1._imm)
|
|
{
|
|
mov(rax, (size_t)&next_pc);
|
|
mov(dword[rax], op.rs2._imm);
|
|
}
|
|
|
|
mov(call_regs[0], op.rs3._imm);
|
|
|
|
call(OpDesc[op.rs3._imm]->oph);
|
|
break;
|
|
|
|
case shop_jdyn:
|
|
{
|
|
mov(rax, (size_t)op.rs1.reg_ptr());
|
|
|
|
mov(ecx, dword[rax]);
|
|
|
|
if (op.rs2.is_imm()) {
|
|
add(ecx, op.rs2._imm);
|
|
}
|
|
mov(dword[rax], ecx);
|
|
}
|
|
break;
|
|
|
|
case shop_mov32:
|
|
{
|
|
verify(op.rd.is_reg());
|
|
|
|
verify(op.rs1.is_reg() || op.rs1.is_imm());
|
|
|
|
sh_to_reg(op.rs1, mov, ecx);
|
|
|
|
reg_to_sh(op.rd, ecx);
|
|
}
|
|
break;
|
|
|
|
case shop_mov64:
|
|
{
|
|
verify(op.rd.is_reg());
|
|
|
|
verify(op.rs1.is_reg() || op.rs1.is_imm());
|
|
|
|
sh_to_reg(op.rs1, mov, rcx);
|
|
|
|
reg_to_sh(op.rd, rcx);
|
|
}
|
|
break;
|
|
|
|
case shop_readm:
|
|
{
|
|
sh_to_reg(op.rs1, mov, call_regs[0]);
|
|
sh_to_reg(op.rs3, add, call_regs[0]);
|
|
|
|
u32 size = op.flags & 0x7f;
|
|
|
|
if (size == 1) {
|
|
call(ReadMem8);
|
|
movsx(rcx, al);
|
|
}
|
|
else if (size == 2) {
|
|
call(ReadMem16);
|
|
movsx(rcx, ax);
|
|
}
|
|
else if (size == 4) {
|
|
call(ReadMem32);
|
|
mov(rcx, rax);
|
|
}
|
|
else if (size == 8) {
|
|
call(ReadMem64);
|
|
mov(rcx, rax);
|
|
}
|
|
else {
|
|
die("1..8 bytes");
|
|
}
|
|
|
|
if (size != 8)
|
|
reg_to_sh(op.rd, ecx);
|
|
else
|
|
reg_to_sh(op.rd, rcx);
|
|
}
|
|
break;
|
|
|
|
case shop_writem:
|
|
{
|
|
u32 size = op.flags & 0x7f;
|
|
sh_to_reg(op.rs1, mov, call_regs[0]);
|
|
sh_to_reg(op.rs3, add, call_regs[0]);
|
|
|
|
if (size != 8)
|
|
sh_to_reg(op.rs2, mov, call_regs[1]);
|
|
else
|
|
sh_to_reg(op.rs2, mov, call_regs64[1]);
|
|
|
|
if (size == 1)
|
|
call(WriteMem8);
|
|
else if (size == 2)
|
|
call(WriteMem16);
|
|
else if (size == 4)
|
|
call(WriteMem32);
|
|
else if (size == 8)
|
|
call(WriteMem64);
|
|
else {
|
|
die("1..8 bytes");
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
shil_chf[op.op](&op);
|
|
break;
|
|
}
|
|
}
|
|
|
|
verify(block->BlockType == BET_DynamicJump);
|
|
|
|
add(rsp, 0x28);
|
|
ret();
|
|
|
|
ready();
|
|
|
|
block->code = (DynarecCodeEntryPtr)getCode();
|
|
|
|
emit_Skip(getSize());
|
|
}
|
|
|
|
struct CC_PS
|
|
{
|
|
CanonicalParamType type;
|
|
shil_param* prm;
|
|
};
|
|
vector<CC_PS> CC_pars;
|
|
|
|
void ngen_CC_Start(shil_opcode* op)
|
|
{
|
|
CC_pars.clear();
|
|
}
|
|
|
|
void ngen_CC_param(shil_opcode& op, shil_param& prm, CanonicalParamType tp) {
|
|
switch (tp)
|
|
{
|
|
|
|
case CPT_u32:
|
|
case CPT_ptr:
|
|
case CPT_f32:
|
|
{
|
|
CC_PS t = { tp, &prm };
|
|
CC_pars.push_back(t);
|
|
}
|
|
break;
|
|
|
|
|
|
//store from EAX
|
|
case CPT_u64rvL:
|
|
case CPT_u32rv:
|
|
mov(rcx, rax);
|
|
reg_to_sh(prm, ecx);
|
|
break;
|
|
|
|
case CPT_u64rvH:
|
|
shr(rcx, 32);
|
|
reg_to_sh(prm, ecx);
|
|
break;
|
|
|
|
//Store from ST(0)
|
|
case CPT_f32rv:
|
|
reg_to_sh_ss(prm, xmm0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ngen_CC_Call(shil_opcode*op, void* function)
|
|
{
|
|
int regused = 0;
|
|
int xmmused = 0;
|
|
|
|
for (int i = CC_pars.size(); i-- > 0;)
|
|
{
|
|
verify(xmmused < 4 && regused < 4);
|
|
shil_param& prm = *CC_pars[i].prm;
|
|
switch (CC_pars[i].type) {
|
|
//push the contents
|
|
|
|
case CPT_u32:
|
|
sh_to_reg(prm, mov, call_regs[regused++]);
|
|
break;
|
|
|
|
case CPT_f32:
|
|
sh_to_reg_noimm(prm, movss, call_regsxmm[xmmused++]);
|
|
break;
|
|
|
|
//push the ptr itself
|
|
case CPT_ptr:
|
|
verify(prm.is_reg());
|
|
|
|
mov(call_regs64[regused++], (size_t)prm.reg_ptr());
|
|
|
|
//die("FAIL");
|
|
|
|
break;
|
|
}
|
|
}
|
|
call(function);
|
|
}
|
|
|
|
};
|
|
|
|
BlockCompiler* compiler;
|
|
|
|
void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise)
|
|
{
|
|
verify(emit_FreeSpace() >= 16 * 1024);
|
|
|
|
compiler = new BlockCompiler();
|
|
|
|
|
|
compiler->compile(block, force_checks, reset, staging, optimise);
|
|
|
|
delete compiler;
|
|
}
|
|
|
|
|
|
|
|
void ngen_CC_Start(shil_opcode* op)
|
|
{
|
|
compiler->ngen_CC_Start(op);
|
|
}
|
|
|
|
void ngen_CC_Param(shil_opcode* op, shil_param* par, CanonicalParamType tp)
|
|
{
|
|
compiler->ngen_CC_param(*op, *par, tp);
|
|
}
|
|
|
|
void ngen_CC_Call(shil_opcode*op, void* function)
|
|
{
|
|
compiler->ngen_CC_Call(op, function);
|
|
}
|
|
|
|
void ngen_CC_Finish(shil_opcode* op)
|
|
{
|
|
|
|
}
|
|
#endif |