Skip to content

Commit 607ce4f

Browse files
committed
feat: make boot rom specifiable via cli arg
1 parent 5a1e837 commit 607ce4f

File tree

3 files changed

+77
-42
lines changed

3 files changed

+77
-42
lines changed

src/game_boy.c

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ static void GameBoy_simulate_boot(GameBoy* const gb) {
7777
gb->boot_rom_enable = false;
7878
}
7979

80-
GameBoy GameBoy_new(const uint8_t* const boot_rom, uint8_t* const rom, const size_t rom_len) {
80+
GameBoy GameBoy_new(uint8_t* const boot_rom, uint8_t* const rom, const size_t rom_len) {
8181
GameBoy gb = (GameBoy){
8282
.cpu = Cpu_new(),
8383
.boot_rom = boot_rom,
@@ -114,6 +114,9 @@ GameBoy GameBoy_new(const uint8_t* const boot_rom, uint8_t* const rom, const siz
114114

115115
void GameBoy_destroy(GameBoy* const restrict gb) {
116116
free(gb->rom);
117+
if (gb->boot_rom != nullptr) {
118+
free(gb->boot_rom);
119+
}
117120
gb->rom = nullptr;
118121
gb->rom_len = 0;
119122
}
@@ -126,9 +129,8 @@ uint8_t GameBoy_read_io(const GameBoy* const restrict gb, const uint16_t addr) {
126129

127130
if (addr == 0xFF01) {
128131
// FF01 (serial transfer data)
129-
// TODO: implement properly
132+
// TODO: implement serial transfer
130133
return 0xFF;
131-
// return gb->sb;
132134
}
133135

134136
if (addr == 0xFF02) {
@@ -138,22 +140,16 @@ uint8_t GameBoy_read_io(const GameBoy* const restrict gb, const uint16_t addr) {
138140

139141
if (addr >= 0xFF04 && addr <= 0xFF07) {
140142
// FF04-FF07 (timer and divider)
143+
144+
// clang-format off
141145
switch (addr) {
142-
case 0xFF04:
143-
return gb->div;
144-
break;
145-
case 0xFF05:
146-
return gb->tima;
147-
break;
148-
case 0xFF06:
149-
return gb->tma;
150-
break;
151-
case 0xFF07:
152-
return gb->tac;
153-
break;
154-
default:
155-
BAIL("Unexpected I/O timer and divider read ($%04X)", addr);
146+
case 0xFF04: return gb->div;
147+
case 0xFF05: return gb->tima;
148+
case 0xFF06: return gb->tma;
149+
case 0xFF07: return gb->tac;
150+
default: BAIL("Unexpected I/O timer and divider read ($%04X)", addr);
156151
}
152+
// clang-format on
157153
}
158154

159155
if (addr == 0xFF0F) {
@@ -193,13 +189,12 @@ uint8_t GameBoy_read_io(const GameBoy* const restrict gb, const uint16_t addr) {
193189
}
194190

195191
if (addr == 0xFF4D) {
196-
// FF4D (CGB registers, unimplemented as of now)
192+
// FF4D (CGB registers, CGB-only)
197193
return 0xFF;
198194
}
199195

200196
if (addr == 0xFF4F) {
201-
// FF4F
202-
BAIL("TODO: I/O VRAM bank select read ($%04X)", addr);
197+
// FF4F (VRAM bank select, CGB-only)
203198
}
204199

205200
if (addr == 0xFF50) {
@@ -208,18 +203,18 @@ uint8_t GameBoy_read_io(const GameBoy* const restrict gb, const uint16_t addr) {
208203
}
209204

210205
if (addr >= 0xFF51 && addr <= 0xFF55) {
211-
// FF51-FF55 (VRAM DMA)
212-
BAIL("TODO: I/O VRAM DMA read ($%04X)", addr);
206+
// FF51-FF55 (VRAM DMA, CGB-only)
207+
return 0xFF;
213208
}
214209

215210
if (addr >= 0xFF68 && addr <= 0xFF6B) {
216-
// FF68-FF6B (palettes)
217-
BAIL("TODO: I/O palettes read ($%04X)", addr);
211+
// FF68-FF6B (LCD color palettes, CGB-only)
212+
return 0xFF;
218213
}
219214

220215
if (addr == 0xFF70) {
221-
// FF70 (WRAM bank select)
222-
BAIL("TODO: I/O WRAM bank select read ($%04X)", addr);
216+
// FF70 (WRAM bank select, CGB-only)
217+
return 0xFF;
223218
}
224219

225220
BAIL("Unexpected I/O read (addr = $%04X)", addr);
@@ -358,14 +353,11 @@ void GameBoy_write_io(GameBoy* const restrict gb, const uint16_t addr, const uin
358353
if (value != 0)
359354
gb->boot_rom_enable = false;
360355
} else if (addr >= 0xFF51 && addr <= 0xFF55) {
361-
// FF51-FF55 (VRAM DMA)
362-
BAIL("I/O VRAM DMA write ($%04X, $%02X)", addr, value);
356+
// FF51-FF55 (VRAM DMA, CGB-only)
363357
} else if (addr >= 0xFF68 && addr <= 0xFF6B) {
364-
// FF68-FF6B (palettes)
365-
BAIL("I/O palettes write ($%04X, $%02X)", addr, value);
358+
// FF68-FF6B (LCD color palettes, CGB-only)
366359
} else if (addr == 0xFF70) {
367-
// FF70 (WRAM bank select)
368-
BAIL("I/O WRAM bank select write ($%04X, $%02X)", addr, value);
360+
// FF70 (WRAM bank select, CGB-only)
369361
} else if (addr == 0xFF7F) {
370362
// Tetris tries to write here. Probably a no-op.
371363
} else {

src/game_boy.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ typedef struct GameBoy {
7676
uint8_t vram[0x2000];
7777
uint8_t hram[0x7F];
7878
uint8_t oam[0xA0];
79-
const uint8_t* boot_rom;
79+
uint8_t* boot_rom;
8080
uint8_t* rom;
8181
size_t rom_len;
8282
bool boot_rom_enable;
@@ -102,7 +102,7 @@ typedef struct GameBoy {
102102
uint8_t joyp;
103103
} GameBoy;
104104

105-
[[nodiscard]] GameBoy GameBoy_new(const uint8_t* boot_rom, uint8_t* rom, size_t rom_len);
105+
[[nodiscard]] GameBoy GameBoy_new(uint8_t* boot_rom, uint8_t* rom, size_t rom_len);
106106

107107
void GameBoy_destroy(GameBoy* restrict gb);
108108

src/main.c

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
#include "SDL3/SDL_iostream.h"
33
#include "SDL3/SDL_pixels.h"
44
#include "SDL3/SDL_render.h"
5+
#include "SDL3/SDL_stdinc.h"
56
#include "SDL3/SDL_surface.h"
67
#include "SDL3/SDL_video.h"
7-
#include "control.h"
8+
#include "argparse.h"
89
#include "data.h"
910
#include "frontend.h"
1011
#include "game_boy.h"
@@ -16,21 +17,40 @@
1617
#include <stdio.h>
1718
#include <stdlib.h>
1819

19-
int main(const int argc, const char* const argv[]) {
20+
static constexpr int WINDOW_WIDTH_INITIAL = GB_LCD_WIDTH * 4;
21+
static constexpr int WINDOW_HEIGHT_INITIAL = GB_LCD_HEIGHT * 4;
22+
23+
static const char* const usages[] = {
24+
"gemu [options] [--] <path-to-rom>",
25+
nullptr,
26+
};
27+
28+
int main(int argc, const char* argv[]) {
2029
logger_init(
2130
LogCategory_All & ~LogCategory_Memory & ~LogCategory_Instruction & ~LogCategory_Interrupt
2231
);
2332

24-
static constexpr int WINDOW_WIDTH_INITIAL = GB_LCD_WIDTH * 4;
25-
static constexpr int WINDOW_HEIGHT_INITIAL = GB_LCD_HEIGHT * 4;
33+
const char* boot_rom_path = nullptr;
34+
35+
struct argparse_option options[] = {
36+
OPT_HELP(),
37+
OPT_STRING('b', "boot-rom", (void*)&boot_rom_path, "path to boot ROM", nullptr, 0, 0),
38+
OPT_END(),
39+
};
40+
41+
struct argparse argparse;
42+
argparse_init(&argparse, options, usages, 0);
43+
argparse_describe(&argparse, "A Game Boy emulator written in C.", nullptr);
44+
45+
argc = argparse_parse(&argparse, argc, argv);
2646

27-
if (argc < 2) {
28-
log_error(LogCategory_Keep, "No ROM filename specified.");
47+
if (argc < 1) {
48+
argparse_usage(&argparse);
2949
return 1;
3050
}
3151

3252
size_t rom_len = 0;
33-
uint8_t* const rom = SDL_LoadFile(argv[1], &rom_len);
53+
uint8_t* const rom = SDL_LoadFile(argv[0], &rom_len);
3454

3555
if (rom == nullptr) {
3656
log_error(LogCategory_Keep, "Could not read ROM file.");
@@ -67,8 +87,31 @@ int main(const int argc, const char* const argv[]) {
6787

6888
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
6989

90+
uint8_t* boot_rom = nullptr;
91+
92+
if (boot_rom_path != nullptr) {
93+
size_t boot_rom_len = 0;
94+
boot_rom = SDL_LoadFile(boot_rom_path, &boot_rom_len);
95+
96+
if (boot_rom == nullptr) {
97+
log_error(LogCategory_Keep, "Could not read boot ROM file.");
98+
return 1;
99+
}
100+
101+
if (boot_rom_len != GB_BOOT_ROM_LEN_EXPECTED) {
102+
log_error(
103+
LogCategory_Keep,
104+
"Boot ROM must be exactly %zu bytes long (was %zu)",
105+
GB_BOOT_ROM_LEN_EXPECTED,
106+
boot_rom_len
107+
);
108+
SDL_free(boot_rom);
109+
return 1;
110+
}
111+
}
112+
70113
State state = (State){
71-
.gb = GameBoy_new(nullptr, rom, rom_len),
114+
.gb = GameBoy_new(boot_rom, rom, rom_len),
72115
.window_width = WINDOW_WIDTH_INITIAL,
73116
.window_height = WINDOW_HEIGHT_INITIAL,
74117
.cycle_accumulator = 0.0,

0 commit comments

Comments
 (0)