#ifndef GB_H #define GB_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include typedef enum { GB_CPU_STATE_RUN, GB_CPU_STATE_STOP, GB_CPU_STATE_HALT, GB_CPU_STATE_INVALID, GB_CPU_STATE_NOT_IMPLEMENTED, GB_CPU_STATE_LAST, } gb_cpu_state_t; #define GB_BTN_UP 1 #define GB_BTN_DOWN (1 << 1) #define GB_BTN_LEFT (1 << 2) #define GB_BTN_RIGHT (1 << 3) #define GB_BTN_A (1 << 4) #define GB_BTN_B (1 << 5) #define GB_BTN_SELECT (1 << 6) #define GB_BTN_START (1 << 7) // frame buffer size, in bytes #define GB_FB_SIZE (3 * 160 * 144) typedef struct gb_t_ gb_t; typedef struct { void (*on_set_rom_bank)(const gb_t *, const uint16_t); void (*on_set_ram_bank)(const gb_t *, const uint16_t); void (*on_vblank)(const gb_t *); void (*on_hblank)(const gb_t *); void (*on_set_cpu_state)(const gb_t *, const gb_cpu_state_t); void (*on_timer)(const gb_t *); void (*on_gpu_step)(const gb_t *); void (*on_gpu_set_mode)(const gb_t *, const uint8_t); void (*on_rst)(const gb_t *, const uint16_t); void (*on_mmu_rb)(const gb_t *, const uint16_t, const uint8_t); void (*on_mmu_wb)(const gb_t *, const uint16_t, const uint8_t); void (*on_oam_wb)(const gb_t *, const uint16_t, const uint8_t); void *cb_data; } gb_config_t; struct gb_t_ { const gb_config_t *config; struct { // _Bool in_bios; // FIXME: combine these? uint8_t ram[0x2000]; // working ram (8k) uint8_t eram[0x10000]; // external ram (banked, up to 64k) uint8_t vram[0x2000]; // vram (8k) uint8_t oam[0xA0]; // oam (160 bytes) uint8_t zram[0x80]; // zram (128 bytes) // rom (at least 32k) const uint8_t *rom; uint32_t rom_size; union { struct { // current rom bank (5 bits); see note about // rom_ram_mode below uint8_t rom_bank; // is external ram enabled? _Bool ram_enable; // ram bank // rom_ram_mode == 0: high two bits of rom bank // rom_ram_mode == 1: ram bank uint8_t ram_bank; // set interpretation of ram_bank _Bool rom_ram_mode; } mbc1; struct { // current rom bank (4 bits) uint8_t rom_bank; // is eram enabled? _Bool ram_enable; } mbc2; }; // interrupts // ie: interrupt enable (addr: 0xFFFF) // iv: interrupt vector (addr: 0xFF0F) // (gb-manual, p26) uint8_t ie; uint8_t iv; // buttons (addr: 0xFF00) // gb-manual, p23 uint8_t p1_mode; uint8_t btns; } mmu; struct { uint16_t clock; uint8_t mode, lcdc, stat, scy, scx, wy, wx, bgp, obp0, obp1, line, lyc; // frame buffer uint8_t fb[GB_FB_SIZE]; // frame counter uint32_t frame; struct { int16_t x, y; uint8_t tile; bool priority, y_flip, x_flip, palette; } oam_cache[40]; bool oam_dirty; } gpu; struct { uint16_t div; uint32_t tima; // NOTE: cannot be uint16_t because of 10-bit shift uint8_t tma, tac; } timer; struct { uint16_t rs[6 /* RW_LAST */]; uint32_t clock; /* FIXME: uint16_t? */ gb_cpu_state_t state; // interrupt master enable _Bool ime; } cpu; }; void gb_init( gb_t * const ctx, const gb_config_t * const config, const uint8_t *rom, const uint32_t rom_size ); void gb_set_buttons(gb_t * const, const uint8_t); void gb_frame(gb_t * const); void gb_step(gb_t * const); _Bool gb_disasm(gb_t * const, char * const, size_t * const); const uint8_t *gb_get_frame(const gb_t * const); #ifdef __cplusplus }; #endif /* __cplusplus */ #endif /* GB_H */