more memory work
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include <cstdint>
|
||||
#include <bitset>
|
||||
#include <emmintrin.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
@@ -19,4 +20,6 @@ using m128 = __m128i;
|
||||
#define UINT128_MIN 0
|
||||
#define INT128_MAX ((u128)0x7FFF'FFFF'FFFF'FFFF << 64) | 0xFFFF'FFFF'FFFF'FFFF
|
||||
#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_REQUIRED ON)
|
||||
|
||||
add_library(gb Core.hpp Core.cpp Cpu.hpp Cpu.cpp Ppu.hpp Ppu.cpp)
|
||||
target_include_directories(gb PUBLIC .)
|
||||
find_package(mio REQUIRED)
|
||||
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 {
|
||||
Core::Core() {}
|
||||
|
||||
void Core::Run() {
|
||||
while(true) {
|
||||
cpu.Step(mem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
#include "Cpu.hpp"
|
||||
#include "Ppu.hpp"
|
||||
#include "Mem.hpp"
|
||||
|
||||
namespace natsukashii::core {
|
||||
struct Core {
|
||||
Core();
|
||||
void Run();
|
||||
private:
|
||||
Mem mem;
|
||||
Cpu cpu;
|
||||
// Ppu ppu;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
#include <Cpu.hpp>
|
||||
#include <util.hpp>
|
||||
|
||||
namespace natsukashii::core {
|
||||
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
|
||||
#include "../common.hpp"
|
||||
#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
|
||||
@@ -76,11 +78,14 @@ struct Registers {
|
||||
REGIMPL(u8, B, u8, C);
|
||||
REGIMPL(u8, C, u8, E);
|
||||
REGIMPL(u8, D, u8, L);
|
||||
u16 PC = 0, SP = 0;
|
||||
};
|
||||
|
||||
struct Cpu {
|
||||
Cpu();
|
||||
void Step(Mem&);
|
||||
private:
|
||||
void DecodeAndExecute(u8);
|
||||
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>
|
||||
|
||||
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>
|
||||
using BitSliceType =
|
||||
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;
|
||||
return (num >> start) & ((1 << correctedEnd) - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@ App::App() {
|
||||
|
||||
void App::Run() {
|
||||
while(!quit) {
|
||||
SDL_Event e;
|
||||
SDL_PollEvent(&e);
|
||||
quit = e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE && e.window.windowID == id;
|
||||
gb.Run();
|
||||
|
||||
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