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 <AVFoundation/AVFoundation.h>
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include "GBAudioClient.h" #include "GBAudioClient.h"
#include "Document.h" #include "Document.h"
#include "AppDelegate.h" #include "AppDelegate.h"
#include "gb.h"
#include "debugger.h"
#include "memory.h"
#include "camera.h"
#include "display.h"
#include "HexFiend/HexFiend.h" #include "HexFiend/HexFiend.h"
#include "GBMemoryByteArray.h" #include "GBMemoryByteArray.h"
#include "GBWarningPopover.h" #include "GBWarningPopover.h"
#include "gb.h"
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */ /* 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!!! */ /* Todo: Split into category files! This is so messy!!! */
@ -61,25 +58,25 @@
static void vblank(GB_gameboy_t *gb) 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]; [self vblank];
} }
static void consoleLog(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes) 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]; [self log:string withAttributes: attributes];
} }
static char *consoleInput(GB_gameboy_t *gb) 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]); return strdup([self getDebuggerInput]);
} }
static char *asyncConsoleInput(GB_gameboy_t *gb) 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]; const char *ret = [self getAsyncDebuggerInput];
return ret? strdup(ret) : NULL; 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) 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]; [self cameraRequestUpdate];
} }
static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y) 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]; return [self cameraGetPixelAtX:x andY:y];
} }
static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure) 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]; [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); GB_init_cgb(&gb);
[self initCommon]; [self initCommon];
GB_load_boot_rom(&gb, [[[NSBundle mainBundle] pathForResource:@"cgb_boot" ofType:@"bin"] UTF8String]); GB_load_boot_rom(&gb, [[[NSBundle mainBundle] pathForResource:@"cgb_boot" ofType:@"bin"] UTF8String]);
} }
- (void) initCommon - (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_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog); GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput); 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_rgb_encode_callback(&gb, rgbEncode);
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel); GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate); GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
NSString *rom_warnings = [self captureOutputForBlock:^{ [self loadROM];
[self loadROM];
}];
if (rom_warnings && !rom_warning_issued) {
rom_warning_issued = true;
[GBWarningPopover popoverWithContents:rom_warnings onWindow:self.mainWindow];
}
} }
- (void) vblank - (void) vblank
@ -203,52 +193,37 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
- (void) stop - (void) stop
{ {
if (!running) return; if (!running) return;
gb.debug_disable = true; GB_debugger_set_disabled(&gb, true);
if (gb.debug_stopped) { if (GB_debugger_is_stopped(&gb)) {
gb.debug_stopped = false; gb.debug_stopped = false;
[self consoleInput:nil]; [self consoleInput:nil];
} }
stopping = true; stopping = true;
running = false; running = false;
while (stopping); while (stopping);
gb.debug_disable = false; GB_debugger_set_disabled(&gb, false);
} }
- (IBAction)reset:(id)sender - (IBAction)reset:(id)sender
{ {
bool was_cgb = gb.is_cgb;
[self stop]; [self stop];
/* Back up user's breakpoints/watchpoints */ if ([sender tag] == 0) {
typeof(gb.breakpoints) breakpoints = gb.breakpoints; GB_reset(&gb);
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];
} }
else { 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) { if ([sender tag] != 0) {
/* User explictly selected a model, save the preference */ /* 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]; [self start];
if (hex_controller) { if (hex_controller) {
@ -380,10 +355,16 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
- (void) loadROM - (void) loadROM
{ {
GB_load_rom(&gb, [self.fileName UTF8String]); NSString *rom_warnings = [self captureOutputForBlock:^{
GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]); GB_load_rom(&gb, [self.fileName UTF8String]);
GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]); GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] 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 - (void)close
@ -398,7 +379,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
- (IBAction) interrupt:(id)sender - (IBAction) interrupt:(id)sender
{ {
[self log:"^C\n"]; [self log:"^C\n"];
gb.debug_stopped = true; GB_debugger_break(&gb);
if (!running) { if (!running) {
[self start]; [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]; [(NSMenuItem*)anItem setState:!self.audioClient.isPlaying];
} }
else if ([anItem action] == @selector(togglePause:)) { else if ([anItem action] == @selector(togglePause:)) {
[(NSMenuItem*)anItem setState:(!running) || (gb.debug_stopped)]; [(NSMenuItem*)anItem setState:(!running) || (GB_debugger_is_stopped(&gb))];
return !gb.debug_stopped; return !GB_debugger_is_stopped(&gb);
} }
else if ([anItem action] == @selector(reset:) && anItem.tag != 0) { else if ([anItem action] == @selector(reset:) && anItem.tag != 0) {
[(NSMenuItem*)anItem setState:(anItem.tag == 1 && !gb.is_cgb) || (anItem.tag == 2 && gb.is_cgb)]; [(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 - (void) performAtomicBlock: (void (^)())block
{ {
while (!GB_is_inited(&gb)); while (!GB_is_inited(&gb));
bool was_running = running && !gb.debug_stopped; bool was_running = running && !GB_debugger_is_stopped(&gb);
if (was_running) { if (was_running) {
[self stop]; [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 "GBMemoryByteArray.h"
#import "GBCompleteByteSlice.h" #import "GBCompleteByteSlice.h"

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,10 @@
#ifndef camera_h #ifndef camera_h
#define 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 { enum {
GB_CAMERA_SHOOT_AND_1D_FLAGS = 0, GB_CAMERA_SHOOT_AND_1D_FLAGS = 0,

View File

@ -1,9 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "debugger.h"
#include "memory.h"
#include "z80_cpu.h"
#include "gb.h" #include "gb.h"
typedef struct { typedef struct {
@ -1792,3 +1789,18 @@ bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result
} }
return error; 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 #ifndef debugger_h
#define 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_run(GB_gameboy_t *gb);
void GB_debugger_handle_async_commands(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_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
void GB_debugger_ret_hook(GB_gameboy_t *gb); 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_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_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); 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); 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. */ 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 */ #endif /* debugger_h */

View File

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

View File

@ -2,8 +2,10 @@
#define display_h #define display_h
#include "gb.h" #include "gb.h"
#ifdef GB_INTERNAL
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles); 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); void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
#endif
typedef enum { typedef enum {
GB_PALETTE_NONE, 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_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); 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); uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest);
#endif /* display_h */ #endif /* display_h */

131
Core/gb.c
View File

@ -9,13 +9,6 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/select.h> #include <sys/select.h>
#include "gb.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) 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) void GB_init(GB_gameboy_t *gb)
{ {
memset(gb, 0, sizeof(*gb)); memset(gb, 0, sizeof(*gb));
gb->version = GB_STRUCT_VERSION;
gb->ram = malloc(gb->ram_size = 0x2000); gb->ram = malloc(gb->ram_size = 0x2000);
memset(gb->ram, 0, gb->ram_size);
gb->vram = malloc(gb->vram_size = 0x2000); 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->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback; gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF; GB_reset(gb);
gb->io_registers[GB_IO_JOYP] = 0xF;
gb->io_registers[GB_IO_SC] = 0x7E;
gb->magic = (uintptr_t)'SAME';
} }
void GB_init_cgb(GB_gameboy_t *gb) void GB_init_cgb(GB_gameboy_t *gb)
{ {
memset(gb, 0, sizeof(*gb)); memset(gb, 0, sizeof(*gb));
gb->version = GB_STRUCT_VERSION;
gb->ram = malloc(gb->ram_size = 0x2000 * 8); gb->ram = malloc(gb->ram_size = 0x2000 * 8);
memset(gb->ram, 0, gb->ram_size);
gb->vram = malloc(gb->vram_size = 0x2000 * 2); gb->vram = malloc(gb->vram_size = 0x2000 * 2);
memset(gb->vram, 0, gb->vram_size);
gb->is_cgb = true; 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->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback; gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF; GB_reset(gb);
gb->io_registers[GB_IO_JOYP] = 0xF;
gb->io_registers[GB_IO_SC] = 0x7C;
gb->magic = 'SAME';
} }
void GB_free(GB_gameboy_t *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++; gb->rom_size++;
} }
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
if (gb->rom) {
free(gb->rom);
}
gb->rom = malloc(gb->rom_size); gb->rom = malloc(gb->rom_size);
memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */ memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */
fread(gb->rom, gb->rom_size, 1, f); 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) 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; 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. */ /* Reset any internally-emulated device. Currently, only the printer. */
memset(&gb->printer, 0, sizeof(gb->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 <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <time.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 #define GB_STRUCT_VERSION 10
@ -140,12 +150,6 @@ enum {
GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only 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 { typedef enum {
GB_LOG_BOLD = 1, GB_LOG_BOLD = 1,
GB_LOG_DASHED_UNDERLINE = 2, GB_LOG_DASHED_UNDERLINE = 2,
@ -153,40 +157,23 @@ typedef enum {
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
} GB_log_attributes; } GB_log_attributes;
struct GB_gameboy_s; #ifdef GB_INTERNAL
typedef struct GB_gameboy_s GB_gameboy_t; #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_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 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 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 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 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_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 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 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 { typedef struct {
bool state; bool state;
long delay; 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. */ /* 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"); _Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1");
enum { #ifdef GB_INTERNAL
GB_TIMA_RUNNING = 0, struct GB_gameboy_s {
GB_TIMA_RELOADING = 1, #else
GB_TIMA_RELOADED = 2 struct GB_gameboy_internal_s {
}; #endif
typedef struct GB_gameboy_s {
GB_SECTION(header, GB_SECTION(header,
/* The magic makes sure a state file is: /* The magic makes sure a state file is:
- Indeed a SameBoy state file. - 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 */ /* 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 */ /* Various RAMs */
uint8_t *rom; uint8_t *ram;
uint32_t rom_size; uint8_t *vram;
const GB_cartridge_t *cartridge_type; uint8_t *mbc_ram;
enum {
GB_STANDARD_MBC1_WIRING,
GB_MBC1M_WIRING,
} mbc1_wiring;
/* Various RAMs */ /* I/O */
uint8_t *ram; uint32_t *screen;
uint8_t *vram; GB_sample_t *audio_buffer;
uint8_t *mbc_ram; bool keys[GB_KEY_MAX];
/* I/O */ /* Audio Specific */
uint32_t *screen; unsigned int buffer_size;
GB_sample_t *audio_buffer; unsigned int sample_rate;
bool keys[8]; 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 */ /* Callbacks */
unsigned int buffer_size; void *user_data;
unsigned int sample_rate; GB_log_callback_t log_callback;
unsigned int audio_position; GB_input_callback_t input_callback;
bool audio_stream_started; // detects first copy request to minimize lag GB_input_callback_t async_input_callback;
volatile bool audio_copy_in_progress; GB_rgb_encode_callback_t rgb_encode_callback;
volatile bool apu_lock; 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 */ /*** Debugger ***/
void *user_data; volatile bool debug_stopped, debug_disable;
GB_log_callback_t log_callback; bool debug_fin_command, debug_next_command;
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 ***/ /* Breakpoints */
volatile bool debug_stopped, debug_disable; uint16_t n_breakpoints;
bool debug_fin_command, debug_next_command; struct GB_breakpoint_s *breakpoints;
/* Breakpoints */ /* SLD (Todo: merge with backtrace) */
uint16_t n_breakpoints; bool stack_leak_detection;
struct GB_breakpoint_s *breakpoints; 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) */ /* Backtrace */
bool stack_leak_detection; unsigned int backtrace_size;
int debug_call_depth; uint16_t backtrace_sps[0x200];
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */ struct {
uint16_t addr_for_call_depth[0x200]; uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Backtrace */ /* Watchpoints */
unsigned int backtrace_size; uint16_t n_watchpoints;
uint16_t backtrace_sps[0x200]; struct GB_watchpoint_s *watchpoints;
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Watchpoints */ /* Symbol tables */
uint16_t n_watchpoints; GB_symbol_map_t *bank_symbols[0x200];
struct GB_watchpoint_s *watchpoints; GB_reversed_symbol_map_t reversed_symbol_map;
/* Symbol tables */ /* Ticks command */
GB_symbol_map_t *bank_symbols[0x200]; unsigned long debugger_ticks;
GB_reversed_symbol_map_t reversed_symbol_map;
/* Ticks command */ /* Misc */
unsigned long debugger_ticks; 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 */ #ifndef GB_INTERNAL
bool turbo; struct GB_gameboy_s {
bool turbo_dont_skip; char __internal[sizeof(struct GB_gameboy_internal_s)];
bool disable_rendering; };
uint32_t ram_size; // Different between CGB and DMG #endif
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
} GB_gameboy_t;
#ifndef __printflike #ifndef __printflike
/* Missing from Linux headers. */ /* Missing from Linux headers. */
@ -488,6 +481,8 @@ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
void GB_init(GB_gameboy_t *gb); void GB_init(GB_gameboy_t *gb);
void GB_init_cgb(GB_gameboy_t *gb); void GB_init_cgb(GB_gameboy_t *gb);
void GB_free(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_boot_rom(GB_gameboy_t *gb, const char *path);
int GB_load_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); 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); void GB_disconnect_serial(GB_gameboy_t *gb);
static inline bool GB_is_inited(GB_gameboy_t *gb) bool GB_is_inited(GB_gameboy_t *gb);
{ void GB_set_turbo_mode(GB_gameboy_t *gb, bool on);
return gb->magic == 'SAME'; void *GB_get_user_data(GB_gameboy_t *gb);
} void GB_set_user_data(GB_gameboy_t *gb, void *data);
#endif /* GB_h */ #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 <stdio.h>
#include "gb.h" #include "gb.h"
#include "joypad.h" #include <assert.h>
void GB_update_joyp(GB_gameboy_t *gb) 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 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 #ifndef joypad_h
#define 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_joyp(GB_gameboy_t *gb);
void GB_update_keys_status(GB_gameboy_t *gb); #endif
#endif /* joypad_h */ #endif /* joypad_h */

View File

@ -1,8 +1,31 @@
#ifndef MBC_h #ifndef MBC_h
#define 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]; extern const GB_cartridge_t GB_cart_defs[256];
void GB_update_mbc_mappings(GB_gameboy_t *gb); void GB_update_mbc_mappings(GB_gameboy_t *gb);
void GB_configure_cart(GB_gameboy_t *gb); void GB_configure_cart(GB_gameboy_t *gb);
#endif
#endif /* MBC_h */ #endif /* MBC_h */

View File

@ -1,13 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include "gb.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 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); 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); 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); 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_dma_run(GB_gameboy_t *gb);
void GB_hdma_run(GB_gameboy_t *gb); void GB_hdma_run(GB_gameboy_t *gb);
#endif
#endif /* memory_h */ #endif /* memory_h */

View File

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

View File

@ -21,16 +21,17 @@ typedef struct {
size_t n_symbols; size_t n_symbols;
} GB_symbol_map_t; } 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 { typedef struct {
GB_symbol_t *buckets[0x400]; GB_symbol_t *buckets[0x400];
} GB_reversed_symbol_map_t; } 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); 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); 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 */ #endif /* symbol_hash_h */

View File

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

View File

@ -2,8 +2,17 @@
#define timing_h #define timing_h
#include "gb.h" #include "gb.h"
#ifdef GB_INTERNAL
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles); 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_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value);
void GB_rtc_run(GB_gameboy_t *gb); 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); 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 */ #endif /* timing_h */

View File

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

View File

@ -1,7 +1,10 @@
#ifndef z80_cpu_h #ifndef z80_cpu_h
#define z80_cpu_h #define z80_cpu_h
#include "gb.h" #include "gb.h"
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count); 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); void GB_cpu_run(GB_gameboy_t *gb);
#endif
#endif /* z80_cpu_h */ #endif /* z80_cpu_h */

View File

@ -1,9 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include "z80_cpu.h"
#include "memory.h"
#include "gb.h" #include "gb.h"
#include "debugger.h"
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc); 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 # Compilation rules
$(OBJ)/Core/%.c.o: Core/%.c
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) -DGB_INTERNAL -c $< -o $@
$(OBJ)/%.c.o: %.c $(OBJ)/%.c.o: %.c
-@$(MKDIR) -p $(dir $@) -@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@ $(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 <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <unistd.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) 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) { if (local_data->frames == LENGTH) {
local_data->running = false; 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 */ /* Run emulation */
struct local_data local_data = {0,}; struct local_data local_data = {0,};
gb.user_data = &local_data; GB_set_user_data(&gb, &local_data);
local_data.running = true; local_data.running = true;
local_data.frames = 0; local_data.frames = 0;
gb.turbo = gb.turbo_dont_skip = gb.disable_rendering = true; gb.turbo = gb.turbo_dont_skip = gb.disable_rendering = true;

View File

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