more memory work
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <emmintrin.h>
|
#include <emmintrin.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
using u8 = uint8_t;
|
using u8 = uint8_t;
|
||||||
using u16 = uint16_t;
|
using u16 = uint16_t;
|
||||||
@@ -19,4 +20,6 @@ using m128 = __m128i;
|
|||||||
#define UINT128_MIN 0
|
#define UINT128_MIN 0
|
||||||
#define INT128_MAX ((u128)0x7FFF'FFFF'FFFF'FFFF << 64) | 0xFFFF'FFFF'FFFF'FFFF
|
#define INT128_MAX ((u128)0x7FFF'FFFF'FFFF'FFFF << 64) | 0xFFFF'FFFF'FFFF'FFFF
|
||||||
#define INT128_MIN (-INT128_MAX - 1LL)
|
#define INT128_MIN (-INT128_MAX - 1LL)
|
||||||
|
#define KiB * 1024
|
||||||
|
#define MiB (KiB * 1024)
|
||||||
|
#define GiB (MiB * 1024)
|
||||||
|
|||||||
@@ -3,5 +3,9 @@ project(gb CXX)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
add_library(gb Core.hpp Core.cpp Cpu.hpp Cpu.cpp Ppu.hpp Ppu.cpp)
|
find_package(mio REQUIRED)
|
||||||
target_include_directories(gb PUBLIC .)
|
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)
|
||||||
|
target_include_directories(gb PUBLIC . ..)
|
||||||
|
target_link_libraries(gb PUBLIC mio::mio fmt::fmt)
|
||||||
|
|||||||
@@ -2,4 +2,10 @@
|
|||||||
|
|
||||||
namespace natsukashii::core {
|
namespace natsukashii::core {
|
||||||
Core::Core() {}
|
Core::Core() {}
|
||||||
}
|
|
||||||
|
void Core::Run() {
|
||||||
|
while(true) {
|
||||||
|
cpu.Step(mem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Cpu.hpp"
|
#include "Cpu.hpp"
|
||||||
#include "Ppu.hpp"
|
#include "Ppu.hpp"
|
||||||
|
#include "Mem.hpp"
|
||||||
|
|
||||||
namespace natsukashii::core {
|
namespace natsukashii::core {
|
||||||
struct Core {
|
struct Core {
|
||||||
Core();
|
Core();
|
||||||
void Run();
|
void Run();
|
||||||
private:
|
private:
|
||||||
|
Mem mem;
|
||||||
Cpu cpu;
|
Cpu cpu;
|
||||||
// Ppu ppu;
|
// Ppu ppu;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
#include <Cpu.hpp>
|
#include <Cpu.hpp>
|
||||||
|
#include <util.hpp>
|
||||||
|
|
||||||
namespace natsukashii::core {
|
namespace natsukashii::core {
|
||||||
Cpu::Cpu() {
|
Cpu::Cpu() {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
void Cpu::Step(Mem& mem) {
|
||||||
|
u8 opcode = mem.Consume<u8>(pc);
|
||||||
|
DecodeAndExecute(opcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cpu::DecodeAndExecute(u8 opcode) {
|
||||||
|
switch(opcode) {
|
||||||
|
default: util::panic("Unimplemented opcode %02X", opcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "../common.hpp"
|
#include "Mem.hpp"
|
||||||
|
|
||||||
namespace natsukashii::core {
|
namespace natsukashii::core {
|
||||||
#define af regs.AF
|
#define af regs.AF
|
||||||
#define bc regs.BC
|
#define bc regs.BC
|
||||||
#define de regs.DE
|
#define de regs.DE
|
||||||
#define hl regs.HL
|
#define hl regs.HL
|
||||||
|
#define pc regs.PC
|
||||||
|
#define sp regs.SP
|
||||||
|
|
||||||
#define a af.A
|
#define a af.A
|
||||||
#define f af.F
|
#define f af.F
|
||||||
@@ -76,11 +78,14 @@ struct Registers {
|
|||||||
REGIMPL(u8, B, u8, C);
|
REGIMPL(u8, B, u8, C);
|
||||||
REGIMPL(u8, C, u8, E);
|
REGIMPL(u8, C, u8, E);
|
||||||
REGIMPL(u8, D, u8, L);
|
REGIMPL(u8, D, u8, L);
|
||||||
|
u16 PC = 0, SP = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Cpu {
|
struct Cpu {
|
||||||
Cpu();
|
Cpu();
|
||||||
|
void Step(Mem&);
|
||||||
private:
|
private:
|
||||||
|
void DecodeAndExecute(u8);
|
||||||
Registers regs;
|
Registers regs;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
81
src/core/gb/Mem.cpp
Normal file
81
src/core/gb/Mem.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include <Mem.hpp>
|
||||||
|
#include <util.hpp>
|
||||||
|
#include <memory_regions.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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::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();
|
||||||
|
|
||||||
|
cart = std::make_unique<NoMBC>(rom);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T Mem::Read(u16 addr) {
|
||||||
|
switch(addr) {
|
||||||
|
case ROM_RNG00: return io.BootROMMapped() ? bootrom[addr] : ReadCart<T>(cart, addr);
|
||||||
|
case ROM_RNGNN: return ReadCart<T>(cart, 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) {
|
||||||
|
switch(addr) {
|
||||||
|
case ROM_RNG00: case ROM_RNGNN: WriteCart<T>(cart, 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);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template u8 Mem::Consume<u8>(u16&);
|
||||||
|
template u16 Mem::Consume<u16>(u16&);
|
||||||
|
template u32 Mem::Consume<u32>(u16&);
|
||||||
|
}
|
||||||
30
src/core/gb/Mem.hpp
Normal file
30
src/core/gb/Mem.hpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <common.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <mio/mmap.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory_regions.hpp>
|
||||||
|
#include <mbc.hpp>
|
||||||
|
|
||||||
|
namespace natsukashii::core {
|
||||||
|
struct IO {
|
||||||
|
[[nodiscard]] bool BootROMMapped() const { return ff50 != 1; }
|
||||||
|
private:
|
||||||
|
u8 ff50 = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Mem {
|
||||||
|
Mem() = default;
|
||||||
|
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);
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Cartridge> cart;
|
||||||
|
IO io{};
|
||||||
|
u8 bootrom[BOOTROM_SIZE]{};
|
||||||
|
};
|
||||||
|
}
|
||||||
24
src/core/gb/mbc.cpp
Normal file
24
src/core/gb/mbc.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include <mbc.hpp>
|
||||||
|
#include <utility>
|
||||||
|
#include <util.hpp>
|
||||||
|
|
||||||
|
namespace natsukashii::core {
|
||||||
|
NoMBC::NoMBC(std::vector<u8> rom) : data(std::move(rom)) {}
|
||||||
|
|
||||||
|
u8 NoMBC::Read8(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/core/gb/mbc.hpp
Normal file
23
src/core/gb/mbc.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <common.hpp>
|
||||||
|
#include <memory_regions.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace natsukashii::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);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
private:
|
||||||
|
std::vector<u8> data{};
|
||||||
|
};
|
||||||
|
}
|
||||||
34
src/core/gb/memory_regions.hpp
Normal file
34
src/core/gb/memory_regions.hpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <common.hpp>
|
||||||
|
|
||||||
|
#define BOOTROM_SIZE 512
|
||||||
|
#define ROM_STR00 0x0000
|
||||||
|
#define ROM_END00 ROM_STR00 + 16 KiB - 1
|
||||||
|
#define ROM_STRNN ROM_END00 + 1
|
||||||
|
#define ROM_ENDNN ROM_STRNN + 16 KiB - 1
|
||||||
|
#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_RANGE VRAM_START ... VRAM_END
|
||||||
|
#define EXTRAM_START VRAM_END + 1
|
||||||
|
#define EXTRAM_END EXTRAM_START + 8 KiB - 1
|
||||||
|
#define EXTRAM_RANGE EXTRAM_START ... EXTRAM_END
|
||||||
|
#define WRAM_STR00 EXTRAM_END + 1
|
||||||
|
#define WRAM_END00 WRAM_STR00 + 4 KiB - 1
|
||||||
|
#define WRAM_STRNN WRAM_END00 + 1
|
||||||
|
#define WRAM_ENDNN WRAM_STRNN + 4 KiB - 1
|
||||||
|
#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
|
||||||
@@ -4,6 +4,12 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace natsukashii::util {
|
namespace natsukashii::util {
|
||||||
|
template <typename ...Args>
|
||||||
|
constexpr void panic(const std::string& fmt, Args... args) {
|
||||||
|
fmt::print(fmt, args...);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
template <u8 start, u8 end>
|
template <u8 start, u8 end>
|
||||||
using BitSliceType =
|
using BitSliceType =
|
||||||
typename std::conditional<(end - start) <= 7, u8,
|
typename std::conditional<(end - start) <= 7, u8,
|
||||||
@@ -29,4 +35,5 @@ T BitSlice(const T& num, int start, int end) {
|
|||||||
auto correctedEnd = end == (sizeof(T) * 8) - 1 ? end : end + 1;
|
auto correctedEnd = end == (sizeof(T) * 8) - 1 ? end : end + 1;
|
||||||
return (num >> start) & ((1 << correctedEnd) - 1);
|
return (num >> start) & ((1 << correctedEnd) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ App::App() {
|
|||||||
|
|
||||||
void App::Run() {
|
void App::Run() {
|
||||||
while(!quit) {
|
while(!quit) {
|
||||||
SDL_Event e;
|
gb.Run();
|
||||||
SDL_PollEvent(&e);
|
|
||||||
quit = e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE && e.window.windowID == id;
|
SDL_Event event;
|
||||||
|
SDL_PollEvent(&event);
|
||||||
|
quit = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user