Stabilizing API: New joypad, debugger and reset APIs; internal APIs and direct struct access are no longer available without defining GB_INTERNAL. The SDL port uses the new “public” APIs, as well as most of the non-debug Cocoa code.

This commit is contained in:
Lior Halphon 2017-04-17 20:16:17 +03:00
parent 0b1e2784cd
commit a925ef130d
30 changed files with 399 additions and 287 deletions

View File

@ -1,16 +1,13 @@
#define GB_INTERNAL // Todo: several debugging functions access the GB struct directly
#include <AVFoundation/AVFoundation.h>
#include <CoreAudio/CoreAudio.h>
#include "GBAudioClient.h"
#include "Document.h"
#include "AppDelegate.h"
#include "gb.h"
#include "debugger.h"
#include "memory.h"
#include "camera.h"
#include "display.h"
#include "HexFiend/HexFiend.h"
#include "GBMemoryByteArray.h"
#include "GBWarningPopover.h"
#include "gb.h"
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
/* Todo: Split into category files! This is so messy!!! */
@ -61,25 +58,25 @@
static void vblank(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)(gb->user_data);
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self vblank];
}
static void consoleLog(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
{
Document *self = (__bridge Document *)(gb->user_data);
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self log:string withAttributes: attributes];
}
static char *consoleInput(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)(gb->user_data);
Document *self = (__bridge Document *)GB_get_user_data(gb);
return strdup([self getDebuggerInput]);
}
static char *asyncConsoleInput(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)(gb->user_data);
Document *self = (__bridge Document *)GB_get_user_data(gb);
const char *ret = [self getAsyncDebuggerInput];
return ret? strdup(ret) : NULL;
}
@ -91,20 +88,20 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
static void cameraRequestUpdate(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)(gb->user_data);
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self cameraRequestUpdate];
}
static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
{
Document *self = (__bridge Document *)(gb->user_data);
Document *self = (__bridge Document *)GB_get_user_data(gb);
return [self cameraGetPixelAtX:x andY:y];
}
static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure)
{
Document *self = (__bridge Document *)(gb->user_data);
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure];
}
@ -138,12 +135,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
GB_init_cgb(&gb);
[self initCommon];
GB_load_boot_rom(&gb, [[[NSBundle mainBundle] pathForResource:@"cgb_boot" ofType:@"bin"] UTF8String]);
}
- (void) initCommon
{
gb.user_data = (__bridge void *)(self);
GB_set_user_data(&gb, (__bridge void *)(self));
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
@ -151,13 +147,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
GB_set_rgb_encode_callback(&gb, rgbEncode);
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
NSString *rom_warnings = [self captureOutputForBlock:^{
[self loadROM];
}];
if (rom_warnings && !rom_warning_issued) {
rom_warning_issued = true;
[GBWarningPopover popoverWithContents:rom_warnings onWindow:self.mainWindow];
}
[self loadROM];
}
- (void) vblank
@ -203,52 +193,37 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
- (void) stop
{
if (!running) return;
gb.debug_disable = true;
if (gb.debug_stopped) {
GB_debugger_set_disabled(&gb, true);
if (GB_debugger_is_stopped(&gb)) {
gb.debug_stopped = false;
[self consoleInput:nil];
}
stopping = true;
running = false;
while (stopping);
gb.debug_disable = false;
GB_debugger_set_disabled(&gb, false);
}
- (IBAction)reset:(id)sender
{
bool was_cgb = gb.is_cgb;
[self stop];
/* Back up user's breakpoints/watchpoints */
typeof(gb.breakpoints) breakpoints = gb.breakpoints;
typeof(gb.n_breakpoints) n_breakpoints = gb.n_breakpoints;
typeof(gb.watchpoints) watchpoints = gb.watchpoints;
typeof(gb.n_watchpoints) n_watchpoints = gb.n_watchpoints;
/* Reset them so they're not freed*/
gb.watchpoints = NULL;
gb.breakpoints = NULL;
gb.n_watchpoints = gb.n_breakpoints = 0;
GB_free(&gb);
if (([sender tag] == 0 && was_cgb) || [sender tag] == 2) {
[self initCGB];
if ([sender tag] == 0) {
GB_reset(&gb);
}
else {
[self initDMG];
GB_switch_model_and_reset(&gb, [sender tag] == 2);
GB_load_boot_rom(&gb, [[[NSBundle mainBundle] pathForResource:[sender tag] == 2? @"cgb_boot" : @"dmg_boot" ofType:@"bin"] UTF8String]);
}
/* Restore backpoints/watchpoints */
gb.breakpoints = breakpoints;
gb.n_breakpoints = n_breakpoints;
gb.watchpoints = watchpoints;
gb.n_watchpoints = n_watchpoints;
if ([sender tag] != 0) {
/* User explictly selected a model, save the preference */
[[NSUserDefaults standardUserDefaults] setBool:!gb.is_cgb forKey:@"EmulateDMG"];
[[NSUserDefaults standardUserDefaults] setBool:[sender tag] == 1 forKey:@"EmulateDMG"];
}
[self readFromFile:self.fileName ofType:@"gb"];
/* Reload the ROM, SAV and SYM files */
[self loadROM];
[self start];
if (hex_controller) {
@ -380,10 +355,16 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
- (void) loadROM
{
GB_load_rom(&gb, [self.fileName UTF8String]);
GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]);
GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]);
NSString *rom_warnings = [self captureOutputForBlock:^{
GB_load_rom(&gb, [self.fileName UTF8String]);
GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]);
GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]);
}];
if (rom_warnings && !rom_warning_issued) {
rom_warning_issued = true;
[GBWarningPopover popoverWithContents:rom_warnings onWindow:self.mainWindow];
}
}
- (void)close
@ -398,7 +379,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
- (IBAction) interrupt:(id)sender
{
[self log:"^C\n"];
gb.debug_stopped = true;
GB_debugger_break(&gb);
if (!running) {
[self start];
}
@ -428,8 +409,8 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
[(NSMenuItem*)anItem setState:!self.audioClient.isPlaying];
}
else if ([anItem action] == @selector(togglePause:)) {
[(NSMenuItem*)anItem setState:(!running) || (gb.debug_stopped)];
return !gb.debug_stopped;
[(NSMenuItem*)anItem setState:(!running) || (GB_debugger_is_stopped(&gb))];
return !GB_debugger_is_stopped(&gb);
}
else if ([anItem action] == @selector(reset:) && anItem.tag != 0) {
[(NSMenuItem*)anItem setState:(anItem.tag == 1 && !gb.is_cgb) || (anItem.tag == 2 && gb.is_cgb)];
@ -653,7 +634,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
- (void) performAtomicBlock: (void (^)())block
{
while (!GB_is_inited(&gb));
bool was_running = running && !gb.debug_stopped;
bool was_running = running && !GB_debugger_is_stopped(&gb);
if (was_running) {
[self stop];
}

View File

@ -1,3 +1,4 @@
#define GB_INTERNAL // Todo: Some memory accesses are being done using the struct directly
#import "GBMemoryByteArray.h"
#import "GBCompleteByteSlice.h"

View File

@ -172,11 +172,11 @@
handled = true;
switch (i) {
case GBTurbo:
_gb->turbo = true;
GB_set_turbo_mode(_gb, true);
break;
default:
_gb->keys[i] = true;
GB_set_key_state(_gb, (GB_key_t)i, true);
break;
}
}
@ -198,11 +198,11 @@
handled = true;
switch (i) {
case GBTurbo:
_gb->turbo = false;
GB_set_turbo_mode(_gb, false);
break;
default:
_gb->keys[i] = false;
GB_set_key_state(_gb, (GB_key_t)i, false);
break;
}
}

View File

@ -1,7 +1,6 @@
#include <stdint.h>
#include <math.h>
#include <string.h>
#include "apu.h"
#include "gb.h"
#undef max

View File

@ -2,15 +2,11 @@
#define apu_h
#include <stdbool.h>
#include <stdint.h>
#include "gb_struct_def.h"
/* Divides nicely and never overflows with 4 channels */
#define MAX_CH_AMP 0x1E00
#define CH_STEP (0x1E00/0xF)
#include "save_struct.h"
struct GB_gameboy_s;
typedef struct GB_gameboy_s GB_gameboy_t;
typedef struct
{
@ -59,10 +55,13 @@ typedef struct
} GB_apu_t;
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count);
#ifdef GB_INTERNAL
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_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *samples);
void GB_apu_init(GB_gameboy_t *gb);
void GB_apu_run(GB_gameboy_t *gb);
#endif
#endif /* apu_h */

View File

@ -1,4 +1,4 @@
#include "camera.h"
#include "gb.h"
static int noise_seed = 0;

View File

@ -1,6 +1,10 @@
#ifndef camera_h
#define camera_h
#include "gb.h"
#include <stdint.h>
#include "gb_struct_def.h"
typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y);
typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb);
enum {
GB_CAMERA_SHOOT_AND_1D_FLAGS = 0,

View File

@ -1,9 +1,6 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "debugger.h"
#include "memory.h"
#include "z80_cpu.h"
#include "gb.h"
typedef struct {
@ -1792,3 +1789,18 @@ bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result
}
return error;
}
void GB_debugger_break(GB_gameboy_t *gb)
{
gb->debug_stopped = true;
}
bool GB_debugger_is_stopped(GB_gameboy_t *gb)
{
return gb->debug_stopped;
}
void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled)
{
gb->debug_disable = disabled;
}

View File

@ -1,15 +1,24 @@
#ifndef debugger_h
#define debugger_h
#include "gb.h"
#include <stdbool.h>
#include <stdint.h>
#include "gb_struct_def.h"
#include "symbol_hash.h"
#ifdef GB_INTERNAL
void GB_debugger_run(GB_gameboy_t *gb);
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
void GB_debugger_ret_hook(GB_gameboy_t *gb);
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
#endif
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
void GB_debugger_break(GB_gameboy_t *gb);
bool GB_debugger_is_stopped(GB_gameboy_t *gb);
void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled);
#endif /* debugger_h */

View File

@ -5,7 +5,6 @@
#include <assert.h>
#include <string.h>
#include "gb.h"
#include "display.h"
#ifdef _WIN32
#define _WIN32_WINNT 0x0500
#include <Windows.h>

View File

@ -2,8 +2,10 @@
#define display_h
#include "gb.h"
#ifdef GB_INTERNAL
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
#endif
typedef enum {
GB_PALETTE_NONE,
@ -33,6 +35,6 @@ typedef struct {
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type);
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest);
#endif /* display_h */

131
Core/gb.c
View File

@ -9,13 +9,6 @@
#include <sys/time.h>
#include <sys/select.h>
#include "gb.h"
#include "memory.h"
#include "timing.h"
#include "z80_cpu.h"
#include "joypad.h"
#include "display.h"
#include "debugger.h"
#include "mbc.h"
void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args)
{
@ -92,54 +85,28 @@ static char *default_async_input_callback(GB_gameboy_t *gb)
void GB_init(GB_gameboy_t *gb)
{
memset(gb, 0, sizeof(*gb));
gb->version = GB_STRUCT_VERSION;
gb->ram = malloc(gb->ram_size = 0x2000);
memset(gb->ram, 0, gb->ram_size);
gb->vram = malloc(gb->vram_size = 0x2000);
memset(gb->vram, 0, gb->vram_size);
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
gb->last_vblank = clock();
gb->cgb_ram_bank = 1;
/* Todo: this bypasses the rgb encoder because it is not set yet. */
gb->sprite_palletes_rgb[4] = gb->sprite_palletes_rgb[0] = gb->background_palletes_rgb[0] = 0xFFFFFFFF;
gb->sprite_palletes_rgb[5] = gb->sprite_palletes_rgb[1] = gb->background_palletes_rgb[1] = 0xAAAAAAAA;
gb->sprite_palletes_rgb[6] = gb->sprite_palletes_rgb[2] = gb->background_palletes_rgb[2] = 0x55555555;
gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF;
gb->io_registers[GB_IO_JOYP] = 0xF;
gb->io_registers[GB_IO_SC] = 0x7E;
gb->magic = (uintptr_t)'SAME';
GB_reset(gb);
}
void GB_init_cgb(GB_gameboy_t *gb)
{
memset(gb, 0, sizeof(*gb));
gb->version = GB_STRUCT_VERSION;
gb->ram = malloc(gb->ram_size = 0x2000 * 8);
memset(gb->ram, 0, gb->ram_size);
gb->vram = malloc(gb->vram_size = 0x2000 * 2);
memset(gb->vram, 0, gb->vram_size);
gb->is_cgb = true;
gb->cgb_mode = true;
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
gb->last_vblank = clock();
gb->cgb_ram_bank = 1;
gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF;
gb->io_registers[GB_IO_JOYP] = 0xF;
gb->io_registers[GB_IO_SC] = 0x7C;
gb->magic = 'SAME';
GB_reset(gb);
}
void GB_free(GB_gameboy_t *gb)
@ -200,6 +167,9 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path)
gb->rom_size++;
}
fseek(f, 0, SEEK_SET);
if (gb->rom) {
free(gb->rom);
}
gb->rom = malloc(gb->rom_size);
memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */
fread(gb->rom, gb->rom_size, 1, f);
@ -476,6 +446,16 @@ 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)
{
if (!gb->rgb_encode_callback && !gb->is_cgb) {
gb->sprite_palletes_rgb[4] = gb->sprite_palletes_rgb[0] = gb->background_palletes_rgb[0] =
callback(gb, 0xFF, 0xFF, 0xFF);
gb->sprite_palletes_rgb[5] = gb->sprite_palletes_rgb[1] = gb->background_palletes_rgb[1] =
callback(gb, 0xAA, 0xAA, 0xAA);
gb->sprite_palletes_rgb[6] = gb->sprite_palletes_rgb[2] = gb->background_palletes_rgb[2] =
callback(gb, 0x55, 0x55, 0x55);
gb->sprite_palletes_rgb[7] = gb->sprite_palletes_rgb[3] = gb->background_palletes_rgb[3] =
callback(gb, 0, 0, 0);
}
gb->rgb_encode_callback = callback;
}
@ -554,3 +534,84 @@ void GB_disconnect_serial(GB_gameboy_t *gb)
/* Reset any internally-emulated device. Currently, only the printer. */
memset(&gb->printer, 0, sizeof(gb->printer));
}
bool GB_is_inited(GB_gameboy_t *gb)
{
return gb->magic == 'SAME';
}
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on)
{
gb->turbo = on;
}
void *GB_get_user_data(GB_gameboy_t *gb)
{
return gb->user_data;
}
void GB_set_user_data(GB_gameboy_t *gb, void *data)
{
gb->user_data = data;
}
void GB_reset(GB_gameboy_t *gb)
{
bool cgb = gb->is_cgb;
memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved));
gb->version = GB_STRUCT_VERSION;
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
gb->last_vblank = clock();
gb->cgb_ram_bank = 1;
gb->io_registers[GB_IO_JOYP] = 0xF;
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF;
if (cgb) {
gb->ram_size = 0x2000 * 8;
memset(gb->ram, 0, gb->ram_size);
gb->vram_size = 0x2000 * 2;
memset(gb->vram, 0, gb->vram_size);
gb->is_cgb = true;
gb->cgb_mode = true;
gb->io_registers[GB_IO_SC] = 0x7C;
}
else {
gb->ram_size = 0x2000;
memset(gb->ram, 0, gb->ram_size);
gb->vram_size = 0x2000;
memset(gb->vram, 0, gb->vram_size);
if (gb->rgb_encode_callback) {
gb->sprite_palletes_rgb[4] = gb->sprite_palletes_rgb[0] = gb->background_palletes_rgb[0] =
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
gb->sprite_palletes_rgb[5] = gb->sprite_palletes_rgb[1] = gb->background_palletes_rgb[1] =
gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
gb->sprite_palletes_rgb[6] = gb->sprite_palletes_rgb[2] = gb->background_palletes_rgb[2] =
gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
gb->sprite_palletes_rgb[7] = gb->sprite_palletes_rgb[3] = gb->background_palletes_rgb[3] =
gb->rgb_encode_callback(gb, 0, 0, 0);
}
gb->io_registers[GB_IO_SC] = 0x7E;
}
gb->magic = (uintptr_t)'SAME';
}
void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb)
{
if (is_cgb) {
gb->ram = realloc(gb->ram, gb->ram_size = 0x2000 * 8);
gb->vram = realloc(gb->vram, gb->vram_size = 0x2000 * 2);
}
else {
gb->ram = realloc(gb->ram, gb->ram_size = 0x2000);
gb->vram = realloc(gb->vram, gb->vram_size = 0x2000);
}
gb->is_cgb = is_cgb;
GB_reset(gb);
}

237
Core/gb.h
View File

@ -4,11 +4,21 @@
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include "printer.h"
#include "apu.h"
#include "save_struct.h"
#include "symbol_hash.h"
#include "gb_struct_def.h"
#include "save_struct.h"
#include "apu.h"
#include "camera.h"
#include "debugger.h"
#include "display.h"
#include "joypad.h"
#include "mbc.h"
#include "memory.h"
#include "printer.h"
#include "timing.h"
#include "z80_cpu.h"
#include "symbol_hash.h"
#define GB_STRUCT_VERSION 10
@ -140,12 +150,6 @@ enum {
GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only
};
#define LCDC_PERIOD 70224
#define CPU_FREQUENCY 0x400000
#define DIV_CYCLES (0x100)
#define INTERNAL_DIV_CYCLES (0x40000)
#define FRAME_LENGTH 16742706 // in nanoseconds
typedef enum {
GB_LOG_BOLD = 1,
GB_LOG_DASHED_UNDERLINE = 2,
@ -153,40 +157,23 @@ typedef enum {
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
} GB_log_attributes;
struct GB_gameboy_s;
typedef struct GB_gameboy_s GB_gameboy_t;
#ifdef GB_INTERNAL
#define LCDC_PERIOD 70224
#define CPU_FREQUENCY 0x400000
#define DIV_CYCLES (0x100)
#define INTERNAL_DIV_CYCLES (0x40000)
#define FRAME_LENGTH 16742706 // in nanoseconds
#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, long cycles_since_last_update);
typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y);
typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on);
typedef void (*GB_serial_transfer_start_callback_t)(GB_gameboy_t *gb, uint8_t byte_to_send);
typedef uint8_t (*GB_serial_transfer_end_callback_t)(GB_gameboy_t *gb);
typedef struct {
enum {
GB_NO_MBC,
GB_MBC1,
GB_MBC2,
GB_MBC3,
GB_MBC5,
GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */
GB_HUC3,
} mbc_type;
enum {
GB_STANDARD_MBC,
GB_CAMERA,
} mbc_subtype;
bool has_ram;
bool has_battery;
bool has_rtc;
bool has_rumble;
} GB_cartridge_t;
typedef struct {
bool state;
long delay;
@ -205,13 +192,11 @@ struct GB_watchpoint_s;
/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */
_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1");
enum {
GB_TIMA_RUNNING = 0,
GB_TIMA_RELOADING = 1,
GB_TIMA_RELOADED = 2
};
typedef struct GB_gameboy_s {
#ifdef GB_INTERNAL
struct GB_gameboy_s {
#else
struct GB_gameboy_internal_s {
#endif
GB_SECTION(header,
/* The magic makes sure a state file is:
- Indeed a SameBoy state file.
@ -389,95 +374,103 @@ typedef struct GB_gameboy_s {
);
/* 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 */
GB_SECTION(unsaved,
/* ROM */
uint8_t *rom;
uint32_t rom_size;
const GB_cartridge_t *cartridge_type;
enum {
GB_STANDARD_MBC1_WIRING,
GB_MBC1M_WIRING,
} mbc1_wiring;
/* ROM */
uint8_t *rom;
uint32_t rom_size;
const GB_cartridge_t *cartridge_type;
enum {
GB_STANDARD_MBC1_WIRING,
GB_MBC1M_WIRING,
} mbc1_wiring;
/* Various RAMs */
uint8_t *ram;
uint8_t *vram;
uint8_t *mbc_ram;
/* Various RAMs */
uint8_t *ram;
uint8_t *vram;
uint8_t *mbc_ram;
/* I/O */
uint32_t *screen;
GB_sample_t *audio_buffer;
bool keys[GB_KEY_MAX];
/* I/O */
uint32_t *screen;
GB_sample_t *audio_buffer;
bool keys[8];
/* Audio Specific */
unsigned int buffer_size;
unsigned int sample_rate;
unsigned int audio_position;
bool audio_stream_started; // detects first copy request to minimize lag
volatile bool audio_copy_in_progress;
volatile bool apu_lock;
/* Audio Specific */
unsigned int buffer_size;
unsigned int sample_rate;
unsigned int audio_position;
bool audio_stream_started; // detects first copy request to minimize lag
volatile bool audio_copy_in_progress;
volatile bool apu_lock;
/* Callbacks */
void *user_data;
GB_log_callback_t log_callback;
GB_input_callback_t input_callback;
GB_input_callback_t async_input_callback;
GB_rgb_encode_callback_t rgb_encode_callback;
GB_vblank_callback_t vblank_callback;
GB_infrared_callback_t infrared_callback;
GB_camera_get_pixel_callback_t camera_get_pixel_callback;
GB_camera_update_request_callback_t camera_update_request_callback;
GB_rumble_callback_t rumble_callback;
GB_serial_transfer_start_callback_t serial_transfer_start_callback;
GB_serial_transfer_end_callback_t serial_transfer_end_callback;
/* IR */
long cycles_since_ir_change;
long cycles_since_input_ir_change;
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
size_t ir_queue_length;
/* Callbacks */
void *user_data;
GB_log_callback_t log_callback;
GB_input_callback_t input_callback;
GB_input_callback_t async_input_callback;
GB_rgb_encode_callback_t rgb_encode_callback;
GB_vblank_callback_t vblank_callback;
GB_infrared_callback_t infrared_callback;
GB_camera_get_pixel_callback_t camera_get_pixel_callback;
GB_camera_update_request_callback_t camera_update_request_callback;
GB_rumble_callback_t rumble_callback;
GB_serial_transfer_start_callback_t serial_transfer_start_callback;
GB_serial_transfer_end_callback_t serial_transfer_end_callback;
/* IR */
long cycles_since_ir_change;
long cycles_since_input_ir_change;
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
size_t ir_queue_length;
/*** Debugger ***/
volatile bool debug_stopped, debug_disable;
bool debug_fin_command, debug_next_command;
/*** Debugger ***/
volatile bool debug_stopped, debug_disable;
bool debug_fin_command, debug_next_command;
/* Breakpoints */
uint16_t n_breakpoints;
struct GB_breakpoint_s *breakpoints;
/* Breakpoints */
uint16_t n_breakpoints;
struct GB_breakpoint_s *breakpoints;
/* SLD (Todo: merge with backtrace) */
bool stack_leak_detection;
int debug_call_depth;
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
uint16_t addr_for_call_depth[0x200];
/* SLD (Todo: merge with backtrace) */
bool stack_leak_detection;
int 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 int backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Backtrace */
unsigned int backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Watchpoints */
uint16_t n_watchpoints;
struct GB_watchpoint_s *watchpoints;
/* Watchpoints */
uint16_t n_watchpoints;
struct GB_watchpoint_s *watchpoints;
/* Symbol tables */
GB_symbol_map_t *bank_symbols[0x200];
GB_reversed_symbol_map_t reversed_symbol_map;
/* Symbol tables */
GB_symbol_map_t *bank_symbols[0x200];
GB_reversed_symbol_map_t reversed_symbol_map;
/* Ticks command */
unsigned long debugger_ticks;
/* Ticks command */
unsigned long debugger_ticks;
/* Misc */
bool turbo;
bool turbo_dont_skip;
bool disable_rendering;
uint32_t ram_size; // Different between CGB and DMG
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
);
};
/* Misc */
bool turbo;
bool turbo_dont_skip;
bool disable_rendering;
uint32_t ram_size; // Different between CGB and DMG
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
#ifndef GB_INTERNAL
struct GB_gameboy_s {
char __internal[sizeof(struct GB_gameboy_internal_s)];
};
#endif
} GB_gameboy_t;
#ifndef __printflike
/* Missing from Linux headers. */
@ -488,6 +481,8 @@ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
void GB_init(GB_gameboy_t *gb);
void GB_init_cgb(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, bool is_cgb);
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
int GB_load_rom(GB_gameboy_t *gb, const char *path);
int GB_save_battery(GB_gameboy_t *gb, const char *path);
@ -519,8 +514,8 @@ void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data);
void GB_disconnect_serial(GB_gameboy_t *gb);
static inline bool GB_is_inited(GB_gameboy_t *gb)
{
return gb->magic == 'SAME';
}
bool GB_is_inited(GB_gameboy_t *gb);
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on);
void *GB_get_user_data(GB_gameboy_t *gb);
void GB_set_user_data(GB_gameboy_t *gb, void *data);
#endif /* GB_h */

5
Core/gb_struct_def.h Normal file
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

View File

@ -1,6 +1,6 @@
#include <stdio.h>
#include "gb.h"
#include "joypad.h"
#include <assert.h>
void GB_update_joyp(GB_gameboy_t *gb)
{
@ -55,3 +55,9 @@ void GB_update_joyp(GB_gameboy_t *gb)
}
gb->io_registers[GB_IO_JOYP] |= 0xC0; // No SGB support
}
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
{
assert(index >= 0 && index < GB_KEY_MAX);
gb->keys[index] = pressed;
}

View File

@ -1,8 +1,22 @@
#ifndef joypad_h
#define joypad_h
#include "gb.h"
#include "gb_struct_def.h"
typedef enum {
GB_KEY_RIGHT,
GB_KEY_LEFT,
GB_KEY_UP,
GB_KEY_DOWN,
GB_KEY_A,
GB_KEY_B,
GB_KEY_SELECT,
GB_KEY_START,
GB_KEY_MAX
} GB_key_t;
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
#ifdef GB_INTERNAL
void GB_update_joyp(GB_gameboy_t *gb);
void GB_update_keys_status(GB_gameboy_t *gb);
#endif
#endif /* joypad_h */

View File

@ -1,8 +1,31 @@
#ifndef MBC_h
#define MBC_h
#include "gb.h"
#include "gb_struct_def.h"
typedef struct {
enum {
GB_NO_MBC,
GB_MBC1,
GB_MBC2,
GB_MBC3,
GB_MBC5,
GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */
GB_HUC3,
} mbc_type;
enum {
GB_STANDARD_MBC,
GB_CAMERA,
} mbc_subtype;
bool has_ram;
bool has_battery;
bool has_rtc;
bool has_rumble;
} GB_cartridge_t;
#ifdef GB_INTERNAL
extern const GB_cartridge_t GB_cart_defs[256];
void GB_update_mbc_mappings(GB_gameboy_t *gb);
void GB_configure_cart(GB_gameboy_t *gb);
#endif
#endif /* MBC_h */

View File

@ -1,13 +1,6 @@
#include <stdio.h>
#include <stdbool.h>
#include "gb.h"
#include "joypad.h"
#include "display.h"
#include "memory.h"
#include "debugger.h"
#include "mbc.h"
#include "timing.h"
#include "camera.h"
typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr);
typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value);

View File

@ -4,7 +4,9 @@
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr);
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
#ifdef GB_INTERNAL
void GB_dma_run(GB_gameboy_t *gb);
void GB_hdma_run(GB_gameboy_t *gb);
#endif
#endif /* memory_h */

View File

@ -2,12 +2,10 @@
#define printer_h
#include <stdint.h>
#include <stdbool.h>
#include "gb_struct_def.h"
#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280
#define GB_PRINTER_DATA_SIZE 0x280
typedef struct GB_gameboy_s GB_gameboy_t;
typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb,
uint32_t *image,
uint8_t height,
@ -57,7 +55,5 @@ typedef struct
} GB_printer_t;
typedef struct GB_gameboy_s GB_gameboy_t;
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback);
#endif

View File

@ -21,16 +21,17 @@ typedef struct {
size_t n_symbols;
} GB_symbol_map_t;
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name);
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr);
GB_symbol_map_t *GB_map_alloc(void);
void GB_map_free(GB_symbol_map_t *map);
typedef struct {
GB_symbol_t *buckets[0x400];
} GB_reversed_symbol_map_t;
#ifdef GB_INTERNAL
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *symbol);
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name);
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name);
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr);
GB_symbol_map_t *GB_map_alloc(void);
void GB_map_free(GB_symbol_map_t *map);
#endif
#endif /* symbol_hash_h */

View File

@ -1,7 +1,4 @@
#include "gb.h"
#include "timing.h"
#include "memory.h"
#include "display.h"
static void GB_ir_run(GB_gameboy_t *gb)
{

View File

@ -2,8 +2,17 @@
#define timing_h
#include "gb.h"
#ifdef GB_INTERNAL
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value);
void GB_rtc_run(GB_gameboy_t *gb);
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac);
enum {
GB_TIMA_RUNNING = 0,
GB_TIMA_RELOADING = 1,
GB_TIMA_RELOADED = 2
};
#endif
#endif /* timing_h */

View File

@ -1,9 +1,5 @@
#include <stdio.h>
#include <stdbool.h>
#include "z80_cpu.h"
#include "timing.h"
#include "memory.h"
#include "debugger.h"
#include "gb.h"

View File

@ -1,7 +1,10 @@
#ifndef z80_cpu_h
#define z80_cpu_h
#include "gb.h"
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count);
#ifdef GB_INTERNAL
void GB_cpu_run(GB_gameboy_t *gb);
#endif
#endif /* z80_cpu_h */

View File

@ -1,9 +1,6 @@
#include <stdio.h>
#include <stdbool.h>
#include "z80_cpu.h"
#include "memory.h"
#include "gb.h"
#include "debugger.h"
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc);

View File

@ -135,6 +135,10 @@ $(OBJ)/%.dep: %
# Compilation rules
$(OBJ)/Core/%.c.o: Core/%.c
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) -DGB_INTERNAL -c $< -o $@
$(OBJ)/%.c.o: %.c
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@

View File

@ -1,3 +1,6 @@
#define GB_INTERNAL // Todo: This file runs SameBoy in a special configuration
// of the turbo mode, which is not available without direct
// struct access
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
@ -27,7 +30,7 @@ static void log_callback(GB_gameboy_t *gb, const char *string, GB_log_attributes
static void vblank(GB_gameboy_t *gb)
{
struct local_data *local_data = (struct local_data *)gb->user_data;
struct local_data *local_data = (struct local_data *)GB_get_user_data(gb);
if (local_data->frames == LENGTH) {
local_data->running = false;
@ -66,7 +69,7 @@ int get_image_for_rom(const char *filename, const char *boot_path, uint32_t *out
/* Run emulation */
struct local_data local_data = {0,};
gb.user_data = &local_data;
GB_set_user_data(&gb, &local_data);
local_data.running = true;
local_data.frames = 0;
gb.turbo = gb.turbo_dont_skip = gb.disable_rendering = true;

View File

@ -16,7 +16,6 @@
#endif
#include "gb.h"
#include "debugger.h"
static bool running = false;
static char *filename;
@ -38,34 +37,33 @@ static void GB_update_keys_status(GB_gameboy_t *gb)
running = false;
case SDL_KEYDOWN:
case SDL_KEYUP:
gb->stopped = false;
switch (event.key.keysym.sym) {
case SDLK_RIGHT:
gb->keys[0] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_RIGHT, event.type == SDL_KEYDOWN);
break;
case SDLK_LEFT:
gb->keys[1] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_LEFT, event.type == SDL_KEYDOWN);
break;
case SDLK_UP:
gb->keys[2] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_UP, event.type == SDL_KEYDOWN);
break;
case SDLK_DOWN:
gb->keys[3] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_DOWN, event.type == SDL_KEYDOWN);
break;
case SDLK_x:
gb->keys[4] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_A, event.type == SDL_KEYDOWN);
break;
case SDLK_z:
gb->keys[5] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_B, event.type == SDL_KEYDOWN);
break;
case SDLK_BACKSPACE:
gb->keys[6] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_SELECT, event.type == SDL_KEYDOWN);
break;
case SDLK_RETURN:
gb->keys[7] = event.type == SDL_KEYDOWN;
GB_set_key_state(gb, GB_KEY_START, event.type == SDL_KEYDOWN);
break;
case SDLK_SPACE:
gb->turbo = event.type == SDL_KEYDOWN;
GB_set_turbo_mode(gb, event.type == SDL_KEYDOWN);
break;
case SDLK_LCTRL:
case SDLK_RCTRL:
@ -85,7 +83,7 @@ static void GB_update_keys_status(GB_gameboy_t *gb)
case SDLK_c:
if (ctrl && event.type == SDL_KEYDOWN) {
ctrl = false;
gb->debug_stopped = true;
GB_debugger_break(gb);
}
break;
@ -122,7 +120,7 @@ static void GB_update_keys_status(GB_gameboy_t *gb)
static void vblank(GB_gameboy_t *gb)
{
SDL_Surface *screen = gb->user_data;
SDL_Surface *screen = GB_get_user_data(gb);
SDL_Flip(screen);
GB_update_keys_status(gb);
@ -189,10 +187,10 @@ static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
static void debugger_interrupt(int ignore)
{
/* ^C twice to exit */
if (gb.debug_stopped) {
if (GB_debugger_is_stopped(&gb)) {
exit(0);
}
gb.debug_stopped = true;
GB_debugger_break(&gb);
}
@ -280,7 +278,7 @@ usage:
/* Configure Screen */
SDL_LockSurface(screen);
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
gb.user_data = screen;
GB_set_user_data(&gb, screen);
GB_set_pixels_output(&gb, screen->pixels);
GB_set_rgb_encode_callback(&gb, rgb_encode);

View File

@ -1,3 +1,6 @@
// The tester requires low-level access to the GB struct to detect failures
#define GB_INTERNAL
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>