aboutsummaryrefslogtreecommitdiffstats
path: root/src/gb.h
blob: 74396159aef7892762223f97dfb71467c2fa02c6 (plain) (blame)
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
#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_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