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
128
129
130
131
132
133
134
135
136
|
#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;
// APU registers 0xFF10-0xFF3F. We don't synthesize sound, but games read
// these back (e.g. the music fade-out decrements NR50/rAUDVOL until it
// reads 0), so they must behave as real storage, not a 0xFF black hole.
u8 apu[0x30];
// 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_no_eof; // if true, serial-RX EOF stays pending (don't send $04)
bool serial_log; // print serial output to stderr (blargg tests)
int serial_out_fd; // if >=0, transmitted serial bytes written here (stdout)
int serial_in_fd; // if >=0, received serial bytes read here (stdin), else 0xFF
bool poweroff; // set by the $ED opcode: request a clean emulator exit
};
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
|