integration of n64 core
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "external/toml11"]
|
||||
path = external/toml11
|
||||
url = https://github.com/ToruNiina/toml11
|
||||
|
||||
@@ -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
3
external/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include_directories(.)
|
||||
177
external/portable_endian_bswap.h
vendored
Normal file
177
external/portable_endian_bswap.h
vendored
Normal 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
1
external/toml11
vendored
Submodule external/toml11 deleted from 5924325652
7
src/core/BaseCore.hpp
Normal file
7
src/core/BaseCore.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace natsukashii::core {
|
||||
struct BaseCore {
|
||||
virtual void Run() {}
|
||||
};
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
cart = std::make_unique<NoMBC>(rom);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]{};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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{};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
12
src/core/n64/CMakeLists.txt
Normal file
12
src/core/n64/CMakeLists.txt
Normal 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
9
src/core/n64/Core.cpp
Normal 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
12
src/core/n64/Core.hpp
Normal 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
1
src/core/n64/Cpu.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <Cpu.hpp>
|
||||
7
src/core/n64/Cpu.hpp
Normal file
7
src/core/n64/Cpu.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace natsukashii::n64::core {
|
||||
struct Cpu {
|
||||
|
||||
};
|
||||
}
|
||||
33
src/core/n64/Mem.cpp
Normal file
33
src/core/n64/Mem.cpp
Normal 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
16
src/core/n64/Mem.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
14
src/core/n64/memory_regions.hpp
Normal file
14
src/core/n64/memory_regions.hpp
Normal 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)
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
17
src/main.cpp
17
src/main.cpp
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user