1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
#ifndef GBC_GB_H
#define GBC_GB_H
#include "types.h"
#include "cart.h"
// Interrupt bit flags (IE/IF at 0xFFFF / 0xFF0F)
enum {
INT_VBLANK = 0x01,
INT_LCDSTAT= 0x02,
INT_TIMER = 0x04,
INT_SERIAL = 0x08,
INT_JOYPAD = 0x10,
};
// Joypad button bits (as used by our input state; 1 = pressed)
enum {
BTN_A = 0x01,
BTN_B = 0x02,
BTN_SELECT = 0x04,
BTN_START = 0x08,
BTN_RIGHT = 0x10,
BTN_LEFT = 0x20,
BTN_UP = 0x40,
BTN_DOWN = 0x80,
};
#define SCREEN_W 160
#define SCREEN_H 144
typedef struct GB GB;
// ---- CPU (Sharp SM83) ----
typedef struct {
union { struct { u8 f, a; }; u16 af; };
union { struct { u8 c, b; }; u16 bc; };
union { struct { u8 e, d; }; u16 de; };
union { struct { u8 l, h; }; u16 hl; };
u16 sp, pc;
bool ime; // interrupt master enable
bool ime_pending; // EI delayed enable
bool halted;
bool halt_bug;
bool stopped;
} CPU;
// ---- PPU ----
typedef struct {
u8 vram[0x4000]; // 2 banks (CGB)
u8 oam[0xA0];
u8 vram_bank; // CGB
// registers
u8 lcdc, stat, scy, scx, ly, lyc, wy, wx, bgp, obp0, obp1;
u8 vbk; // vram bank select reg (bit0)
// CGB palettes
u8 bcps, ocps;
u8 bg_pal[64]; // 8 palettes * 4 colors * 2 bytes
u8 obj_pal[64];
int mode; // 0 hblank,1 vblank,2 oam,3 draw
int dots; // dot counter within current line
bool frame_ready;
u8 wly; // internal window line counter
// framebuffer: RGB888
u8 fb[SCREEN_H][SCREEN_W][3];
} PPU;
// ---- Timer ----
typedef struct {
u16 div; // internal 16-bit counter; DIV is upper 8 bits
u8 tima, tma, tac;
bool overflow_pending;
int reload_delay;
} Timer;
struct GB {
CPU cpu;
PPU ppu;
Timer timer;
Cart cart;
u8 wram[0x8000]; // 8 banks of 4KB (CGB); DMG uses 2
u8 hram[0x7F];
u8 wram_bank; // svbk (1-7)
u8 ie; // 0xFFFF
u8 iff; // 0xFF0F interrupt flags
u8 joyp_sel; // 0xFF00 selection bits
u8 buttons; // our pressed state (1=pressed)
// serial
u8 sb, sc;
// DMA
u8 dma;
// CGB
bool cgb_mode; // running in CGB mode
u8 key1; // speed switch
bool double_speed;
// HDMA / GDMA (CGB)
u16 hdma_src, hdma_dst;
u8 hdma_len;
bool hdma_active;
u64 cycles; // total T-cycles elapsed
bool serial_log; // print serial output to stderr (blargg tests)
};
void gb_init(GB *gb);
void gb_reset(GB *gb);
// Run one full CPU instruction (advancing all subsystems). Returns T-cycles.
int gb_step(GB *gb);
void gb_run_frame(GB *gb);
// bus access
u8 gb_read(GB *gb, u16 addr);
void gb_write(GB *gb, u16 addr, u8 val);
void gb_tick(GB *gb, int tcycles); // advance ppu/timer/dma by t-cycles
void gb_request_interrupt(GB *gb, u8 flag);
#endif
|