reformatting
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
// 6 n - - Add/Sub-Flag (BCD)
|
||||
// 5 h - - Half Carry Flag (BCD)
|
||||
// 4 cy C NC Carry Flag
|
||||
// 3-0 - - - Not used (always zero)
|
||||
// 3-0 - - - Not used
|
||||
#define CARRY_FLAG 4 //'C'
|
||||
#define HALFCARRY_FLAG 5 //'H'
|
||||
#define SUBTRACT_FLAG 6 //'N'
|
||||
@@ -21,7 +21,7 @@
|
||||
#define SERIAL_INTERRUPT 3
|
||||
#define JOYPAD_INTERRUPT 4
|
||||
|
||||
#define T_CLOCK_FREQ 4194304
|
||||
#define T_CLOCK_FREQ 4194304 //2^22
|
||||
|
||||
#define DIVIDER_REGISTER_FREQ 16384
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
#define MODE2_DURATION 80
|
||||
#define MODE3_MIN_DURATION 172
|
||||
#define MODE0_3_DURATION 376 //mode3 is 172 to 289, mode0 87 to 204
|
||||
#define MODE1_DURATION 4560
|
||||
|
||||
#define H_SYNC 9198
|
||||
#define V_SYNC 59.73
|
||||
#define HBlank_DURATION 204 //GPU_MODE 0
|
||||
#define SCANLINE_OAM_FREQ 80 //GPU_MODE 2
|
||||
#define SCANLINE_VRAM_FREQ 80 //GPU_MODE 3
|
||||
#define HBLANK_DURATION 204 //PPU_MODE 0
|
||||
#define VBLANK_DURATION 4560
|
||||
#define SCANLINE_OAM_FREQ 80 //PPU_MODE 2
|
||||
#define SCANLINE_VRAM_FREQ 80 //PPU_MODE 3
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,60 +1,50 @@
|
||||
#include <iostream>
|
||||
#include "gameboy.hpp"
|
||||
|
||||
bool AddressSpace::getBootromState()
|
||||
{
|
||||
bool AddressSpace::getBootromState() {
|
||||
return bootromLoaded;
|
||||
}
|
||||
|
||||
void AddressSpace::unmapBootrom()
|
||||
{
|
||||
void AddressSpace::unmapBootrom() {
|
||||
bootromLoaded = false;
|
||||
}
|
||||
|
||||
void AddressSpace::mapBootrom()
|
||||
{
|
||||
void AddressSpace::mapBootrom() {
|
||||
bootromLoaded = true;
|
||||
}
|
||||
|
||||
void AddressSpace::loadBootrom(std::string filename)
|
||||
{
|
||||
void AddressSpace::loadBootrom(std::string filename) {
|
||||
std::ifstream file;
|
||||
int size = std::filesystem::file_size(filename);
|
||||
if(size != 256)
|
||||
{
|
||||
if (size != 256) {
|
||||
std::cerr << "Bootrom was an unexpected size!\nQuitting!\n" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
file.open(filename, std::ios::binary);
|
||||
file.read(reinterpret_cast<char *>(bootrom), BOOTROM_SIZE);
|
||||
file.read(reinterpret_cast<char*>(bootrom), BOOTROM_SIZE);
|
||||
}
|
||||
|
||||
void AddressSpace::loadGame(std::string filename)
|
||||
{
|
||||
void AddressSpace::loadGame(std::string filename) {
|
||||
game.open(filename, std::ios::binary);
|
||||
|
||||
if(!game.is_open())
|
||||
{
|
||||
if (!game.is_open()) {
|
||||
std::cerr << "Game was not found!\nQuitting!\n" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
game.read(reinterpret_cast<char *>(memoryLayout.romBank1), ROM_BANK_SIZE*2);
|
||||
game.read(reinterpret_cast<char*>(memoryLayout.romBank1), ROM_BANK_SIZE * 2);
|
||||
}
|
||||
|
||||
void GameBoy::addCycles(uint8_t ticks)
|
||||
{
|
||||
cycles = (cycles+ticks) % T_CLOCK_FREQ;
|
||||
void GameBoy::addCycles(uint8_t ticks) {
|
||||
cycles = (cycles + ticks) % T_CLOCK_FREQ;
|
||||
lastOpTicks = ticks;
|
||||
}
|
||||
|
||||
void GameBoy::start(std::string bootrom, std::string game)
|
||||
{
|
||||
void GameBoy::start(std::string bootrom, std::string game) {
|
||||
addressSpace.loadBootrom(bootrom);
|
||||
addressSpace.loadGame(game);
|
||||
|
||||
bool quit = false;
|
||||
while(!quit)
|
||||
{
|
||||
while (!quit) {
|
||||
// Event loop: Check and handle SDL events
|
||||
// if(SDL_PollEvent(&event))
|
||||
// {
|
||||
@@ -68,16 +58,13 @@ void GameBoy::start(std::string bootrom, std::string game)
|
||||
interruptHandler();
|
||||
//timing();
|
||||
ppuUpdate();
|
||||
if(PC > 0xFF && addressSpace.getBootromState())
|
||||
{
|
||||
if (PC > 0xFF && addressSpace.getBootromState()) {
|
||||
addressSpace.unmapBootrom();
|
||||
}
|
||||
int cyclesSince = cyclesSinceLastRefresh();
|
||||
if(cyclesSince > FRAME_DURATION)
|
||||
{
|
||||
if (cyclesSince > FRAME_DURATION) {
|
||||
lastRefresh = cycles;
|
||||
SDL2present();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
128
src/gameboy.hpp
128
src/gameboy.hpp
@@ -11,66 +11,59 @@
|
||||
#include "defines.hpp"
|
||||
|
||||
//two bits per colour
|
||||
enum Colour
|
||||
{
|
||||
enum Colour {
|
||||
black = 0b11,
|
||||
darkGray = 0b10,
|
||||
lightGray = 0b01,
|
||||
white = 0b00
|
||||
};
|
||||
|
||||
enum PPUMode
|
||||
{
|
||||
enum PPUMode {
|
||||
mode0, // Horizontal Blank (Mode 0): No access to video RAM, occurs during horizontal blanking period.
|
||||
mode1, // Vertical Blank (Mode 1): No access to video RAM, occurs during vertical blanking period.
|
||||
mode2, // OAM Search (Mode 2): Access to OAM (Object Attribute Memory) only, sprite evaluation.
|
||||
mode3 // Pixel Transfer (Mode 3): Access to both OAM and video RAM, actual pixel transfer to the screen.
|
||||
mode3 // Pixel Transfer (Mode 3): Access to both OAM and video RAM, actual pixel transfer to the screen.
|
||||
};
|
||||
|
||||
union RegisterPair
|
||||
{
|
||||
union RegisterPair {
|
||||
Word reg; //register.reg == (hi << 8) + lo. (hi is more significant than lo)
|
||||
|
||||
struct
|
||||
{
|
||||
struct {
|
||||
Byte lo;
|
||||
Byte hi;
|
||||
};
|
||||
};
|
||||
|
||||
class AddressSpace
|
||||
{
|
||||
private:
|
||||
class AddressSpace {
|
||||
private:
|
||||
bool bootromLoaded = true;
|
||||
Byte bootrom[BOOTROM_SIZE];
|
||||
std::ifstream game;
|
||||
|
||||
public:
|
||||
AddressSpace()
|
||||
{
|
||||
public:
|
||||
AddressSpace() {
|
||||
// Initialize the memory to zero
|
||||
memoryLayout = {};
|
||||
std::memset(memoryLayout.memory, 0, sizeof(memoryLayout.memory));
|
||||
}
|
||||
|
||||
// Nested union for the memory layout
|
||||
union MemoryLayout
|
||||
{
|
||||
union MemoryLayout {
|
||||
Byte memory[0x10000];
|
||||
struct
|
||||
{
|
||||
Byte romBank1[ROM_BANK_SIZE]; // Mapped to 0x0000
|
||||
Byte romBankSwitch[ROM_BANK_SIZE]; // Mapped to 0x4000
|
||||
Byte vram[0x2000]; // Mapped to 0x8000
|
||||
Byte externalRam[0x2000]; // Mapped to 0xA000
|
||||
Byte memoryBank1[0x1000]; // Mapped to 0xC000
|
||||
Byte memoryBank2[0x1000]; // Mapped to 0xD000
|
||||
Byte echoRam[0x1E00]; // Mapped to 0xE000 (Echo RAM, mirrors 0xC000 to 0xDFFF)
|
||||
|
||||
struct {
|
||||
Byte romBank1[ROM_BANK_SIZE]; // Mapped to 0x0000
|
||||
Byte romBankSwitch[ROM_BANK_SIZE]; // Mapped to 0x4000
|
||||
Byte vram[0x2000]; // Mapped to 0x8000
|
||||
Byte externalRam[0x2000]; // Mapped to 0xA000
|
||||
Byte memoryBank1[0x1000]; // Mapped to 0xC000
|
||||
Byte memoryBank2[0x1000]; // Mapped to 0xD000
|
||||
Byte echoRam[0x1E00]; // Mapped to 0xE000 (Echo RAM, mirrors 0xC000 to 0xDFFF)
|
||||
Byte spriteAttributeTable[0xA0]; // Mapped to 0xFE00
|
||||
Byte notUsable[0x60]; // Mapped to 0xFEA0
|
||||
Byte io[0x80]; // Mapped to 0xFF00, 0xFF0F is interrupt flag
|
||||
Byte specialRam[0x7F]; // Mapped to 0xFF80
|
||||
Byte interuptEnableReg; // Mapped to 0xFFFF
|
||||
Byte notUsable[0x60]; // Mapped to 0xFEA0
|
||||
Byte io[0x80]; // Mapped to 0xFF00, 0xFF0F is interrupt flag
|
||||
Byte specialRam[0x7F]; // Mapped to 0xFF80
|
||||
Byte interuptEnableReg; // Mapped to 0xFFFF
|
||||
};
|
||||
} memoryLayout;
|
||||
|
||||
@@ -81,20 +74,19 @@ class AddressSpace
|
||||
void loadGame(std::string filename);
|
||||
|
||||
//overload [] for echo ram and bootrom support
|
||||
Byte operator[](uint32_t address) const
|
||||
{
|
||||
if(address >= 0xE000 && address < 0xFE00)
|
||||
Byte operator[](uint32_t address) const {
|
||||
if (address >= 0xE000 && address < 0xFE00)
|
||||
return memoryLayout.echoRam[address - 0xE000];
|
||||
if(address < 0x0100 && bootromLoaded)
|
||||
if (address < 0x0100 && bootromLoaded)
|
||||
return bootrom[address];
|
||||
else
|
||||
return memoryLayout.memory[address];
|
||||
}
|
||||
Byte& operator[](uint32_t address)
|
||||
{
|
||||
if(address >= 0xE000 && address < 0xFE00)
|
||||
|
||||
Byte& operator[](uint32_t address) {
|
||||
if (address >= 0xE000 && address < 0xFE00)
|
||||
return memoryLayout.echoRam[address - 0xE000];
|
||||
if(address < 0x0100 && bootromLoaded)
|
||||
if (address < 0x0100 && bootromLoaded)
|
||||
return bootrom[address];
|
||||
else
|
||||
return memoryLayout.memory[address];
|
||||
@@ -102,7 +94,7 @@ class AddressSpace
|
||||
};
|
||||
|
||||
class GameBoy {
|
||||
private:
|
||||
private:
|
||||
uint32_t cycles = 0;
|
||||
uint32_t lastOpTicks = 0;
|
||||
uint32_t lastRefresh = 0;
|
||||
@@ -181,10 +173,10 @@ class GameBoy {
|
||||
PPUMode currentMode;
|
||||
|
||||
//3 colour channels
|
||||
uint32_t* framebuffer = new uint32_t[RESOLUTION_X*RESOLUTION_Y*SCREEN_BPP];
|
||||
SDL_Window *screen;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
uint32_t* framebuffer = new uint32_t[RESOLUTION_X * RESOLUTION_Y * SCREEN_BPP];
|
||||
SDL_Window* screen;
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Texture* texture;
|
||||
SDL_Event event;
|
||||
|
||||
void opcodeHandler();
|
||||
@@ -220,56 +212,56 @@ class GameBoy {
|
||||
void addCycles(Byte ticks);
|
||||
|
||||
//OPCODE FUNCTIONS
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void ld(T& dest, T src);
|
||||
template<typename T>
|
||||
void orBitwise(T &dest, T src);
|
||||
template<typename T>
|
||||
void andBitwise(T &dest, T src);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void orBitwise(T& dest, T src);
|
||||
template <typename T>
|
||||
void andBitwise(T& dest, T src);
|
||||
template <typename T>
|
||||
void xorBitwise(T& dest, T src);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void bit(T testBit, T reg);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void jp(T address);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
bool jrNZ(T offset);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void inc(T& reg);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void call(T address);
|
||||
void halt();
|
||||
void stop();
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void ldW(T dest, T src);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void cp(T value);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void dec(T& reg);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
bool jrZ(T offset);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void sub(T value);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void jr(T OFFSET);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void push(T reg);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void rl(T& reg);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void pop(T& reg);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void rla(T& reg);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void rst(T address);
|
||||
void ret();
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void add(T& reg, T value);
|
||||
void cpl();
|
||||
void ccf();
|
||||
void swap(Byte &value);
|
||||
void swap(Byte& value);
|
||||
|
||||
public:
|
||||
public:
|
||||
void start(std::string bootrom, std::string game);
|
||||
void SDL2setup();
|
||||
void SDL2destroy();
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
#include "defines.hpp"
|
||||
#include "gameboy.hpp"
|
||||
|
||||
bool GameBoy::testInterruptEnabled(Byte interrupt)
|
||||
{
|
||||
return (*IE) & (Byte) (1 << interrupt);
|
||||
bool GameBoy::testInterruptEnabled(Byte interrupt) {
|
||||
return (*IE) & (Byte)(1 << interrupt);
|
||||
}
|
||||
|
||||
void GameBoy::resetInterrupt(Byte interrupt)
|
||||
{
|
||||
void GameBoy::resetInterrupt(Byte interrupt) {
|
||||
*IF &= ~(1 << interrupt);
|
||||
}
|
||||
|
||||
void GameBoy::interruptHandler()
|
||||
{
|
||||
if(!IME)
|
||||
void GameBoy::interruptHandler() {
|
||||
if (!IME)
|
||||
return;
|
||||
|
||||
if(*IF & (Byte) (1 << VBLANK_INTERRUPT) && testInterruptEnabled(VBLANK_INTERRUPT))
|
||||
if (*IF & (Byte)(1 << VBLANK_INTERRUPT) && testInterruptEnabled(VBLANK_INTERRUPT))
|
||||
VBlankHandle();
|
||||
if(*IF & (Byte) (1 << LCD_STAT_INTERRUPT) && testInterruptEnabled(LCD_STAT_INTERRUPT))
|
||||
if (*IF & (Byte)(1 << LCD_STAT_INTERRUPT) && testInterruptEnabled(LCD_STAT_INTERRUPT))
|
||||
LCDStatHandle();
|
||||
if(*IF & (Byte) (1 << TIMER_INTERRUPT) && testInterruptEnabled(TIMER_INTERRUPT))
|
||||
if (*IF & (Byte)(1 << TIMER_INTERRUPT) && testInterruptEnabled(TIMER_INTERRUPT))
|
||||
timerHandle();
|
||||
if(*IF & (Byte) (1 << SERIAL_INTERRUPT) && testInterruptEnabled(SERIAL_INTERRUPT))
|
||||
if (*IF & (Byte)(1 << SERIAL_INTERRUPT) && testInterruptEnabled(SERIAL_INTERRUPT))
|
||||
serialHandle();
|
||||
if(*IF & (Byte) (1 << JOYPAD_INTERRUPT) && testInterruptEnabled(JOYPAD_INTERRUPT))
|
||||
if (*IF & (Byte)(1 << JOYPAD_INTERRUPT) && testInterruptEnabled(JOYPAD_INTERRUPT))
|
||||
joypadHandle();
|
||||
}
|
||||
|
||||
void GameBoy::VBlankHandle()
|
||||
{
|
||||
void GameBoy::VBlankHandle() {
|
||||
printf("VBlank interrupt");
|
||||
IME = 0;
|
||||
push(PC);
|
||||
@@ -37,8 +33,7 @@ void GameBoy::VBlankHandle()
|
||||
resetInterrupt(VBLANK_INTERRUPT);
|
||||
}
|
||||
|
||||
void GameBoy::LCDStatHandle()
|
||||
{
|
||||
void GameBoy::LCDStatHandle() {
|
||||
printf("LCD stat interrupt");
|
||||
IME = 0;
|
||||
push(PC);
|
||||
@@ -47,8 +42,7 @@ void GameBoy::LCDStatHandle()
|
||||
resetInterrupt(LCD_STAT_INTERRUPT);
|
||||
}
|
||||
|
||||
void GameBoy::timerHandle()
|
||||
{
|
||||
void GameBoy::timerHandle() {
|
||||
printf("timer interrupt");
|
||||
IME = 0;
|
||||
push(PC);
|
||||
@@ -57,8 +51,7 @@ void GameBoy::timerHandle()
|
||||
resetInterrupt(TIMER_INTERRUPT);
|
||||
}
|
||||
|
||||
void GameBoy::serialHandle()
|
||||
{
|
||||
void GameBoy::serialHandle() {
|
||||
printf("serial interrupt");
|
||||
IME = 0;
|
||||
push(PC);
|
||||
@@ -67,12 +60,11 @@ void GameBoy::serialHandle()
|
||||
resetInterrupt(SERIAL_INTERRUPT);
|
||||
}
|
||||
|
||||
void GameBoy::joypadHandle()
|
||||
{
|
||||
void GameBoy::joypadHandle() {
|
||||
printf("joypad interrupt");
|
||||
IME = 0;
|
||||
push(PC);
|
||||
addCycles(16);
|
||||
PC = 0x60;
|
||||
resetInterrupt(JOYPAD_INTERRUPT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <string>
|
||||
#include "gameboy.hpp"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int main(int argc, char** argv) {
|
||||
auto* gb = new GameBoy();
|
||||
gb->SDL2setup();
|
||||
gb->start("../bootrom.bin", "../roms/DrMario.gb");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
void GameBoy::ppuUpdate() {
|
||||
//test HBlank
|
||||
//test for HBlank
|
||||
checkPPUMode();
|
||||
|
||||
if (cyclesToStayInHblank != -1) {
|
||||
@@ -53,7 +53,8 @@ void GameBoy::ppuUpdate() {
|
||||
// bug on DMG models triggers a STAT interrupt anytime the STAT register is written
|
||||
// Road Rage and Zerd no Denetsu rely on this
|
||||
(*STAT) |= (1 << 2);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
(*STAT) &= ~(1 << 2);
|
||||
}
|
||||
|
||||
@@ -96,7 +97,8 @@ void GameBoy::checkPPUMode() {
|
||||
setPPUMode(PPUMode::mode3);
|
||||
else
|
||||
setPPUMode(PPUMode::mode0);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// VBlank Period
|
||||
setPPUMode(PPUMode::mode1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user