aboutsummaryrefslogtreecommitdiffstats
path: root/src/gb.h
blob: 806dabada97c58780e957cd3bbdc9f9b21c64b23 (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
#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