sameboy: it's alive!

This commit is contained in:
nattthebear 2017-07-16 20:36:58 -04:00
parent 34e68c589f
commit f7bb894753
16 changed files with 31 additions and 637 deletions

View File

@ -59,8 +59,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
_cgb = (rom[0x143] & 0xc0) == 0xc0;
Console.WriteLine("Automaticly detected CGB to " + _cgb);
var bios = Util.DecompressGzipFile(new MemoryStream(
_cgb ? Resources.SameboyCgbBoot : Resources.SameboyDmgBoot));
var bios = Util.DecompressGzipFile(new MemoryStream(_cgb ? Resources.SameboyCgbBoot : Resources.SameboyDmgBoot));
// var bios = comm.CoreFileProvider.GetFirmware(_cgb ? "GBC" : "GB", "World", true);
_exe.AddReadonlyFile(rom, "game.rom");
_exe.AddReadonlyFile(bios, "boot.rom");

View File

@ -6,12 +6,11 @@ FLAGS:=-Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=imp
-fomit-frame-pointer -fvisibility=hidden \
-O0 -g
CCFLAGS:=$(FLAGS) -Ilib \
-I../emulibc \
CCFLAGS:=$(FLAGS) \
-std=gnu99 \
-DLSB_FIRST -D_GNU_SOURCE -DGB_INTERNAL
CPPFLAGS:=$(FLAGS) -DSPC_NO_COPY_STATE_FUNCS -std=c++0x
CPPFLAGS:=$(FLAGS) -DSPC_NO_COPY_STATE_FUNCS -std=c++0x -D_GNU_SOURCE -DGB_INTERNAL
TARGET = sameboy.wbx

View File

@ -65,6 +65,9 @@ ECL_EXPORT bool Init(bool cgb)
GB_set_infrared_callback(&GB, InfraredCallback);
GB_set_rumble_callback(&GB, RumbleCallback);
GB_set_sample_rate(&GB, 44100);
GB_set_audio_quality(&GB, 1);
return true;
}
@ -76,9 +79,12 @@ struct MyFrameInfo : public FrameInfo
ECL_EXPORT void FrameAdvance(MyFrameInfo &f)
{
GB_set_pixels_output(&GB, f.VideoBuffer);
// void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
GB_run_frame(&GB);
f.Samples = 735;
for (int i = 0; i < (int)GB_KEY_MAX; i++)
GB_set_key_state(&GB, (GB_key_t)i, false);
f.Cycles = GB_run_cycles(&GB, 35112);
f.Samples = GB_apu_get_current_buffer_length(&GB);
GB_apu_copy_buffer(&GB, (GB_sample_t*)f.SoundBuffer, f.Samples);
f.Width = 160;
f.Height = 144;
}
@ -104,7 +110,7 @@ ECL_EXPORT void GetMemoryAreas(MemoryArea *m)
SetMemoryArea(m + 6, GB_DIRECT_ACCESS_IO, "IO", MEMORYAREA_FLAGS_WORDSIZE1);
SetMemoryArea(m + 7, GB_DIRECT_ACCESS_BOOTROM, "BOOTROM", MEMORYAREA_FLAGS_WORDSIZE1);
SetMemoryArea(m + 8, GB_DIRECT_ACCESS_BGP, "BGP", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE);
SetMemoryArea(m + 8, GB_DIRECT_ACCESS_OBP, "OBP", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE);
SetMemoryArea(m + 9, GB_DIRECT_ACCESS_OBP, "OBP", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE);
}
ECL_EXPORT void SetInputCallback(void (*callback)())

View File

@ -125,7 +125,7 @@ void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
addr &= 0x7F;
if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) {
value &= 0x7;
noise_seed = rand();
noise_seed = 42; // rand();
if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) {
/* If no callback is set, ignore the write as if the camera is instantly done */
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1;

View File

@ -197,12 +197,6 @@ static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
static void display_vblank(GB_gameboy_t *gb)
{
if (gb->turbo) {
if (GB_timing_sync_turbo(gb)) {
return;
}
}
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
/* LCD is off, set screen to white */
uint32_t white = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
@ -212,7 +206,6 @@ static void display_vblank(GB_gameboy_t *gb)
}
gb->vblank_callback(gb);
GB_timing_sync(gb);
gb->vblank_just_occured = true;
}

View File

@ -133,18 +133,6 @@ void GB_free(GB_gameboy_t *gb)
if (gb->breakpoints) {
free(gb->breakpoints);
}
for (int i = 0x200; i--;) {
if (gb->bank_symbols[i]) {
GB_map_free(gb->bank_symbols[i]);
}
}
for (int i = 0x400; i--;) {
if (gb->reversed_symbol_map.buckets[i]) {
GB_symbol_t *next = gb->reversed_symbol_map.buckets[i]->next;
free(gb->reversed_symbol_map.buckets[i]);
gb->reversed_symbol_map.buckets[i] = next;
}
}
memset(gb, 0, sizeof(*gb));
}
@ -267,24 +255,20 @@ void GB_run(GB_gameboy_t *gb)
}
}
uint64_t GB_run_frame(GB_gameboy_t *gb)
uint64_t GB_run_cycles(GB_gameboy_t *gb, uint32_t cycles)
{
/* Configure turbo temporarily, the user wants to handle FPS capping manually. */
bool old_turbo = gb->turbo;
bool old_dont_skip = gb->turbo_dont_skip;
gb->turbo = true;
gb->turbo_dont_skip = true;
gb->cycles_since_last_sync = 0;
while (true) {
uint64_t start = gb->cycles_since_epoch;
uint64_t target = start + cycles;
while (gb->cycles_since_epoch < target) {
GB_run(gb);
if (gb->vblank_just_occured) {
// TODO: fix these up
GB_update_joyp(gb);
GB_rtc_run(gb);
break;
}
}
gb->turbo = old_turbo;
gb->turbo_dont_skip = old_dont_skip;
return gb->cycles_since_last_sync * FRAME_LENGTH * LCDC_PERIOD;
return gb->cycles_since_epoch - start;
}
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output)
@ -416,12 +400,6 @@ bool GB_is_cgb(GB_gameboy_t *gb)
return gb->is_cgb;
}
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip)
{
gb->turbo = on;
gb->turbo_dont_skip = no_frame_skip;
}
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled)
{
gb->disable_rendering = disabled;
@ -441,7 +419,7 @@ void GB_reset(GB_gameboy_t *gb)
{
uint32_t mbc_ram_size = gb->mbc_ram_size;
bool cgb = gb->is_cgb;
memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved));
// memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved));
gb->version = GB_STRUCT_VERSION;
gb->mbc_rom_bank = 1;

View File

@ -4,9 +4,9 @@
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "gb_struct_def.h"
#include "save_state.h"
#include "apu.h"
#include "camera.h"
@ -17,7 +17,6 @@
#include "printer.h"
#include "timing.h"
#include "z80_cpu.h"
#include "symbol_hash.h"
#define GB_STRUCT_VERSION 11
@ -193,7 +192,6 @@ 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.
- Has the same endianess has the current platform. */
@ -201,9 +199,7 @@ struct GB_gameboy_internal_s {
/* The version field makes sure we don't load save state files with a completely different structure.
This happens when struct fields are removed/resized in an backward incompatible manner. */
uint32_t version;
);
GB_SECTION(core_state,
/* Registers */
uint16_t pc;
union {
@ -249,10 +245,8 @@ struct GB_gameboy_internal_s {
/* Misc state */
bool infrared_input;
GB_printer_t printer;
);
/* DMA and HDMA */
GB_SECTION(dma,
bool hdma_on;
bool hdma_on_hblank;
uint8_t hdma_steps_left;
@ -264,10 +258,8 @@ struct GB_gameboy_internal_s {
uint16_t dma_current_src;
int16_t dma_cycles;
bool is_dma_restarting;
);
/* MBC */
GB_SECTION(mbc,
uint16_t mbc_rom_bank;
uint8_t mbc_ram_bank;
uint32_t mbc_ram_size;
@ -311,32 +303,23 @@ struct GB_gameboy_internal_s {
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
bool rumble_state;
);
/* HRAM and HW Registers */
GB_SECTION(hram,
uint8_t hram[0xFFFF - 0xFF80];
uint8_t io_registers[0x80];
);
/* Timing */
GB_SECTION(timing,
uint32_t display_cycles;
uint32_t div_cycles;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
GB_PADDING(uint16_t, serial_cycles);
uint16_t serial_cycles; /* This field changed its meaning in v0.10 */
uint16_t serial_length;
);
/* APU */
GB_SECTION(apu,
GB_apu_t apu;
);
/* RTC */
GB_SECTION(rtc,
union {
struct {
uint8_t seconds;
@ -349,10 +332,8 @@ struct GB_gameboy_internal_s {
} rtc_real, rtc_latched;
time_t last_rtc_second;
bool rtc_latch;
);
/* Video Display */
GB_SECTION(video,
uint32_t vram_size; // Different between CGB and DMG
uint8_t cgb_vram_bank;
uint8_t oam[0xA0];
@ -379,11 +360,9 @@ struct GB_gameboy_internal_s {
bool vram_read_blocked;
bool oam_write_blocked;
bool vram_write_blocked;
);
/* 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;
@ -404,8 +383,7 @@ struct GB_gameboy_internal_s {
bool keys[GB_KEY_MAX];
/* Timing */
uint64_t last_sync;
uint64_t cycles_since_last_sync;
uint64_t cycles_since_epoch;
/* Audio */
unsigned buffer_size;
@ -459,21 +437,11 @@ struct GB_gameboy_internal_s {
uint16_t addr;
} backtrace_returns[0x200];
/* Symbol tables */
GB_symbol_map_t *bank_symbols[0x200];
GB_reversed_symbol_map_t reversed_symbol_map;
/* 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
);
};
#ifndef GB_INTERNAL
@ -497,8 +465,7 @@ 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);
void 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);
uint64_t GB_run_cycles(GB_gameboy_t *gb, uint32_t cycles);
typedef enum {
GB_DIRECT_ACCESS_ROM,
@ -526,7 +493,6 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path);
int GB_save_battery(GB_gameboy_t *gb, const char *path);
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);

View File

@ -22,13 +22,6 @@ void GB_update_joyp(GB_gameboy_t *gb)
for (uint8_t i = 0; i < 4; i++) {
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[i]) << i;
}
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
gb->io_registers[GB_IO_JOYP] |= 2;
}
if (!(gb->io_registers[GB_IO_JOYP] & 4)) {
gb->io_registers[GB_IO_JOYP] |= 8;
}
break;
case 1:

View File

@ -456,10 +456,6 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON;
}
}
else if (!(value & 0x80) && (gb->io_registers[GB_IO_LCDC] & 0x80)) {
/* Sync after turning off LCD */
GB_timing_sync(gb);
}
gb->io_registers[GB_IO_LCDC] = value;
return;

View File

@ -1,304 +0,0 @@
#include "gb.h"
#include <stdio.h>
#include <errno.h>
static bool dump_section(FILE *f, const void *src, uint32_t size)
{
if (fwrite(&size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (fwrite(src, 1, size, f) != size) {
return false;
}
return true;
}
#define DUMP_SECTION(gb, f, section) dump_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
/* Todo: we need a sane and protable save state format. */
int GB_save_state(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "wb");
if (!f) {
GB_log(gb, "Could not open save state: %s.\n", strerror(errno));
return errno;
}
if (fwrite(GB_GET_SECTION(gb, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (!DUMP_SECTION(gb, f, core_state)) goto error;
if (!DUMP_SECTION(gb, f, dma )) goto error;
if (!DUMP_SECTION(gb, f, mbc )) goto error;
if (!DUMP_SECTION(gb, f, hram )) goto error;
if (!DUMP_SECTION(gb, f, timing )) goto error;
if (!DUMP_SECTION(gb, f, apu )) goto error;
if (!DUMP_SECTION(gb, f, rtc )) goto error;
if (!DUMP_SECTION(gb, f, video )) goto error;
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
goto error;
}
if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
goto error;
}
if (fwrite(gb->vram, 1, gb->vram_size, f) != gb->vram_size) {
goto error;
}
errno = 0;
error:
fclose(f);
return errno;
}
#undef DUMP_SECTION
size_t GB_get_save_state_size(GB_gameboy_t *gb)
{
return GB_SECTION_SIZE(header)
+ GB_SECTION_SIZE(core_state) + sizeof(uint32_t)
+ GB_SECTION_SIZE(dma ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(mbc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(hram ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(timing ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
+ gb->mbc_ram_size
+ gb->ram_size
+ gb->vram_size;
}
/* A write-line function for memory copying */
static void buffer_write(const void *src, size_t size, uint8_t **dest)
{
memcpy(*dest, src, size);
*dest += size;
}
static void buffer_dump_section(uint8_t **buffer, const void *src, uint32_t size)
{
buffer_write(&size, sizeof(size), buffer);
buffer_write(src, size, buffer);
}
#define DUMP_SECTION(gb, buffer, section) buffer_dump_section(&buffer, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
{
buffer_write(GB_GET_SECTION(gb, header), GB_SECTION_SIZE(header), &buffer);
DUMP_SECTION(gb, buffer, core_state);
DUMP_SECTION(gb, buffer, dma );
DUMP_SECTION(gb, buffer, mbc );
DUMP_SECTION(gb, buffer, hram );
DUMP_SECTION(gb, buffer, timing );
DUMP_SECTION(gb, buffer, apu );
DUMP_SECTION(gb, buffer, rtc );
DUMP_SECTION(gb, buffer, video );
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
buffer_write(gb->ram, gb->ram_size, &buffer);
buffer_write(gb->vram, gb->vram_size, &buffer);
}
/* Best-effort read function for maximum future compatibility. */
static bool read_section(FILE *f, void *dest, uint32_t size)
{
uint32_t saved_size = 0;
if (fread(&saved_size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (saved_size <= size) {
if (fread(dest, 1, saved_size, f) != saved_size) {
return false;
}
}
else {
if (fread(dest, 1, size, f) != size) {
return false;
}
fseek(f, saved_size - size, SEEK_CUR);
}
return true;
}
#undef DUMP_SECTION
static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
{
if (gb->magic != save->magic) {
GB_log(gb, "File is not a save state, or is from an incompatible operating system.\n");
return false;
}
if (gb->version != save->version) {
GB_log(gb, "Save state is for a different version of SameBoy.\n");
return false;
}
if (gb->mbc_ram_size < save->mbc_ram_size) {
GB_log(gb, "Save state has non-matching MBC RAM size.\n");
return false;
}
if (gb->ram_size != save->ram_size) {
GB_log(gb, "Save state has non-matching RAM size. Try changing emulated model.\n");
return false;
}
if (gb->vram_size != save->vram_size) {
GB_log(gb, "Save state has non-matching VRAM size. Try changing emulated model.\n");
return false;
}
return true;
}
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
int GB_load_state(GB_gameboy_t *gb, const char *path)
{
GB_gameboy_t save;
/* Every unread value should be kept the same. */
memcpy(&save, gb, sizeof(save));
FILE *f = fopen(path, "rb");
if (!f) {
GB_log(gb, "Could not open save state: %s.\n", strerror(errno));
return errno;
}
if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (!READ_SECTION(&save, f, core_state)) goto error;
if (!READ_SECTION(&save, f, dma )) goto error;
if (!READ_SECTION(&save, f, mbc )) goto error;
if (!READ_SECTION(&save, f, hram )) goto error;
if (!READ_SECTION(&save, f, timing )) goto error;
if (!READ_SECTION(&save, f, apu )) goto error;
if (!READ_SECTION(&save, f, rtc )) goto error;
if (!READ_SECTION(&save, f, video )) goto error;
if (!verify_state_compatibility(gb, &save)) {
errno = -1;
goto error;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (fread(gb->mbc_ram, 1, save.mbc_ram_size, f) != save.mbc_ram_size) {
fclose(f);
return EIO;
}
if (fread(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
fclose(f);
return EIO;
}
if (fread(gb->vram, 1, gb->vram_size, f) != gb->vram_size) {
fclose(f);
return EIO;
}
memcpy(gb, &save, sizeof(save));
errno = 0;
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
gb->rumble_callback(gb, gb->rumble_state);
}
error:
fclose(f);
return errno;
}
#undef READ_SECTION
/* An read-like function for buffer-copying */
static size_t buffer_read(void *dest, size_t length, const uint8_t **buffer, size_t *buffer_length)
{
if (length > *buffer_length) {
length = *buffer_length;
}
memcpy(dest, *buffer, length);
*buffer += length;
*buffer_length -= length;
return length;
}
static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, void *dest, uint32_t size)
{
uint32_t saved_size = 0;
if (buffer_read(&saved_size, sizeof(size), buffer, buffer_length) != sizeof(size)) {
return false;
}
if (saved_size <= size) {
if (buffer_read(dest, saved_size, buffer, buffer_length) != saved_size) {
return false;
}
}
else {
if (buffer_read(dest, size, buffer, buffer_length) != size) {
return false;
}
*buffer += saved_size - size;
*buffer_length -= saved_size - size;
}
return true;
}
#define READ_SECTION(gb, buffer, length, section) buffer_read_section(&buffer, &length, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length)
{
GB_gameboy_t save;
/* Every unread value should be kept the same. */
memcpy(&save, gb, sizeof(save));
if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1;
if (!READ_SECTION(&save, buffer, length, core_state)) return -1;
if (!READ_SECTION(&save, buffer, length, dma )) return -1;
if (!READ_SECTION(&save, buffer, length, mbc )) return -1;
if (!READ_SECTION(&save, buffer, length, hram )) return -1;
if (!READ_SECTION(&save, buffer, length, timing )) return -1;
if (!READ_SECTION(&save, buffer, length, apu )) return -1;
if (!READ_SECTION(&save, buffer, length, rtc )) return -1;
if (!READ_SECTION(&save, buffer, length, video )) return -1;
if (!verify_state_compatibility(gb, &save)) {
return -1;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (buffer_read(gb->mbc_ram, save.mbc_ram_size, &buffer, &length) != save.mbc_ram_size) {
return -1;
}
if (buffer_read(gb->ram, gb->ram_size, &buffer, &length) != gb->ram_size) {
return -1;
}
if (buffer_read(gb->vram,gb->vram_size, &buffer, &length) != gb->vram_size) {
return -1;
}
memcpy(gb, &save, sizeof(save));
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
gb->rumble_callback(gb, gb->rumble_state);
}
return 0;
}
#undef READ_SECTION

View File

@ -1,24 +0,0 @@
/* Macros to make the GB_gameboy_t struct more future compatible when state saving */
#ifndef save_state_h
#define save_state_h
#include <stddef.h>
#define GB_PADDING(type, old_usage) type old_usage##__do_not_use
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) struct {} name##_section_start; __VA_ARGS__; struct {} name##_section_end
#define GB_SECTION_OFFSET(name) (offsetof(GB_gameboy_t, name##_section_start))
#define GB_SECTION_SIZE(name) (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start))
#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start))
#define GB_aligned_double __attribute__ ((aligned (8))) double
/* Public calls related to save states */
int GB_save_state(GB_gameboy_t *gb, const char *path);
size_t GB_get_save_state_size(GB_gameboy_t *gb);
/* Assumes buffer is big enough to contain the save state. Use with GB_get_save_state_size(). */
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
int GB_load_state(GB_gameboy_t *gb, const char *path);
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
#endif /* save_state_h */

View File

@ -1,106 +0,0 @@
#include "gb.h"
static size_t GB_map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr)
{
if (!map->symbols) {
return 0;
}
ssize_t min = 0;
ssize_t max = map->n_symbols;
while (min < max) {
size_t pivot = (min + max) / 2;
if (map->symbols[pivot].addr == addr) return pivot;
if (map->symbols[pivot].addr > addr) {
max = pivot;
}
else {
min = pivot + 1;
}
}
return (size_t) min;
}
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name)
{
size_t index = GB_map_find_symbol_index(map, addr);
if (index < map->n_symbols && map->symbols[index].addr == addr) return NULL;
map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0]));
memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0]));
map->symbols[index].addr = addr;
map->symbols[index].name = strdup(name);
map->n_symbols++;
return &map->symbols[index];
}
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr)
{
if (!map) return NULL;
size_t index = GB_map_find_symbol_index(map, addr);
if (index < map->n_symbols && map->symbols[index].addr != addr) {
index--;
}
if (index < map->n_symbols) {
return &map->symbols[index];
}
return NULL;
}
GB_symbol_map_t *GB_map_alloc(void)
{
GB_symbol_map_t *map = malloc(sizeof(*map));
memset(map, 0, sizeof(*map));
return map;
}
void GB_map_free(GB_symbol_map_t *map)
{
for (unsigned i = 0; i < map->n_symbols; i++) {
free(map->symbols[i].name);
}
if (map->symbols) {
free(map->symbols);
}
free(map);
}
static int hash_name(const char *name)
{
int r = 0;
while (*name) {
r <<= 1;
if (r & 0x400) {
r ^= 0x401;
}
r += (unsigned char)*(name++);
}
return r & 0x3FF;
}
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *bank_symbol)
{
int hash = hash_name(bank_symbol->name);
GB_symbol_t *symbol = malloc(sizeof(*symbol));
symbol->name = bank_symbol->name;
symbol->addr = bank_symbol->addr;
symbol->bank = bank;
symbol->next = map->buckets[hash];
map->buckets[hash] = symbol;
}
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name)
{
int hash = hash_name(name);
GB_symbol_t *symbol = map->buckets[hash];
while (symbol) {
if (strcmp(symbol->name, name) == 0) return symbol;
symbol = symbol->next;
}
return NULL;
}

View File

@ -1,37 +0,0 @@
#ifndef symbol_hash_h
#define symbol_hash_h
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
uint16_t addr;
} GB_bank_symbol_t;
typedef struct GB_symbol_s {
struct GB_symbol_s *next;
const char *name;
uint16_t bank;
uint16_t addr;
} GB_symbol_t;
typedef struct {
GB_bank_symbol_t *symbols;
size_t n_symbols;
} GB_symbol_map_t;
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

@ -6,69 +6,6 @@
#include <sys/time.h>
#endif
static int64_t get_nanoseconds(void)
{
#ifndef _WIN32
struct timeval now;
gettimeofday(&now, NULL);
return (now.tv_usec) * 1000 + now.tv_sec * 1000000000L;
#else
FILETIME time;
GetSystemTimeAsFileTime(&time);
return (((int64_t)time.dwHighDateTime << 32) | time.dwLowDateTime) * 100L;
#endif
}
static void nsleep(uint64_t nanoseconds)
{
#ifndef _WIN32
struct timespec sleep = {0, nanoseconds};
nanosleep(&sleep, NULL);
#else
HANDLE timer;
LARGE_INTEGER time;
timer = CreateWaitableTimer(NULL, true, NULL);
time.QuadPart = -(nanoseconds / 100L);
SetWaitableTimer(timer, &time, 0, NULL, NULL, false);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
#endif
}
bool GB_timing_sync_turbo(GB_gameboy_t *gb)
{
if (!gb->turbo_dont_skip) {
int64_t nanoseconds = get_nanoseconds();
if (nanoseconds <= gb->last_sync + FRAME_LENGTH) {
return true;
}
gb->last_sync = nanoseconds;
}
return false;
}
void GB_timing_sync(GB_gameboy_t *gb)
{
if (gb->turbo) {
gb->cycles_since_last_sync = 0;
return;
}
/* Prevent syncing if not enough time has passed.*/
if (gb->cycles_since_last_sync < LCDC_PERIOD / 4) return;
uint64_t target_nanoseconds = gb->cycles_since_last_sync * FRAME_LENGTH / LCDC_PERIOD;
int64_t nanoseconds = get_nanoseconds();
if (labs((signed long)(nanoseconds - gb->last_sync)) < target_nanoseconds ) {
nsleep(target_nanoseconds + gb->last_sync - nanoseconds);
gb->last_sync += target_nanoseconds;
}
else {
gb->last_sync = nanoseconds;
}
gb->cycles_since_last_sync = 0;
}
static void GB_ir_run(GB_gameboy_t *gb)
{
if (gb->ir_queue_length == 0) return;
@ -136,7 +73,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
gb->apu.apu_cycles += cycles;
gb->cycles_since_ir_change += cycles;
gb->cycles_since_input_ir_change += cycles;
gb->cycles_since_last_sync += cycles;
gb->cycles_since_epoch += cycles >> 1;
GB_dma_run(gb);
GB_hdma_run(gb);
GB_apu_run(gb);

View File

@ -7,9 +7,6 @@ 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);
bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
void GB_timing_sync(GB_gameboy_t *gb);
enum {
GB_TIMA_RUNNING = 0,

View File

@ -283,7 +283,7 @@ static void jr_r8(GB_gameboy_t *gb, uint8_t opcode)
{
/* Todo: Verify cycles are not 8 and 4 instead */
GB_advance_cycles(gb, 4);
gb->pc += (int8_t) GB_read_memory(gb, gb->pc++);
gb->pc += (int8_t)GB_read_memory(gb, gb->pc) + 1;
GB_advance_cycles(gb, 8);
}
@ -307,7 +307,7 @@ static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode)
{
if (condition_code(gb, opcode)) {
GB_advance_cycles(gb, 4);
gb->pc += (int8_t)GB_read_memory(gb, gb->pc++);
gb->pc += (int8_t)GB_read_memory(gb, gb->pc) + 1;
GB_advance_cycles(gb, 8);
}
else {