From cdc4289020eabca89f8c0c773199726590721300 Mon Sep 17 00:00:00 2001 From: CocoSimone Date: Mon, 4 Jul 2022 01:13:49 +0200 Subject: [PATCH] N64 Coprocessors integration --- src/core/CMakeLists.txt | 4 +- src/core/{ => n64/core}/Audio.cpp | 4 +- src/core/{ => n64/core}/Audio.hpp | 2 +- src/core/n64/core/CMakeLists.txt | 2 + src/core/n64/core/Cpu.cpp | 2 + src/core/n64/core/Cpu.hpp | 90 +++++++ src/core/n64/core/Mem.cpp | 8 + src/core/n64/core/cpu/CMakeLists.txt | 2 +- src/core/n64/core/cpu/Registers.hpp | 2 + src/core/n64/core/cpu/decode.cpp | 143 ++++++++++ src/core/n64/core/cpu/registers/Cop0.cpp | 21 ++ src/core/n64/core/cpu/registers/Cop0.hpp | 12 + src/core/n64/core/cpu/registers/Cop1.cpp | 139 ++++++++++ src/core/n64/core/cpu/registers/Cop1.hpp | 200 ++++++++++++++ .../core/cpu/registers/cop0instructions.cpp | 81 ++++++ .../core/cpu/registers/cop1instructions.cpp | 255 ++++++++++++++++++ src/core/n64/core/mmio/AI.cpp | 2 +- 17 files changed, 961 insertions(+), 8 deletions(-) rename src/core/{ => n64/core}/Audio.cpp (98%) rename src/core/{ => n64/core}/Audio.hpp (84%) create mode 100644 src/core/n64/core/cpu/decode.cpp create mode 100644 src/core/n64/core/cpu/registers/Cop1.cpp create mode 100644 src/core/n64/core/cpu/registers/Cop1.hpp create mode 100644 src/core/n64/core/cpu/registers/cop0instructions.cpp create mode 100644 src/core/n64/core/cpu/registers/cop1instructions.cpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 70f7a0c3..af9524a1 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -10,8 +10,6 @@ add_library(cores Scheduler.cpp Scheduler.hpp common.hpp - util.hpp - Audio.hpp - Audio.cpp) + util.hpp) target_include_directories(cores PUBLIC . ../../external) target_link_libraries(cores PUBLIC n64) diff --git a/src/core/Audio.cpp b/src/core/n64/core/Audio.cpp similarity index 98% rename from src/core/Audio.cpp rename to src/core/n64/core/Audio.cpp index e9266c5c..b3ffaa8d 100644 --- a/src/core/Audio.cpp +++ b/src/core/n64/core/Audio.cpp @@ -1,6 +1,6 @@ -#include +#include "Audio.hpp" #include -#include +#include "util.hpp" namespace natsukashii::core { #define AUDIO_SAMPLE_RATE 48000 diff --git a/src/core/Audio.hpp b/src/core/n64/core/Audio.hpp similarity index 84% rename from src/core/Audio.hpp rename to src/core/n64/core/Audio.hpp index 38acd696..3bf44e64 100644 --- a/src/core/Audio.hpp +++ b/src/core/n64/core/Audio.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include "common.hpp" namespace natsukashii::core { void PushSample(s16, s16); diff --git a/src/core/n64/core/CMakeLists.txt b/src/core/n64/core/CMakeLists.txt index 59a81450..816a83ef 100644 --- a/src/core/n64/core/CMakeLists.txt +++ b/src/core/n64/core/CMakeLists.txt @@ -12,6 +12,8 @@ add_library(n64-core Mem.hpp RDP.cpp RDP.hpp + Audio.cpp + Audio.hpp mmio/AI.cpp mmio/AI.hpp mmio/Interrupt.cpp diff --git a/src/core/n64/core/Cpu.cpp b/src/core/n64/core/Cpu.cpp index a0b74715..5d9b7185 100644 --- a/src/core/n64/core/Cpu.cpp +++ b/src/core/n64/core/Cpu.cpp @@ -98,5 +98,7 @@ void Cpu::Step(Mem& mem) { regs.oldPC = regs.pc; regs.pc = regs.nextPC; regs.nextPC += 4; + + Exec(mem, instruction); } } \ No newline at end of file diff --git a/src/core/n64/core/Cpu.hpp b/src/core/n64/core/Cpu.hpp index 6502a11d..aecc0ab1 100644 --- a/src/core/n64/core/Cpu.hpp +++ b/src/core/n64/core/Cpu.hpp @@ -7,6 +7,96 @@ struct Cpu { Cpu() = default; void Step(Mem&); Registers regs; +private: + friend struct Cop1; + void special(Mem&, u32); + void regimm(u32); + void Exec(Mem&, u32); + void add(u32); + void addu(u32); + void addi(u32); + void addiu(u32); + void andi(u32); + void and_(u32); + void branch(bool, s64); + void branch_likely(bool, s64); + void b(u32, bool); + void blink(u32, bool); + void bl(u32, bool); + void bllink(u32, bool); + void dadd(u32); + void daddu(u32); + void daddi(u32); + void daddiu(u32); + void ddiv(u32); + void ddivu(u32); + void div_(u32); + void divu(u32); + void dmult(u32); + void dmultu(u32); + void dsll(u32); + void dsllv(u32); + void dsll32(u32); + void dsra(u32); + void dsrav(u32); + void dsra32(u32); + void dsrl(u32); + void dsrlv(u32); + void dsrl32(u32); + void dsub(u32); + void dsubu(u32); + void j(u32); + void jr(u32); + void jal(u32); + void jalr(u32); + void lui(u32); + void lbu(Mem&, u32); + void lb(Mem&, u32); + void ld(Mem&, u32); + void ldl(Mem&, u32); + void ldr(Mem&, u32); + void lh(Mem&, u32); + void lhu(Mem&, u32); + void ll(Mem&, u32); + void lld(Mem&, u32); + void lw(Mem&, u32); + void lwl(Mem&, u32); + void lwu(Mem&, u32); + void lwr(Mem&, u32); + void mfhi(u32); + void mflo(u32); + void mult(u32); + void multu(u32); + void mthi(u32); + void mtlo(u32); + void nor(u32); + void sb(Mem&, u32); + void sc(Mem&, u32); + void scd(Mem&, u32); + void sd(Mem&, u32); + void sdl(Mem&, u32); + void sdr(Mem&, u32); + void sh(Mem&, u32); + void sw(Mem&, u32); + void swl(Mem&, u32); + void swr(Mem&, u32); + void slti(u32); + void sltiu(u32); + void slt(u32); + void sltu(u32); + void sll(u32); + void sllv(u32); + void sub(u32); + void subu(u32); + void sra(u32); + void srav(u32); + void srl(u32); + void srlv(u32); + void trap(bool); + void or_(u32); + void ori(u32); + void xor_(u32); + void xori(u32); }; enum class ExceptionCode : u8 { diff --git a/src/core/n64/core/Mem.cpp b/src/core/n64/core/Mem.cpp index 4b79a55a..b13caddd 100644 --- a/src/core/n64/core/Mem.cpp +++ b/src/core/n64/core/Mem.cpp @@ -81,6 +81,10 @@ template u8 Mem::Read(Registers& regs, u32 vaddr, s64 pc); template u16 Mem::Read(Registers& regs, u32 vaddr, s64 pc); template u32 Mem::Read(Registers& regs, u32 vaddr, s64 pc); template u64 Mem::Read(Registers& regs, u32 vaddr, s64 pc); +template u8 Mem::Read(Registers& regs, u32 vaddr, s64 pc); +template u16 Mem::Read(Registers& regs, u32 vaddr, s64 pc); +template u32 Mem::Read(Registers& regs, u32 vaddr, s64 pc); +template u64 Mem::Read(Registers& regs, u32 vaddr, s64 pc); template void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) { @@ -111,4 +115,8 @@ template void Mem::Write(Registers& regs, u32 vaddr, u8 val, s64 pc); template void Mem::Write(Registers& regs, u32 vaddr, u16 val, s64 pc); template void Mem::Write(Registers& regs, u32 vaddr, u32 val, s64 pc); template void Mem::Write(Registers& regs, u32 vaddr, u64 val, s64 pc); +template void Mem::Write(Registers& regs, u32 vaddr, u8 val, s64 pc); +template void Mem::Write(Registers& regs, u32 vaddr, u16 val, s64 pc); +template void Mem::Write(Registers& regs, u32 vaddr, u32 val, s64 pc); +template void Mem::Write(Registers& regs, u32 vaddr, u64 val, s64 pc); } \ No newline at end of file diff --git a/src/core/n64/core/cpu/CMakeLists.txt b/src/core/n64/core/cpu/CMakeLists.txt index 97c2e9bd..8159e1a9 100644 --- a/src/core/n64/core/cpu/CMakeLists.txt +++ b/src/core/n64/core/cpu/CMakeLists.txt @@ -5,7 +5,7 @@ add_library(n64-cpu Registers.cpp Registers.hpp registers/Cop0.cpp - registers/Cop0.hpp) + registers/Cop0.hpp decode.cpp registers/cop0instructions.cpp registers/Cop1.cpp registers/Cop1.hpp registers/cop1instructions.cpp) target_include_directories(n64-cpu PRIVATE . .. ../../ ../../../) target_include_directories(n64-cpu PUBLIC registers ../../../../../external) diff --git a/src/core/n64/core/cpu/Registers.hpp b/src/core/n64/core/cpu/Registers.hpp index 39ee0446..98930720 100644 --- a/src/core/n64/core/cpu/Registers.hpp +++ b/src/core/n64/core/cpu/Registers.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include namespace natsukashii::n64::core { struct Registers { @@ -7,6 +8,7 @@ struct Registers { void SetPC(s64); s64 gpr[32]; Cop0 cop0; + Cop1 cop1; s64 oldPC, pc, nextPC; s64 hi, lo; bool LLBit; diff --git a/src/core/n64/core/cpu/decode.cpp b/src/core/n64/core/cpu/decode.cpp new file mode 100644 index 00000000..7570dfa3 --- /dev/null +++ b/src/core/n64/core/cpu/decode.cpp @@ -0,0 +1,143 @@ +#include +#include + +namespace natsukashii::n64::core { +void Cpu::special(Mem& mem, u32 instr) { + u8 mask = (instr & 0x3F); + // 00rr_rccc + switch (mask) { // TODO: named constants for clearer code + case 0: + if (instr != 0) { + sll(instr); + } + break; + case 0x02: srl(instr); break; + case 0x03: sra(instr); break; + case 0x04: sllv(instr); break; + case 0x06: srlv(instr); break; + case 0x07: srav(instr); break; + case 0x08: jr(instr); break; + case 0x09: jalr(instr); break; + case 0x0C: FireException(regs, ExceptionCode::Syscall, 0, regs.oldPC); break; + case 0x0D: FireException(regs, ExceptionCode::Breakpoint, 0, regs.oldPC); break; + case 0x0F: break; + case 0x10: mfhi(instr); break; + case 0x11: mthi(instr); break; + case 0x12: mflo(instr); break; + case 0x13: mtlo(instr); break; + case 0x14: dsllv(instr); break; + case 0x16: dsrlv(instr); break; + case 0x17: dsrav(instr); break; + case 0x18: mult(instr); break; + case 0x19: multu(instr); break; + case 0x1A: div_(instr); break; + case 0x1B: divu(instr); break; + case 0x1C: dmult(instr); break; + case 0x1D: dmultu(instr); break; + case 0x1E: ddiv(instr); break; + case 0x1F: ddivu(instr); break; + case 0x20: add(instr); break; + case 0x21: addu(instr); break; + case 0x22: sub(instr); break; + case 0x23: subu(instr); break; + case 0x24: and_(instr); break; + case 0x25: or_(instr); break; + case 0x26: xor_(instr); break; + case 0x27: nor(instr); break; + case 0x2A: slt(instr); break; + case 0x2B: sltu(instr); break; + case 0x2C: dadd(instr); break; + case 0x2D: daddu(instr); break; + case 0x2E: dsub(instr); break; + case 0x2F: dsubu(instr); break; + case 0x34: trap(regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; + case 0x38: dsll(instr); break; + case 0x3A: dsrl(instr); break; + case 0x3B: dsra(instr); break; + case 0x3C: dsll32(instr); break; + case 0x3E: dsrl32(instr); break; + case 0x3F: dsra32(instr); break; + default: + util::panic("Unimplemented special {} {}", (mask >> 3) & 7, mask & 7); + } +} + +void Cpu::regimm(u32 instr) { + u8 mask = ((instr >> 16) & 0x1F); + // 000r_rccc + switch (mask) { // TODO: named constants for clearer code + case 0x00: b(instr, regs.gpr[RS(instr)] < 0); break; + case 0x01: b(instr, regs.gpr[RS(instr)] >= 0); break; + case 0x02: bl(instr, regs.gpr[RS(instr)] < 0); break; + case 0x03: bl(instr, regs.gpr[RS(instr)] >= 0); break; + case 0x10: blink(instr, regs.gpr[RS(instr)] < 0); break; + case 0x11: blink(instr, regs.gpr[RS(instr)] >= 0); break; + case 0x12: bllink(instr, regs.gpr[RS(instr)] < 0); break; + case 0x13: bllink(instr, regs.gpr[RS(instr)] >= 0); break; + default: + util::panic("Unimplemented regimm {} {}", (mask >> 3) & 3, mask & 7); + } +} + +void Cpu::Exec(Mem& mem, u32 instr) { + u8 mask = (instr >> 26) & 0x3f; + // 00rr_rccc + switch(mask) { // TODO: named constants for clearer code + case 0x00: special(mem, instr); break; + case 0x01: regimm(instr); break; + case 0x02: j(instr); break; + case 0x03: jal(instr); break; + case 0x04: b(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; + case 0x05: b(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; + case 0x06: b(instr, regs.gpr[RS(instr)] <= 0); break; + case 0x07: b(instr, regs.gpr[RS(instr)] > 0); break; + case 0x08: addi(instr); break; + case 0x09: addiu(instr); break; + case 0x0A: slti(instr); break; + case 0x0B: sltiu(instr); break; + case 0x0C: andi(instr); break; + case 0x0D: ori(instr); break; + case 0x0E: xori(instr); break; + case 0x0F: lui(instr); break; + case 0x10: regs.cop0.decode(regs, mem, instr); break; + case 0x11: regs.cop1.decode(regs, instr); break; + case 0x14: bl(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; + case 0x15: bl(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; + case 0x16: bl(instr, regs.gpr[RS(instr)] <= 0); break; + case 0x17: bl(instr, regs.gpr[RS(instr)] > 0); break; + case 0x18: daddi(instr); break; + case 0x19: daddiu(instr); break; + case 0x1A: ldl(mem, instr); break; + case 0x1B: ldr(mem, instr); break; + case 0x1F: FireException(regs, ExceptionCode::ReservedInstruction, 0, regs.oldPC); break; + case 0x20: lb(mem, instr); break; + case 0x21: lh(mem, instr); break; + case 0x22: lwl(mem, instr); break; + case 0x23: lw(mem, instr); break; + case 0x24: lbu(mem, instr); break; + case 0x25: lhu(mem, instr); break; + case 0x26: lwr(mem, instr); break; + case 0x27: lwu(mem, instr); break; + case 0x28: sb(mem, instr); break; + case 0x29: sh(mem, instr); break; + case 0x2A: swl(mem, instr); break; + case 0x2B: sw(mem, instr); break; + case 0x2C: sdl(mem, instr); break; + case 0x2D: sdr(mem, instr); break; + case 0x2E: swr(mem, instr); break; + case 0x2F: break; // CACHE + case 0x30: ll(mem, instr); break; + case 0x31: lwc1(mem, instr); break; + case 0x34: lld(mem, instr); break; + case 0x35: ldc1(mem, instr); break; + case 0x37: ld(mem, instr); break; + case 0x38: sc(mem, instr); break; + case 0x39: swc1(mem, instr); break; + case 0x3C: scd(mem, instr); break; + case 0x3D: sdc1(mem, instr); break; + case 0x3F: sd(mem, instr); break; + default: + util::panic("Unimplemented instruction {} {}", (mask >> 3) & 7, mask & 7); + } +} +} \ No newline at end of file diff --git a/src/core/n64/core/cpu/registers/Cop0.cpp b/src/core/n64/core/cpu/registers/Cop0.cpp index 09cc64cd..0d8e377e 100644 --- a/src/core/n64/core/cpu/registers/Cop0.cpp +++ b/src/core/n64/core/cpu/registers/Cop0.cpp @@ -192,4 +192,25 @@ ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType) { util::panic("Getting TLB exception for unknown error code! ({})\n", error); } } + +void Cop0::decode(Registers& regs, Mem& mem, u32 instr) { + u8 mask_cop = (instr >> 21) & 0x1F; + u8 mask_cop2 = instr & 0x3F; + switch(mask_cop) { + case 0x00: mfc0(regs, instr); break; + case 0x01: dmfc0(regs, instr); break; + case 0x04: mtc0(regs, instr); break; + case 0x05: dmtc0(regs, instr); break; + case 0x10 ... 0x1F: + switch(mask_cop2) { + case 0x01: tlbr(regs); break; + case 0x02: tlbwi(regs); break; + case 0x08: tlbp(regs); break; + case 0x18: eret(regs); break; + default: util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr, regs->old_pc); + } + break; + default: util::panic("Unimplemented COP0 instruction {} {}", mask_cop >> 4, mask_cop & 7); + } +} } \ No newline at end of file diff --git a/src/core/n64/core/cpu/registers/Cop0.hpp b/src/core/n64/core/cpu/registers/Cop0.hpp index ae969dac..a76035c9 100644 --- a/src/core/n64/core/cpu/registers/Cop0.hpp +++ b/src/core/n64/core/cpu/registers/Cop0.hpp @@ -5,6 +5,7 @@ namespace natsukashii::n64::core { #define STATUS_MASK 0xFF77FFFF struct Cpu; +struct Registers; struct Mem; union Cop0Cause { @@ -176,6 +177,17 @@ struct Cop0 { u32 r31{}; TLBEntry tlb[32]{}; TLBError tlbError = NONE; + void decode(Registers&, Mem&, u32); +private: + void mtc0(Registers&, u32); + void dmtc0(Registers&, u32); + void mfc0(Registers&, u32); + void dmfc0(Registers&, u32); + void eret(Registers&); + + void tlbr(Registers&); + void tlbwi(Registers&); + void tlbp(Registers&); }; struct Registers; diff --git a/src/core/n64/core/cpu/registers/Cop1.cpp b/src/core/n64/core/cpu/registers/Cop1.cpp new file mode 100644 index 00000000..059afeeb --- /dev/null +++ b/src/core/n64/core/cpu/registers/Cop1.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include + +namespace natsukashii::n64::core { +void Cop1::decode(Cpu& cpu, u32 instr) { + Registers& regs = cpu.regs; + if(!regs.cop0.status.cu1) { + FireException(regs, ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return; + } + + u8 mask_sub = (instr >> 21) & 0x1F; + u8 mask_fun = instr & 0x3F; + u8 mask_branch = (instr >> 16) & 0x1F; + switch(mask_sub) { + // 000r_rccc + case 0x00: mfc1(regs, instr); break; + case 0x01: dmfc1(regs, instr); break; + case 0x02: cfc1(regs, instr); break; + case 0x04: mtc1(regs, instr); break; + case 0x05: dmtc1(regs, instr); break; + case 0x06: ctc1(regs, instr); break; + case 0x08: + switch(mask_branch) { + case 0: cpu.b(instr, !regs.cop1.fcr31.compare); break; + case 1: cpu.b(instr, regs.cop1.fcr31.compare); break; + case 2: cpu.bl(instr, !regs.cop1.fcr31.compare); break; + case 3: cpu.bl(instr, regs.cop1.fcr31.compare); break; + default: util::panic("Undefined BC COP1 {:02X}\n", mask_branch); + } + break; + case 0x10: // s + switch(mask_fun) { + case 0x00: adds(regs, instr); break; + case 0x01: subs(regs, instr); break; + case 0x02: muls(regs, instr); break; + case 0x03: divs(regs, instr); break; + case 0x04: sqrts(regs, instr); break; + case 0x05: abss(regs, instr); break; + case 0x06: movs(regs, instr); break; + case 0x07: negs(regs, instr); break; + case 0x08: roundls(regs, instr); break; + case 0x09: truncls(regs, instr); break; + case 0x0A: ceills(regs, instr); break; + case 0x0B: floorls(regs, instr); break; + case 0x0C: roundws(regs, instr); break; + case 0x0D: truncws(regs, instr); break; + case 0x0E: ceilws(regs, instr); break; + case 0x0F: floorws(regs, instr); break; + case 0x21: cvtds(regs, instr); break; + case 0x24: cvtws(regs, instr); break; + case 0x25: cvtls(regs, instr); break; + case 0x30: cconds(regs, instr, F); break; + case 0x31: cconds(regs, instr, UN); break; + case 0x32: cconds(regs, instr, EQ); break; + case 0x33: cconds(regs, instr, UEQ); break; + case 0x34: cconds(regs, instr, OLT); break; + case 0x35: cconds(regs, instr, ULT); break; + case 0x36: cconds(regs, instr, OLE); break; + case 0x37: cconds(regs, instr, ULE); break; + case 0x38: cconds(regs, instr, SF); break; + case 0x39: cconds(regs, instr, NGLE); break; + case 0x3A: cconds(regs, instr, SEQ); break; + case 0x3B: cconds(regs, instr, NGL); break; + case 0x3C: cconds(regs, instr, LT); break; + case 0x3D: cconds(regs, instr, NGE); break; + case 0x3E: cconds(regs, instr, LE); break; + case 0x3F: cconds(regs, instr, NGT); break; + default: util::panic("Unimplemented COP1 function S[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + } + break; + case 0x11: // d + switch(mask_fun) { + case 0x00: addd(regs, instr); break; + case 0x01: subd(regs, instr); break; + case 0x02: muld(regs, instr); break; + case 0x03: divd(regs, instr); break; + case 0x04: sqrtd(regs, instr); break; + case 0x05: absd(regs, instr); break; + case 0x06: movd(regs, instr); break; + case 0x07: negd(regs, instr); break; + case 0x08: roundld(regs, instr); break; + case 0x09: truncld(regs, instr); break; + case 0x0A: ceilld(regs, instr); break; + case 0x0B: floorld(regs, instr); break; + case 0x0C: roundwd(regs, instr); break; + case 0x0D: truncwd(regs, instr); break; + case 0x0E: ceilwd(regs, instr); break; + case 0x0F: floorwd(regs, instr); break; + case 0x20: cvtsd(regs, instr); break; + case 0x24: cvtwd(regs, instr); break; + case 0x25: cvtld(regs, instr); break; + case 0x30: ccondd(regs, instr, F); break; + case 0x31: ccondd(regs, instr, UN); break; + case 0x32: ccondd(regs, instr, EQ); break; + case 0x33: ccondd(regs, instr, UEQ); break; + case 0x34: ccondd(regs, instr, OLT); break; + case 0x35: ccondd(regs, instr, ULT); break; + case 0x36: ccondd(regs, instr, OLE); break; + case 0x37: ccondd(regs, instr, ULE); break; + case 0x38: ccondd(regs, instr, SF); break; + case 0x39: ccondd(regs, instr, NGLE); break; + case 0x3A: ccondd(regs, instr, SEQ); break; + case 0x3B: ccondd(regs, instr, NGL); break; + case 0x3C: ccondd(regs, instr, LT); break; + case 0x3D: ccondd(regs, instr, NGE); break; + case 0x3E: ccondd(regs, instr, LE); break; + case 0x3F: ccondd(regs, instr, NGT); break; + default: util::panic("Unimplemented COP1 function D[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + } + break; + case 0x14: // w + switch(mask_fun) { + case 0x01: subw(regs, instr); break; + case 0x05: absw(regs, instr); break; + case 0x02: mulw(regs, instr); break; + case 0x06: movw(regs, instr); break; + case 0x20: cvtsw(regs, instr); break; + case 0x21: cvtdw(regs, instr); break; + default: util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + } + break; + case 0x15: // l + switch(mask_fun) { + case 0x01: subl(regs, instr); break; + case 0x05: absl(regs, instr); break; + case 0x02: mull(regs, instr); break; + case 0x06: movl(regs, instr); break; + case 0x20: cvtsl(regs, instr); break; + case 0x21: cvtdl(regs, instr); break; + default: util::panic("Unimplemented COP1 function L[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + } + break; + default: util::panic("Unimplemented COP1 instruction {} {}", mask_sub >> 3, mask_sub & 7); + } +} +} \ No newline at end of file diff --git a/src/core/n64/core/cpu/registers/Cop1.hpp b/src/core/n64/core/cpu/registers/Cop1.hpp new file mode 100644 index 00000000..b2a80c5b --- /dev/null +++ b/src/core/n64/core/cpu/registers/Cop1.hpp @@ -0,0 +1,200 @@ +#pragma once +#include + +namespace natsukashii::n64::core { +union FCR31 { + struct { + unsigned rounding_mode:2; + unsigned flag_inexact_operation:1; + unsigned flag_underflow:1; + unsigned flag_overflow:1; + unsigned flag_division_by_zero:1; + unsigned flag_invalid_operation:1; + unsigned enable_inexact_operation:1; + unsigned enable_underflow:1; + unsigned enable_overflow:1; + unsigned enable_division_by_zero:1; + unsigned enable_invalid_operation:1; + unsigned cause_inexact_operation:1; + unsigned cause_underflow:1; + unsigned cause_overflow:1; + unsigned cause_division_by_zero:1; + unsigned cause_invalid_operation:1; + unsigned cause_unimplemented_operation:1; + unsigned:5; + unsigned compare:1; + unsigned fs:1; + unsigned:7; + } __attribute__((__packed__)); + + struct { + unsigned:7; + unsigned enable:5; + unsigned cause:6; + unsigned:14; + } __attribute__((__packed__)); + + u32 raw; +}; + +union FGR { + struct { + s32 lo:32; + s32 hi:32; + } __attribute__((__packed__)); + + s64 raw; +}; + +struct Cpu; +struct Registers; + +struct Cop1 { + u32 fcr0; + FCR31 fcr31; + FGR fgr[32]; + void decode(Cpu&, u32); +private: + template + inline void SetReg(Cop0& cop0, u8 index, T value) { + if constexpr(sizeof(T) == 4) { + if (cop0.status.fr) { + fgr[index].lo = value; + } else { + if (index & 1) { + fgr[index & ~1].hi = value; + } else { + fgr[index].lo = value; + } + } + } else if constexpr(sizeof(T) == 8) { + if(!cop0.status.fr) { + index &= ~1; + } + + fgr[index].raw = value; + } + } + + template + inline T GetReg(Cop0& cop0, u8 index) { + if constexpr(sizeof(T) == 4) { + if(cop0.status.fr) { + return fgr[index].lo; + } else { + if (index & 1) { + return fgr[index & ~1].hi; + } else { + return fgr[index].lo; + } + } + } else if constexpr(sizeof(T) == 8) { + if(!cop0.status.fr) { + index &= ~1; + } + + return fgr[index].raw; + } + } + + inline void SetCop1RegDouble(Cop0& cop0, u8 index, double value) { + u64 raw; + memcpy(&raw, &value, sizeof(double)); + SetReg(cop0, index, raw); + } + + inline double GetCop1RegDouble(Cop0& cop0, u8 index) { + double doublevalue; + u64 raw = GetReg(cop0, index); + memcpy(&doublevalue, &raw, sizeof(double)); + return doublevalue; + } + + inline void SetCop1RegFloat(Cop0& cop0, u8 index, float value) { + u32 raw; + memcpy(&raw, &value, sizeof(float)); + SetReg(cop0, index, raw); + } + + inline float GetCop1RegFloat(Cop0& cop0, u8 index) { + u32 raw = GetReg(cop0, index); + float floatvalue; + memcpy(&floatvalue, &raw, sizeof(float)); + return floatvalue; + } + + enum CompConds { + T, UN, EQ, UEQ, + OLT, ULT, OLE, ULE, + SF, NGLE, SEQ, NGL, + LT, NGE, LE, NGT, + F, OR, NEQ, OLG, + UGE, OGE, UGT, OGT, + ST, GLE, SNE, GL, + NLT, GE, NLE, GT + }; + + void absd(Registers&, u32 instr); + void abss(Registers&, u32 instr); + void absw(Registers&, u32 instr); + void absl(Registers&, u32 instr); + void adds(Registers&, u32 instr); + void addd(Registers&, u32 instr); + void subs(Registers&, u32 instr); + void subd(Registers&, u32 instr); + void subw(Registers&, u32 instr); + void subl(Registers&, u32 instr); + void ceills(Registers&, u32 instr); + void ceilws(Registers&, u32 instr); + void ceilld(Registers&, u32 instr); + void ceilwd(Registers&, u32 instr); + void cfc1(Registers&, u32 instr); + void ctc1(Registers&, u32 instr); + void roundls(Registers&, u32 instr); + void roundld(Registers&, u32 instr); + void roundws(Registers&, u32 instr); + void roundwd(Registers&, u32 instr); + void floorls(Registers&, u32 instr); + void floorld(Registers&, u32 instr); + void floorws(Registers&, u32 instr); + void floorwd(Registers&, u32 instr); + void cvtls(Registers&, u32 instr); + void cvtws(Registers&, u32 instr); + void cvtds(Registers&, u32 instr); + void cvtsw(Registers&, u32 instr); + void cvtdw(Registers&, u32 instr); + void cvtsd(Registers&, u32 instr); + void cvtwd(Registers&, u32 instr); + void cvtld(Registers&, u32 instr); + void cvtdl(Registers&, u32 instr); + void cvtsl(Registers&, u32 instr); + void ccondd(Registers&, u32 instr, CompConds); + void cconds(Registers&, u32 instr, CompConds); + void divs(Registers&, u32 instr); + void divd(Registers&, u32 instr); + void muls(Registers&, u32 instr); + void muld(Registers&, u32 instr); + void mulw(Registers&, u32 instr); + void mull(Registers&, u32 instr); + void movs(Registers&, u32 instr); + void movd(Registers&, u32 instr); + void movw(Registers&, u32 instr); + void movl(Registers&, u32 instr); + void negs(Registers&, u32 instr); + void negd(Registers&, u32 instr); + void sqrts(Registers&, u32 instr); + void sqrtd(Registers&, u32 instr); + void lwc1(Registers&, Mem&, u32 instr); + void swc1(Registers&, Mem&, u32 instr); + void ldc1(Registers&, Mem&, u32 instr); + void mfc1(Registers&, u32 instr); + void dmfc1(Registers&, u32 instr); + void mtc1(Registers&, u32 instr); + void dmtc1(Registers&, u32 instr); + void sdc1(Registers&, Mem&, u32 instr); + void truncws(Registers&, u32 instr); + void truncwd(Registers&, u32 instr); + void truncls(Registers&, u32 instr); + void truncld(Registers&, u32 instr); +}; +} diff --git a/src/core/n64/core/cpu/registers/cop0instructions.cpp b/src/core/n64/core/cpu/registers/cop0instructions.cpp new file mode 100644 index 00000000..4c426247 --- /dev/null +++ b/src/core/n64/core/cpu/registers/cop0instructions.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +namespace natsukashii::n64::core { +void Cop0::mtc0(Registers& regs, u32 instr) { + SetReg(RD(instr), regs.gpr[RT(instr)]); +} + +void Cop0::dmtc0(Registers& regs, u32 instr) { + SetReg(RD(instr), regs.gpr[RT(instr)]); +} + +void Cop0::mfc0(Registers& regs, u32 instr) { + regs.gpr[RT(instr)] = GetReg(RD(instr)); +} + +void Cop0::dmfc0(Registers& regs, u32 instr) { + regs.gpr[RT(instr)] = GetReg(RD(instr)); +} + +void Cop0::eret(Registers& regs) { + if(status.erl) { + regs.SetPC((s64)ErrorEPC); + status.erl = false; + } else { + regs.SetPC((s64)EPC); + status.exl = false; + } +} + + +void Cop0::tlbr(Registers& regs) { + int Index = index & 0b111111; + if (Index >= 32) { + util::panic("TLBR with TLB index {}", index); + } + + TLBEntry entry = tlb[Index]; + + entryHi.raw = entry.entryHi.raw; + entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF; + entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF; + + entryLo0.g = entry.global; + entryLo1.g = entry.global; + pageMask.raw = entry.pageMask.raw; +} + +void Cop0::tlbwi(Registers& regs) { + PageMask page_mask; + page_mask = pageMask; + u32 top = page_mask.mask & 0xAAA; + page_mask.mask = top | (top >> 1); + + int Index = index & 0x3F; + if(Index >= 32) { + util::panic("TLBWI with TLB index {}", Index); + } + + tlb[Index].entryHi.raw = entryHi.raw; + tlb[Index].entryHi.vpn2 &= ~page_mask.mask; + + tlb[Index].entryLo0.raw = entryLo0.raw & 0x03FFFFFE; + tlb[Index].entryLo1.raw = entryLo1.raw & 0x03FFFFFE; + tlb[Index].pageMask.raw = page_mask.raw; + + tlb[Index].global = entryLo0.g && entryLo1.g; +} + +void Cop0::tlbp(Registers& regs) { + int match = -1; + TLBEntry* entry = TLBTryMatch(regs, entryHi.raw, &match); + if(entry && match >= 0) { + index = match; + } else { + index = 0x80000000; + } +} + +} \ No newline at end of file diff --git a/src/core/n64/core/cpu/registers/cop1instructions.cpp b/src/core/n64/core/cpu/registers/cop1instructions.cpp new file mode 100644 index 00000000..67132efa --- /dev/null +++ b/src/core/n64/core/cpu/registers/cop1instructions.cpp @@ -0,0 +1,255 @@ +#include +#include + +namespace natsukashii::n64::core { +void Cop1::absd(Registers& regs, u32 instr) { + double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + SetCop1RegDouble(regs.cop0, FD(instr), fabs(fs)); +} + +void Cop1::abss(Registers& regs, u32 instr) { + float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + SetCop1RegFloat(regs.cop0, FD(instr), fabsf(fs)); +} + +void Cop1::absw(Registers& regs, u32 instr) { + +} + +void Cop1::absl(Registers& regs, u32 instr) { + +} + +void Cop1::adds(Registers& regs, u32 instr) { + +} + +void Cop1::addd(Registers& regs, u32 instr) { + +} + +void Cop1::subs(Registers& regs, u32 instr) { + +} + +void Cop1::subd(Registers& regs, u32 instr) { + +} + +void Cop1::subw(Registers& regs, u32 instr) { + +} + +void Cop1::subl(Registers& regs, u32 instr) { + +} + +void Cop1::ceills(Registers& regs, u32 instr) { + +} + +void Cop1::ceilws(Registers& regs, u32 instr) { + +} + +void Cop1::ceilld(Registers& regs, u32 instr) { + +} + +void Cop1::ceilwd(Registers& regs, u32 instr) { + +} + +void Cop1::cfc1(Registers& regs, u32 instr) { + +} + +void Cop1::ctc1(Registers& regs, u32 instr) { + +} + +void Cop1::roundls(Registers& regs, u32 instr) { + +} + +void Cop1::roundld(Registers& regs, u32 instr) { + +} + +void Cop1::roundws(Registers& regs, u32 instr) { + +} + +void Cop1::roundwd(Registers& regs, u32 instr) { + +} + +void Cop1::floorls(Registers& regs, u32 instr) { + +} + +void Cop1::floorld(Registers& regs, u32 instr) { + +} + +void Cop1::floorws(Registers& regs, u32 instr) { + +} + +void Cop1::floorwd(Registers& regs, u32 instr) { + +} + +void Cop1::cvtls(Registers& regs, u32 instr) { + +} + +void Cop1::cvtws(Registers& regs, u32 instr) { + +} + +void Cop1::cvtds(Registers& regs, u32 instr) { + +} + +void Cop1::cvtsw(Registers& regs, u32 instr) { + +} + +void Cop1::cvtdw(Registers& regs, u32 instr) { + +} + +void Cop1::cvtsd(Registers& regs, u32 instr) { + +} + +void Cop1::cvtwd(Registers& regs, u32 instr) { + +} + +void Cop1::cvtld(Registers& regs, u32 instr) { + +} + +void Cop1::cvtdl(Registers& regs, u32 instr) { + +} + +void Cop1::cvtsl(Registers& regs, u32 instr) { + +} + +void Cop1::ccondd(Registers& regs, u32 instr, CompConds cond) { + +} + +void Cop1::cconds(Registers& regs, u32 instr, CompConds cond) { + +} + +void Cop1::divs(Registers& regs, u32 instr) { + +} + +void Cop1::divd(Registers& regs, u32 instr) { + +} + +void Cop1::muls(Registers& regs, u32 instr) { + +} + +void Cop1::muld(Registers& regs, u32 instr) { + +} + +void Cop1::mulw(Registers& regs, u32 instr) { + +} + +void Cop1::mull(Registers& regs, u32 instr) { + +} + +void Cop1::movs(Registers& regs, u32 instr) { + +} + +void Cop1::movd(Registers& regs, u32 instr) { + +} + +void Cop1::movw(Registers& regs, u32 instr) { + +} + +void Cop1::movl(Registers& regs, u32 instr) { + +} + +void Cop1::negs(Registers& regs, u32 instr) { + +} + +void Cop1::negd(Registers& regs, u32 instr) { + +} + +void Cop1::sqrts(Registers& regs, u32 instr) { + +} + +void Cop1::sqrtd(Registers& regs, u32 instr) { + +} + +void Cop1::lwc1(Registers& regs, Mem& mem, u32 instr) { + +} + +void Cop1::swc1(Registers& regs, Mem& mem, u32 instr) { + +} + +void Cop1::ldc1(Registers& regs, Mem& mem, u32 instr) { + +} + +void Cop1::mfc1(Registers& regs, u32 instr) { + +} + +void Cop1::dmfc1(Registers& regs, u32 instr) { + +} + +void Cop1::mtc1(Registers& regs, u32 instr) { + +} + +void Cop1::dmtc1(Registers& regs, u32 instr) { + +} + +void Cop1::sdc1(Registers& regs, Mem&, u32 instr) { + +} + +void Cop1::truncws(Registers& regs, u32 instr) { + +} + +void Cop1::truncwd(Registers& regs, u32 instr) { + +} + +void Cop1::truncls(Registers& regs, u32 instr) { + +} + +void Cop1::truncld(Registers& regs, u32 instr) { + +} + +} \ No newline at end of file diff --git a/src/core/n64/core/mmio/AI.cpp b/src/core/n64/core/mmio/AI.cpp index c31a22a7..00a3be25 100644 --- a/src/core/n64/core/mmio/AI.cpp +++ b/src/core/n64/core/mmio/AI.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace natsukashii::n64::core { auto AI::Read(u32 addr) const -> u32 {