get rid of JIT and other things

This commit is contained in:
SimoneN64
2023-06-04 22:28:23 +02:00
parent 1ed2506523
commit 30fce7ecf7
36 changed files with 143 additions and 3602 deletions

View File

@@ -49,7 +49,6 @@ add_subdirectory(frontend)
add_subdirectory(frontend/imgui)
add_subdirectory(backend)
add_subdirectory(backend/core)
add_subdirectory(backend/core/jit)
add_subdirectory(backend/core/interpreter)
add_subdirectory(backend/core/mmio)
add_subdirectory(backend/core/registers)
@@ -68,7 +67,7 @@ file(REMOVE
${PROJECT_BINARY_DIR}/resources/shader.vert)
target_link_libraries(kaizen PUBLIC frontend frontend-imgui
discord-rpc imgui nfd parallel-rdp backend fmt::fmt nlohmann_json::nlohmann_json core jit registers interpreter mmio rsp SDL2::SDL2main SDL2::SDL2)
discord-rpc imgui nfd parallel-rdp backend fmt::fmt nlohmann_json::nlohmann_json core registers interpreter mmio rsp SDL2::SDL2main SDL2::SDL2)
if(WIN32)
target_compile_definitions(kaizen PUBLIC NOMINMAX _CRT_SECURE_NO_WARNINGS)

View File

@@ -11,59 +11,75 @@ Core::Core() {
}
void Core::Stop() {
cpu->Reset();
cpu->mem.Reset();
cpu.Reset();
cpu.mem.Reset();
pause = true;
romLoaded = false;
}
void Core::LoadROM(const std::string& rom_) {
rom = rom_;
cpu->Reset();
cpu->mem.Reset();
cpu.Reset();
cpu.mem.Reset();
pause = false;
romLoaded = true;
cpu->mem.LoadROM(rom);
GameDB::match(cpu->mem);
cpu->mem.mmio.si.pif.InitDevices(cpu->mem.saveType);
cpu->mem.mmio.si.pif.LoadMempak(rom_);
cpu->mem.mmio.si.pif.LoadEeprom(cpu->mem.saveType, rom_);
isPAL = cpu->mem.IsROMPAL();
cpu->mem.mmio.si.pif.ExecutePIF(cpu->mem, cpu->regs);
cpu.mem.LoadROM(rom);
GameDB::match(cpu.mem);
cpu.mem.mmio.si.pif.InitDevices(cpu.mem.saveType);
cpu.mem.mmio.si.pif.LoadMempak(rom_);
cpu.mem.mmio.si.pif.LoadEeprom(cpu.mem.saveType, rom_);
isPAL = cpu.mem.IsROMPAL();
cpu.mem.mmio.vi.isPal = isPAL;
cpu.mem.mmio.si.pif.ExecutePIF(cpu.mem, cpu.regs);
}
void Core::Run(Window& window, float volumeL, float volumeR) {
MMIO& mmio = cpu->mem.mmio;
void Core::Run(float volumeL, float volumeR) {
Mem& mem = cpu.mem;
MMIO& mmio = mem.mmio;
Registers& regs = cpu.regs;
for (int field = 0; field < mmio.vi.numFields; field++) {
if (!pause && romLoaded) {
int frameCycles = 0;
for (int i = 0; i < mmio.vi.numHalflines; i++) {
mmio.vi.current = (i << 1) + field;
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
InterruptRaise(mmio.mi, cpu->regs, Interrupt::VI);
}
int cpuCount = cpu->Run();
cpu->RunRSP(cpuCount);
frameCycles += cpuCount;
}
int frameCycles = 0;
for (int i = 0; i < mmio.vi.numHalflines; i++) {
mmio.vi.current = (i << 1) + field;
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
InterruptRaise(mmio.mi, cpu->regs, Interrupt::VI);
InterruptRaise(mmio.mi, regs, Interrupt::VI);
}
UpdateScreenParallelRdp(*this, window, GetVI());
for(; cycles < mem.mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) {
int taken = cpu.Step();
static int cpuSteps = 0;
cpuSteps += taken;
if(mmio.rsp.spStatus.halt) {
cpuSteps = 0;
mmio.rsp.steps = 0;
} else {
while(cpuSteps > 2) {
mmio.rsp.steps += 2;
cpuSteps -= 3;
}
mmio.ai.Step(cpu->mem, cpu->regs, frameCycles, volumeL, volumeR);
scheduler.tick(frameCycles, cpu->mem, cpu->regs);
} else if (pause && romLoaded) {
UpdateScreenParallelRdp(*this, window, GetVI());
} else if (pause && !romLoaded) {
UpdateScreenParallelRdpNoGame(*this, window);
while(mmio.rsp.steps > 0) {
mmio.rsp.steps--;
mmio.rsp.Step(regs, mem);
}
}
cycles += taken;
frameCycles += taken;
}
cycles -= mmio.vi.cyclesPerHalfline;
}
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
InterruptRaise(mmio.mi, regs, Interrupt::VI);
}
mmio.ai.Step(cpu.mem, regs, frameCycles, volumeL, volumeR);
scheduler.tick(frameCycles, mem, regs);
}
}
}

View File

@@ -3,36 +3,29 @@
#include <backend/core/Interpreter.hpp>
#include <backend/core/Mem.hpp>
#include <string>
#include <backend/core/JIT.hpp>
#include <backend/core/registers/Registers.hpp>
#include <Debugger.hpp>
#include <SDL2/SDL_timer.h>
struct Window;
namespace n64 {
enum class CpuType {
Interpreter, JIT, COUNT
};
struct Core {
~Core() { Stop(); }
Core();
void Stop();
void LoadROM(const std::string&);
void Run(Window&, float volumeL, float volumeR);
void Run(float volumeL, float volumeR);
void TogglePause() { pause = !pause; }
VI& GetVI() { return cpu->mem.mmio.vi; }
VI& GetVI() { return cpu.mem.mmio.vi; }
u32 breakpoint = 0;
bool pause = true;
int cycles = 0;
bool isPAL = false;
bool romLoaded = false;
bool done = false;
std::string rom;
CpuType cpuType = CpuType::COUNT;
std::unique_ptr<BaseCPU> cpu;
Debugger debugger{*this};
Interpreter cpu;
};
}

View File

@@ -1 +0,0 @@
#include <Debugger.hpp>

View File

@@ -1,32 +0,0 @@
#pragma once
#include <common.hpp>
struct Breakpoint {
u32 address = 0;
Breakpoint* next = nullptr;
};
namespace n64 { struct Core; }
struct Debugger {
Debugger(n64::Core& core) :core(core) {}
~Debugger() = default;
bool broken = false, enabled = true;
Breakpoint* breakpoints = nullptr;
n64::Core& core;
[[nodiscard]] inline bool checkBreakpoint(u32 address) const {
auto* cur = breakpoints;
while (cur != nullptr) {
if (cur->address == address) {
return true;
}
cur = cur->next;
}
return false;
}
void tick() const {}
void breakpointHit() {}
};

View File

@@ -1,15 +0,0 @@
#pragma once
#include <Registers.hpp>
namespace n64 {
struct BaseCPU {
virtual ~BaseCPU() {}
virtual void Reset() {}
virtual int Run() {}
void RunRSP(int cpuCount) {
mem.mmio.rsp.Run(cpuCount, regs, mem);
}
Registers regs;
Mem mem;
};
}

View File

@@ -1,59 +1,7 @@
#include <Core.hpp>
#include <log.hpp>
namespace n64 {
inline bool ShouldServiceInterrupt(Registers& regs) {
bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
bool interrupts_enabled = regs.cop0.status.ie == 1;
bool currently_handling_exception = regs.cop0.status.exl == 1;
bool currently_handling_error = regs.cop0.status.erl == 1;
return interrupts_pending && interrupts_enabled &&
!currently_handling_exception && !currently_handling_error;
}
inline void CheckCompareInterrupt(MI& mi, Registers& regs) {
regs.cop0.count++;
regs.cop0.count &= 0x1FFFFFFFF;
if(regs.cop0.count == (u64)regs.cop0.compare << 1) {
regs.cop0.cause.ip7 = 1;
UpdateInterrupt(mi, regs);
}
}
void Interpreter::Reset() {
regs.Reset();
}
int Interpreter::Run() {
int cycles = 1;
for(; cycles < mem.mmio.vi.cyclesPerHalfline; cycles++) {
CheckCompareInterrupt(mem.mmio.mi, regs);
regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false;
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, regs.pc, paddr)) {
HandleTLBException(regs, regs.pc);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, false);
return cycles;
}
u32 instruction = mem.Read32(regs, paddr);
if(ShouldServiceInterrupt(regs)) {
FireException(regs, ExceptionCode::Interrupt, 0, false);
return cycles;
}
regs.oldPC = regs.pc;
regs.pc = regs.nextPC;
regs.nextPC += 4;
Exec(instruction);
}
return cycles;
}
}

View File

@@ -2,19 +2,66 @@
#include <core/registers/Registers.hpp>
#include <Mem.hpp>
#include <vector>
#include <BaseCPU.hpp>
namespace n64 {
struct Core;
struct Interpreter : BaseCPU {
struct Interpreter {
Interpreter() = default;
~Interpreter() = default;
int Run() override;
void Reset() override;
FORCE_INLINE int Step() {
CheckCompareInterrupt();
regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false;
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, regs.pc, paddr)) {
HandleTLBException(regs, regs.pc);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, false);
return 0;
}
u32 instruction = mem.Read32(regs, paddr);
if(ShouldServiceInterrupt()) {
FireException(regs, ExceptionCode::Interrupt, 0, false);
return 0;
}
regs.oldPC = regs.pc;
regs.pc = regs.nextPC;
regs.nextPC += 4;
Exec(instruction);
return 1;
}
void Reset();
Registers regs;
Mem mem;
private:
u64 cop2Latch{};
friend struct Cop1;
[[nodiscard]] FORCE_INLINE bool ShouldServiceInterrupt() const {
bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
bool interrupts_enabled = regs.cop0.status.ie == 1;
bool currently_handling_exception = regs.cop0.status.exl == 1;
bool currently_handling_error = regs.cop0.status.erl == 1;
return interrupts_pending && interrupts_enabled &&
!currently_handling_exception && !currently_handling_error;
}
FORCE_INLINE void CheckCompareInterrupt() {
regs.cop0.count++;
regs.cop0.count &= 0x1FFFFFFFF;
if(regs.cop0.count == (u64)regs.cop0.compare << 1) {
regs.cop0.cause.ip7 = 1;
UpdateInterrupt(mem.mmio.mi, regs);
}
}
void cop2Decode(u32);
void special(u32);
void regimm(u32);

View File

@@ -1,133 +0,0 @@
#include <Registers.hpp>
#include <filesystem>
#include <JIT.hpp>
namespace n64 {
namespace fs = std::filesystem;
JIT::~JIT() {
Util::aligned_free(codeCache);
dump.close();
}
JIT::JIT() : code(CODECACHE_SIZE, codeCache) {
codeCache = (u8*)Util::aligned_alloc(4096, CODECACHE_SIZE);
CodeArray::protect(
codeCache,
CODECACHE_SIZE,
CodeArray::PROTECT_RWE
);
if(fs::exists("jit.dump")) {
fs::remove("jit.dump");
}
dump.open("jit.dump", std::ios::ate | std::ios::binary);
dump.unsetf(std::ios::skipws);
regs.Reset();
}
inline bool ShouldServiceInterrupt(Registers& regs) {
bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
bool interrupts_enabled = regs.cop0.status.ie == 1;
bool currently_handling_exception = regs.cop0.status.exl == 1;
bool currently_handling_error = regs.cop0.status.erl == 1;
return interrupts_pending && interrupts_enabled &&
!currently_handling_exception && !currently_handling_error;
}
inline void CheckCompareInterrupt(MI& mi, Registers& regs) {
regs.cop0.count++;
regs.cop0.count &= 0x1FFFFFFFF;
if(regs.cop0.count == (u64)regs.cop0.compare << 1) {
regs.cop0.cause.ip7 = 1;
UpdateInterrupt(mi, regs);
}
}
void JIT::Recompile(Mem& mem, u32 pc) {
bool branch = false, prevBranch = false;
u32 startPC = pc;
u32 loopPC = pc;
Fn block = code.getCurr<Fn>();
if(code.getSize() >= CODECACHE_OVERHEAD) {
Util::debug("Code cache overflow!\n");
code.setSize(0);
InvalidateCache();
}
code.sub(rsp, 8);
while(!prevBranch) {
instrInBlock++;
prevBranch = branch;
u32 instr = mem.Read32(regs, loopPC);
loopPC += 4;
code.mov(rdi, (uintptr_t)this);
code.mov(qword[rdi + GPR_OFFSET(0, this)], 0);
code.mov(r8, qword[rdi + REG_OFFSET(oldPC, this)]);
code.mov(r9, qword[rdi + REG_OFFSET(pc, this)]);
code.mov(r10, qword[rdi + REG_OFFSET(nextPC, this)]);
code.mov(r8, r9);
code.mov(r9, r10);
code.add(r10, 4);
code.mov(qword[rdi + REG_OFFSET(oldPC, this)], r8);
code.mov(qword[rdi + REG_OFFSET(pc, this)], r9);
code.mov( qword[rdi + REG_OFFSET(nextPC, this)], r10);
code.mov(esi, instr);
branch = Exec(mem, instr);
}
code.add(rsp, 8);
code.ret();
dump.write(code.getCode<char*>(), code.getSize());
blockCache[startPC >> 20][startPC & 0xFFF] = block;
}
void JIT::AllocateOuter(u32 pc) {
blockCache[pc >> 20] = (Fn*)bumpAlloc(0x1000 * sizeof(Fn));
}
int JIT::Run() {
instrInBlock = 0;
regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false;
u32 pc{};
if(!MapVAddr(regs, LOAD, regs.pc, pc)) {
HandleTLBException(regs, regs.pc);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, false);
return 0;
}
if(!blockCache[pc >> 20]) {
AllocateOuter(pc);
}
if(!blockCache[pc >> 20][pc & 0xfff]) {
Recompile(mem, pc);
}
CheckCompareInterrupt(mem.mmio.mi, regs);
if(ShouldServiceInterrupt(regs)) {
FireException(regs, ExceptionCode::Interrupt, 0, false);
return 0;
}
blockCache[pc >> 20][pc & 0xfff]();
return instrInBlock;
}
void JIT::Reset() {
code.reset();
regs.Reset();
InvalidateCache();
}
}

View File

@@ -1,42 +0,0 @@
#pragma once
#include <xbyak/xbyak.h>
#include <backend/core/Mem.hpp>
#include <fstream>
#include <BaseCPU.hpp>
namespace n64 {
using namespace Xbyak;
using namespace Xbyak::util;
using Fn = void (*)();
#define GPR_OFFSET(x, jit) ((uintptr_t)&regs.gpr[(x)] - (uintptr_t)jit)
#define REG_OFFSET(kind, jit) ((uintptr_t)&regs.kind - (uintptr_t)jit)
#define CODECACHE_SIZE (2 << 25)
#define CODECACHE_OVERHEAD (CODECACHE_SIZE - 1_kb)
struct JIT : BaseCPU {
JIT();
~JIT() override;
int Run() override;
void Reset() override;
u64 cop2Latch{};
CodeGenerator code;
void InvalidatePage(u32);
void InvalidateCache();
private:
friend struct n64::Cop1;
Fn* blockCache[0x80000]{};
u8* codeCache;
int instrInBlock = 0;
u64 sizeUsed = 0;
std::ofstream dump;
void* bumpAlloc(u64 size, u8 val = 0);
void Recompile(Mem&, u32 pc);
void AllocateOuter(u32 pc);
void cop2Decode(u32);
bool special(u32);
bool regimm(u32);
bool Exec(Mem&, u32);
};
}

View File

@@ -3,7 +3,6 @@
#include <core/registers/Registers.hpp>
#include <core/registers/Cop0.hpp>
#include <core/Interpreter.hpp>
#include <core/JIT.hpp>
#include <File.hpp>
namespace n64 {
@@ -254,26 +253,6 @@ u64 Mem::Read64(n64::Registers &regs, u32 paddr) {
}
}
void Mem::Write8(Registers& regs, JIT& dyn, u32 paddr, u32 val) {
dyn.InvalidatePage(BYTE_ADDRESS(paddr));
return Write8(regs, paddr, val);
}
void Mem::Write16(Registers& regs, JIT& dyn, u32 paddr, u32 val) {
dyn.InvalidatePage(HALF_ADDRESS(paddr));
return Write16(regs, paddr, val);
}
void Mem::Write32(Registers& regs, JIT& dyn, u32 paddr, u32 val) {
dyn.InvalidatePage(paddr);
return Write32(regs, paddr, val);
}
void Mem::Write64(Registers& regs, JIT& dyn, u32 paddr, u64 val) {
dyn.InvalidatePage(paddr);
return Write64(regs, paddr, val);
}
void Mem::Write8(Registers& regs, u32 paddr, u32 val) {
const auto page = paddr >> 12;
auto offset = paddr & 0xFFF;

View File

@@ -16,8 +16,6 @@ struct CartInfo {
u32 crc;
};
struct JIT;
struct ROMHeader {
u8 initialValues[4];
u32 clockRate;
@@ -61,10 +59,6 @@ struct Mem {
u16 Read16(Registers&, u32);
u32 Read32(Registers&, u32);
u64 Read64(Registers&, u32);
void Write8(Registers&, JIT&, u32, u32);
void Write16(Registers&, JIT&, u32, u32);
void Write32(Registers&, JIT&, u32, u32);
void Write64(Registers&, JIT&, u32, u64);
void Write8(Registers&, u32, u32);
void Write16(Registers&, u32, u32);
void Write32(Registers&, u32, u32);
@@ -116,7 +110,7 @@ private:
bool IsROMPAL() {
static const char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'};
return std::any_of(std::begin(pal_codes), std::end(pal_codes), [this](char a) {
return rom.cart[0x3e] == a;
return rom.cart[0x3d] == a;
});
}
};

View File

@@ -66,30 +66,6 @@ inline void logRSP(const RSP& rsp, const u32 instr) {
}
*/
void RSP::Run(int cpuCount, Registers& regs, Mem& mem) {
while(cpuCount--) {
if (!spStatus.halt) {
regs.steps++;
if (regs.steps > 2) {
steps += 2;
regs.steps -= 3;
}
while (steps > 0) {
steps--;
gpr[0] = 0;
u32 instr = Util::ReadAccess<u32>(imem, pc & IMEM_DSIZE);
oldPC = pc & 0xFFC;
pc = nextPC & 0xFFC;
nextPC += 4;
Exec(regs, mem, instr);
//logRSP(*this, instr);
}
}
}
}
auto RSP::Read(u32 addr) -> u32{
switch (addr) {
case 0x04040000: return lastSuccessfulSPAddr.raw & 0x1FF8;

View File

@@ -2,6 +2,7 @@
#include <core/mmio/MI.hpp>
#include <core/RDP.hpp>
#include <MemoryRegions.hpp>
#include <MemoryHelpers.hpp>
#include <Interrupt.hpp>
#define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF])
@@ -113,7 +114,16 @@ struct Registers;
struct RSP {
RSP();
void Reset();
void Run(int, Registers& regs, Mem& mem);
FORCE_INLINE void Step(Registers& regs, Mem& mem) {
gpr[0] = 0;
u32 instr = Util::ReadAccess<u32>(imem, pc & IMEM_DSIZE);
oldPC = pc & 0xFFC;
pc = nextPC & 0xFFC;
nextPC += 4;
Exec(regs, mem, instr);
}
auto Read(u32 addr) -> u32;
void Write(Mem& mem, Registers& regs, u32 addr, u32 value);
void Exec(Registers& regs, Mem& mem, u32 instr);

View File

@@ -1,4 +0,0 @@
file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB_RECURSE HEADERS *.hpp)
add_library(jit ${SOURCES} ${HEADERS})

View File

@@ -1,27 +0,0 @@
#include <core/JIT.hpp>
namespace n64 {
void JIT::InvalidatePage(u32 paddr) {
blockCache[paddr >> 20] = nullptr;
}
void JIT::InvalidateCache() {
sizeUsed = 0;
for(auto &i : blockCache) {
i = nullptr;
}
}
void* JIT::bumpAlloc(u64 size, u8 val) {
if(sizeUsed + size >= CODECACHE_SIZE) {
InvalidateCache();
}
void* ptr = &codeCache[sizeUsed];
sizeUsed += size;
memset(ptr, val, size);
return ptr;
}
}

View File

@@ -1,61 +0,0 @@
#include <jit/cop/cop0decode.hpp>
#include <jit/cop/cop0instructions.hpp>
#include <Registers.hpp>
namespace n64 {
void cop0Decode(JIT& cpu, u32 instr) {
u8 mask_cop = (instr >> 21) & 0x1F;
u8 mask_cop2 = instr & 0x3F;
Xbyak::CodeGenerator& code = cpu.code;
Registers& regs = cpu.regs;
switch(mask_cop) {
case 0x00:
code.mov(code.rax, (u64)mfc0);
code.call(code.rax);
break;
case 0x01:
code.mov(code.rax, (u64)dmfc0);
code.call(code.rax);
break;
case 0x04:
code.mov(code.rax, (uintptr_t)mtc0);
code.call(code.rax);
break;
case 0x05:
code.mov(code.rax, (u64)dmtc0);
code.call(code.rax);
break;
case 0x10 ... 0x1F:
switch(mask_cop2) {
case 0x01:
code.mov(code.rax, (u64)tlbr);
code.call(code.rax);
break;
case 0x02:
code.mov(code.rcx, code.dword[code.rdi + REG_OFFSET(cop0.index, &cpu)]);
code.and_(code.rcx, 0x3F);
code.mov(code.rsi, code.rcx);
code.mov(code.rax, (u64)tlbw);
code.call(code.rax);
break;
case 0x06:
code.mov(code.rsi, (u64)regs.cop0.GetRandom());
code.mov(code.rax, (u64)tlbw);
code.call(code.rax);
break;
case 0x08:
code.mov(code.rax, (u64)tlbp);
code.call(code.rax);
break;
case 0x18:
code.mov(code.rax, (u64)eret);
code.call(code.rax);
break;
default: Util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr, regs.oldPC);
}
break;
default: Util::panic("Unimplemented COP0 instruction {} {}", mask_cop >> 4, mask_cop & 7);
}
}
}

View File

@@ -1,7 +0,0 @@
#pragma once
#include <JIT.hpp>
namespace n64 {
struct Registers;
void cop0Decode(JIT& cpu, u32 instr);
}

View File

@@ -1,89 +0,0 @@
#include <jit/cop/cop0instructions.hpp>
#include <log.hpp>
#include <Registers.hpp>
namespace n64 {
void mtc0(JIT& cpu, u32 instr) {
Registers& regs = cpu.regs;
regs.cop0.SetReg32(RD(instr), regs.gpr[RT(instr)]);
}
void dmtc0(JIT& cpu, u32 instr) {
Registers& regs = cpu.regs;
regs.cop0.SetReg64(RD(instr), regs.gpr[RT(instr)]);
}
void mfc0(JIT& cpu, u32 instr) {
Registers& regs = cpu.regs;
regs.gpr[RT(instr)] = s32(regs.cop0.GetReg32(RD(instr)));
}
void dmfc0(JIT& cpu, u32 instr) {
Registers& regs = cpu.regs;
regs.gpr[RT(instr)] = s64(regs.cop0.GetReg64(RD(instr)));
}
void eret(JIT& cpu) {
Registers& regs = cpu.regs;
if(regs.cop0.status.erl) {
regs.SetPC64(regs.cop0.ErrorEPC);
regs.cop0.status.erl = false;
} else {
regs.SetPC64(regs.cop0.EPC);
regs.cop0.status.exl = false;
}
regs.cop0.llbit = false;
}
void tlbr(JIT& cpu) {
Registers& regs = cpu.regs;
u8 Index = regs.cop0.index & 0b111111;
if (Index >= 32) {
Util::panic("TLBR with TLB index {}", Index);
}
TLBEntry entry = regs.cop0.tlb[Index];
regs.cop0.entryHi.raw = entry.entryHi.raw;
regs.cop0.entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF;
regs.cop0.entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF;
regs.cop0.entryLo0.g = entry.global;
regs.cop0.entryLo1.g = entry.global;
regs.cop0.pageMask.raw = entry.pageMask.raw;
}
void tlbw(JIT& cpu, int index_) {
Registers& regs = cpu.regs;
PageMask page_mask = regs.cop0.pageMask;
u32 top = page_mask.mask & 0xAAA;
page_mask.mask = top | (top >> 1);
if(index_ >= 32) {
Util::panic("TLBWI with TLB index {}", index_);
}
regs.cop0.tlb[index_].entryHi.raw = regs.cop0.entryHi.raw;
regs.cop0.tlb[index_].entryHi.vpn2 &= ~page_mask.mask;
regs.cop0.tlb[index_].entryLo0.raw = regs.cop0.entryLo0.raw & 0x03FFFFFE;
regs.cop0.tlb[index_].entryLo1.raw = regs.cop0.entryLo1.raw & 0x03FFFFFE;
regs.cop0.tlb[index_].pageMask.raw = page_mask.raw;
regs.cop0.tlb[index_].global = regs.cop0.entryLo0.g && regs.cop0.entryLo1.g;
regs.cop0.tlb[index_].initialized = true;
}
void tlbp(JIT& cpu) {
Registers& regs = cpu.regs;
int match = -1;
TLBEntry* entry = TLBTryMatch(regs, regs.cop0.entryHi.raw, &match);
if(entry && match >= 0) {
regs.cop0.index = match;
} else {
regs.cop0.index = 0x80000000;
}
}
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include <JIT.hpp>
namespace n64 {
void mtc0(JIT&, u32);
void dmtc0(JIT&, u32);
void mfc0(JIT&, u32);
void dmfc0(JIT&, u32);
void eret(JIT&);
void tlbr(JIT&);
void tlbw(JIT&, int);
void tlbp(JIT&);
}

View File

@@ -1,464 +0,0 @@
#include <jit/cop/cop1decode.hpp>
#include <jit/instructions.hpp>
#include <Registers.hpp>
namespace n64 {
bool cop1Decode(JIT& cpu, u32 instr) {
Xbyak::CodeGenerator& code = cpu.code;
Registers& regs = cpu.regs;
u8 mask_sub = (instr >> 21) & 0x1F;
u8 mask_fun = instr & 0x3F;
u8 mask_branch = (instr >> 16) & 0x1F;
switch(mask_sub) {
// 000r_rccc
case 0x00:
code.mov(code.rax, (u64)mfc1);
code.call(code.rax);
break;
case 0x01:
code.mov(code.rax, (u64)dmfc1);
code.call(code.rax);
break;
case 0x02:
code.mov(code.rax, (u64)cfc1);
code.call(code.rax);
break;
case 0x03:
Util::panic("[RECOMPILER] FPU Reserved instruction exception! {:08X}\n", instr);
case 0x04:
code.mov(code.rax, (u64)mtc1);
code.call(code.rax);
break;
case 0x05:
code.mov(code.rax, (u64)dmtc1);
code.call(code.rax);
break;
case 0x06:
code.mov(code.rax, (u64)ctc1);
code.call(code.rax);
break;
case 0x07:
Util::panic("[RECOMPILER] FPU Reserved instruction exception! {:08X}\n", instr);
case 0x08:
switch(mask_branch) {
case 0:
code.mov(code.rdx, !regs.cop1.fcr31.compare);
code.mov(code.rax, (u64)b);
code.call(code.rax);
return true;
case 1:
code.mov(code.rdx, regs.cop1.fcr31.compare);
code.mov(code.rax, (u64)b);
code.call(code.rax);
return true;
case 2:
code.mov(code.rdx, !regs.cop1.fcr31.compare);
code.mov(code.rax, (u64)bl);
code.call(code.rax);
return true;
case 3:
code.mov(code.rdx, regs.cop1.fcr31.compare);
code.mov(code.rax, (u64)bl);
code.call(code.rax);
return true;
default: Util::panic("Undefined BC COP1 {:02X}\n", mask_branch);
}
break;
case 0x10: // s
switch(mask_fun) {
case 0x00:
code.mov(code.rax, (u64)adds);
code.call(code.rax);
break;
case 0x01:
code.mov(code.rax, (u64)subs);
code.call(code.rax);
break;
case 0x02:
code.mov(code.rax, (u64)muls);
code.call(code.rax);
break;
case 0x03:
code.mov(code.rax, (u64)divs);
code.call(code.rax);
break;
case 0x04:
code.mov(code.rax, (u64)sqrts);
code.call(code.rax);
break;
case 0x05:
code.mov(code.rax, (u64)abss);
code.call(code.rax);
break;
case 0x06:
code.mov(code.rax, (u64)movs);
code.call(code.rax);
break;
case 0x07:
code.mov(code.rax, (u64)negs);
code.call(code.rax);
break;
case 0x08:
code.mov(code.rax, (u64)roundls);
code.call(code.rax);
break;
case 0x09:
code.mov(code.rax, (u64)truncls);
code.call(code.rax);
break;
case 0x0A:
code.mov(code.rax, (u64)ceills);
code.call(code.rax);
break;
case 0x0B:
code.mov(code.rax, (u64)floorls);
code.call(code.rax);
break;
case 0x0C:
code.mov(code.rax, (u64)roundws);
code.call(code.rax);
break;
case 0x0D:
code.mov(code.rax, (u64)truncws);
code.call(code.rax);
break;
case 0x0E:
code.mov(code.rax, (u64)ceilws);
code.call(code.rax);
break;
case 0x0F:
code.mov(code.rax, (u64)floorws);
code.call(code.rax);
break;
case 0x20:
Util::panic("[RECOMPILER] FPU Reserved instruction exception! {:08X}\n", instr);
case 0x21:
code.mov(code.rax, (u64)cvtds);
code.call(code.rax);
break;
case 0x24:
code.mov(code.rax, (u64)cvtws);
code.call(code.rax);
break;
case 0x25:
code.mov(code.rax, (u64)cvtls);
code.call(code.rax);
break;
case 0x30:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, F);
code.call(code.rax);
break;
case 0x31:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, UN);
code.call(code.rax);
break;
case 0x32:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, EQ);
code.call(code.rax);
break;
case 0x33:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, UEQ);
code.call(code.rax);
break;
case 0x34:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, OLT);
code.call(code.rax);
break;
case 0x35:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, ULT);
code.call(code.rax);
break;
case 0x36:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, OLE);
code.call(code.rax);
break;
case 0x37:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, ULE);
code.call(code.rax);
break;
case 0x38:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, SF);
code.call(code.rax);
break;
case 0x39:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, NGLE);
code.call(code.rax);
break;
case 0x3A:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, SEQ);
code.call(code.rax);
break;
case 0x3B:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, NGL);
code.call(code.rax);
break;
case 0x3C:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, LT);
code.call(code.rax);
break;
case 0x3D:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, NGE);
code.call(code.rax);
break;
case 0x3E:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, LE);
code.call(code.rax);
break;
case 0x3F:
code.mov(code.rax, (u64)ccond<float>);
code.mov(code.rdx, NGT);
code.call(code.rax);
break;
default: Util::panic("Unimplemented COP1 function S[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
}
break;
case 0x11: // d
switch(mask_fun) {
case 0x00:
code.mov(code.rax, (u64)addd);
code.call(code.rax);
break;
case 0x01:
code.mov(code.rax, (u64)subd);
code.call(code.rax);
break;
case 0x02:
code.mov(code.rax, (u64)muld);
code.call(code.rax);
break;
case 0x03:
code.mov(code.rax, (u64)divd);
code.call(code.rax);
break;
case 0x04:
code.mov(code.rax, (u64)sqrtd);
code.call(code.rax);
break;
case 0x05:
code.mov(code.rax, (u64)absd);
code.call(code.rax);
break;
case 0x06:
code.mov(code.rax, (u64)movd);
code.call(code.rax);
break;
case 0x07:
code.mov(code.rax, (u64)negd);
code.call(code.rax);
break;
case 0x08:
code.mov(code.rax, (u64)roundld);
code.call(code.rax);
break;
case 0x09:
code.mov(code.rax, (u64)truncld);
code.call(code.rax);
break;
case 0x0A:
code.mov(code.rax, (u64)ceilld);
code.call(code.rax);
break;
case 0x0B:
code.mov(code.rax, (u64)floorld);
code.call(code.rax);
break;
case 0x0C:
code.mov(code.rax, (u64)roundwd);
code.call(code.rax);
break;
case 0x0D:
code.mov(code.rax, (u64)truncwd);
code.call(code.rax);
break;
case 0x0E:
code.mov(code.rax, (u64)ceilwd);
code.call(code.rax);
break;
case 0x0F:
code.mov(code.rax, (u64)floorwd);
code.call(code.rax);
break;
case 0x20:
code.mov(code.rax, (u64)cvtsd);
code.call(code.rax);
break;
case 0x21:
Util::panic("[RECOMPILER] FPU Reserved instruction exception! {:08X}\n", instr);
case 0x24:
code.mov(code.rax, (u64)cvtwd);
code.call(code.rax);
break;
case 0x25:
code.mov(code.rax, (u64)cvtld);
code.call(code.rax);
break;
case 0x30:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, F);
code.call(code.rax);
break;
case 0x31:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, UN);
code.call(code.rax);
break;
case 0x32:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, EQ);
code.call(code.rax);
break;
case 0x33:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, UEQ);
code.call(code.rax);
break;
case 0x34:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, OLT);
code.call(code.rax);
break;
case 0x35:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, ULT);
code.call(code.rax);
break;
case 0x36:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, OLE);
code.call(code.rax);
break;
case 0x37:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, ULE);
code.call(code.rax);
break;
case 0x38:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, SF);
code.call(code.rax);
break;
case 0x39:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, NGLE);
code.call(code.rax);
break;
case 0x3A:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, SEQ);
code.call(code.rax);
break;
case 0x3B:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, NGL);
code.call(code.rax);
break;
case 0x3C:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, LT);
code.call(code.rax);
break;
case 0x3D:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, NGE);
code.call(code.rax);
break;
case 0x3E:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, LE);
code.call(code.rax);
break;
case 0x3F:
code.mov(code.rax, (u64)ccond<double>);
code.mov(code.rdx, NGT);
code.call(code.rax);
break;
default: Util::panic("Unimplemented COP1 function D[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
}
break;
case 0x14: // w
switch(mask_fun) {
case 0x01:
code.mov(code.rax, (u64)subw);
code.call(code.rax);
break;
case 0x05:
code.mov(code.rax, (u64)absw);
code.call(code.rax);
break;
case 0x02:
code.mov(code.rax, (u64)mulw);
code.call(code.rax);
break;
case 0x06:
code.mov(code.rax, (u64)movw);
code.call(code.rax);
break;
case 0x20:
code.mov(code.rax, (u64)cvtsw);
code.call(code.rax);
break;
case 0x21:
code.mov(code.rax, (u64)cvtdw);
code.call(code.rax);
break;
case 0x24:
Util::panic("[RECOMPILER] FPU Reserved instruction exception! {:08X}\n", instr);
default: Util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
}
break;
case 0x15: // l
switch(mask_fun) {
case 0x01:
code.mov(code.rax, (u64)subl);
code.call(code.rax);
break;
case 0x05:
code.mov(code.rax, (u64)absl);
code.call(code.rax);
break;
case 0x02:
code.mov(code.rax, (u64)mull);
code.call(code.rax);
break;
case 0x06:
code.mov(code.rax, (u64)movl);
code.call(code.rax);
break;
case 0x20:
code.mov(code.rax, (u64)cvtsl);
code.call(code.rax);
break;
case 0x21:
code.mov(code.rax, (u64)cvtdl);
code.call(code.rax);
break;
case 0x24:
Util::panic("[RECOMPILER] FPU Reserved instruction exception! {:08X}\n", instr);
case 0x25:
Util::panic("[RECOMPILER] FPU Reserved instruction exception! {:08X}\n", instr);
default: Util::panic("Unimplemented COP1 function L[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
}
break;
default: Util::panic("Unimplemented COP1 instruction {} {}", mask_sub >> 3, mask_sub & 7);
}
return false;
}
}

View File

@@ -1,6 +0,0 @@
#pragma once
#include <jit/cop/cop1instructions.hpp>
namespace n64 {
bool cop1Decode(JIT& cpu, u32 instr);
}

View File

@@ -1,617 +0,0 @@
#include <jit/cop/cop1instructions.hpp>
#include <cfenv>
#include <cmath>
#include <Cop1.hpp>
#include <Registers.hpp>
namespace n64 {
inline int PushRoundingMode(const FCR31& fcr31) {
int og = fegetround();
switch(fcr31.rounding_mode) {
case 0: fesetround(FE_TONEAREST); break;
case 1: fesetround(FE_TOWARDZERO); break;
case 2: fesetround(FE_UPWARD); break;
case 3: fesetround(FE_DOWNWARD); break;
}
return og;
}
#define PUSHROUNDINGMODE int og = PushRoundingMode(regs.cop1.fcr31)
#define POPROUNDINGMODE fesetround(og)
#define checknanregs(fs, ft) do { \
if(std::isnan(fs) || std::isnan(ft)) { \
regs.cop1.fcr31.flag_invalid_operation = true; \
regs.cop1.fcr31.cause_invalid_operation = true; \
FireException(regs, ExceptionCode::FloatingPointError, 1, true); \
return; \
} \
} while(0)
void absd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
regs.cop1.SetCop1Reg<double>(regs.cop0, FD(instr), std::abs(fs));
}
void abss(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
regs.cop1.SetCop1Reg<float>(regs.cop0, FD(instr), std::abs(fs));
}
void absw(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
s32 fs = regs.cop1.GetReg<s32>(regs.cop0, FS(instr));
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), std::abs(fs));
}
void absl(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
s64 fs = regs.cop1.GetReg<s64>(regs.cop0, FS(instr));
regs.cop1.SetReg(regs.cop0, FD(instr), std::abs(fs));
}
void adds(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = regs.cop1.GetCop1Reg<float>(regs.cop0, FT(instr));
checknanregs(fs, ft);
float result = fs + ft;
regs.cop1.SetCop1Reg<float>(regs.cop0, FD(instr), result);
}
void addd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = regs.cop1.GetCop1Reg<double>(regs.cop0, FT(instr));
checknanregs(fs, ft);
double result = fs + ft;
regs.cop1.SetCop1Reg<double>(regs.cop0, FD(instr), result);
}
void ceills(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
s64 result = std::ceil(fs);
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), result);
}
void ceilws(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
s32 result = std::ceil(fs);
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), result);
}
void ceilld(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
s64 result = std::ceil(fs);
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), result);
}
void ceilwd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
s32 result = std::ceil(fs);
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), result);
}
void cfc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
u8 fd = RD(instr);
s32 val = 0;
switch(fd) {
case 0: val = regs.cop1.fcr0; break;
case 31:
val = regs.cop1.fcr31.raw;
break;
default: Util::panic("Undefined CFC1 with rd != 0 or 31\n");
}
regs.gpr[RT(instr)] = val;
}
void ctc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
u8 fs = RD(instr);
u32 val = regs.gpr[RT(instr)];
switch(fs) {
case 0: break;
case 31: {
val &= 0x183ffff;
regs.cop1.fcr31.raw = val;
} break;
default: Util::panic("Undefined CTC1 with rd != 0 or 31\n");
}
}
void cvtds(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<double>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
}
void cvtsd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<float>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
}
void cvtwd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u32>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
}
void cvtws(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u32>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
}
void cvtls(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u64>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
}
void cvtsl(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<float>(
regs.cop0,
FD(instr),
(s64)regs.cop1.GetReg<u64>(
regs.cop0,
FS(instr)
)
);
}
void cvtdw(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<double>(
regs.cop0,
FD(instr),
(s32)regs.cop1.GetReg<u32>(
regs.cop0,
FS(instr)
)
);
}
void cvtsw(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<float>(
regs.cop0,
FD(instr),
(s32)regs.cop1.GetReg<u32>(
regs.cop0,
FS(instr)
)
);
}
void cvtdl(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<double>(
regs.cop0,
FD(instr),
(s64)regs.cop1.GetReg<u64>(
regs.cop0,
FS(instr)
)
);
}
void cvtld(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u64>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
}
template <typename T>
inline bool CalculateCondition(Registers& regs, T fs, T ft, CompConds cond) {
switch(cond) {
case F: return false;
case UN: return std::isnan(fs) || std::isnan(ft);
case EQ: return fs == ft;
case UEQ: return (std::isnan(fs) || std::isnan(ft)) || (fs == ft);
case OLT: return (!std::isnan(fs) && !std::isnan(ft)) && (fs < ft);
case ULT: return (std::isnan(fs) || std::isnan(ft)) || (fs < ft);
case OLE: return (!std::isnan(fs) && !std::isnan(ft)) && (fs <= ft);
case ULE: return (std::isnan(fs) || std::isnan(ft)) || (fs <= ft);
default:
if(std::isnan(fs) || std::isnan(ft)) {
regs.cop1.fcr31.flag_invalid_operation = true;
regs.cop1.fcr31.cause_invalid_operation = true;
FireException(regs, ExceptionCode::FloatingPointError, 1, true);
return false;
}
return CalculateCondition(regs, fs, ft, static_cast<CompConds>(cond - 8));
}
}
template <typename T>
void ccond(JIT& dyn, u32 instr, CompConds cond) {
Registers& regs = dyn.regs;
T fs = regs.cop1.GetCop1Reg<T>(regs.cop0, FS(instr));
T ft = regs.cop1.GetCop1Reg<T>(regs.cop0, FT(instr));
regs.cop1.fcr31.compare = CalculateCondition(regs, fs, ft, cond);
}
template void ccond<float>(JIT& dyn, u32 instr, CompConds cond);
template void ccond<double>(JIT& dyn, u32 instr, CompConds cond);
void divs(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = regs.cop1.GetCop1Reg<float>(regs.cop0, FT(instr));
regs.cop1.SetCop1Reg<float>(regs.cop0, FD(instr), fs / ft);
}
void divd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = regs.cop1.GetCop1Reg<double>(regs.cop0, FT(instr));
regs.cop1.SetCop1Reg<double>(regs.cop0, FD(instr), fs / ft);
}
void muls(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = regs.cop1.GetCop1Reg<float>(regs.cop0, FT(instr));
regs.cop1.SetCop1Reg<float>(regs.cop0, FD(instr), fs * ft);
}
void muld(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = regs.cop1.GetCop1Reg<double>(regs.cop0, FT(instr));
regs.cop1.SetCop1Reg<double>(regs.cop0, FD(instr), fs * ft);
}
void mulw(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
u32 fs = regs.cop1.GetReg<u32>(regs.cop0, FS(instr));
u32 ft = regs.cop1.GetReg<u32>(regs.cop0, FT(instr));
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), fs * ft);
}
void mull(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
u64 fs = regs.cop1.GetReg<u64>(regs.cop0, FS(instr));
u64 ft = regs.cop1.GetReg<u64>(regs.cop0, FT(instr));
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), fs * ft);
}
void subs(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = regs.cop1.GetCop1Reg<float>(regs.cop0, FT(instr));
regs.cop1.SetCop1Reg<float>(regs.cop0, FD(instr), fs - ft);
}
void subd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = regs.cop1.GetCop1Reg<double>(regs.cop0, FT(instr));
regs.cop1.SetCop1Reg<double>(regs.cop0, FD(instr), fs - ft);
}
void subw(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
u32 fs = regs.cop1.GetReg<u32>(regs.cop0, FS(instr));
u32 ft = regs.cop1.GetReg<u32>(regs.cop0, FT(instr));
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), fs - ft);
}
void subl(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
u64 fs = regs.cop1.GetReg<u64>(regs.cop0, FS(instr));
u64 ft = regs.cop1.GetReg<u64>(regs.cop0, FT(instr));
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), fs - ft);
}
void movs(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<float>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
}
void movd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<double>(
regs.cop0,
FD(instr),
regs.cop1.GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
}
void movw(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u32>(
regs.cop0,
FD(instr),
regs.cop1.GetReg<u32>(
regs.cop0,
FS(instr)
)
);
}
void movl(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u64>(
regs.cop0,
FD(instr),
regs.cop1.GetReg<u64>(
regs.cop0,
FS(instr)
)
);
}
void negs(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<float>(
regs.cop0,
FD(instr),
-regs.cop1.GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
}
void negd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetCop1Reg<double>(
regs.cop0,
FD(instr),
-regs.cop1.GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
}
void sqrts(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
regs.cop1.SetCop1Reg<float>(regs.cop0, FD(instr), std::sqrt(fs));
}
void sqrtd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
regs.cop1.SetCop1Reg<double>(regs.cop0, FD(instr), std::sqrt(fs));
}
void roundls(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), (s32)std::nearbyint(fs));
POPROUNDINGMODE;
}
void roundld(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), (s64)std::nearbyint(fs));
POPROUNDINGMODE;
}
void roundws(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), (s32)std::nearbyint(fs));
POPROUNDINGMODE;
}
void roundwd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), (s32)std::nearbyint(fs));
POPROUNDINGMODE;
}
void floorls(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), (s64)std::floor(fs));
}
void floorld(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), (s64)std::floor(fs));
}
void floorws(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), (s64)std::floor(fs));
}
void floorwd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), (s64)std::floor(fs));
}
void lwc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
if(!regs.cop0.status.cu1) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true);
return;
}
u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)];
u32 physical;
if(!MapVAddr(regs, LOAD, addr, physical)) {
HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
u32 data = dyn.mem.Read32(regs, physical);
regs.cop1.SetReg<u32>(regs.cop0, FT(instr), data);
}
}
void swc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
if(!regs.cop0.status.cu1) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true);
return;
}
u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)];
u32 physical;
if(!MapVAddr(regs, STORE, addr, physical)) {
HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
dyn.mem.Write32(regs, physical, regs.cop1.GetReg<u32>(regs.cop0, FT(instr)));
}
}
void ldc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
if(!regs.cop0.status.cu1) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true);
return;
}
u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)];
u32 physical;
if(!MapVAddr(regs, LOAD, addr, physical)) {
HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
u64 data = dyn.mem.Read64(regs, physical);
regs.cop1.SetReg<u64>(regs.cop0, FT(instr), data);
}
}
void sdc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
if(!regs.cop0.status.cu1) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true);
return;
}
u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)];
u32 physical;
if(!MapVAddr(regs, STORE, addr, physical)) {
HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
dyn.mem.Write64(regs, physical, regs.cop1.GetReg<u64>(regs.cop0, FT(instr)));
}
}
void truncws(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
s32 result = (s32)std::trunc(fs);
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), result);
}
void truncwd(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
s32 result = (s32)std::trunc(fs);
regs.cop1.SetReg<u32>(regs.cop0, FD(instr), result);
}
void truncls(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
float fs = regs.cop1.GetCop1Reg<float>(regs.cop0, FS(instr));
s64 result = (s64)std::trunc(fs);
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), result);
}
void truncld(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
double fs = regs.cop1.GetCop1Reg<double>(regs.cop0, FS(instr));
s64 result = (s64)std::trunc(fs);
regs.cop1.SetReg<u64>(regs.cop0, FD(instr), result);
}
void mfc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.gpr[RT(instr)] = (s32)regs.cop1.GetReg<u32>(regs.cop0, FS(instr));
}
void dmfc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.gpr[RT(instr)] = (s64)regs.cop1.GetReg<u64>(regs.cop0, FS(instr));
}
void mtc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u32>(regs.cop0, FS(instr), regs.gpr[RT(instr)]);
}
void dmtc1(JIT& dyn, u32 instr) {
Registers& regs = dyn.regs;
regs.cop1.SetReg<u64>(regs.cop0, FS(instr), regs.gpr[RT(instr)]);
}
}

View File

@@ -1,68 +0,0 @@
#pragma once
#include <JIT.hpp>
#include <Cop1.hpp>
namespace n64 {
void absd(JIT&, u32 instr);
void abss(JIT&, u32 instr);
void absw(JIT&, u32 instr);
void absl(JIT&, u32 instr);
void adds(JIT&, u32 instr);
void addd(JIT&, u32 instr);
void subs(JIT&, u32 instr);
void subd(JIT&, u32 instr);
void subw(JIT&, u32 instr);
void subl(JIT&, u32 instr);
void ceills(JIT&, u32 instr);
void ceilws(JIT&, u32 instr);
void ceilld(JIT&, u32 instr);
void ceilwd(JIT&, u32 instr);
void cfc1(JIT&, u32 instr);
void ctc1(JIT&, u32 instr);
void roundls(JIT&, u32 instr);
void roundld(JIT&, u32 instr);
void roundws(JIT&, u32 instr);
void roundwd(JIT&, u32 instr);
void floorls(JIT&, u32 instr);
void floorld(JIT&, u32 instr);
void floorws(JIT&, u32 instr);
void floorwd(JIT&, u32 instr);
void cvtls(JIT&, u32 instr);
void cvtws(JIT&, u32 instr);
void cvtds(JIT&, u32 instr);
void cvtsw(JIT&, u32 instr);
void cvtdw(JIT&, u32 instr);
void cvtsd(JIT&, u32 instr);
void cvtwd(JIT&, u32 instr);
void cvtld(JIT&, u32 instr);
void cvtdl(JIT&, u32 instr);
void cvtsl(JIT&, u32 instr);
template <typename T>
void ccond(JIT&, u32 instr, CompConds);
void divs(JIT&, u32 instr);
void divd(JIT&, u32 instr);
void muls(JIT&, u32 instr);
void muld(JIT&, u32 instr);
void mulw(JIT&, u32 instr);
void mull(JIT&, u32 instr);
void movs(JIT&, u32 instr);
void movd(JIT&, u32 instr);
void movw(JIT&, u32 instr);
void movl(JIT&, u32 instr);
void negs(JIT&, u32 instr);
void negd(JIT&, u32 instr);
void sqrts(JIT&, u32 instr);
void sqrtd(JIT&, u32 instr);
void lwc1(JIT&, u32 instr);
void swc1(JIT&, u32 instr);
void ldc1(JIT&, u32 instr);
void mfc1(JIT&, u32 instr);
void dmfc1(JIT&, u32 instr);
void mtc1(JIT&, u32 instr);
void dmtc1(JIT&, u32 instr);
void sdc1(JIT&, u32 instr);
void truncws(JIT&, u32 instr);
void truncwd(JIT&, u32 instr);
void truncls(JIT&, u32 instr);
void truncld(JIT&, u32 instr);
}

View File

@@ -1,655 +0,0 @@
#include <jit/instructions.hpp>
#include <jit/cop/cop1decode.hpp>
#include <jit/cop/cop0decode.hpp>
#include <Registers.hpp>
namespace n64 {
void JIT::cop2Decode(u32 instr) {
switch(RS(instr)) {
case 0x00:
code.mov(rax, (u64)mfc2);
code.call(rax);
break;
case 0x01:
code.mov(rax, (u64)dmfc2);
code.call(rax);
break;
case 0x02: case 0x06: break;
case 0x04:
code.mov(rax, (u64)mtc2);
code.call(rax);
break;
case 0x05:
code.mov(rax, (u64)dmtc2);
code.call(rax);
break;
default:
Util::panic("[RECOMPILER] Unhandled reserved instruction exception {:016X}\n", (u64)regs.pc);
}
}
bool JIT::special(u32 instr) {
u8 mask = (instr & 0x3F);
bool res = false;
// 00rr_rccc
switch (mask) { // TODO: named constants for clearer code
case 0:
if (instr != 0) {
code.mov(rax, (u64)sll);
code.call(rax);
}
break;
case 0x02:
code.mov(rax, (u64)srl);
code.call(rax);
break;
case 0x03:
code.mov(rax, (u64)sra);
code.call(rax);
break;
case 0x04:
code.mov(rax, (u64)sllv);
code.call(rax);
break;
case 0x06:
code.mov(rax, (u64)srlv);
code.call(rax);
break;
case 0x07:
code.mov(rax, (u64)srav);
code.call(rax);
break;
case 0x08:
code.mov(rax, (u64)jr);
code.call(rax);
res = true;
break;
case 0x09:
code.mov(rax, (u64)jalr);
code.call(rax);
res = true;
break;
case 0x0C: Util::panic("[RECOMPILER] Unhandled syscall instruction {:016X}\n", (u64)regs.pc);
case 0x0D: Util::panic("[RECOMPILER] Unhandled break instruction {:016X}\n", (u64)regs.pc);
case 0x0F: break; // SYNC
case 0x10:
code.mov(rax, (u64)mfhi);
code.call(rax);
break;
case 0x11:
code.mov(rax, (u64)mthi);
code.call(rax);
break;
case 0x12:
code.mov(rax, (u64)mflo);
code.call(rax);
break;
case 0x13:
code.mov(rax, (u64)mtlo);
code.call(rax);
break;
case 0x14:
code.mov(rax, (u64)dsllv);
code.call(rax);
break;
case 0x16:
code.mov(rax, (u64)dsrlv);
code.call(rax);
break;
case 0x17:
code.mov(rax, (u64)dsrav);
code.call(rax);
break;
case 0x18:
code.mov(rax, (u64)mult);
code.call(rax);
break;
case 0x19:
code.mov(rax, (u64)multu);
code.call(rax);
break;
case 0x1A:
code.mov(rax, (u64)div);
code.call(rax);
break;
case 0x1B:
code.mov(rax, (u64)divu);
code.call(rax);
break;
case 0x1C:
code.mov(rax, (u64)dmult);
code.call(rax);
break;
case 0x1D:
code.mov(rax, (u64)dmultu);
code.call(rax);
break;
case 0x1E:
code.mov(rax, (u64)ddiv);
code.call(rax);
break;
case 0x1F:
code.mov(rax, (u64)ddivu);
code.call(rax);
break;
case 0x20:
code.mov(rax, (u64)add);
code.call(rax);
break;
case 0x21:
code.mov(rax, (u64)addu);
code.call(rax);
break;
case 0x22:
code.mov(rax, (u64)sub);
code.call(rax);
break;
case 0x23:
code.mov(rax, (u64)subu);
code.call(rax);
break;
case 0x24:
code.mov(rax, (u64)and_);
code.call(rax);
break;
case 0x25:
code.mov(rax, (u64)or_);
code.call(rax);
break;
case 0x26:
code.mov(rax, (u64)xor_);
code.call(rax);
break;
case 0x27:
code.mov(rax, (u64)nor);
code.call(rax);
break;
case 0x2A:
code.mov(rax, (u64)slt);
code.call(rax);
break;
case 0x2B:
code.mov(rax, (u64)sltu);
code.call(rax);
break;
case 0x2C:
code.mov(rax, (u64)dadd);
code.call(rax);
break;
case 0x2D:
code.mov(rax, (u64)daddu);
code.call(rax);
break;
case 0x2E:
code.mov(rax, (u64)dsub);
code.call(rax);
break;
case 0x2F:
code.mov(rax, (u64)dsubu);
code.call(rax);
break;
case 0x30:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, rcx);
code.setge(sil);
code.mov(rax, (u64)trap);
code.call(rax);
res = true;
break;
case 0x31:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, rcx);
code.setae(sil);
code.mov(rax, (u64)trap);
code.call(rax);
res = true;
break;
case 0x32:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, rcx);
code.setl(sil);
code.mov(rax, (u64)trap);
code.call(rax);
res = true;
break;
case 0x33:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, rcx);
code.setb(sil);
code.mov(rax, (u64)trap);
code.call(rax);
res = true;
break;
case 0x34:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, rcx);
code.sete(sil);
code.mov(rax, (u64)trap);
code.call(rax);
res = true;
break;
case 0x36:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, rcx);
code.setne(sil);
code.mov(rax, (u64)trap);
code.call(rax);
res = true;
break;
case 0x38:
code.mov(rax, (u64)dsll);
code.call(rax);
break;
case 0x3A:
code.mov(rax, (u64)dsrl);
code.call(rax);
break;
case 0x3B:
code.mov(rax, (u64)dsra);
code.call(rax);
break;
case 0x3C:
code.mov(rax, (u64)dsll32);
code.call(rax);
break;
case 0x3E:
code.mov(rax, (u64)dsrl32);
code.call(rax);
break;
case 0x3F:
code.mov(rax, (u64)dsra32);
code.call(rax);
break;
default:
Util::panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})\n", (mask >> 3) & 7, mask & 7, instr, (u64)regs.oldPC);
}
return res;
}
bool JIT::regimm(u32 instr) {
u8 mask = ((instr >> 16) & 0x1F);
// 000r_rccc
switch (mask) { // TODO: named constants for clearer code
case 0x00:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, 0);
code.setl(dl);
code.mov(rax, (u64)b);
code.call(rax);
break;
case 0x01:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, 0);
code.setge(dl);
code.mov(rax, (u64)b);
code.call(rax);
break;
case 0x02:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, 0);
code.setl(dl);
code.mov(rax, (u64)bl);
code.call(rax);
break;
case 0x03:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, 0);
code.setge(dl);
code.mov(rax, (u64)bl);
code.call(rax);
break;
case 0x08:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, s64(s16(instr)));
code.setge(sil);
code.mov(rax, (u64)trap);
code.call(rax);
break;
case 0x09:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, u64(s64(s16(instr))));
code.setae(sil);
code.mov(rax, (u64)trap);
code.call(rax);
break;
case 0x0A:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, s64(s16(instr)));
code.setl(sil);
code.mov(rax, (u64)trap);
code.call(rax);
break;
case 0x0B:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, u64(s64(s16(instr))));
code.setb(sil);
code.mov(rax, (u64)trap);
code.call(rax);
break;
case 0x0C:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, s64(s16(instr)));
code.sete(sil);
code.mov(rax, (u64)trap);
code.call(rax);
break;
case 0x0E:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rsi, rsi);
code.cmp(r8, s64(s16(instr)));
code.setne(sil);
code.mov(rax, (u64)trap);
code.call(rax);
break;
case 0x10:
code.mov(rcx, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(rcx, 0);
code.setl(dl);
code.mov(rax, (u64)blink);
code.call(rax);
break;
case 0x11:
code.mov(rcx, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(rcx, 0);
code.setge(dl);
code.mov(rax, (u64)blink);
code.call(rax);
break;
case 0x12:
code.mov(rcx, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(rcx, 0);
code.setl(dl);
code.mov(rax, (u64)bllink);
code.call(rax);
break;
case 0x13:
code.mov(rcx, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(rcx, 0);
code.setge(dl);
code.mov(rax, (u64)bllink);
code.call(rax);
break;
default:
Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})\n", (mask >> 3) & 3, mask & 7, instr, (u64)regs.oldPC);
}
return true;
}
bool JIT::Exec(Mem& mem, u32 instr) {
u8 mask = (instr >> 26) & 0x3f;
bool res = false;
// 00rr_rccc
switch(mask) { // TODO: named constants for clearer code
case 0x00: res = special(instr); break;
case 0x01: res = regimm(instr); break;
case 0x02:
code.mov(rax, (u64)j);
code.call(rax);
res = true;
break;
case 0x03:
code.mov(rax, (u64)jal);
code.call(rax);
res = true;
break;
case 0x04:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(r9, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, r9);
code.sete(dl);
code.mov(rax, (u64)b);
code.call(rax);
res = true;
break;
case 0x05:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(r9, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, r9);
code.setne(dl);
code.mov(rax, (u64)b);
code.call(rax);
res = true;
break;
case 0x06:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.test(r8, r8);
code.setnz(dl);
code.mov(rax, (u64)b);
code.call(rax);
res = true;
break;
case 0x07:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.test(r8, r8);
code.setg(dl);
code.mov(rax, (u64)b);
code.call(rax);
res = true;
break;
case 0x08:
code.mov(rax, (u64)addi);
code.call(rax);
break;
case 0x09:
code.mov(rax, (u64)addiu);
code.call(rax);
break;
case 0x0A:
code.mov(rax, (u64)slti);
code.call(rax);
break;
case 0x0B:
code.mov(rax, (u64)sltiu);
code.call(rax);
break;
case 0x0C:
code.mov(rax, (u64)andi);
code.call(rax);
break;
case 0x0D:
code.mov(rax, (u64)ori);
code.call(rax);
break;
case 0x0E:
code.mov(rax, (u64)xori);
code.call(rax);
break;
case 0x0F:
code.mov(rax, (u64)lui);
code.call(rax);
break;
case 0x10: cop0Decode(*this, instr); break;
case 0x11: res = cop1Decode(*this, instr); break;
case 0x12: cop2Decode(instr); break;
case 0x14:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, rcx);
code.sete(dl);
code.mov(rax, (u64)bl);
code.call(rax);
break;
case 0x15:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.mov(rcx, qword[rdi + GPR_OFFSET(RT(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, rcx);
code.setne(dl);
code.mov(rax, (u64)bl);
code.call(rax);
break;
case 0x16:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, 0);
code.setle(dl);
code.mov(rax, (u64)bl);
code.call(rax);
break;
case 0x17:
code.mov(r8, qword[rdi + GPR_OFFSET(RS(instr), this)]);
code.xor_(rdx, rdx);
code.cmp(r8, 0);
code.setg(dl);
code.mov(rax, (u64)b);
code.call(rax);
break;
case 0x18:
code.mov(rax, (u64)daddi);
code.call(rax);
break;
case 0x19:
code.mov(rax, (u64)daddiu);
code.call(rax);
break;
case 0x1A:
code.mov(rax, (u64)ldl);
code.call(rax);
break;
case 0x1B:
code.mov(rax, (u64)ldr);
code.call(rax);
break;
case 0x1F: Util::panic("[RECOMPILER] Unhandled reserved instruction exception {:016X}\n", regs.oldPC); break;
case 0x20:
code.mov(rax, (u64)lb);
code.call(rax);
break;
case 0x21:
code.mov(rax, (u64)lh);
code.call(rax);
break;
case 0x22:
code.mov(rax, (u64)lwl);
code.call(rax);
break;
case 0x23:
code.mov(rax, (u64)lw);
code.call(rax);
break;
case 0x24:
code.mov(rax, (u64)lbu);
code.call(rax);
break;
case 0x25:
code.mov(rax, (u64)lhu);
code.call(rax);
break;
case 0x26:
code.mov(rax, (u64)lwr);
code.call(rax);
break;
case 0x27:
code.mov(rax, (u64)lwu);
code.call(rax);
break;
case 0x28:
code.mov(rax, (u64)sb);
code.call(rax);
break;
case 0x29:
code.mov(rax, (u64)sh);
code.call(rax);
break;
case 0x2A:
code.mov(rax, (u64)swl);
code.call(rax);
break;
case 0x2B:
code.mov(rax, (u64)sw);
code.call(rax);
break;
case 0x2C:
code.mov(rax, (u64)sdl);
code.call(rax);
break;
case 0x2D:
code.mov(rax, (u64)sdr);
code.call(rax);
break;
case 0x2E:
code.mov(rax, (u64)swr);
code.call(rax);
break;
case 0x2F: break; // CACHE
case 0x30:
code.mov(rax, (u64)ll);
code.call(rax);
break;
case 0x31:
code.mov(rax, (u64)lwc1);
code.call(rax);
break;
case 0x34:
code.mov(rax, (u64)lld);
code.call(rax);
break;
case 0x35:
code.mov(rax, (u64)ldc1);
code.call(rax);
break;
case 0x37:
code.mov(rax, (u64)ld);
code.call(rax);
break;
case 0x38:
code.mov(rax, (u64)sc);
code.call(rax);
break;
case 0x39:
code.mov(rax, (u64)swc1);
code.call(rax);
break;
case 0x3C:
code.mov(rax, (u64)scd);
code.call(rax);
break;
case 0x3D:
code.mov(rax, (u64)sdc1);
code.call(rax);
break;
case 0x3F:
code.mov(rax, (u64)sd);
code.call(rax);
break;
default:
Util::panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})\n", mask, instr, (u64)regs.oldPC);
}
return res;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,94 +0,0 @@
#pragma once
#include <JIT.hpp>
namespace n64 {
void add(JIT&, u32);
void addu(JIT&, u32);
void addi(JIT&, u32);
void addiu(JIT&, u32);
void andi(JIT&, u32);
void and_(JIT&, u32);
void branch(JIT&, bool, s64);
void branch_likely(JIT&, bool, s64);
void b(JIT&, u32, bool);
void blink(JIT&, u32, bool);
void bl(JIT&, u32, bool);
void bllink(JIT&, u32, bool);
void dadd(JIT&, u32);
void daddu(JIT&, u32);
void daddi(JIT&, u32);
void daddiu(JIT&, u32);
void ddiv(JIT&, u32);
void ddivu(JIT&, u32);
void div(JIT&, u32);
void divu(JIT&, u32);
void dmult(JIT&, u32);
void dmultu(JIT&, u32);
void dsll(JIT&, u32);
void dsllv(JIT&, u32);
void dsll32(JIT&, u32);
void dsra(JIT&, u32);
void dsrav(JIT&, u32);
void dsra32(JIT&, u32);
void dsrl(JIT&, u32);
void dsrlv(JIT&, u32);
void dsrl32(JIT&, u32);
void dsub(JIT&, u32);
void dsubu(JIT&, u32);
void j(JIT&, u32);
void jr(JIT&, u32);
void jal(JIT&, u32);
void jalr(JIT&, u32);
void lui(JIT&, u32);
void lbu(JIT&, u32);
void lb(JIT&, u32);
void ld(JIT&, u32);
void ldl(JIT&, u32);
void ldr(JIT&, u32);
void lh(JIT&, u32);
void lhu(JIT&, u32);
void ll(JIT&, u32);
void lld(JIT&, u32);
void lw(JIT&, u32);
void lwl(JIT&, u32);
void lwu(JIT&, u32);
void lwr(JIT&, u32);
void mfhi(JIT&, u32);
void mflo(JIT&, u32);
void mult(JIT&, u32);
void multu(JIT&, u32);
void mthi(JIT&, u32);
void mtlo(JIT&, u32);
void nor(JIT&, u32);
void sb(JIT&, u32);
void sc(JIT&, u32);
void scd(JIT&, u32);
void sd(JIT&, u32);
void sdl(JIT&, u32);
void sdr(JIT&, u32);
void sh(JIT&, u32);
void sw(JIT&, u32);
void swl(JIT&, u32);
void swr(JIT&, u32);
void slti(JIT&, u32);
void sltiu(JIT&, u32);
void slt(JIT&, u32);
void sltu(JIT&, u32);
void sll(JIT&, u32);
void sllv(JIT&, u32);
void sub(JIT&, u32);
void subu(JIT&, u32);
void sra(JIT&, u32);
void srav(JIT&, u32);
void srl(JIT&, u32);
void srlv(JIT&, u32);
void trap(JIT&, bool);
void or_(JIT&, u32);
void ori(JIT&, u32);
void xor_(JIT&, u32);
void xori(JIT&, u32);
void mtc2(JIT&, u32);
void mfc2(JIT&, u32);
void dmtc2(JIT&, u32);
void dmfc2(JIT&, u32);
}

View File

@@ -349,6 +349,7 @@ void PIF::UpdateController() {
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
joybusDevices[channel].controller.joy_x = xclamped;

View File

@@ -4,12 +4,12 @@
namespace n64 {
void PIF::InitDevices(SaveType saveType) {
for (int i = 0; i < 4; i++) { //TODO: make this configurable
joybusDevices[0].type = JOYBUS_CONTROLLER;
joybusDevices[0].accessoryType = ACCESSORY_MEMPACK;
for (int i = 1; i < 4; i++) { //TODO: make this configurable
joybusDevices[i].type = JOYBUS_NONE;
joybusDevices[i].accessoryType = ACCESSORY_NONE;
}
joybusDevices[0].type = JOYBUS_CONTROLLER;
joybusDevices[0].accessoryType = ACCESSORY_MEMPACK;
if (saveType == SAVE_EEPROM_4k) {
joybusDevices[4].type = JOYBUS_4KB_EEPROM;

View File

@@ -69,7 +69,7 @@ void VI::Write(MI& mi, Registers& regs, u32 paddr, u32 val) {
case 0x04400018: {
vsync = val & 0x3FF;
numHalflines = vsync >> 1;
cyclesPerHalfline = N64_CYCLES_PER_FRAME(false) / numHalflines;
cyclesPerHalfline = N64_CYCLES_PER_FRAME(isPal) / numHalflines;
} break;
case 0x0440001C: {
hsync = val & 0x3FF;

View File

@@ -88,6 +88,7 @@ struct VI {
u32 origin, width, current;
u32 vsync, hsync, intr;
AxisStart hstart{}, vstart{};
bool isPal = false;
int swaps{};
int numHalflines;
int numFields;

View File

@@ -37,4 +37,4 @@ using m128i = __m128i;
#define unlikely(exp) __builtin_expect(exp, 0)
#define likely(exp) __builtin_expect(exp, 1)
#define INLINE static inline __attribute__((always_inline))
#define FORCE_INLINE inline __attribute__((always_inline))

View File

@@ -9,10 +9,17 @@ App::App() : window(core) {
void App::Run() {
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
n64::SI& si = core.cpu->mem.mmio.si;
n64::SI& si = core.cpu.mem.mmio.si;
while (!core.done) {
core.Run(window, window.settings.GetVolumeL(), window.settings.GetVolumeL());
if(core.romLoaded) {
if(!core.pause) {
core.Run(window.settings.GetVolumeL(), window.settings.GetVolumeR());
}
UpdateScreenParallelRdp(core, window, core.GetVI());
} else {
UpdateScreenParallelRdpNoGame(core, window);
}
SDL_Event event;
while (SDL_PollEvent(&event)) {

View File

@@ -27,34 +27,16 @@ Settings::Settings(n64::Core& core) {
if(fileExists) {
settingsFile = std::fstream("resources/settings.json", std::fstream::in | std::fstream::out);
settings = json::parse(settingsFile);
auto entryCpuType = settings["cpu"]["type"];
if(!entryCpuType.empty()) {
cpuType = entryCpuType.get<std::string>();
if(cpuType == "jit") {
core.cpuType = n64::CpuType::JIT;
} else if(cpuType == "interpreter") {
core.cpuType = n64::CpuType::Interpreter;
} else {
Util::panic("Unrecognized cpu type: {}\n", cpuType);
}
} else {
settingsFile.clear();
settings["cpu"]["type"] = "interpreter";
settingsFile << settings;
core.cpuType = n64::CpuType::Interpreter;
}
checkjsonentry(volumeR, float, "audio", "volumeR", 0.5);
checkjsonentry(volumeL, float, "audio", "volumeL", 0.5);
checkjsonentry(lockChannels, bool, "audio", "lockChannels", true);
} else {
settingsFile = std::fstream("resources/settings.json", std::fstream::trunc | std::fstream::in | std::fstream::out);
settings["cpu"]["type"] = "interpreter";
settings["audio"]["volumeR"] = 0.5;
settings["audio"]["volumeL"] = 0.5;
settings["audio"]["lockChannels"] = true;
core.cpuType = n64::CpuType::Interpreter;
volumeR = 0.5;
volumeL = 0.5;
lockChannels = true;
@@ -62,17 +44,6 @@ Settings::Settings(n64::Core& core) {
settingsFile << settings;
}
settingsFile.close();
switch(core.cpuType) {
case n64::CpuType::Interpreter:
core.cpu = std::make_unique<n64::Interpreter>();
break;
case n64::CpuType::JIT:
core.cpu = std::make_unique<n64::JIT>();
break;
case n64::CpuType::COUNT:
Util::panic("BRUH\n");
}
}
Settings::~Settings() {
@@ -81,7 +52,6 @@ Settings::~Settings() {
if(fileExists) {
settingsFile = std::fstream("resources/settings.json", std::fstream::trunc | std::fstream::out);
settings["cpu"]["type"] = cpuType;
settings["audio"]["volumeR"] = volumeR;
settings["audio"]["volumeL"] = volumeL;
settings["audio"]["lockChannels"] = lockChannels;
@@ -89,7 +59,6 @@ Settings::~Settings() {
} else {
settingsFile = std::fstream("resources/settings.json", std::fstream::out);
settings["cpu"]["type"] = cpuType;
settings["audio"]["volumeR"] = volumeR;
settings["audio"]["volumeL"] = volumeL;
settings["audio"]["lockChannels"] = lockChannels;
@@ -103,30 +72,12 @@ void Settings::RenderWidget(bool& show) {
if(show) {
ImGui::OpenPopup("Settings");
if(ImGui::BeginPopupModal("Settings", &show)) {
enum class SelectedSetting { CPU, Audio, COUNT };
enum class SelectedSetting { Audio, COUNT };
static SelectedSetting selectedSetting = SelectedSetting::Audio;
const char *categories[(int)SelectedSetting::COUNT] = { "CPU", "Audio" };
const char *categories[(int)SelectedSetting::COUNT] = { "Audio" };
CreateComboList("##", (int*)&selectedSetting, categories, (int)SelectedSetting::COUNT);
ImGui::Separator();
switch (selectedSetting) {
case SelectedSetting::CPU: {
const char* cpuTypes[(int)n64::CpuType::COUNT] = { "Interpreter", "JIT" };
static n64::CpuType currentType = n64::CpuType::Interpreter;
if (cpuType == "jit") currentType = n64::CpuType::JIT;
if (CreateComboList("Core type", (int*)&currentType, cpuTypes, (int)n64::CpuType::COUNT)) {
switch (currentType) {
case n64::CpuType::Interpreter:
cpuType = "interpreter";
break;
case n64::CpuType::JIT:
cpuType = "jit";
break;
case n64::CpuType::COUNT:
Util::panic("BRUH\n");
}
}
} break;
case SelectedSetting::Audio:
ImGui::Checkbox("Lock channels", &lockChannels);
ImGui::SliderFloat("Volume L", &volumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);

View File

@@ -11,11 +11,9 @@ struct Settings {
inline float GetVolumeL() const { return volumeL; };
inline float GetVolumeR() const { return volumeR; };
inline bool GetLockChannels() const { return lockChannels; }
inline std::string GetCpuType() const { return cpuType; }
void RenderWidget(bool& show);
private:
std::string cpuType = "interpreter";
float volumeL = 0.0, volumeR = 0.0;
bool lockChannels = true;
json settings;

View File

@@ -11,7 +11,7 @@ namespace fs = std::filesystem;
Window::Window(n64::Core& core) : settings(core) {
InitSDL();
InitParallelRDP(core.cpu->mem.GetRDRAM(), window);
InitParallelRDP(core.cpu.mem.GetRDRAM(), window);
InitImgui();
NFD::Init();
}
@@ -152,7 +152,7 @@ ImDrawData* Window::Present(n64::Core& core) {
void Window::LoadROM(n64::Core& core, const std::string &path) {
if(!path.empty()) {
core.LoadROM(path);
gameName = core.cpu->mem.rom.gameNameDB;
gameName = core.cpu.mem.rom.gameNameDB;
if(gameName.empty()) {
gameName = fs::path(path).stem().string();
@@ -181,13 +181,13 @@ void Window::RenderMainMenuBar(n64::Core &core) {
}
}
if (ImGui::MenuItem("Dump RDRAM")) {
core.cpu->mem.DumpRDRAM();
core.cpu.mem.DumpRDRAM();
}
if (ImGui::MenuItem("Dump IMEM")) {
core.cpu->mem.DumpIMEM();
core.cpu.mem.DumpIMEM();
}
if (ImGui::MenuItem("Dump DMEM")) {
core.cpu->mem.DumpDMEM();
core.cpu.mem.DumpDMEM();
}
if (ImGui::MenuItem("Exit")) {
core.done = true;
@@ -232,8 +232,8 @@ void Window::Render(n64::Core& core) {
static u32 lastFrame = 0;
if(!core.pause && lastFrame < ticks - 1000) {
lastFrame = ticks;
windowTitle += fmt::format(" | {:02d} VI/s", core.cpu->mem.mmio.vi.swaps);
core.cpu->mem.mmio.vi.swaps = 0;
windowTitle += fmt::format(" | {:02d} VI/s", core.cpu.mem.mmio.vi.swaps);
core.cpu.mem.mmio.vi.swaps = 0;
SDL_SetWindowTitle(window, windowTitle.c_str());
windowTitle = shadowWindowTitle;
}