prepare for game boy platform

using SameBoy code (MIT License)
This commit is contained in:
tildearrow 2021-05-22 21:10:25 -05:00
parent 26463fd8d9
commit 3c20bffd46
6 changed files with 2352 additions and 0 deletions

View file

@ -26,6 +26,7 @@ src/log.cpp
extern/Nuked-OPN2/ym3438.c extern/Nuked-OPN2/ym3438.c
src/engine/platform/sound/sn76496.cpp src/engine/platform/sound/sn76496.cpp
src/engine/platform/sound/gb/apu.c
src/engine/blip_buf.c src/engine/blip_buf.c
src/engine/safeReader.cpp src/engine/safeReader.cpp

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015-2021 Lior Halphon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,186 @@
#ifndef apu_h
#define apu_h
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "gb_struct_def.h"
/* Speed = 1 / Length (in seconds) */
#define DAC_DECAY_SPEED 20000
#define DAC_ATTACK_SPEED 20000
/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */
#ifdef WIIU
/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/
#define MAX_CH_AMP (0xFF0 / 2)
#else
#define MAX_CH_AMP 0xFF0
#endif
#define CH_STEP (MAX_CH_AMP/0xF/8)
/* APU ticks are 2MHz, triggered by an internal APU clock. */
typedef struct
{
int16_t left;
int16_t right;
} GB_sample_t;
typedef struct
{
double left;
double right;
} GB_double_sample_t;
enum GB_CHANNELS {
GB_SQUARE_1,
GB_SQUARE_2,
GB_WAVE,
GB_NOISE,
GB_N_CHANNELS
};
typedef struct
{
bool locked:1;
bool clock:1; // Represents FOSY on channel 4
unsigned padding:6;
} GB_envelope_clock_t;
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
typedef struct
{
bool global_enable;
uint8_t apu_cycles;
uint8_t samples[GB_N_CHANNELS];
bool is_active[GB_N_CHANNELS];
uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided
// once more to generate 128Hz and 64Hz clocks
uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide
// need to divide the signal.
uint8_t square_sweep_countdown; // In 128Hz
uint8_t square_sweep_calculate_countdown; // In 2 MHz
uint16_t sweep_length_addend;
uint16_t shadow_sweep_sample_length;
bool unshifted_sweep;
bool enable_zombie_calculate_stepping;
struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NRX2
uint8_t volume_countdown; // Reloaded from NRX2
uint8_t current_sample_index; /* For save state compatibility,
highest bit is reused (See NR14/NR24's
write code)*/
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
bool length_enabled; // NRX4
} square_channels[2];
struct {
bool enable; // NR30
uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks
uint8_t shift; // NR32
uint16_t sample_length; // NR33, NR34, in APU ticks
bool length_enabled; // NR34
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint8_t current_sample_index;
uint8_t current_sample; // Current sample before shifting.
int8_t wave_form[32];
bool wave_form_just_read;
} wave_channel;
struct {
uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NR42
uint8_t volume_countdown; // Reloaded from NR42
uint16_t lfsr;
bool narrow;
uint8_t counter_countdown; // Counts from 0-7 to 0 to tick counter (Scaled from 512KHz to 2MHz)
uint8_t __padding;
uint16_t counter; // A bit from this 14-bit register ticks LFSR
bool length_enabled; // NR44
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
// 1MHz. This variable keeps track of the alignment.
} noise_channel;
#define GB_SKIP_DIV_EVENT_INACTIVE 0
#define GB_SKIP_DIV_EVENT_SKIPPED 1
#define GB_SKIP_DIV_EVENT_SKIP 2
uint8_t skip_div_event;
bool current_lfsr_sample;
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
uint8_t channel_1_restart_hold;
int8_t channel_4_delta;
bool channel_4_countdown_reloaded;
uint8_t channel_4_dmg_delayed_start;
uint16_t channel1_completed_addend;
GB_envelope_clock_t square_envelope_clock[2];
GB_envelope_clock_t noise_envelope_clock;
} GB_apu_t;
typedef enum {
GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset
GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware
GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform
GB_HIGHPASS_MAX
} GB_highpass_mode_t;
typedef struct {
unsigned sample_rate;
double sample_cycles; // In 8 MHz units
double cycles_per_sample;
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
unsigned cycles_since_render;
unsigned last_update[GB_N_CHANNELS];
GB_sample_t current_sample[GB_N_CHANNELS];
GB_sample_t summed_samples[GB_N_CHANNELS];
double dac_discharge[GB_N_CHANNELS];
GB_highpass_mode_t highpass_mode;
double highpass_rate;
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
double interference_volume;
double interference_highpass;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
void GB_set_interference_volume(GB_gameboy_t *gb, double volume);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
void GB_apu_div_event(GB_gameboy_t *gb);
void GB_apu_div_secondary_event(GB_gameboy_t *gb);
void GB_apu_init(GB_gameboy_t *gb);
void GB_apu_run(GB_gameboy_t *gb);
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
void GB_borrow_sgb_border(GB_gameboy_t *gb);
#endif /* apu_h */

View file

@ -0,0 +1,677 @@
#ifndef GB_h
#define GB_h
#define typeof __typeof__
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include "gb_struct_def.h"
#include "apu.h"
#define GB_STRUCT_VERSION 13
#define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100
#define GB_MODEL_CGB_FAMILY 0x200
#define GB_MODEL_PAL_BIT 0x40
#define GB_MODEL_NO_SFC_BIT 0x80
#define GB_MODEL_PAL_BIT_OLD 0x1000
#define GB_MODEL_NO_SFC_BIT_OLD 0x2000
#if __clang__
#define unrolled _Pragma("unroll")
#elif __GNUC__ >= 8
#define unrolled _Pragma("GCC unroll 8")
#else
#define unrolled
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define GB_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define GB_LITTLE_ENDIAN
#else
#error Unable to detect endianess
#endif
#ifdef GB_BIG_ENDIAN
#define LE16(x) __builtin_bswap16(x)
#define LE32(x) __builtin_bswap32(x)
#define LE64(x) __builtin_bswap64(x)
#define BE16(x) (x)
#define BE32(x) (x)
#define BE64(x) (x)
#else
#define LE16(x) (x)
#define LE32(x) (x)
#define LE64(x) (x)
#define BE16(x) __builtin_bswap16(x)
#define BE32(x) __builtin_bswap32(x)
#define BE64(x) __builtin_bswap64(x)
#endif
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
#define __builtin_bswap16(x) ({ typeof(x) _x = (x); _x >> 8 | _x << 8; })
#endif
typedef struct {
struct {
uint8_t r, g, b;
} colors[5];
} GB_palette_t;
extern const GB_palette_t GB_PALETTE_GREY;
extern const GB_palette_t GB_PALETTE_DMG;
extern const GB_palette_t GB_PALETTE_MGB;
extern const GB_palette_t GB_PALETTE_GBL;
typedef union {
struct {
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t days;
uint8_t high;
};
struct {
uint8_t seconds;
uint8_t minutes;
uint8_t hours:5;
uint8_t weekday:3;
uint8_t weeks;
} tpp1;
uint8_t data[5];
} GB_rtc_time_t;
typedef struct __attribute__((packed)) {
uint64_t last_rtc_second;
uint16_t minutes;
uint16_t days;
uint16_t alarm_minutes, alarm_days;
uint8_t alarm_enabled;
} GB_huc3_rtc_time_t;
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
GB_MODEL_DMG_B = 0x002,
// GB_MODEL_DMG_C = 0x003,
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
// GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
// GB_MODEL_CGB_0 = 0x200,
// GB_MODEL_CGB_A = 0x201,
// GB_MODEL_CGB_B = 0x202,
GB_MODEL_CGB_C = 0x203,
// GB_MODEL_CGB_D = 0x204,
GB_MODEL_CGB_E = 0x205,
GB_MODEL_AGB = 0x206,
} GB_model_t;
enum {
GB_REGISTER_AF,
GB_REGISTER_BC,
GB_REGISTER_DE,
GB_REGISTER_HL,
GB_REGISTER_SP,
GB_REGISTERS_16_BIT /* Count */
};
/* Todo: Actually use these! */
enum {
GB_CARRY_FLAG = 16,
GB_HALF_CARRY_FLAG = 32,
GB_SUBTRACT_FLAG = 64,
GB_ZERO_FLAG = 128,
};
typedef enum {
GB_BORDER_SGB,
GB_BORDER_NEVER,
GB_BORDER_ALWAYS,
} GB_border_mode_t;
enum {
/* Joypad and Serial */
GB_IO_JOYP = 0x00, // Joypad (R/W)
GB_IO_SB = 0x01, // Serial transfer data (R/W)
GB_IO_SC = 0x02, // Serial Transfer Control (R/W)
/* Missing */
/* Timers */
GB_IO_DIV = 0x04, // Divider Register (R/W)
GB_IO_TIMA = 0x05, // Timer counter (R/W)
GB_IO_TMA = 0x06, // Timer Modulo (R/W)
GB_IO_TAC = 0x07, // Timer Control (R/W)
/* Missing */
GB_IO_IF = 0x0f, // Interrupt Flag (R/W)
/* Sound */
GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W)
GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W)
GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W)
GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only)
GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W)
/* NR20 does not exist */
GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W)
GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W)
GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W)
GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W)
GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W)
GB_IO_NR31 = 0x1b, // Channel 3 Sound Length
GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W)
GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W)
GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W)
/* NR40 does not exist */
GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W)
GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W)
GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W)
GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W)
GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W)
GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W)
GB_IO_NR52 = 0x26, // Sound on/off
/* Missing */
GB_IO_WAV_START = 0x30, // Wave pattern start
GB_IO_WAV_END = 0x3f, // Wave pattern end
/* Graphics */
GB_IO_LCDC = 0x40, // LCD Control (R/W)
GB_IO_STAT = 0x41, // LCDC Status (R/W)
GB_IO_SCY = 0x42, // Scroll Y (R/W)
GB_IO_SCX = 0x43, // Scroll X (R/W)
GB_IO_LY = 0x44, // LCDC Y-Coordinate (R)
GB_IO_LYC = 0x45, // LY Compare (R/W)
GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W)
GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only
GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
GB_IO_WY = 0x4a, // Window Y Position (R/W)
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
// Has some undocumented compatibility flags written at boot.
// Unfortunately it is not readable or writable after boot has finished, so research of this
// register is quite limited. The value written to this register, however, can be controlled
// in some cases.
GB_IO_KEY0 = 0x4c,
/* General CGB features */
GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch
/* Missing */
GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank
GB_IO_BANK = 0x50, // Write to disable the BIOS mapping
/* CGB DMA */
GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High
GB_IO_HDMA2 = 0x52, // CGB Mode Only - New DMA Source, Low
GB_IO_HDMA3 = 0x53, // CGB Mode Only - New DMA Destination, High
GB_IO_HDMA4 = 0x54, // CGB Mode Only - New DMA Destination, Low
GB_IO_HDMA5 = 0x55, // CGB Mode Only - New DMA Length/Mode/Start
/* IR */
GB_IO_RP = 0x56, // CGB Mode Only - Infrared Communications Port
/* Missing */
/* CGB Paletts */
GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index
GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data
GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index
GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data
GB_IO_OPRI = 0x6c, // Affects object priority (X based or index based)
/* Missing */
GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank
GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write)
GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write)
GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only
GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write)
GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes
GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes
GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only
};
typedef enum {
GB_LOG_BOLD = 1,
GB_LOG_DASHED_UNDERLINE = 2,
GB_LOG_UNDERLINE = 4,
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
} GB_log_attributes;
typedef enum {
GB_BOOT_ROM_DMG0,
GB_BOOT_ROM_DMG,
GB_BOOT_ROM_MGB,
GB_BOOT_ROM_SGB,
GB_BOOT_ROM_SGB2,
GB_BOOT_ROM_CGB0,
GB_BOOT_ROM_CGB,
GB_BOOT_ROM_AGB,
} GB_boot_rom_t;
typedef enum {
GB_RTC_MODE_SYNC_TO_HOST,
GB_RTC_MODE_ACCURATE,
} GB_rtc_mode_t;
#define LCDC_PERIOD 70224
#define CPU_FREQUENCY 0x400000
#define SGB_NTSC_FREQUENCY (21477272 / 5)
#define SGB_PAL_FREQUENCY (21281370 / 5)
#define DIV_CYCLES (0x100)
#define INTERNAL_DIV_CYCLES (0x40000)
#if !defined(MIN)
#define MIN(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
#endif
#if !defined(MAX)
#define MAX(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
#endif
typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb);
typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on);
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude);
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value);
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type);
struct GB_breakpoint_s;
struct GB_watchpoint_s;
typedef struct {
uint8_t pixel; // Color, 0-3
uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
uint8_t priority; // Sprite priority 0 in DMG, OAM index in CGB
bool bg_priority; // For sprite FIFO the BG priority bit. For the BG FIFO  the CGB attributes priority bit
} GB_fifo_item_t;
#define GB_FIFO_LENGTH 16
typedef struct {
GB_fifo_item_t fifo[GB_FIFO_LENGTH];
uint8_t read_end;
uint8_t write_end;
} GB_fifo_t;
typedef struct {
uint32_t magic;
uint8_t track_count;
uint8_t first_track;
uint16_t load_address;
uint16_t init_address;
uint16_t play_address;
uint16_t sp;
uint8_t TMA;
uint8_t TAC;
char title[32];
char author[32];
char copyright[32];
} GB_gbs_header_t;
typedef struct {
uint8_t track_count;
uint8_t first_track;
char title[33];
char author[33];
char copyright[33];
} GB_gbs_info_t;
/* When state saving, each section is dumped independently of other sections.
This allows adding data to the end of the section without worrying about future compatibility.
Some other changes might be "safe" as well.
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
bit platforms. */
struct GB_gameboy_s {
/* Registers */
uint16_t pc;
union {
uint16_t registers[GB_REGISTERS_16_BIT];
struct {
uint16_t af,
bc,
de,
hl,
sp;
};
struct {
#ifdef GB_BIG_ENDIAN
uint8_t a, f,
b, c,
d, e,
h, l;
#else
uint8_t f, a,
c, b,
e, d,
l, h;
#endif
};
};
uint8_t ime;
uint8_t interrupt_enable;
uint8_t cgb_ram_bank;
/* CPU and General Hardware Flags*/
GB_model_t model;
bool cgb_mode;
bool cgb_double_speed;
bool halted;
bool stopped;
bool boot_rom_finished;
bool ime_toggle; /* ei has delayed a effect.*/
bool halt_bug;
bool just_halted;
/* Misc state */
bool infrared_input;
uint8_t extra_oam[0xff00 - 0xfea0];
uint32_t ram_size; // Different between CGB and DMG
int32_t ir_sensor;
bool effective_ir_input;
/* DMA and HDMA */
bool hdma_on;
bool hdma_on_hblank;
uint8_t hdma_steps_left;
int16_t hdma_cycles; // in 8MHz units
uint16_t hdma_current_src, hdma_current_dest;
uint8_t dma_steps_left;
uint8_t dma_current_dest;
uint16_t dma_current_src;
int16_t dma_cycles;
bool is_dma_restarting;
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
bool hdma_starting;
/* MBC */
uint16_t mbc_rom_bank;
uint8_t mbc_ram_bank;
uint32_t mbc_ram_size;
bool mbc_ram_enable;
union {
struct {
uint8_t bank_low:5;
uint8_t bank_high:2;
uint8_t mode:1;
} mbc1;
struct {
uint8_t rom_bank:4;
} mbc2;
struct {
uint8_t rom_bank:8;
uint8_t ram_bank:3;
} mbc3;
struct {
uint8_t rom_bank_low;
uint8_t rom_bank_high:1;
uint8_t ram_bank:4;
} mbc5;
struct {
uint8_t bank_low:6;
uint8_t bank_high:3;
bool mode:1;
bool ir_mode:1;
} huc1;
struct {
uint8_t rom_bank:7;
uint8_t padding:1;
uint8_t ram_bank:4;
} huc3;
};
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
uint8_t rumble_strength;
bool cart_ir;
// TODO: move to huc3/mbc3/tpp1 struct when breaking save compat
uint8_t huc3_mode;
uint8_t huc3_access_index;
uint16_t huc3_minutes, huc3_days;
uint16_t huc3_alarm_minutes, huc3_alarm_days;
bool huc3_alarm_enabled;
uint8_t huc3_read;
uint8_t huc3_access_flags;
bool mbc3_rtc_mapped;
uint16_t tpp1_rom_bank;
uint8_t tpp1_ram_bank;
uint8_t tpp1_mode;
/* HRAM and HW Registers */
uint8_t hram[0xFFFF - 0xFF80];
uint8_t io_registers[0x80];
// oops
uint16_t div_counter;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
uint16_t serial_cycles;
uint16_t serial_length;
uint8_t double_speed_alignment;
uint8_t serial_count;
GB_apu_t apu;
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
/* This data is reserved on reset and must come last in the struct */
/* ROM */
unsigned pending_cycles;
/* Various RAMs */
uint8_t *ram;
uint8_t *vram;
uint8_t *mbc_ram;
/* I/O */
uint32_t *screen;
uint32_t background_palettes_rgb[0x20];
uint32_t sprite_palettes_rgb[0x20];
const GB_palette_t *dmg_palette;
double light_temperature;
/* Timing */
uint64_t last_sync;
uint64_t cycles_since_last_sync; // In 8MHz units
GB_rtc_mode_t rtc_mode;
/* Audio */
GB_apu_output_t apu_output;
/*** Debugger ***/
volatile bool debug_stopped, debug_disable;
bool debug_fin_command, debug_next_command;
/* SLD (Todo: merge with backtrace) */
bool stack_leak_detection;
signed debug_call_depth;
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
uint16_t addr_for_call_depth[0x200];
/* Backtrace */
unsigned backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Ticks command */
uint64_t debugger_ticks;
/* Undo */
uint8_t *undo_state;
const char *undo_label;
/* Rewind */
#define GB_REWIND_FRAMES_PER_KEY 255
size_t rewind_buffer_length;
struct {
uint8_t *key_state;
uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY];
unsigned pos;
} *rewind_sequences; // lasts about 4 seconds
size_t rewind_pos;
/* Misc */
bool turbo;
bool turbo_dont_skip;
bool disable_rendering;
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
double clock_multiplier;
/* Temporary state */
bool wx_just_changed;
bool tile_sel_glitch;
};
#ifndef __printflike
/* Missing from Linux headers. */
#define __printflike(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#endif
void GB_init(GB_gameboy_t *gb, GB_model_t model);
bool GB_is_inited(GB_gameboy_t *gb);
bool GB_is_cgb(GB_gameboy_t *gb);
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd
GB_model_t GB_get_model(GB_gameboy_t *gb);
void GB_free(GB_gameboy_t *gb);
void GB_reset(GB_gameboy_t *gb);
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model);
/* Returns the time passed, in 8MHz ticks. */
uint8_t GB_run(GB_gameboy_t *gb);
/* Returns the time passed since the last frame, in nanoseconds */
uint64_t GB_run_frame(GB_gameboy_t *gb);
typedef enum {
GB_DIRECT_ACCESS_ROM,
GB_DIRECT_ACCESS_RAM,
GB_DIRECT_ACCESS_CART_RAM,
GB_DIRECT_ACCESS_VRAM,
GB_DIRECT_ACCESS_HRAM,
GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */
GB_DIRECT_ACCESS_BOOTROM,
GB_DIRECT_ACCESS_OAM,
GB_DIRECT_ACCESS_BGP,
GB_DIRECT_ACCESS_OBP,
GB_DIRECT_ACCESS_IE,
} GB_direct_access_t;
/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank
is returned at *bank, even if only a portion of the memory is banked. */
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank);
void *GB_get_user_data(GB_gameboy_t *gb);
void GB_set_user_data(GB_gameboy_t *gb, void *data);
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size);
int GB_load_rom(GB_gameboy_t *gb, const char *path);
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
int GB_load_isx(GB_gameboy_t *gb, const char *path);
int GB_load_gbs_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size, GB_gbs_info_t *info);
int GB_load_gbs(GB_gameboy_t *gb, const char *path, GB_gbs_info_t *info);
void GB_gbs_switch_track(GB_gameboy_t *gb, uint8_t track);
int GB_save_battery_size(GB_gameboy_t *gb);
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
int GB_save_battery(GB_gameboy_t *gb, const char *path);
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
void GB_load_battery(GB_gameboy_t *gb, const char *path);
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled);
void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode);
void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);
void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback);
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
/* Called when a new boot ROM is needed. The callback should call GB_load_boot_rom or GB_load_boot_rom_from_buffer */
void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t callback);
void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette);
/* These APIs are used when using internal clock */
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback);
/* These APIs are used when using external clock */
bool GB_serial_get_data_bit(GB_gameboy_t *gb);
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
void GB_disconnect_serial(GB_gameboy_t *gb);
/* For cartridges with an alarm clock */
unsigned GB_time_to_alarm(GB_gameboy_t *gb); // 0 if no alarm
/* RTC emulation mode */
void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode);
/* For integration with SFC/SNES emulators */
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback);
void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback);
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
uint32_t GB_get_unmultiplied_clock_rate(GB_gameboy_t *gb);
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
unsigned GB_get_screen_width(GB_gameboy_t *gb);
unsigned GB_get_screen_height(GB_gameboy_t *gb);
double GB_get_usual_frame_rate(GB_gameboy_t *gb);
unsigned GB_get_player_count(GB_gameboy_t *gb);
#endif /* GB_h */

View file

@ -0,0 +1,5 @@
#ifndef gb_struct_def_h
#define gb_struct_def_h
struct GB_gameboy_s;
typedef struct GB_gameboy_s GB_gameboy_t;
#endif