mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
52edf07657
* feat: added guestBranchKind enum to categorize branch types feat: added missingFunctionPolicy enum to define behaviors for missing function scenarios refactor: added handle guest branches and report missing functions feat lookupFunction to utilize new dispatch logic and improve error handling for unregistered functions * fix: fix test conflict * feat: added debug sound driver logs * feat: emmiter for return * feat: added recompiler reporter feat: added strict diagnostics flag for heavy debug calls * feat: staticc table insted of hashmap for runtime * feat: back file to ignore * feat: explode code across helpers and classes * feat: update codegen test feat: better guest nop check * feat: fix link problem on linux * feat: fix Segmentation fault * feat: added recompile replace for DMA and MMIO feat: added a clean memory helpers feat: use memory helpers across the project feat: fix ucrt on msvc * feat: undo messup merge
366 lines
19 KiB
C++
366 lines
19 KiB
C++
#include "ps2recomp/Translators/instruction_translator.h"
|
|
#include "ps2recomp/code_generator.h"
|
|
#include "ps2recomp/codegen_helpers.h"
|
|
#include "ps2recomp/instructions.h"
|
|
#include "ps2recomp/types.h"
|
|
#include "ps2recomp/control_flow_utils.h"
|
|
#include "runtime/ps2_address.h"
|
|
|
|
#include <fmt/format.h>
|
|
|
|
namespace ps2recomp
|
|
{
|
|
namespace
|
|
{
|
|
std::string addressLiteral(uint32_t address)
|
|
{
|
|
return fmt::format("0x{:X}u", address);
|
|
}
|
|
|
|
uint32_t memoryAccessSize(int width)
|
|
{
|
|
return static_cast<uint32_t>(width / 8);
|
|
}
|
|
|
|
std::string memoryValueType(int width)
|
|
{
|
|
switch (width)
|
|
{
|
|
case 8:
|
|
return "uint8_t";
|
|
case 16:
|
|
return "uint16_t";
|
|
case 32:
|
|
return "uint32_t";
|
|
case 64:
|
|
return "uint64_t";
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
std::string genFastWrite(int width, uint32_t address, const std::string &val)
|
|
{
|
|
const std::string addr = addressLiteral(address);
|
|
if (width == 128)
|
|
{
|
|
return fmt::format(
|
|
"do {{ __m128i _value = ({}); "
|
|
"const uint64_t _lo = static_cast<uint64_t>(PS2_EXTRACT_EPI64_0(_value)); "
|
|
"const uint64_t _hi = static_cast<uint64_t>(PS2_EXTRACT_EPI64_1(_value)); "
|
|
"ps2TraceGuestWrite(rdram, {}, 16u, _lo, _hi, \"WRITE128\", ctx); "
|
|
"FAST_WRITE128({}, _value); }} while (0)",
|
|
val, addr, addr);
|
|
}
|
|
|
|
const std::string valueType = memoryValueType(width);
|
|
return fmt::format(
|
|
"do {{ {} _value = static_cast<{}>({}); "
|
|
"ps2TraceGuestWrite(rdram, {}, {}u, _value, 0u, \"WRITE{}\", ctx); "
|
|
"FAST_WRITE{}({}, _value); }} while (0)",
|
|
valueType, valueType, val, addr, memoryAccessSize(width), width, width, addr);
|
|
}
|
|
}
|
|
|
|
InstructionTranslator::InstructionTranslator(CodeGenerator &codeGenerator)
|
|
: m_codeGenerator(codeGenerator)
|
|
{
|
|
}
|
|
|
|
MemoryAccessHint InstructionTranslator::effectiveMemoryHintFor(const Instruction &inst, const MemoryAccessHint &memoryHint) const
|
|
{
|
|
MemoryAccessHint effectiveMemoryHint = memoryHint;
|
|
if (inst.isMmio)
|
|
{
|
|
effectiveMemoryHint.hasAddress = true;
|
|
effectiveMemoryHint.address = inst.mmioAddress;
|
|
}
|
|
|
|
return effectiveMemoryHint;
|
|
}
|
|
|
|
std::string InstructionTranslator::translateMemoryRead(const Instruction &inst,
|
|
const MemoryAccessHint &memoryHint,
|
|
int width,
|
|
const std::string &addr) const
|
|
{
|
|
if (memoryHint.hasAddress)
|
|
{
|
|
const uint32_t resolvedAddress = memoryHint.address;
|
|
const std::string resolvedAddressExpr = addressLiteral(resolvedAddress);
|
|
if (inst.isMmio || Ps2IsSpecialAddress(resolvedAddress))
|
|
{
|
|
return fmt::format("runtime->Load{}(rdram, ctx, {})", width, resolvedAddressExpr);
|
|
}
|
|
return fmt::format("FAST_READ{}({})", width, resolvedAddressExpr);
|
|
}
|
|
|
|
if (inst.isMmio)
|
|
{
|
|
return fmt::format("runtime->Load{}(rdram, ctx, {})", width, addr);
|
|
}
|
|
return fmt::format("READ{}({})", width, addr);
|
|
}
|
|
|
|
std::string InstructionTranslator::translateMemoryWrite(const Instruction &inst,
|
|
const MemoryAccessHint &memoryHint,
|
|
int width,
|
|
const std::string &addr,
|
|
const std::string &value) const
|
|
{
|
|
if (memoryHint.hasAddress)
|
|
{
|
|
const uint32_t resolvedAddress = memoryHint.address;
|
|
const std::string resolvedAddressExpr = addressLiteral(resolvedAddress);
|
|
if (inst.isMmio || Ps2IsSpecialAddress(resolvedAddress))
|
|
{
|
|
return fmt::format("runtime->Store{}(rdram, ctx, {}, {})", width, resolvedAddressExpr, value);
|
|
}
|
|
return genFastWrite(width, resolvedAddress, value);
|
|
}
|
|
|
|
if (inst.isMmio)
|
|
{
|
|
return fmt::format("runtime->Store{}(rdram, ctx, {}, {})", width, addr, value);
|
|
}
|
|
return fmt::format("WRITE{}({}, {})", width, addr, value);
|
|
}
|
|
|
|
std::string InstructionTranslator::translate(const Instruction &inst, const MemoryAccessHint &memoryHint)
|
|
{
|
|
if (inst.isMMI)
|
|
{
|
|
return m_codeGenerator.translateMMIInstruction(inst);
|
|
}
|
|
|
|
const MemoryAccessHint effectiveMemoryHint = effectiveMemoryHintFor(inst, memoryHint);
|
|
|
|
auto genRead = [&](int width, const std::string &addr)
|
|
{
|
|
return translateMemoryRead(inst, effectiveMemoryHint, width, addr);
|
|
};
|
|
|
|
auto genWrite = [&](int width, const std::string &addr, const std::string &val)
|
|
{
|
|
return translateMemoryWrite(inst, effectiveMemoryHint, width, addr, val);
|
|
};
|
|
|
|
switch (inst.opcode)
|
|
{
|
|
case OPCODE_SPECIAL:
|
|
return m_codeGenerator.translateSpecialInstruction(inst);
|
|
case OPCODE_REGIMM:
|
|
return m_codeGenerator.translateRegimmInstruction(inst);
|
|
case OPCODE_COP0:
|
|
return m_codeGenerator.translateCOP0Instruction(inst);
|
|
case OPCODE_COP1:
|
|
return m_codeGenerator.translateFPUInstruction(inst);
|
|
case OPCODE_COP2:
|
|
return m_codeGenerator.translateVUInstruction(inst);
|
|
case OPCODE_ADDI:
|
|
if (inst.rt == 0)
|
|
return "// NOP (addi to $zero)";
|
|
return fmt::format(
|
|
"{{ uint32_t tmp; bool ov; "
|
|
"ADD32_OV(GPR_U32(ctx, {}), (int32_t){}, tmp, ov); "
|
|
"if (ov) runtime->SignalException(ctx, EXCEPTION_INTEGER_OVERFLOW); "
|
|
"else SET_GPR_S32(ctx, {}, (int32_t)tmp); }}",
|
|
inst.rs, inst.simmediate, inst.rt);
|
|
|
|
case OPCODE_ADDIU:
|
|
if (inst.rt == 0)
|
|
return "// NOP (addiu $zero, ...)";
|
|
return fmt::format("SET_GPR_S32(ctx, {}, (int32_t)ADD32(GPR_U32(ctx, {}), {}));", inst.rt, inst.rs, inst.simmediate);
|
|
case OPCODE_SLTI:
|
|
return fmt::format("SET_GPR_U64(ctx, {}, ((int64_t)GPR_S64(ctx, {}) < (int64_t)(int32_t){}) ? 1 : 0);", inst.rt, inst.rs, inst.simmediate);
|
|
case OPCODE_SLTIU:
|
|
return fmt::format("SET_GPR_U64(ctx, {}, ((uint64_t)GPR_U64(ctx, {}) < (uint64_t)(int64_t)(int32_t){}) ? 1 : 0);", inst.rt, inst.rs, inst.simmediate);
|
|
case OPCODE_ANDI:
|
|
return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) & (uint64_t)(uint16_t){});", inst.rt, inst.rs, inst.immediate);
|
|
case OPCODE_ORI:
|
|
return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) | (uint64_t)(uint16_t){});", inst.rt, inst.rs, inst.immediate);
|
|
case OPCODE_XORI:
|
|
return fmt::format("SET_GPR_U64(ctx, {}, GPR_U64(ctx, {}) ^ (uint64_t)(uint16_t){});", inst.rt, inst.rs, inst.immediate);
|
|
case OPCODE_LUI:
|
|
return fmt::format("SET_GPR_S32(ctx, {}, (int32_t)((uint32_t){} << 16));", inst.rt, inst.immediate);
|
|
case OPCODE_LB:
|
|
return fmt::format("SET_GPR_S32(ctx, {}, (int8_t){});", inst.rt, genRead(8, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_LH:
|
|
return fmt::format("SET_GPR_S32(ctx, {}, (int16_t){});", inst.rt, genRead(16, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_LW:
|
|
return fmt::format("SET_GPR_S32(ctx, {}, (int32_t){});", inst.rt, genRead(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_LBU:
|
|
return fmt::format("SET_GPR_U32(ctx, {}, (uint8_t){});", inst.rt, genRead(8, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_LHU:
|
|
return fmt::format("SET_GPR_U32(ctx, {}, (uint16_t){});", inst.rt, genRead(16, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_LWU:
|
|
return fmt::format("SET_GPR_U32(ctx, {}, {});", inst.rt, genRead(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_SB:
|
|
return genWrite(8, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), fmt::format("(uint8_t)GPR_U32(ctx, {})", inst.rt)) + ";";
|
|
case OPCODE_SH:
|
|
return genWrite(16, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), fmt::format("(uint16_t)GPR_U32(ctx, {})", inst.rt)) + ";";
|
|
case OPCODE_SW:
|
|
return genWrite(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), fmt::format("GPR_U32(ctx, {})", inst.rt)) + ";";
|
|
case OPCODE_LQ:
|
|
return fmt::format("SET_GPR_VEC(ctx, {}, {});", inst.rt, genRead(128, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_SQ:
|
|
return genWrite(128, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), fmt::format("GPR_VEC(ctx, {})", inst.rt)) + ";";
|
|
case OPCODE_LD:
|
|
return fmt::format("SET_GPR_U64(ctx, {}, {});", inst.rt, genRead(64, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_SD:
|
|
return genWrite(64, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), fmt::format("GPR_U64(ctx, {})", inst.rt)) + ";";
|
|
case OPCODE_LWC1:
|
|
return fmt::format("{{ uint32_t bits = {}; float f; std::memcpy(&f, &bits, sizeof(f)); ctx->f[{}] = f; }}", genRead(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)), inst.rt);
|
|
case OPCODE_SWC1:
|
|
return fmt::format(
|
|
"{{ float f = ctx->f[{}]; uint32_t bits; std::memcpy(&bits, &f, sizeof(bits)); {}; }}",
|
|
inst.rt,
|
|
genWrite(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), "bits"));
|
|
case OPCODE_LDC2:
|
|
return fmt::format("ctx->vu0_vf[{}] = _mm_castsi128_ps({});", inst.rt, genRead(128, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate)));
|
|
case OPCODE_SDC2:
|
|
return genWrite(128, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), fmt::format("_mm_castps_si128(ctx->vu0_vf[{}])", inst.rt)) + ";";
|
|
case OPCODE_DADDI:
|
|
return fmt::format(
|
|
"{{ int64_t src = (int64_t)GPR_S64(ctx, {}); "
|
|
"int64_t imm = (int64_t)(int32_t){}; "
|
|
"int64_t res = src + imm; "
|
|
"if (((src ^ imm) >= 0) && ((src ^ res) < 0)) "
|
|
" runtime->SignalException(ctx, EXCEPTION_INTEGER_OVERFLOW); "
|
|
"else SET_GPR_S64(ctx, {}, res); }}",
|
|
inst.rs, inst.simmediate, inst.rt);
|
|
case OPCODE_DADDIU:
|
|
return fmt::format(
|
|
"SET_GPR_S64(ctx, {}, (int64_t)GPR_S64(ctx, {}) + (int64_t)(int32_t){});",
|
|
inst.rt, inst.rs, inst.simmediate);
|
|
case OPCODE_J:
|
|
return fmt::format("// J 0x{:X} - Handled by branch logic", buildAbsoluteJumpTarget(inst.address, inst.target));
|
|
case OPCODE_JAL:
|
|
return fmt::format("// JAL 0x{:X} - Handled by branch logic", buildAbsoluteJumpTarget(inst.address, inst.target));
|
|
case OPCODE_BEQ:
|
|
case OPCODE_BNE:
|
|
case OPCODE_BLEZ:
|
|
case OPCODE_BGTZ:
|
|
case OPCODE_BEQL:
|
|
case OPCODE_BNEL:
|
|
case OPCODE_BLEZL:
|
|
case OPCODE_BGTZL:
|
|
return fmt::format("// Likely branch instruction at 0x{:X} - Handled by branch logic", inst.address);
|
|
|
|
case OPCODE_LDL:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~7u; "
|
|
"uint32_t offset = addr & 7u; "
|
|
"uint64_t mem = {}; "
|
|
"uint32_t shift = (7u - offset) << 3; "
|
|
"uint64_t keepMask = (shift == 0) ? 0ull : ((1ull << shift) - 1ull); "
|
|
"SET_GPR_U64(ctx, {}, (GPR_U64(ctx, {}) & keepMask) | (mem << shift)); }}",
|
|
inst.rs, inst.simmediate, genRead(64, "aligned_addr"), inst.rt, inst.rt);
|
|
|
|
case OPCODE_LDR:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~7u; "
|
|
"uint32_t offset = addr & 7u; "
|
|
"uint64_t mem = {}; "
|
|
"uint32_t shift = offset << 3; "
|
|
"uint64_t keepMask = (offset == 0) ? 0ull : (0xFFFFFFFFFFFFFFFFull << ((8u - offset) << 3)); "
|
|
"SET_GPR_U64(ctx, {}, (GPR_U64(ctx, {}) & keepMask) | (mem >> shift)); }}",
|
|
inst.rs, inst.simmediate, genRead(64, "aligned_addr"), inst.rt, inst.rt);
|
|
|
|
case OPCODE_LWL:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~3u; "
|
|
"uint32_t offset = addr & 3u; "
|
|
"uint32_t mem = {}; "
|
|
"uint32_t shift = (3u - offset) << 3; "
|
|
"uint32_t keepMask = (shift == 0) ? 0u : ((1u << shift) - 1u); "
|
|
"uint32_t merged = (GPR_U32(ctx, {}) & keepMask) | (mem << shift); "
|
|
"SET_GPR_S32(ctx, {}, (int32_t)merged); }}",
|
|
inst.rs, inst.simmediate, genRead(32, "aligned_addr"), inst.rt, inst.rt);
|
|
|
|
case OPCODE_LWR:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~3u; "
|
|
"uint32_t offset = addr & 3u; "
|
|
"uint32_t mem = {}; "
|
|
"uint32_t shift = offset << 3; "
|
|
"uint32_t keepMask = (offset == 0) ? 0u : (0xFFFFFFFFu << ((4u - offset) << 3)); "
|
|
"uint32_t merged32 = (GPR_U32(ctx, {}) & keepMask) | (mem >> shift); "
|
|
"uint64_t merged64 = (GPR_U64(ctx, {}) & 0xFFFFFFFF00000000ull) | (uint64_t)merged32; "
|
|
"if (offset == 0) merged64 = (uint64_t)(int64_t)(int32_t)merged32; "
|
|
"SET_GPR_U64(ctx, {}, merged64); }}",
|
|
inst.rs, inst.simmediate, genRead(32, "aligned_addr"),
|
|
inst.rt, inst.rt, inst.rt);
|
|
|
|
case OPCODE_SWL:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~3u; "
|
|
"uint32_t offset = addr & 3u; "
|
|
"uint32_t shift = (3u - offset) << 3; "
|
|
"uint32_t mask = 0xFFFFFFFFu >> shift; "
|
|
"uint32_t old_data = {}; "
|
|
"uint32_t val = GPR_U32(ctx, {}); "
|
|
"uint32_t new_data = (old_data & ~mask) | ((val >> shift) & mask); "
|
|
"{}; }}",
|
|
inst.rs, inst.simmediate, genRead(32, "aligned_addr"), inst.rt, genWrite(32, "aligned_addr", "new_data"));
|
|
|
|
case OPCODE_SWR:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~3u; "
|
|
"uint32_t offset = addr & 3u; "
|
|
"uint32_t shift = offset << 3; "
|
|
"uint32_t mask = 0xFFFFFFFFu << shift; "
|
|
"uint32_t old_data = {}; "
|
|
"uint32_t val = GPR_U32(ctx, {}); "
|
|
"uint32_t new_data = (old_data & ~mask) | ((val << shift) & mask); "
|
|
"{}; }}",
|
|
inst.rs, inst.simmediate, genRead(32, "aligned_addr"), inst.rt, genWrite(32, "aligned_addr", "new_data"));
|
|
|
|
case OPCODE_SDL:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~7u; "
|
|
"uint32_t offset = addr & 7u; "
|
|
"uint32_t shift = (7u - offset) << 3; "
|
|
"uint64_t mask = 0xFFFFFFFFFFFFFFFFull >> shift; "
|
|
"uint64_t old_data = {}; "
|
|
"uint64_t val = GPR_U64(ctx, {}); "
|
|
"uint64_t new_data = (old_data & ~mask) | ((val >> shift) & mask); "
|
|
"{}; }}",
|
|
inst.rs, inst.simmediate, genRead(64, "aligned_addr"), inst.rt, genWrite(64, "aligned_addr", "new_data"));
|
|
|
|
case OPCODE_SDR:
|
|
return fmt::format("{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"uint32_t aligned_addr = addr & ~7u; "
|
|
"uint32_t offset = addr & 7u; "
|
|
"uint32_t shift = offset << 3; "
|
|
"uint64_t mask = 0xFFFFFFFFFFFFFFFFull << shift; "
|
|
"uint64_t old_data = {}; "
|
|
"uint64_t val = GPR_U64(ctx, {}); "
|
|
"uint64_t new_data = (old_data & ~mask) | ((val << shift) & mask); "
|
|
"{}; }}",
|
|
inst.rs, inst.simmediate, genRead(64, "aligned_addr"), inst.rt, genWrite(64, "aligned_addr", "new_data"));
|
|
case OPCODE_CACHE:
|
|
return "// CACHE instruction (ignored)";
|
|
case OPCODE_PREF:
|
|
return "// PREF instruction (ignored)";
|
|
case OPCODE_LL:
|
|
return fmt::format(
|
|
"{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"SET_GPR_S32(ctx, {}, (int32_t)READ32(addr)); "
|
|
"ctx->llbit = 1; ctx->lladdr = addr; }}",
|
|
inst.rs, inst.simmediate, inst.rt);
|
|
case OPCODE_SC:
|
|
return fmt::format(
|
|
"{{ uint32_t addr = ADD32(GPR_U32(ctx, {}), {}); "
|
|
"if (ctx->llbit && ctx->lladdr == addr) {{ WRITE32(addr, GPR_U32(ctx, {})); "
|
|
"SET_GPR_S32(ctx, {}, 1); }} "
|
|
"else {{ SET_GPR_S32(ctx, {}, 0); }} "
|
|
"ctx->llbit = 0; ctx->lladdr = 0; }}",
|
|
inst.rs, inst.simmediate, inst.rt, inst.rt, inst.rt);
|
|
default:
|
|
return m_codeGenerator.emitUnhandledInstruction(inst, fmt::format("Unhandled opcode: 0x{:X}", inst.opcode));
|
|
}
|
|
}
|
|
|
|
}
|