Remove GB core for now

This commit is contained in:
CocoSimone
2022-07-02 22:08:40 +02:00
parent 474e30da73
commit c15bfce2fc
33 changed files with 135 additions and 598 deletions

View File

@@ -4,7 +4,6 @@ project(cores CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(gb)
add_subdirectory(n64) add_subdirectory(n64)
add_library(cores add_library(cores

View File

@@ -1,37 +0,0 @@
cmake_minimum_required(VERSION 3.20)
project(gb CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
mio
GIT_REPOSITORY https://github.com/CocoSimone/mio
GIT_TAG master
)
FetchContent_Declare(
toml11
GIT_REPOSITORY https://github.com/ToruNiina/toml11
GIT_TAG dcfe39a783a94e8d52c885e5883a6fbb21529019
)
FetchContent_MakeAvailable(mio toml11)
find_package(fmt REQUIRED)
add_library(gb
Core.hpp
Core.cpp
Cpu.hpp
Cpu.cpp
Ppu.hpp
Ppu.cpp
Mem.cpp
Mem.hpp
mbc.cpp
mbc.hpp
memory_regions.hpp
../BaseCore.cpp
../BaseCore.hpp)
target_include_directories(gb PRIVATE . .. ../../../external)
target_include_directories(gb PUBLIC ${mio_SOURCE_DIR}/include ${toml11_SOURCE_DIR}/include)
target_link_libraries(gb PRIVATE toml11::toml11 mio::mio fmt::fmt)

View File

@@ -1,20 +0,0 @@
#include <Core.hpp>
#include <SDL2/SDL_events.h>
namespace natsukashii::gb::core {
Core::Core(const std::string& rom) {
mem.LoadROM(rom);
}
void Core::Run() {
while(true) {
cpu.Step(mem);
}
}
void Core::PollInputs(u32 windowID) {
SDL_Event event;
SDL_PollEvent(&event);
ShouldQuit() = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == windowID;
}
}

View File

@@ -1,19 +0,0 @@
#pragma once
#include <Cpu.hpp>
#include <Ppu.hpp>
#include <Mem.hpp>
#include <BaseCore.hpp>
namespace natsukashii::gb::core {
using namespace natsukashii::core;
struct Core : BaseCore {
~Core() override = default;
explicit Core(const std::string&);
void Run() override;
void PollInputs(u32) override;
private:
Mem mem;
Cpu cpu;
// Ppu ppu;
};
}

View File

@@ -1,30 +0,0 @@
#include <Cpu.hpp>
#include <util.hpp>
namespace natsukashii::gb::core {
Cpu::Cpu() {}
void Cpu::Step(Mem& mem) {
FetchDecodeExecute(mem);
}
void Cpu::FetchDecodeExecute(Mem& mem) {
u8 opcode = mem.Read8(regs.pc);
switch(opcode) {
case 0x01: case 0x11: case 0x21: case 0x31: // LD r16, u16
SetR16<1>((opcode >> 4) & 3, mem.Consume16(regs.pc));
break;
case 0xA8 ... 0xAF: // XOR A, r8
regs.a() ^= GetR8(opcode & 7, mem);
regs.f().set(regs.a() == 0, false, false, false);
break;
case 0x02: case 0x12: case 0x22: case 0x32: {
u8 bits = (opcode >> 4) & 3;
mem.Write8(GetR16<2>(bits), regs.a());
} break;
default: util::panic("Unimplemented opcode {:02X}, pc: {:04X}", opcode, regs.pc);
}
regs.pc++;
}
}

View File

@@ -1,184 +0,0 @@
#pragma once
#include <Mem.hpp>
#include <util.hpp>
namespace natsukashii::gb::core {
template <class T1, class T2>
struct Reg {
Reg() : raw(0) {};
union {
T1 hi;
T2 lo;
};
u16 raw = 0;
};
struct RegF {
RegF() : raw(0) {}
RegF(const u8& val) : raw(val) {}
RegF& operator=(const u8& rhs) {
raw |= ((rhs >> 7) << 7);
raw |= ((rhs >> 6) << 6);
raw |= ((rhs >> 5) << 5);
raw |= ((rhs >> 4) << 4);
return *this;
}
bool zero() { return (raw >> 7) & 1; }
bool negative() { return (raw >> 6) & 1; }
bool halfcarry() { return (raw >> 5) & 1; }
bool carry() { return (raw >> 4) & 1; }
void reset() {
zero(false);
negative(false);
halfcarry(false);
carry(false);
}
void set(bool z, bool n, bool hc, bool ca) {
zero(z);
negative(n);
halfcarry(hc);
carry(ca);
}
u8& get() { return raw; }
private:
u8 raw = 0;
void zero(const bool& rhs) {
raw &= ~0xF;
raw |= (rhs << 7);
}
void negative(const bool& rhs) {
raw &= ~0xF;
raw |= (rhs << 6);
}
void halfcarry(const bool& rhs) {
raw &= ~0xF;
raw |= (rhs << 5);
}
void carry(const bool& rhs) {
raw &= ~0xF;
raw |= (rhs << 4);
}
};
struct Registers {
Reg<u8, RegF> AF;
Reg<u8, u8> BC;
Reg<u8, u8> DE;
Reg<u8, u8> HL;
u16 pc = 0, sp = 0;
u8& a() { return AF.hi; }
RegF& f() { return AF.lo; }
u8& b() { return BC.hi; }
u8& c() { return BC.lo; }
u8& d() { return DE.hi; }
u8& e() { return DE.lo; }
u8& h() { return HL.hi; }
u8& l() { return HL.lo; }
u16& af() { return AF.raw; }
u16& bc() { return BC.raw; }
u16& de() { return DE.raw; }
u16& hl() { return HL.raw; }
};
struct Cpu {
Cpu();
void Step(Mem&);
private:
void FetchDecodeExecute(Mem& mem);
Registers regs;
template <int group>
u16 GetR16(u8 bits) {
static_assert(group > 0 && group < 3, "Invalid GetR16 group");
if constexpr (group == 1) {
switch(bits & 3) {
case 0: return regs.bc();
case 1: return regs.de();
case 2: return regs.hl();
case 3: return regs.sp;
}
} else if constexpr (group == 2) {
switch(bits & 3) {
case 0: return regs.bc();
case 1: return regs.de();
case 2: return regs.hl()++;
case 3: return regs.hl()--;
}
} else if constexpr (group == 3) {
switch(bits & 3) {
case 0: return regs.bc();
case 1: return regs.de();
case 2: return regs.hl();
case 3: return regs.af();
}
}
return 0;
}
template <int group>
void SetR16(u8 bits, u16 val) {
static_assert(group > 0 && group < 3, "Invalid SetR16 group");
if constexpr (group == 1) {
switch(bits & 3) {
case 0: regs.bc() = val; break;
case 1: regs.de() = val; break;
case 2: regs.hl() = val; break;
case 3: regs.sp = val; break;
}
} else if constexpr (group == 2) {
switch(bits & 3) {
case 0: regs.bc() = val; break;
case 1: regs.de() = val; break;
case 2: regs.hl() = val; regs.hl()++; break;
case 3: regs.hl() = val; regs.hl()--; break;
}
} else if constexpr (group == 3) {
switch(bits & 3) {
case 0: regs.bc() = val; break;
case 1: regs.de() = val; break;
case 2: regs.hl() = val; break;
case 3: regs.af() = val; break;
}
}
}
u8 GetR8(u8 bits, Mem& mem) {
switch(bits & 7) {
case 0: return regs.b();
case 1: return regs.c();
case 2: return regs.d();
case 3: return regs.e();
case 4: return regs.h();
case 5: return regs.l();
case 6: return mem.Read8(regs.hl());
case 7: return regs.a();
}
return 0;
}
void SetR8(u8 bits, u8 val, Mem& mem) {
switch(bits & 7) {
case 0: regs.b() = val; break;
case 1: regs.c() = val; break;
case 2: regs.d() = val; break;
case 3: regs.e() = val; break;
case 4: regs.h() = val; break;
case 5: regs.l() = val; break;
case 6: return mem.Write8(regs.hl(), val);
case 7: regs.a() = val; break;
}
}
};
}

View File

@@ -1,92 +0,0 @@
#include <Mem.hpp>
#include <util.hpp>
#include <memory_regions.hpp>
#include <fstream>
#include <toml.hpp>
namespace natsukashii::gb::core {
Mem::Mem() {
auto data = toml::parse("config.toml");
auto gb = toml::find(data, "gb");
auto bootromPath = toml::find<std::string>(gb, "bootrom");
LoadBootROM(bootromPath);
}
void Mem::LoadBootROM(const std::string &filename) {
std::ifstream file(filename, std::ios::binary);
file.unsetf(std::ios::skipws);
if(!file.is_open()) {
util::panic("Unable to open {}!", filename);
}
file.read(reinterpret_cast<char*>(bootrom), 256);
file.close();
}
void Mem::LoadROM(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
file.unsetf(std::ios::skipws);
if(!file.is_open()) {
util::panic("Unable to open {}!", filename);
}
file.seekg(std::ios::end);
auto size = file.tellg();
file.seekg(std::ios::beg);
std::vector<u8> rom;
rom.reserve(size);
rom.insert(rom.begin(),
std::istream_iterator<u8>(file),
std::istream_iterator<u8>());
file.close();
switch(rom[0x147]) {
case 0:
cart = std::make_unique<NoMBC>(rom);
break;
default:
util::panic("Unimplemented cartridge type {:02X}!", rom[0x147]);
}
}
u8 Mem::Read8(u16 addr) {
switch(addr) {
case ROM_RNG00: return io.BootROMMapped() ? bootrom[addr] : cart->Read(addr);
case ROM_RNGNN: return cart->Read(addr);
default: util::panic("[READ] Unimplemented addr: {:04X}", addr);
}
return 0;
}
void Mem::Write8(u16 addr, u8 val) {
switch(addr) {
case ROM_RNG00: case ROM_RNGNN: cart->Write(addr, val);
default: util::panic("[WRITE] Unimplemented addr: {:04X}", addr);
}
}
u8 Mem::Consume8(u16& pc) {
u8 result = Read8(pc);
pc += 1;
return result;
}
u16 Mem::Read16(u16 addr) {
return ((u16)Read8(addr) << 8) | Read8(addr + 1);
}
void Mem::Write16(u16 addr, u16 val) {
Write8(addr, val >> 8);
Write8(addr + 1, val & 0xff);
}
u16 Mem::Consume16(u16& pc) {
u8 hi = Consume8(pc);
return ((u16)hi << 8) | Consume8(pc);
}
}

View File

@@ -1,31 +0,0 @@
#pragma once
#include <common.hpp>
#include <memory>
#include <vector>
#include <mio/mmap.hpp>
#include <memory_regions.hpp>
#include <mbc.hpp>
namespace natsukashii::gb::core {
struct IO {
[[nodiscard]] bool BootROMMapped() const { return ff50 != 1; }
private:
u8 ff50 = 0;
};
struct Mem {
Mem();
void LoadROM(const std::string& filename);
u8 Read8(u16 addr);
void Write8(u16 addr, u8 val);
u8 Consume8(u16& pc);
u16 Read16(u16 addr);
void Write16(u16 addr, u16 val);
u16 Consume16(u16& pc);
private:
void LoadBootROM(const std::string& filename);
std::unique_ptr<Cartridge> cart;
IO io{};
u8 bootrom[BOOTROM_SIZE]{};
};
}

View File

View File

View File

@@ -1,15 +0,0 @@
#include <mbc.hpp>
#include <utility>
#include <util.hpp>
namespace natsukashii::gb::core {
NoMBC::NoMBC(std::vector<u8> rom) : data(std::move(rom)) {}
u8 NoMBC::Read(u16 addr) {
return data[addr];
}
void NoMBC::Write(u16 addr, u8 val) {
util::panic("Writing to a NoMBC cartridge is not allowed! (Addr: {:04X})", addr);
}
}

View File

@@ -1,18 +0,0 @@
#pragma once
#include <common.hpp>
#include <vector>
namespace natsukashii::gb::core {
struct Cartridge {
virtual u8 Read(u16 addr) { return 0; }
virtual void Write(u16 addr, u8 val) {}
};
struct NoMBC : public Cartridge {
explicit NoMBC(std::vector<u8> rom);
u8 Read(u16 addr) override;
void Write(u16 addr, u8 val) override;
private:
std::vector<u8> data{};
};
}

View File

@@ -1,34 +0,0 @@
#pragma once
#include <common.hpp>
#define BOOTROM_SIZE 512
#define ROM_STR00 0x0000
#define ROM_END00 ROM_STR00 + 0x3fff
#define ROM_STRNN ROM_END00 + 1
#define ROM_ENDNN ROM_STRNN + 0x3fff
#define ROM_RNG00 ROM_STR00 ... ROM_END00
#define ROM_RNGNN ROM_STRNN ... ROM_ENDNN
#define VRAM_START ROM_ENDNN + 1
#define VRAM_END VRAM_START + 0x1fff
#define VRAM_RANGE VRAM_START ... VRAM_END
#define EXTRAM_START VRAM_END + 1
#define EXTRAM_END EXTRAM_START + 0x1fff
#define EXTRAM_RANGE EXTRAM_START ... EXTRAM_END
#define WRAM_STR00 EXTRAM_END + 1
#define WRAM_END00 WRAM_STR00 + 0xfff
#define WRAM_STRNN WRAM_END00 + 1
#define WRAM_ENDNN WRAM_STRNN + 0xfff
#define WRAM_RNG00 WRAM_STR00 ... WRAM_END00
#define WRAM_RNGNN WRAM_STRNN ... WRAM_ENDNN
#define OAM_START WRAM_ENDNN + 1
#define OAM_END OAM_START + 0x9F
#define OAM_RANGE OAM_START ... OAM_END
#define UNUSABLE_START OAM_END + 1
#define UNUSABLE_END UNUSABLE_START + 0x5F
#define UNUSABLE_RANGE UNUSABLE_START ... UNUSABLE_END
#define IO_START UNUSABLE_END + 1
#define IO_END IO_START + 0x7F
#define IO_RANGE IO_START ... IO_END
#define HRAM_START IO_END + 1
#define HRAM_END HRAM_START + 0x7E
#define HRAM_RANGE HRAM_START ... HRAM_END

View File

@@ -3,6 +3,8 @@ project(n64-core)
add_subdirectory(cpu) add_subdirectory(cpu)
add_subdirectory(../../../../external temp)
add_library(n64-core add_library(n64-core
Cpu.hpp Cpu.hpp
Cpu.cpp Cpu.cpp
@@ -11,7 +13,17 @@ add_library(n64-core
RDP.cpp RDP.cpp
RDP.hpp RDP.hpp
mmio/VI.cpp mmio/VI.cpp
mmio/VI.hpp mmio/Interrupt.hpp mmio/MI.cpp mmio/MI.hpp mmio/Interrupt.cpp MMIO.cpp MMIO.hpp RSP.cpp RSP.hpp rsp/decode.cpp rsp/instructions.cpp) mmio/VI.hpp
mmio/Interrupt.hpp
mmio/MI.cpp
mmio/MI.hpp
mmio/Interrupt.cpp
MMIO.cpp
MMIO.hpp
RSP.cpp
RSP.hpp
rsp/decode.cpp
rsp/instructions.cpp)
target_include_directories(n64-core PRIVATE . .. ../../ mmio) target_include_directories(n64-core PRIVATE . .. ../../ mmio)
target_link_libraries(n64-core PUBLIC n64-cpu) target_link_libraries(n64-core PUBLIC n64-cpu parallel-rdp)

View File

@@ -1,6 +1,6 @@
#include <Cpu.hpp> #include <n64/core/Cpu.hpp>
#include <MI.hpp> #include <n64/core/mmio/MI.hpp>
#include <Interrupt.hpp> #include <n64/core/mmio/Interrupt.hpp>
#include <util.hpp> #include <util.hpp>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
@@ -49,22 +49,22 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) {
regs.cop0.status.exl = true; regs.cop0.status.exl = true;
regs.cop0.cause.copError = cop; regs.cop0.cause.copError = cop;
regs.cop0.cause.exceptionCode = code; regs.cop0.cause.exceptionCode = static_cast<u8>(code);
if(regs.cop0.status.bev) { if(regs.cop0.status.bev) {
util::panic("BEV bit set!\n"); util::panic("BEV bit set!\n");
} else { } else {
switch(code) { switch(code) {
case Interrupt: case TLBModification: case ExceptionCode::Interrupt: case ExceptionCode::TLBModification:
case AddressErrorLoad: case AddressErrorStore: case ExceptionCode::AddressErrorLoad: case ExceptionCode::AddressErrorStore:
case InstructionBusError: case DataBusError: case ExceptionCode::InstructionBusError: case ExceptionCode::DataBusError:
case Syscall: case Breakpoint: case ExceptionCode::Syscall: case ExceptionCode::Breakpoint:
case ReservedInstruction: case CoprocessorUnusable: case ExceptionCode::ReservedInstruction: case ExceptionCode::CoprocessorUnusable:
case Overflow: case Trap: case ExceptionCode::Overflow: case ExceptionCode::Trap:
case FloatingPointError: case Watch: case ExceptionCode::FloatingPointError: case ExceptionCode::Watch:
regs.SetPC((s64)((s32)0x80000180)); regs.SetPC((s64)((s32)0x80000180));
break; break;
case TLBLoad: case TLBStore: case ExceptionCode::TLBLoad: case ExceptionCode::TLBStore:
if(old_exl || regs.cop0.tlbError == INVALID) { if(old_exl || regs.cop0.tlbError == INVALID) {
regs.SetPC((s64)((s32)0x80000180)); regs.SetPC((s64)((s32)0x80000180));
} else if(Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) { } else if(Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) {
@@ -73,14 +73,14 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) {
regs.SetPC((s64)((s32)0x80000000)); regs.SetPC((s64)((s32)0x80000000));
} }
break; break;
default: util::panic("Unhandled exception! {}\n", code); default: util::panic("Unhandled exception! {}\n", static_cast<u8>(code));
} }
} }
} }
inline void HandleInterrupt(Registers& regs) { inline void HandleInterrupt(Registers& regs) {
if(ShouldServiceInterrupt(regs)) { if(ShouldServiceInterrupt(regs)) {
FireException(regs, Interrupt, 0, regs.pc); FireException(regs, ExceptionCode::Interrupt, 0, regs.pc);
} }
} }
@@ -89,9 +89,9 @@ void Cpu::Step(Mem& mem) {
regs.prevDelaySlot = regs.delaySlot; regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false; regs.delaySlot = false;
CheckCompareInterrupt(mem.mmio.mi); CheckCompareInterrupt(mem.mmio.mi, regs);
u32 instruction = mem.Read(regs, regs.pc, regs.pc); u32 instruction = mem.Read<u32>(regs, regs.pc, regs.pc);
HandleInterrupt(regs); HandleInterrupt(regs);

View File

@@ -1,6 +1,8 @@
#pragma once #pragma once
#include <mmio/VI.hpp> #include <n64/core/mmio/VI.hpp>
#include <mmio/MI.hpp> #include <n64/core/mmio/MI.hpp>
#include <n64/core/RSP.hpp>
#include <n64/core/RDP.hpp>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
struct Mem; struct Mem;
@@ -10,6 +12,8 @@ struct MMIO {
MMIO() = default; MMIO() = default;
VI vi; VI vi;
MI mi; MI mi;
RSP rsp;
RDP rdp;
u32 Read(u32); u32 Read(u32);
void Write(Mem&, Registers& regs, u32, u32); void Write(Mem&, Registers& regs, u32, u32);

View File

@@ -47,6 +47,8 @@ inline bool MapVAddr(Registers& regs, TLBAccessType accessType, u32 vaddr, u32&
default: default:
util::panic("Should never end up in default case in map_vaddr! ({:08X})\n", vaddr); util::panic("Should never end up in default case in map_vaddr! ({:08X})\n", vaddr);
} }
return false;
} }
template <class T, bool tlb> template <class T, bool tlb>
@@ -72,16 +74,13 @@ T Mem::Read(Registers& regs, u32 vaddr, s64 pc) {
case 0x80000000 ... 0xFFFFFFFF: case 0x1FC00800 ... 0x7FFFFFFF: return 0; case 0x80000000 ... 0xFFFFFFFF: case 0x1FC00800 ... 0x7FFFFFFF: return 0;
default: util::panic("Unimplemented {}-bit read at address {:08X} (PC = {:016X})\n", sizeof(T) * 8, paddr, regs.pc); default: util::panic("Unimplemented {}-bit read at address {:08X} (PC = {:016X})\n", sizeof(T) * 8, paddr, regs.pc);
} }
return 0;
} }
template u8 Mem::Read<u8, true>(Registers& regs, u32 vaddr, s64 pc); template u8 Mem::Read<u8>(Registers& regs, u32 vaddr, s64 pc);
template u16 Mem::Read<u16, true>(Registers& regs, u32 vaddr, s64 pc); template u16 Mem::Read<u16>(Registers& regs, u32 vaddr, s64 pc);
template u32 Mem::Read<u32, true>(Registers& regs, u32 vaddr, s64 pc); template u32 Mem::Read<u32>(Registers& regs, u32 vaddr, s64 pc);
template u64 Mem::Read<u64, true>(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> template <class T, bool tlb>
void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) { void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) {
@@ -108,12 +107,8 @@ void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) {
} }
} }
template void Mem::Write<u8, true>(Registers& regs, u32 vaddr, u8 val, s64 pc); template void Mem::Write<u8>(Registers& regs, u32 vaddr, u8 val, s64 pc);
template void Mem::Write<u16, true>(Registers& regs, u32 vaddr, u16 val, s64 pc); template void Mem::Write<u16>(Registers& regs, u32 vaddr, u16 val, s64 pc);
template void Mem::Write<u32, true>(Registers& regs, u32 vaddr, u32 val, s64 pc); template void Mem::Write<u32>(Registers& regs, u32 vaddr, u32 val, s64 pc);
template void Mem::Write<u64, true>(Registers& regs, u32 vaddr, u64 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);
} }

View File

@@ -13,11 +13,12 @@ struct Mem {
[[nodiscard]] auto GetRDRAM() -> u8* { [[nodiscard]] auto GetRDRAM() -> u8* {
return rdram.data(); return rdram.data();
} }
template <class T, bool tlb> template <class T, bool tlb = true>
T Read(Registers&, u32, s64); T Read(Registers&, u32, s64);
template <class T, bool tlb> template <class T, bool tlb = true>
void Write(Registers&, u32, T, s64); void Write(Registers&, u32, T, s64);
private: private:
friend struct Cpu;
friend struct RSP; friend struct RSP;
MMIO mmio; MMIO mmio;
std::vector<u8> cart, rdram, sram; std::vector<u8> cart, rdram, sram;

View File

@@ -1,8 +1,8 @@
#include <RDP.hpp> #include <n64/core/RDP.hpp>
#include <util.hpp> #include <util.hpp>
#include <RSP.hpp> #include <n64/core/RSP.hpp>
#include <../../../frontend/ParallelRDPWrapper.hpp> #include <../../../frontend/ParallelRDPWrapper.hpp>
#include <Interrupt.hpp> #include <n64/core/mmio/Interrupt.hpp>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
static const int cmd_lens[64] = { static const int cmd_lens[64] = {
@@ -12,11 +12,12 @@ static const int cmd_lens[64] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
}; };
u32 RDP::Read(u32 addr) { auto RDP::Read(u32 addr) const -> u32{
switch(addr) { switch(addr) {
case 0x0410000C: return dpc.status.raw; case 0x0410000C: return dpc.status.raw;
default: util::panic("Unhandled DP Command Registers read (addr: {:08X})\n", addr); default: util::panic("Unhandled DP Command Registers read (addr: {:08X})\n", addr);
} }
return 0;
} }
void RDP::Write(u32 addr, u32 val) { void RDP::Write(u32 addr, u32 val) {

View File

@@ -53,7 +53,7 @@ struct RDP {
RDP() = default; RDP() = default;
u32 Read(u32 addr); auto Read(u32 addr) const -> u32;
void Write(u32 addr, u32 val); void Write(u32 addr, u32 val);
void StatusWrite(u32 val); void StatusWrite(u32 val);
void RunCommand(MI& mi, Registers& regs, RSP& rsp); void RunCommand(MI& mi, Registers& regs, RSP& rsp);

View File

@@ -1,7 +1,7 @@
#include <RSP.hpp> #include <n64/core/RSP.hpp>
#include <util.hpp> #include <util.hpp>
#include <n64/core/Mem.hpp> #include <n64/core/Mem.hpp>
#include "Interrupt.hpp" #include <n64/core/mmio/Interrupt.hpp>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
void RSP::StepRSP(MI& mi, Registers& regs, RDP& rdp) { void RSP::StepRSP(MI& mi, Registers& regs, RDP& rdp) {
@@ -15,7 +15,7 @@ void RSP::StepRSP(MI& mi, Registers& regs, RDP& rdp) {
} }
} }
u32 RSP::Read(u32 addr) { auto RSP::Read(u32 addr) const -> u32{
switch (addr) { switch (addr) {
case 0x04040000: return spDMASPAddr.raw & 0xFFFFF8; case 0x04040000: return spDMASPAddr.raw & 0xFFFFF8;
case 0x04040004: return spDMADRAMAddr.raw & 0x1FF8; case 0x04040004: return spDMADRAMAddr.raw & 0x1FF8;
@@ -26,6 +26,7 @@ u32 RSP::Read(u32 addr) {
case 0x04080000: return pc & 0xFFF; case 0x04080000: return pc & 0xFFF;
default: util::panic("Unimplemented SP register read %08X\n", addr); default: util::panic("Unimplemented SP register read %08X\n", addr);
} }
return 0;
} }
template <bool isDRAMdest> template <bool isDRAMdest>

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <mmio/MI.hpp> #include <n64/core/mmio/MI.hpp>
#include <RDP.hpp> #include <n64/core/RDP.hpp>
#include <n64/memory_regions.hpp> #include <n64/memory_regions.hpp>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
@@ -107,7 +107,7 @@ struct Registers;
struct RSP { struct RSP {
RSP() = default; RSP() = default;
void StepRSP(MI& mi, Registers& regs, RDP& rdp); void StepRSP(MI& mi, Registers& regs, RDP& rdp);
u32 Read(u32 addr); auto Read(u32 addr) const -> u32;
void Write(Mem& mem, Registers& regs, u32 addr, u32 value); void Write(Mem& mem, Registers& regs, u32 addr, u32 value);
void Exec(MI& mi, Registers& regs, RDP& rdp, u32 instr); void Exec(MI& mi, Registers& regs, RDP& rdp, u32 instr);
SPStatus spStatus{.raw = 1}; SPStatus spStatus{.raw = 1};
@@ -156,7 +156,7 @@ struct RSP {
} }
return val; return val;
} }
private:
void add(u32 instr); void add(u32 instr);
void addi(u32 instr); void addi(u32 instr);
void and_(u32 instr); void and_(u32 instr);
@@ -186,7 +186,7 @@ private:
void vsar(u32 instr); void vsar(u32 instr);
void mfc0(RDP& rdp, u32 instr); void mfc0(RDP& rdp, u32 instr);
void mtc0(MI& mi, Registers& regs, RDP& rdp, u32 instr); void mtc0(MI& mi, Registers& regs, RDP& rdp, u32 instr);
private:
inline void branch(u16 address, bool cond) { inline void branch(u16 address, bool cond) {
if(cond) { if(cond) {
nextPC = address & 0xFFF; nextPC = address & 0xFFF;

View File

@@ -12,7 +12,7 @@ MI::MI() {
miMode = 0; miMode = 0;
} }
u32 MI::Read(u32 paddr) { auto MI::Read(u32 paddr) const -> u32 {
switch(paddr & 0xF) { switch(paddr & 0xF) {
case 0x0: return miMode & 0x3FF; case 0x0: return miMode & 0x3FF;
case 0x4: return MI_VERSION_REG; case 0x4: return MI_VERSION_REG;
@@ -20,6 +20,7 @@ u32 MI::Read(u32 paddr) {
case 0xC: return miIntrMask.raw & 0x3F; case 0xC: return miIntrMask.raw & 0x3F;
default: util::panic("Unhandled MI[%08X] read\n", paddr); default: util::panic("Unhandled MI[%08X] read\n", paddr);
} }
return 0;
} }
void MI::Write(Registers& regs, u32 paddr, u32 val) { void MI::Write(Registers& regs, u32 paddr, u32 val) {

View File

@@ -20,7 +20,7 @@ struct Registers;
struct MI { struct MI {
MI(); MI();
u32 Read(u32); [[nodiscard]] auto Read(u32) const -> u32;
void Write(Registers& regs, u32, u32); void Write(Registers& regs, u32, u32);
u32 miMode; u32 miMode;

View File

@@ -18,7 +18,7 @@ VI::VI () {
cyclesPerHalfline = 1000; cyclesPerHalfline = 1000;
} }
u32 VI::Read(u32 paddr) { u32 VI::Read(u32 paddr) const {
switch(paddr) { switch(paddr) {
case 0x04400000: return status.raw; case 0x04400000: return status.raw;
case 0x04400004: return origin; case 0x04400004: return origin;
@@ -37,6 +37,7 @@ u32 VI::Read(u32 paddr) {
default: default:
util::panic("Unimplemented VI[%08X] read\n", paddr); util::panic("Unimplemented VI[%08X] read\n", paddr);
} }
return 0;
} }
void VI::Write(MI& mi, Registers& regs, u32 paddr, u32 val) { void VI::Write(MI& mi, Registers& regs, u32 paddr, u32 val) {

View File

@@ -65,7 +65,7 @@ struct Registers;
struct VI { struct VI {
VI(); VI();
u32 Read(u32); [[nodiscard]] u32 Read(u32) const;
void Write(MI&, Registers&, u32, u32); void Write(MI&, Registers&, u32, u32);
VIScale xscale{}, yscale{}; VIScale xscale{}, yscale{};
VIVideo hvideo{}, vvideo{}; VIVideo hvideo{}, vvideo{};

View File

@@ -1,24 +1,25 @@
#include <RSP.hpp> #include <n64/core/RSP.hpp>
#include <util.hpp> #include <util.hpp>
#include <n64/core/cpu/Registers.hpp>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
inline void special(RSP& rsp, u32 instr) { inline void special(RSP& rsp, u32 instr) {
u8 mask = instr & 0x3f; u8 mask = instr & 0x3f;
switch(mask) { switch(mask) {
case 0x00: rsp_sll(rsp, instr); break; case 0x00: rsp.sll(instr); break;
case 0x04: rsp_sllv(rsp, instr); break; case 0x04: rsp.sllv(instr); break;
case 0x08: rsp_jr(rsp, instr); break; case 0x08: rsp.jr(instr); break;
case 0x0C: case 0x0C:
case 0x0D: case 0x0D:
rsp.spStatus.halt = true; rsp.spStatus.halt = true;
rsp.spStatus.broke = true; rsp.spStatus.broke = true;
break; break;
case 0x20: case 0x21: case 0x20: case 0x21:
rsp_add(rsp, instr); rsp.add(instr);
break; break;
case 0x24: rsp_and_(rsp, instr); break; case 0x24: rsp.and_(instr); break;
case 0x25: rsp_or_(rsp, instr); break; case 0x25: rsp.or_(instr); break;
case 0x27: rsp_nor(rsp, instr); break; case 0x27: rsp.nor(instr); break;
default: util::panic("Unhandled RSP special instruction %d %d\n", (mask >> 3) & 7, mask & 7); default: util::panic("Unhandled RSP special instruction %d %d\n", (mask >> 3) & 7, mask & 7);
} }
} }
@@ -26,8 +27,8 @@ inline void special(RSP& rsp, u32 instr) {
inline void regimm(RSP& rsp, u32 instr) { inline void regimm(RSP& rsp, u32 instr) {
u8 mask = ((instr >> 16) & 0x1F); u8 mask = ((instr >> 16) & 0x1F);
switch(mask) { switch(mask) {
case 0x00: rsp_b(rsp, instr, (s32)rsp.gpr[RS(instr)] < 0); break; case 0x00: rsp.b(instr, (s32)rsp.gpr[RS(instr)] < 0); break;
case 0x01: rsp_b(rsp, instr, (s32)rsp.gpr[RS(instr)] >= 0); break; case 0x01: rsp.b(instr, (s32)rsp.gpr[RS(instr)] >= 0); break;
default: util::panic("Unhandled RSP regimm instruction %d %d\n", (mask >> 3) & 3, mask & 7); default: util::panic("Unhandled RSP regimm instruction %d %d\n", (mask >> 3) & 3, mask & 7);
} }
} }
@@ -35,7 +36,7 @@ inline void regimm(RSP& rsp, u32 instr) {
inline void lwc2(RSP& rsp, u32 instr) { inline void lwc2(RSP& rsp, u32 instr) {
u8 mask = (instr >> 11) & 0x1F; u8 mask = (instr >> 11) & 0x1F;
switch(mask) { switch(mask) {
case 0x04: rsp_lqv(rsp, instr); break; case 0x04: rsp.lqv(instr); break;
default: util::panic("Unhandled RSP LWC2 %d %d\n", (mask >> 3) & 3, mask & 7); default: util::panic("Unhandled RSP LWC2 %d %d\n", (mask >> 3) & 3, mask & 7);
} }
} }
@@ -43,7 +44,7 @@ inline void lwc2(RSP& rsp, u32 instr) {
inline void swc2(RSP& rsp, u32 instr) { inline void swc2(RSP& rsp, u32 instr) {
u8 mask = (instr >> 11) & 0x1F; u8 mask = (instr >> 11) & 0x1F;
switch(mask) { switch(mask) {
case 0x04: rsp_sqv(rsp, instr); break; case 0x04: rsp.sqv(instr); break;
default: util::panic("Unhandled RSP SWC2 %d %d\n", (mask >> 3) & 3, mask & 7); default: util::panic("Unhandled RSP SWC2 %d %d\n", (mask >> 3) & 3, mask & 7);
} }
} }
@@ -54,15 +55,15 @@ inline void cop2(RSP& rsp, u32 instr) {
switch(mask) { switch(mask) {
case 0x00: case 0x00:
switch(mask_sub) { switch(mask_sub) {
case 0x02: rsp_cfc2(rsp, instr); break; case 0x02: rsp.cfc2(instr); break;
default: logfatal("Unhandled RSP COP2 sub %d %d\n", (mask_sub >> 3) & 3, mask_sub & 3); default: util::panic("Unhandled RSP COP2 sub %d %d\n", (mask_sub >> 3) & 3, mask_sub & 3);
} }
break; break;
case 0x13: rsp_vabs(rsp, instr); break; case 0x13: rsp.vabs(instr); break;
case 0x1D: rsp_vsar(rsp, instr); break; case 0x1D: rsp.vsar(instr); break;
case 0x21: rsp_veq(rsp, instr); break; case 0x21: rsp.veq(instr); break;
case 0x22: rsp_vne(rsp, instr); break; case 0x22: rsp.vne(instr); break;
case 0x33: rsp_vmov(rsp, instr); break; case 0x33: rsp.vmov(instr); break;
default: util::panic("Unhandled RSP COP2 %d %d\n", (mask >> 3) & 7, mask & 7); default: util::panic("Unhandled RSP COP2 %d %d\n", (mask >> 3) & 7, mask & 7);
} }
} }
@@ -70,8 +71,8 @@ inline void cop2(RSP& rsp, u32 instr) {
inline void cop0(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u32 instr) { inline void cop0(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u32 instr) {
u8 mask = (instr >> 21) & 0x1F; u8 mask = (instr >> 21) & 0x1F;
switch(mask) { switch(mask) {
case 0x00: rsp_mfc0(rsp, rdp, instr); break; case 0x00: rsp.mfc0(rdp, instr); break;
case 0x04: rsp_mtc0(mi, regs, rsp, rdp, instr); break; case 0x04: rsp.mtc0(mi, regs, rdp, instr); break;
default: util::panic("Unhandled RSP COP0 %d %d\n", (mask >> 3) & 3, mask & 7); default: util::panic("Unhandled RSP COP0 %d %d\n", (mask >> 3) & 3, mask & 7);
} }
} }
@@ -81,22 +82,22 @@ void RSP::Exec(MI &mi, Registers &regs, RDP &rdp, u32 instr) {
switch(mask) { switch(mask) {
case 0x00: special(*this, instr); break; case 0x00: special(*this, instr); break;
case 0x01: regimm(*this, instr); break; case 0x01: regimm(*this, instr); break;
case 0x02: rsp_j(instr); break; case 0x02: j(instr); break;
case 0x03: rsp_jal(instr); break; case 0x03: jal(instr); break;
case 0x04: rsp_b(instr, gpr[RT(instr)] == gpr[RS(instr)]); break; case 0x04: b(instr, gpr[RT(instr)] == gpr[RS(instr)]); break;
case 0x05: rsp_b(instr, gpr[RT(instr)] != gpr[RS(instr)]); break; case 0x05: b(instr, gpr[RT(instr)] != gpr[RS(instr)]); break;
case 0x07: rsp_b(instr, gpr[RS(instr)] > 0); break; case 0x07: b(instr, gpr[RS(instr)] > 0); break;
case 0x08: case 0x09: rsp_addi(instr); break; case 0x08: case 0x09: addi(instr); break;
case 0x0C: rsp_andi(instr); break; case 0x0C: andi(instr); break;
case 0x0D: rsp_ori(instr); break; case 0x0D: ori(instr); break;
case 0x0F: rsp_lui(instr); break; case 0x0F: lui(instr); break;
case 0x10: cop0(mi, regs, *this, rdp, instr); break; case 0x10: cop0(mi, regs, *this, rdp, instr); break;
case 0x12: cop2(*this, instr); break; case 0x12: cop2(*this, instr); break;
case 0x21: rsp_lh(instr); break; case 0x21: lh(instr); break;
case 0x23: rsp_lw(instr); break; case 0x23: lw(instr); break;
case 0x28: rsp_sb(instr); break; case 0x28: sb(instr); break;
case 0x29: rsp_sh(instr); break; case 0x29: sh(instr); break;
case 0x2B: rsp_sw(instr); break; case 0x2B: sw(instr); break;
case 0x32: lwc2(*this, instr); break; case 0x32: lwc2(*this, instr); break;
case 0x3A: swc2(*this, instr); break; case 0x3A: swc2(*this, instr); break;
default: util::panic("Unhandled RSP instruction %d %d\n", (mask >> 3) & 7, mask & 7); default: util::panic("Unhandled RSP instruction %d %d\n", (mask >> 3) & 7, mask & 7);

View File

@@ -20,7 +20,7 @@ inline void ReleaseSemaphore(RSP& rsp) {
rsp.semaphore = false; rsp.semaphore = false;
} }
inline u32 GetCop0Reg(RSP& rsp, RDP& rdp, u8 index) { inline auto GetCop0Reg(RSP& rsp, RDP& rdp, u8 index) -> u32{
switch(index) { switch(index) {
case 4: return rsp.spStatus.raw; case 4: return rsp.spStatus.raw;
case 5: return rsp.spStatus.dmaFull; case 5: return rsp.spStatus.dmaFull;
@@ -29,6 +29,7 @@ inline u32 GetCop0Reg(RSP& rsp, RDP& rdp, u8 index) {
case 11: return rdp.dpc.status.raw; case 11: return rdp.dpc.status.raw;
default: util::panic("Unhandled RSP COP0 register read at index {}\n", index); default: util::panic("Unhandled RSP COP0 register read at index {}\n", index);
} }
return 0;
} }
inline void SetCop0Reg(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u8 index, u32 val) { inline void SetCop0Reg(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u8 index, u32 val) {
@@ -49,7 +50,7 @@ inline void SetCop0Reg(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u8 index, u3
break; break;
case 9: case 9:
rdp.dpc.end = val & 0xFFFFF8; rdp.dpc.end = val & 0xFFFFF8;
rdp.RunCommand(mi, regs, rdp, rsp); rdp.RunCommand(mi, regs, rsp);
break; break;
case 11: rdp.StatusWrite(val); break; case 11: rdp.StatusWrite(val); break;
default: util::panic("Unhandled RSP COP0 register write at index {}\n", index); default: util::panic("Unhandled RSP COP0 register write at index {}\n", index);

View File

@@ -39,6 +39,7 @@ constexpr void info(const std::string& fmt, Args... args) {
template <typename T, bool HToBE = false> template <typename T, bool HToBE = false>
auto GetSwapFunc(T num) -> T { auto GetSwapFunc(T num) -> T {
static_assert(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "GetSwapFunc used with invalid size!");
if constexpr(sizeof(T) == 2) { if constexpr(sizeof(T) == 2) {
if constexpr(HToBE) { if constexpr(HToBE) {
return htobe16(num); return htobe16(num);
@@ -59,17 +60,26 @@ auto GetSwapFunc(T num) -> T {
template <typename T> template <typename T>
inline T ReadAccess(u8* data, u32 index) { inline T ReadAccess(u8* data, u32 index) {
static_assert(sizeof(T) != 2 || sizeof(T) != 4 || sizeof(T) != 8); if constexpr(sizeof(T) == 1) {
return data[index];
} else {
static_assert(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
T result = 0; T result = 0;
memcpy(&result, &data[index], sizeof(T)); memcpy(&result, &data[index], sizeof(T));
return GetSwapFunc<T>(result); return GetSwapFunc<T>(result);
}
} }
template <typename T> template <typename T>
inline void WriteAccess(u8* data, u32 index, T val) { inline void WriteAccess(u8* data, u32 index, T val) {
static_assert(sizeof(T) != 2 || sizeof(T) != 4 || sizeof(T) != 8); if constexpr(sizeof(T) == 1) {
data[index] = val;
return;
} else {
static_assert(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
T temp = GetSwapFunc<T, true>(val); T temp = GetSwapFunc<T, true>(val);
memcpy(&data[index], &temp, sizeof(T)); memcpy(&data[index], &temp, sizeof(T));
}
} }
#define Z64 0x80371240 #define Z64 0x80371240

View File

@@ -1,9 +1,9 @@
#pragma once #pragma once
#include "n64/Core.hpp" #include <n64/Core.hpp>
#include "parallel-rdp-standalone/vulkan/wsi.hpp" #include <parallel-rdp-standalone/vulkan/wsi.hpp>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "n64/core/mmio/VI.hpp" #include <n64/core/mmio/VI.hpp>
#include "BaseCore.hpp" #include <BaseCore.hpp>
enum class Platform : bool { enum class Platform : bool {
SDL, Qt SDL, Qt

View File

@@ -15,7 +15,5 @@ FetchContent_Declare(
) )
FetchContent_MakeAvailable(argparse) FetchContent_MakeAvailable(argparse)
add_subdirectory(../../../external temp) target_include_directories(natsukashii-sdl PRIVATE . ../../core)
target_link_libraries(natsukashii-sdl PRIVATE cores argparse::argparse SDL2)
target_include_directories(natsukashii-sdl PRIVATE . ../../core ../../core/gb ../../core/n64 ../../core/n64/core ../../core/n64/core/cpu/registers)
target_link_libraries(natsukashii-sdl PRIVATE cores argparse::argparse SDL2 parallel-rdp)

View File

@@ -1,5 +1,4 @@
#include <Frontend.hpp> #include <Frontend.hpp>
#include <gb/Core.hpp>
#include <n64/Core.hpp> #include <n64/Core.hpp>
#include <volk.h> #include <volk.h>
#include "../ParallelRDPWrapper.hpp" #include "../ParallelRDPWrapper.hpp"
@@ -15,13 +14,7 @@ App::~App() {
App::App(const std::string& rom, const std::string& selectedCore) { App::App(const std::string& rom, const std::string& selectedCore) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_VIDEO_VULKAN); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_VIDEO_VULKAN);
if(selectedCore == "gb") { if(selectedCore == "n64") {
window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderSetLogicalSize(renderer, 160, 144);
windowID = SDL_GetWindowID(window);
g_Core = std::make_unique<gb::core::Core>(rom);
} else if(selectedCore == "n64") {
window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN); window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN);
windowID = SDL_GetWindowID(window); windowID = SDL_GetWindowID(window);
if(volkInitialize() != VK_SUCCESS) { if(volkInitialize() != VK_SUCCESS) {
@@ -29,7 +22,7 @@ App::App(const std::string& rom, const std::string& selectedCore) {
} }
g_Core = std::make_unique<n64::core::Core>(Platform::SDL, rom); g_Core = std::make_unique<n64::core::Core>(Platform::SDL, rom);
} else { } else {
util::panic("Unimplemented core!"); util::panic("Unimplemented core \"{}\"!", selectedCore);
} }
} }

View File

@@ -2,7 +2,6 @@
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h> #include <SDL2/SDL_vulkan.h>
#include <BaseCore.hpp>
#include <string> #include <string>
#include <memory> #include <memory>
#include <util.hpp> #include <util.hpp>