#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 call_regs; vector call_regs64; vector 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_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