integration of n64 core

This commit is contained in:
CocoSimone
2022-06-06 22:37:42 +02:00
parent bc3a0ff63a
commit e70aa0b072
31 changed files with 627 additions and 137 deletions

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "external/toml11"]
path = external/toml11
url = https://github.com/ToruNiina/toml11

View File

@@ -6,4 +6,5 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
add_subdirectory(src)
add_executable(natsukashii src/main.cpp)
target_include_directories(natsukashii PUBLIC src/frontend/)
target_link_libraries(natsukashii cores frontend)

3
external/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.20)
include_directories(.)

177
external/portable_endian_bswap.h vendored Normal file
View File

@@ -0,0 +1,177 @@
// "License": Public Domain
// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to
// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it
// an example on how to get the endian conversion functions on different platforms.
#pragma once
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
# define __WINDOWS__
#endif
#if defined(__linux__) || defined(__CYGWIN__)
# include <endian.h>
#elif defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# define htobe16(x) OSSwapHostToBigInt16(x)
# define htole16(x) OSSwapHostToLittleInt16(x)
# define be16toh(x) OSSwapBigToHostInt16(x)
# define le16toh(x) OSSwapLittleToHostInt16(x)
# define htobe32(x) OSSwapHostToBigInt32(x)
# define htole32(x) OSSwapHostToLittleInt32(x)
# define be32toh(x) OSSwapBigToHostInt32(x)
# define le32toh(x) OSSwapLittleToHostInt32(x)
# define htobe64(x) OSSwapHostToBigInt64(x)
# define htole64(x) OSSwapHostToLittleInt64(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
# define le64toh(x) OSSwapLittleToHostInt64(x)
# define __BYTE_ORDER BYTE_ORDER
# define __BIG_ENDIAN BIG_ENDIAN
# define __LITTLE_ENDIAN LITTLE_ENDIAN
# define __PDP_ENDIAN PDP_ENDIAN
#elif defined(__OpenBSD__)
# include <sys/endian.h>
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
# include <sys/endian.h>
# define be16toh(x) betoh16(x)
# define le16toh(x) letoh16(x)
# define be32toh(x) betoh32(x)
# define le32toh(x) letoh32(x)
# define be64toh(x) betoh64(x)
# define le64toh(x) letoh64(x)
#elif defined(__WINDOWS__)
# include <winsock2.h>
# include <sys/param.h>
# if BYTE_ORDER == LITTLE_ENDIAN
# define htobe16(x) htons(x)
# define htole16(x) (x)
# define be16toh(x) ntohs(x)
# define le16toh(x) (x)
# define htobe32(x) htonl(x)
# define htole32(x) (x)
# define be32toh(x) ntohl(x)
# define le32toh(x) (x)
# define htobe64(x) htonll(x)
# define htole64(x) (x)
# define be64toh(x) ntohll(x)
# define le64toh(x) (x)
# elif BYTE_ORDER == BIG_ENDIAN
/* that would be xbox 360 */
# define htobe16(x) (x)
# define htole16(x) __builtin_bswap16(x)
# define be16toh(x) (x)
# define le16toh(x) __builtin_bswap16(x)
# define htobe32(x) (x)
# define htole32(x) __builtin_bswap32(x)
# define be32toh(x) (x)
# define le32toh(x) __builtin_bswap32(x)
# define htobe64(x) (x)
# define htole64(x) __builtin_bswap64(x)
# define be64toh(x) (x)
# define le64toh(x) __builtin_bswap64(x)
# else
# error byte order not supported
# endif
# define __BYTE_ORDER BYTE_ORDER
# define __BIG_ENDIAN BIG_ENDIAN
# define __LITTLE_ENDIAN LITTLE_ENDIAN
# define __PDP_ENDIAN PDP_ENDIAN
#else
# error platform not supported
#endif
// Adapted from Google's CityHash source code:
//
// https://github.com/google/cityhash/blob/8af9b8c2b889d80c22d6bc26ba0df1afb79a30db/src/city.cc#L50
#if defined(_MSC_VER)
#include <stdlib.h>
#define bswap_16(x) _byteswap_ushort(x)
#define bswap_32(x) _byteswap_ulong(x)
#define bswap_64(x) _byteswap_uint64(x)
#elif defined(__APPLE__)
// Mac OS X / Darwin features
#include <libkern/OSByteOrder.h>
#define bswap_16(x) OSSwapInt16(x)
#define bswap_32(x) OSSwapInt32(x)
#define bswap_64(x) OSSwapInt64(x)
#elif defined(__sun) || defined(sun)
#include <sys/byteorder.h>
#define bswap_16(x) BSWAP_16(x)
#define bswap_32(x) BSWAP_32(x)
#define bswap_64(x) BSWAP_64(x)
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#define bswap_16(x) bswap16(x)
#define bswap_32(x) bswap32(x)
#define bswap_64(x) bswap64(x)
#elif defined(__OpenBSD__)
#include <sys/types.h>
#define bswap_16(x) swap16(x)
#define bswap_32(x) swap32(x)
#define bswap_64(x) swap64(x)
#elif defined(__NetBSD__)
#include <sys/types.h>
#include <machine/bswap.h>
#if defined(__BSWAP_RENAME) && !defined(__bswap_32)
#define bswap_16(x) bswap16(x)
#define bswap_32(x) bswap32(x)
#define bswap_64(x) bswap64(x)
#endif
#elif __has_builtin(__builtin_bswap16) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64)
#define bswap_16(x) __builtin_bswap16(x)
#define bswap_32(x) __builtin_bswap32(x)
#define bswap_64(x) __builtin_bswap64(x)
#else
#include <byteswap.h>
#endif

1
external/toml11 vendored

Submodule external/toml11 deleted from 5924325652

7
src/core/BaseCore.hpp Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
namespace natsukashii::core {
struct BaseCore {
virtual void Run() {}
};
}

View File

@@ -5,9 +5,13 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(gb)
add_subdirectory(n64)
add_library(cores
Scheduler.cpp
Scheduler.hpp
common.hpp)
target_include_directories(cores PUBLIC .)
target_link_libraries(cores PUBLIC gb)
common.hpp
BaseCore.hpp)
target_include_directories(cores PRIVATE .)
target_include_directories(cores PUBLIC ../../external)
target_link_libraries(cores PUBLIC gb n64)

View File

@@ -5,7 +5,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(mio REQUIRED)
find_package(fmt REQUIRED)
find_package(toml11 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)
target_include_directories(gb PUBLIC . ..)
target_link_libraries(gb PUBLIC mio::mio fmt::fmt)
target_include_directories(gb PRIVATE . ..)
target_link_libraries(gb PUBLIC toml11::toml11 mio::mio fmt::fmt)

View File

@@ -1,7 +1,9 @@
#include <Core.hpp>
namespace natsukashii::core {
Core::Core() {}
namespace natsukashii::gb::core {
Core::Core(const std::string& rom) {
mem.LoadROM(rom);
}
void Core::Run() {
while(true) {

View File

@@ -1,12 +1,13 @@
#pragma once
#include "Cpu.hpp"
#include "Ppu.hpp"
#include "Mem.hpp"
#include <Cpu.hpp>
#include <Ppu.hpp>
#include <Mem.hpp>
#include <BaseCore.hpp>
namespace natsukashii::core {
struct Core {
Core();
void Run();
namespace natsukashii::gb::core {
struct Core : natsukashii::core::BaseCore {
Core(const std::string&);
void Run() override;
private:
Mem mem;
Cpu cpu;

View File

@@ -1,18 +1,30 @@
#include <Cpu.hpp>
#include <util.hpp>
namespace natsukashii::core {
Cpu::Cpu() {
}
namespace natsukashii::gb::core {
Cpu::Cpu() {}
void Cpu::Step(Mem& mem) {
u8 opcode = mem.Consume<u8>(pc);
DecodeAndExecute(opcode);
FetchDecodeExecute(mem);
}
void Cpu::DecodeAndExecute(u8 opcode) {
void Cpu::FetchDecodeExecute(Mem& mem) {
u8 opcode = mem.Read8(regs.pc);
switch(opcode) {
default: util::panic("Unimplemented opcode %02X", 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,23 +1,8 @@
#pragma once
#include "Mem.hpp"
namespace natsukashii::core {
#define af regs.AF
#define bc regs.BC
#define de regs.DE
#define hl regs.HL
#define pc regs.PC
#define sp regs.SP
#define a af.A
#define f af.F
#define b bc.B
#define c bc.C
#define d de.D
#define e de.E
#define h hl.H
#define l hl.L
#include <Mem.hpp>
#include <util.hpp>
namespace natsukashii::gb::core {
#define REGIMPL(type1, reg1, type2, reg2) \
struct reg##reg1##reg2 { \
reg##reg1##reg2() {} \
@@ -26,17 +11,11 @@ namespace natsukashii::core {
type2 reg2; \
}; \
u16 raw = 0; \
reg##reg1##reg2& operator=(const u16& rhs) { \
reg1 = rhs >> 8; \
reg2 = rhs & 0xff; \
return *this; \
} \
} reg1##reg2
struct RegF {
RegF() : raw(0) {}
RegF(const u8& val) : raw(val) {}
u8 raw = 0;
RegF& operator=(const u8& rhs) {
raw |= ((rhs >> 7) << 7);
@@ -52,6 +31,24 @@ struct RegF {
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);
@@ -76,16 +73,111 @@ struct RegF {
struct Registers {
REGIMPL(u8, A, RegF, F);
REGIMPL(u8, B, u8, C);
REGIMPL(u8, C, u8, E);
REGIMPL(u8, D, u8, L);
u16 PC = 0, SP = 0;
REGIMPL(u8, D, u8, E);
REGIMPL(u8, H, u8, L);
u16 pc = 0, sp = 0;
u8& a() { return AF.A; }
RegF& f() { return AF.F; }
u8& b() { return BC.B; }
u8& c() { return BC.C; }
u8& d() { return DE.D; }
u8& e() { return DE.E; }
u8& h() { return HL.H; }
u8& l() { return HL.L; }
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 DecodeAndExecute(u8);
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();
}
}
}
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

@@ -2,20 +2,27 @@
#include <util.hpp>
#include <memory_regions.hpp>
#include <fstream>
#include <toml.hpp>
namespace natsukashii::core {
template <class T>
T ReadCart(const std::unique_ptr<Cartridge>& cart, u16 addr) {
if constexpr(sizeof(T) == 1) return cart->Read8(addr);
else if constexpr(sizeof(T) == 2) return cart->Read16(addr);
else if constexpr(sizeof(T) == 4) return cart->Read32(addr);
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);
}
template <class T>
void WriteCart(const std::unique_ptr<Cartridge>& cart, u16 addr, T val) {
if constexpr(sizeof(T) == 1) cart->Write8(addr, val);
else if constexpr(sizeof(T) == 2) cart->Write16(addr, val);
else if constexpr(sizeof(T) == 4) cart->Write32(addr, val);
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) {
@@ -37,45 +44,49 @@ void Mem::LoadROM(const std::string& filename) {
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]);
}
}
template <typename T>
T Mem::Read(u16 addr) {
u8 Mem::Read8(u16 addr) {
switch(addr) {
case ROM_RNG00: return io.BootROMMapped() ? bootrom[addr] : ReadCart<T>(cart, addr);
case ROM_RNGNN: return ReadCart<T>(cart, 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;
}
template u8 Mem::Read<u8>(u16);
template u16 Mem::Read<u16>(u16);
template u32 Mem::Read<u32>(u16);
template <typename T>
void Mem::Write(u16 addr, T val) {
void Mem::Write8(u16 addr, u8 val) {
switch(addr) {
case ROM_RNG00: case ROM_RNGNN: WriteCart<T>(cart, addr, val);
case ROM_RNG00: case ROM_RNGNN: cart->Write(addr, val);
default: util::panic("[WRITE] Unimplemented addr: {:04X}", addr);
}
}
template void Mem::Write<u8>(u16, u8);
template void Mem::Write<u16>(u16, u16);
template void Mem::Write<u32>(u16, u32);
template <typename T>
T Mem::Consume(u16& pc) {
T result = Read<T>(pc);
pc += sizeof(T);
u8 Mem::Consume8(u16& pc) {
u8 result = Read8(pc);
pc += 1;
return result;
}
template u8 Mem::Consume<u8>(u16&);
template u16 Mem::Consume<u16>(u16&);
template u32 Mem::Consume<u32>(u16&);
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

@@ -3,10 +3,10 @@
#include <memory>
#include <mio/mmap.hpp>
#include <vector>
#include <memory_regions.hpp>
#include <mbc.hpp>
#include "memory_regions.hpp"
#include "mbc.hpp"
namespace natsukashii::core {
namespace natsukashii::gb::core {
struct IO {
[[nodiscard]] bool BootROMMapped() const { return ff50 != 1; }
private:
@@ -14,15 +14,16 @@ private:
};
struct Mem {
Mem() = default;
Mem();
void LoadROM(const std::string& filename);
template <typename T>
T Read(u16 addr);
template <typename T>
void Write(u16 addr, T val);
template <typename T>
T Consume(u16& pc);
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

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

View File

@@ -1,22 +1,17 @@
#pragma once
#include <common.hpp>
#include <memory_regions.hpp>
#include <vector>
namespace natsukashii::core {
namespace natsukashii::gb::core {
struct Cartridge {
virtual u8 Read8(u16 addr);
virtual u16 Read16(u16 addr);
virtual void Write8(u16 addr, u8 val);
virtual void Write16(u16 addr, u16 val);
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 Read8(u16 addr) override;
void Write8(u16 addr, u8 val) override;
u16 Read16(u16 addr) override;
void Write16(u16 addr, u16 val) override;
u8 Read(u16 addr) override;
void Write(u16 addr, u8 val) override;
private:
std::vector<u8> data{};
};

View File

@@ -3,21 +3,21 @@
#define BOOTROM_SIZE 512
#define ROM_STR00 0x0000
#define ROM_END00 ROM_STR00 + 16 KiB - 1
#define ROM_END00 ROM_STR00 + 0x3fff
#define ROM_STRNN ROM_END00 + 1
#define ROM_ENDNN ROM_STRNN + 16 KiB - 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 + 8 KiB - 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 + 8 KiB - 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 + 4 KiB - 1
#define WRAM_END00 WRAM_STR00 + 0xfff
#define WRAM_STRNN WRAM_END00 + 1
#define WRAM_ENDNN WRAM_STRNN + 4 KiB - 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

View File

@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.20)
project(n64 CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#find_package(mio REQUIRED)
find_package(fmt REQUIRED)
#find_package(toml11 REQUIRED)
add_library(n64 Core.hpp Core.cpp Cpu.hpp Cpu.cpp Mem.cpp Mem.hpp memory_regions.hpp)
target_include_directories(n64 PRIVATE . ..)
target_link_libraries(n64 PUBLIC fmt::fmt)

9
src/core/n64/Core.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include <Core.hpp>
namespace natsukashii::n64::core {
Core::Core(const std::string& rom) {}
void Core::Run() {
}
}

12
src/core/n64/Core.hpp Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <BaseCore.hpp>
#include <Cpu.hpp>
#include <Mem.hpp>
#include <string>
namespace natsukashii::n64::core {
struct Core : natsukashii::core::BaseCore {
Core(const std::string&);
virtual void Run() override;
};
}

1
src/core/n64/Cpu.cpp Normal file
View File

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

7
src/core/n64/Cpu.hpp Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
namespace natsukashii::n64::core {
struct Cpu {
};
}

33
src/core/n64/Mem.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include <Mem.hpp>
#include <fstream>
#include <util.hpp>
namespace natsukashii::n64::core {
Mem::Mem() {
rdram.resize(RDRAM_SIZE);
sram.resize(SRAM_SIZE);
}
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();
util::SwapN64Rom(size, rom.data());
memcpy(dmem, rom.data(), 0x1000);
}
}

16
src/core/n64/Mem.hpp Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include <common.hpp>
#include <memory_regions.hpp>
#include <vector>
namespace natsukashii::n64::core {
struct Mem {
Mem();
void LoadROM(const std::string&);
private:
std::vector<u8> cart, rdram, sram;
u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{}, pif_ram[PIF_RAM_SIZE]{};
u8 pif_bootrom[PIF_BOOTROM_SIZE]{};
size_t rom_mask;
};
}

View File

@@ -0,0 +1,14 @@
#pragma once
#define RDRAM_SIZE 0x800000
#define RDRAM_DSIZE (RDRAM_SIZE - 1)
#define SRAM_SIZE 0x8000000
#define SRAM_DSIZE (SRAM_SIZE - 1)
#define DMEM_SIZE 0x1000
#define DMEM_DSIZE (DMEM_SIZE - 1)
#define IMEM_SIZE 0x1000
#define IMEM_DSIZE (IMEM_SIZE - 1)
#define PIF_RAM_SIZE 0x40
#define PIF_RAM_DSIZE (PIF_RAM_SIZE - 1)
#define PIF_BOOTROM_SIZE 0x7C0
#define PIF_BOOTROM_DSIZE (PIF_BOOTROM_SIZE - 1)

View File

@@ -2,6 +2,7 @@
#include <common.hpp>
#include <type_traits>
#include <cassert>
#include <portable_endian_bswap.h>
namespace natsukashii::util {
template <typename ...Args>
@@ -36,4 +37,62 @@ T BitSlice(const T& num, int start, int end) {
return (num >> start) & ((1 << correctedEnd) - 1);
}
template <typename T, bool FromHToBE = false>
auto GetSwapFunc(T num) -> T {
if constexpr(sizeof(T) == 2) {
if constexpr(FromHToBE)
return htobe16(num);
return be16toh(num);
} else if constexpr(sizeof(T) == 4) {
if constexpr(FromHToBE)
return htobe32(num);
return be32toh(num);
} else if constexpr(sizeof(T) == 8) {
if constexpr(FromHToBE)
return htobe32(num);
return be32toh(num);
}
}
template <typename T>
inline T ReadAccess(u8* data, u32 index) {
static_assert(sizeof(T) != 2 && sizeof(T) != 4 && sizeof(T) != 8);
T result = 0;
memcpy(&result, &data[index], sizeof(T));
return GetSwapFunc(result);
}
template <typename T>
inline void WriteAccess(u8* data, u32 index, T val) {
static_assert(sizeof(T) != 2 && sizeof(T) != 4 && sizeof(T) != 8);
T temp = GetSwapFunc<T, true>(val);
memcpy(&data[index], &temp, sizeof(T));
}
#define Z64 0x80371240
#define N64 0x40123780
#define V64 0x37804012
inline void SwapN64Rom(size_t size, u8* data) {
u32 endianness;
memcpy(&endianness, data, 4);
endianness = be32toh(endianness);
switch(endianness) {
case V64:
for(int i = 0; i < size; i += 2) {
u16 original = *(u16*)&data[i];
*(u16*)&data[i] = bswap_16(original);
}
break;
case N64:
for(int i = 0; i < size; i += 4) {
u32 original = *(u32*)&data[i];
*(u32*)&data[i] = bswap_32(original);
}
break;
case Z64: break;
default:
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n");
}
}
}

View File

@@ -9,6 +9,8 @@ set(CMAKE_AUTOUIC ON)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
add_library(frontend Frontend.hpp)
add_library(frontend Frontend.hpp Frontend.cpp)
target_compile_definitions(frontend PUBLIC FRONTEND_QT)
target_include_directories(frontend PUBLIC .)
target_link_libraries(frontend PUBLIC Qt5::Widgets)

View File

@@ -6,5 +6,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(SDL2 REQUIRED)
add_library(frontend Frontend.cpp Frontend.hpp)
target_include_directories(frontend PUBLIC . ../../core)
target_compile_definitions(frontend PUBLIC FRONTEND_SDL)
target_include_directories(frontend PUBLIC . ../../core ../../core/gb ../../core/n64)
target_link_libraries(frontend PUBLIC SDL2)

View File

@@ -1,4 +1,8 @@
#include <Frontend.hpp>
#include <algorithm>
#include <cctype>
#include <gb/Core.hpp>
#include <n64/Core.hpp>
namespace natsukashii::frontend {
App::~App() {
@@ -7,16 +11,25 @@ App::~App() {
SDL_Quit();
}
App::App() {
App::App(const std::string& rom) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
id = SDL_GetWindowID(window);
std::string ext{rom.begin() + rom.find_first_of('.') + 1, rom.end()};
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){
return std::tolower(c);
});
if(ext == "gb") core = std::make_unique<gb::core::Core>(rom);
else if(ext == "n64") core = std::make_unique<n64::core::Core>(rom);
else util::panic("Unimplemented core!");
}
void App::Run() {
while(!quit) {
gb.Run();
core->Run();
SDL_Event event;
SDL_PollEvent(&event);

View File

@@ -1,17 +1,21 @@
#pragma once
#include <SDL2/SDL.h>
#include <gb/Core.hpp>
#include <BaseCore.hpp>
#include <string>
#include <memory>
#include <util.hpp>
namespace natsukashii::frontend {
using namespace natsukashii::core;
struct App {
~App();
App();
App(const std::string&);
void Run();
private:
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
Uint32 id;
bool quit = false;
core::Core gb;
std::unique_ptr<BaseCore> core;
};
}

View File

@@ -1,7 +1,20 @@
#include <Frontend.hpp>
#ifdef FRONTEND_QT
#include <qt/Frontend.hpp>
#elifdef FRONTEND_SDL
#include <sdl/Frontend.hpp>
#endif
int main() {
#include <util.hpp>
int main(int argc, char* argv[]) {
#ifdef FRONTEND_SDL
if(argc < 2) {
natsukashii::util::panic("Usage: natsukashii [rom]");
}
natsukashii::frontend::App app(argv[1]);
#else
natsukashii::frontend::App app;
#endif
app.Run();
return 0;
}