251 lines
6.2 KiB
C
251 lines
6.2 KiB
C
/*
|
|
* Copyright (C) 2017 Ben Smith
|
|
*
|
|
* This software may be modified and distributed under the terms
|
|
* of the MIT license. See the LICENSE file for details.
|
|
*/
|
|
#ifndef BINJGB_EMULATOR_H_
|
|
#define BINJGB_EMULATOR_H_
|
|
|
|
#include "common.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#define MAXIMUM_ROM_SIZE MEGABYTES(8)
|
|
#define MINIMUM_ROM_SIZE KILOBYTES(32)
|
|
|
|
#define SCREEN_WIDTH 160
|
|
#define SCREEN_HEIGHT 144
|
|
#define SCREEN_HEIGHT_WITH_VBLANK 154
|
|
#define SGB_SCREEN_WIDTH 256
|
|
#define SGB_SCREEN_HEIGHT 224
|
|
#define SGB_SCREEN_LEFT ((SGB_SCREEN_WIDTH - SCREEN_WIDTH) / 2)
|
|
#define SGB_SCREEN_RIGHT ((SGB_SCREEN_WIDTH + SCREEN_WIDTH) / 2)
|
|
#define SGB_SCREEN_TOP ((SGB_SCREEN_HEIGHT - SCREEN_HEIGHT) / 2)
|
|
#define SGB_SCREEN_BOTTOM ((SGB_SCREEN_HEIGHT + SCREEN_HEIGHT) / 2)
|
|
|
|
#define CPU_TICKS_PER_SECOND 4194304
|
|
#define APU_TICKS_PER_SECOND 2097152
|
|
#define PPU_LINE_TICKS 456
|
|
#define PPU_VBLANK_TICKS (PPU_LINE_TICKS * 10)
|
|
#define PPU_FRAME_TICKS (PPU_LINE_TICKS * SCREEN_HEIGHT_WITH_VBLANK)
|
|
|
|
#define SOUND_OUTPUT_COUNT 2
|
|
#define PALETTE_COLOR_COUNT 4
|
|
#define OBJ_COUNT 40
|
|
|
|
#define OBJ_X_OFFSET 8
|
|
#define OBJ_Y_OFFSET 16
|
|
|
|
#define BUILTIN_PALETTE_COUNT 84
|
|
|
|
#define RGBA_WHITE 0xffffffffu
|
|
#define RGBA_LIGHT_GRAY 0xffaaaaaau
|
|
#define RGBA_DARK_GRAY 0xff555555u
|
|
#define RGBA_BLACK 0xff000000u
|
|
|
|
#define MAX_APU_LOG_FRAME_WRITES 1024
|
|
|
|
typedef struct Emulator Emulator;
|
|
|
|
enum {
|
|
APU_CHANNEL1,
|
|
APU_CHANNEL2,
|
|
APU_CHANNEL3,
|
|
APU_CHANNEL4,
|
|
APU_CHANNEL_COUNT,
|
|
};
|
|
|
|
typedef void (*JoypadCallback)(struct JoypadButtons* joyp, void* user_data);
|
|
|
|
typedef struct JoypadCallbackInfo {
|
|
JoypadCallback callback;
|
|
void* user_data;
|
|
} JoypadCallbackInfo;
|
|
|
|
typedef RGBA FrameBuffer[SCREEN_WIDTH * SCREEN_HEIGHT];
|
|
typedef RGBA SgbFrameBuffer[SGB_SCREEN_WIDTH * SGB_SCREEN_HEIGHT];
|
|
|
|
typedef enum Color {
|
|
COLOR_WHITE = 0,
|
|
COLOR_LIGHT_GRAY = 1,
|
|
COLOR_DARK_GRAY = 2,
|
|
COLOR_BLACK = 3,
|
|
} Color;
|
|
|
|
typedef enum PaletteType {
|
|
PALETTE_TYPE_BGP,
|
|
PALETTE_TYPE_OBP0,
|
|
PALETTE_TYPE_OBP1,
|
|
PALETTE_TYPE_COUNT,
|
|
} PaletteType;
|
|
|
|
typedef enum {
|
|
TILE_DATA_8800_97FF = 0,
|
|
TILE_DATA_8000_8FFF = 1,
|
|
} TileDataSelect;
|
|
|
|
typedef enum TileMapSelect {
|
|
TILE_MAP_9800_9BFF = 0,
|
|
TILE_MAP_9C00_9FFF = 1,
|
|
} TileMapSelect;
|
|
|
|
typedef enum {
|
|
OBJ_SIZE_8X8 = 0,
|
|
OBJ_SIZE_8X16 = 1,
|
|
} ObjSize;
|
|
|
|
typedef enum ObjPriority {
|
|
OBJ_PRIORITY_ABOVE_BG = 0,
|
|
OBJ_PRIORITY_BEHIND_BG = 1,
|
|
} ObjPriority;
|
|
|
|
typedef enum {
|
|
TIMER_CLOCK_4096_HZ = 0,
|
|
TIMER_CLOCK_262144_HZ = 1,
|
|
TIMER_CLOCK_65536_HZ = 2,
|
|
TIMER_CLOCK_16384_HZ = 3,
|
|
} TimerClock;
|
|
|
|
/* TODO(binji): endianness */
|
|
#define REGISTER_PAIR(X, Y) \
|
|
union { \
|
|
struct { u8 Y; u8 X; }; \
|
|
u16 X##Y; \
|
|
}
|
|
|
|
typedef struct {
|
|
u8 A;
|
|
REGISTER_PAIR(B, C);
|
|
REGISTER_PAIR(D, E);
|
|
REGISTER_PAIR(H, L);
|
|
u16 SP;
|
|
u16 PC;
|
|
struct { Bool Z, N, H, C; } F;
|
|
} Registers;
|
|
|
|
typedef struct Obj {
|
|
u8 y;
|
|
u8 x;
|
|
u8 tile;
|
|
u8 byte3;
|
|
ObjPriority priority;
|
|
Bool yflip;
|
|
Bool xflip;
|
|
u8 palette;
|
|
u8 bank;
|
|
u8 cgb_palette;
|
|
} Obj;
|
|
|
|
typedef struct { Color color[PALETTE_COLOR_COUNT]; } Palette;
|
|
|
|
typedef struct PaletteRGBA {
|
|
RGBA color[PALETTE_COLOR_COUNT];
|
|
} PaletteRGBA;
|
|
|
|
typedef struct AudioBuffer {
|
|
u32 frequency; /* Sample frequency, as N samples per second */
|
|
u32 freq_counter; /* Used for resampling; [0..APU_TICKS_PER_SECOND). */
|
|
u32 divisor;
|
|
u32 frames; /* Number of frames to generate per call to emulator_run. */
|
|
u8* data; /* Unsigned 8-bit 2-channel samples @ |frequency| */
|
|
u8* end;
|
|
u8* position;
|
|
} AudioBuffer;
|
|
|
|
typedef enum CgbColorCurve {
|
|
CGB_COLOR_CURVE_NONE,
|
|
CGB_COLOR_CURVE_SAMEBOY_EMULATE_HARDWARE,
|
|
CGB_COLOR_CURVE_GAMBATTE,
|
|
} CgbColorCurve;
|
|
|
|
typedef struct EmulatorInit {
|
|
FileData rom;
|
|
int audio_frequency;
|
|
int audio_frames;
|
|
u32 random_seed;
|
|
u32 builtin_palette;
|
|
Bool force_dmg;
|
|
CgbColorCurve cgb_color_curve;
|
|
} EmulatorInit;
|
|
|
|
typedef struct EmulatorConfig {
|
|
Bool disable_sound[APU_CHANNEL_COUNT];
|
|
Bool disable_bg;
|
|
Bool disable_window;
|
|
Bool disable_obj;
|
|
Bool allow_simulataneous_dpad_opposites;
|
|
Bool log_apu_writes;
|
|
} EmulatorConfig;
|
|
|
|
typedef struct {
|
|
u8 addr;
|
|
u8 value;
|
|
} ApuWrite;
|
|
|
|
typedef struct {
|
|
ApuWrite writes[MAX_APU_LOG_FRAME_WRITES];
|
|
size_t write_count;
|
|
} ApuLog;
|
|
|
|
typedef u32 EmulatorEvent;
|
|
enum {
|
|
EMULATOR_EVENT_NEW_FRAME = 0x1,
|
|
EMULATOR_EVENT_AUDIO_BUFFER_FULL = 0x2,
|
|
EMULATOR_EVENT_UNTIL_TICKS = 0x4,
|
|
EMULATOR_EVENT_BREAKPOINT = 0x8,
|
|
EMULATOR_EVENT_INVALID_OPCODE = 0x10,
|
|
};
|
|
|
|
extern const size_t s_emulator_state_size;
|
|
|
|
Emulator* emulator_new(const EmulatorInit*);
|
|
void emulator_delete(Emulator*);
|
|
|
|
void emulator_set_joypad_buttons(Emulator*, JoypadButtons*);
|
|
void emulator_set_joypad_callback(Emulator*, JoypadCallback, void* user_data);
|
|
JoypadCallbackInfo emulator_get_joypad_callback(Emulator*);
|
|
void emulator_set_config(Emulator*, const EmulatorConfig*);
|
|
EmulatorConfig emulator_get_config(Emulator*);
|
|
FrameBuffer* emulator_get_frame_buffer(Emulator*);
|
|
SgbFrameBuffer* emulator_get_sgb_frame_buffer(Emulator*);
|
|
AudioBuffer* emulator_get_audio_buffer(Emulator*);
|
|
Ticks emulator_get_ticks(Emulator*);
|
|
u32 emulator_get_ppu_frame(Emulator*);
|
|
u32 audio_buffer_get_frames(AudioBuffer*);
|
|
void emulator_set_builtin_palette(Emulator*, u32 index);
|
|
void emulator_set_bw_palette(Emulator*, PaletteType, const PaletteRGBA*);
|
|
void emulator_set_all_bw_palettes(Emulator*, const PaletteRGBA*);
|
|
|
|
void emulator_ticks_to_time(Ticks, u32* day, u32* hr, u32* min, u32* sec,
|
|
u32* ms);
|
|
|
|
Bool emulator_was_ext_ram_updated(Emulator*);
|
|
|
|
void emulator_init_state_file_data(FileData*);
|
|
void emulator_init_ext_ram_file_data(Emulator*, FileData*);
|
|
Result emulator_read_state(Emulator*, const FileData*);
|
|
Result emulator_write_state(Emulator*, FileData*);
|
|
Result emulator_read_ext_ram(Emulator*, const FileData*);
|
|
Result emulator_write_ext_ram(Emulator*, FileData*);
|
|
|
|
#ifndef __wasm__
|
|
Result emulator_read_state_from_file(Emulator*, const char* filename);
|
|
Result emulator_write_state_to_file(Emulator*, const char* filename);
|
|
Result emulator_read_ext_ram_from_file(Emulator*, const char* filename);
|
|
Result emulator_write_ext_ram_to_file(Emulator*, const char* filename);
|
|
#endif
|
|
|
|
EmulatorEvent emulator_step(Emulator*);
|
|
EmulatorEvent emulator_run_until(Emulator*, Ticks until_ticks);
|
|
|
|
ApuLog* emulator_get_apu_log(Emulator*);
|
|
void emulator_reset_apu_log(Emulator*);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* BINJGB_EMULATOR_H_ */
|