N64 Coprocessors integration
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <Audio.hpp>
|
||||
#include "Audio.hpp"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <util.hpp>
|
||||
#include "util.hpp"
|
||||
|
||||
namespace natsukashii::core {
|
||||
#define AUDIO_SAMPLE_RATE 48000
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <common.hpp>
|
||||
#include "common.hpp"
|
||||
|
||||
namespace natsukashii::core {
|
||||
void PushSample(s16, s16);
|
||||
@@ -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
|
||||
|
||||
@@ -98,5 +98,7 @@ void Cpu::Step(Mem& mem) {
|
||||
regs.oldPC = regs.pc;
|
||||
regs.pc = regs.nextPC;
|
||||
regs.nextPC += 4;
|
||||
|
||||
Exec(mem, instruction);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -81,6 +81,10 @@ template u8 Mem::Read<u8>(Registers& regs, u32 vaddr, s64 pc);
|
||||
template u16 Mem::Read<u16>(Registers& regs, u32 vaddr, s64 pc);
|
||||
template u32 Mem::Read<u32>(Registers& regs, u32 vaddr, s64 pc);
|
||||
template u64 Mem::Read<u64>(Registers& regs, u32 vaddr, s64 pc);
|
||||
template u8 Mem::Read<u8, false>(Registers& regs, u32 vaddr, s64 pc);
|
||||
template u16 Mem::Read<u16, false>(Registers& regs, u32 vaddr, s64 pc);
|
||||
template u32 Mem::Read<u32, false>(Registers& regs, u32 vaddr, s64 pc);
|
||||
template u64 Mem::Read<u64, false>(Registers& regs, u32 vaddr, s64 pc);
|
||||
|
||||
template <class T, bool tlb>
|
||||
void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) {
|
||||
@@ -111,4 +115,8 @@ template void Mem::Write<u8>(Registers& regs, u32 vaddr, u8 val, s64 pc);
|
||||
template void Mem::Write<u16>(Registers& regs, u32 vaddr, u16 val, s64 pc);
|
||||
template void Mem::Write<u32>(Registers& regs, u32 vaddr, u32 val, s64 pc);
|
||||
template void Mem::Write<u64>(Registers& regs, u32 vaddr, u64 val, s64 pc);
|
||||
template void Mem::Write<u8, false>(Registers& regs, u32 vaddr, u8 val, s64 pc);
|
||||
template void Mem::Write<u16, false>(Registers& regs, u32 vaddr, u16 val, s64 pc);
|
||||
template void Mem::Write<u32, false>(Registers& regs, u32 vaddr, u32 val, s64 pc);
|
||||
template void Mem::Write<u64, false>(Registers& regs, u32 vaddr, u64 val, s64 pc);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <n64/core/cpu/registers/Cop0.hpp>
|
||||
#include <n64/core/cpu/registers/Cop1.hpp>
|
||||
|
||||
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;
|
||||
|
||||
143
src/core/n64/core/cpu/decode.cpp
Normal file
143
src/core/n64/core/cpu/decode.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <n64/core/Cpu.hpp>
|
||||
#include <util.hpp>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
139
src/core/n64/core/cpu/registers/Cop1.cpp
Normal file
139
src/core/n64/core/cpu/registers/Cop1.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include <n64/core/cpu/registers/Cop1.hpp>
|
||||
#include <n64/core/cpu/Registers.hpp>
|
||||
#include <n64/core/Cpu.hpp>
|
||||
#include <util.hpp>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
200
src/core/n64/core/cpu/registers/Cop1.hpp
Normal file
200
src/core/n64/core/cpu/registers/Cop1.hpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
#include <n64/core/cpu/registers/Cop0.hpp>
|
||||
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
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<u64>(cop0, index, raw);
|
||||
}
|
||||
|
||||
inline double GetCop1RegDouble(Cop0& cop0, u8 index) {
|
||||
double doublevalue;
|
||||
u64 raw = GetReg<u64>(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<u32>(cop0, index, raw);
|
||||
}
|
||||
|
||||
inline float GetCop1RegFloat(Cop0& cop0, u8 index) {
|
||||
u32 raw = GetReg<u32>(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);
|
||||
};
|
||||
}
|
||||
81
src/core/n64/core/cpu/registers/cop0instructions.cpp
Normal file
81
src/core/n64/core/cpu/registers/cop0instructions.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <n64/core/cpu/registers/Cop0.hpp>
|
||||
#include <n64/core/cpu/Registers.hpp>
|
||||
#include <util.hpp>
|
||||
|
||||
namespace natsukashii::n64::core {
|
||||
void Cop0::mtc0(Registers& regs, u32 instr) {
|
||||
SetReg<u32>(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<s32>(RD(instr));
|
||||
}
|
||||
|
||||
void Cop0::dmfc0(Registers& regs, u32 instr) {
|
||||
regs.gpr[RT(instr)] = GetReg<s64>(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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
255
src/core/n64/core/cpu/registers/cop1instructions.cpp
Normal file
255
src/core/n64/core/cpu/registers/cop1instructions.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
#include <n64/core/cpu/registers/Cop1.hpp>
|
||||
#include <n64/core/cpu/Registers.hpp>
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <util.hpp>
|
||||
#include <n64/core/Mem.hpp>
|
||||
#include <n64/core/cpu/Registers.hpp>
|
||||
#include <Audio.hpp>
|
||||
#include <n64/core/Audio.hpp>
|
||||
|
||||
namespace natsukashii::n64::core {
|
||||
auto AI::Read(u32 addr) const -> u32 {
|
||||
|
||||
Reference in New Issue
Block a user