Merge branch 'master' into timing

# Conflicts:
#	Core/gb.c
This commit is contained in:
Lior Halphon 2018-03-22 22:37:35 +02:00
commit 18e32d1755
4 changed files with 136 additions and 72 deletions

View File

@ -476,7 +476,6 @@ void GB_reset(GB_gameboy_t *gb)
gb->is_cgb = true; gb->is_cgb = true;
gb->cgb_mode = true; gb->cgb_mode = true;
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0x00;
} }
else { else {
gb->ram_size = 0x2000; gb->ram_size = 0x2000;
@ -494,11 +493,14 @@ void GB_reset(GB_gameboy_t *gb)
gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] = gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
gb->rgb_encode_callback(gb, 0, 0, 0); gb->rgb_encode_callback(gb, 0, 0, 0);
} }
gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF;
} }
/* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */ /* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */
gb->serial_cycles = 0x100-0xF7; gb->serial_cycles = 0x100-0xF7;
gb->io_registers[GB_IO_SC] = 0x7E; gb->io_registers[GB_IO_SC] = 0x7E;
/* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */
gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = gb->is_cgb? 0x00 : 0xFF;
gb->magic = (uintptr_t)'SAME'; gb->magic = (uintptr_t)'SAME';
} }

View File

@ -31,7 +31,7 @@ static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr)
static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
{ {
if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting)) return false; if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false;
return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src); return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src);
} }
@ -174,6 +174,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
case GB_IO_WX: case GB_IO_WX:
case GB_IO_SC: case GB_IO_SC:
case GB_IO_SB: case GB_IO_SB:
case GB_IO_DMA:
return gb->io_registers[addr & 0xFF]; return gb->io_registers[addr & 0xFF];
case GB_IO_TIMA: case GB_IO_TIMA:
if (gb->tima_reload_state == GB_TIMA_RELOADING) { if (gb->tima_reload_state == GB_TIMA_RELOADING) {
@ -232,9 +233,6 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
} }
return ret; return ret;
} }
case GB_IO_DMA:
/* Todo: is this documented? */
return gb->is_cgb? 0x00 : 0xFF;
case GB_IO_UNKNOWN2: case GB_IO_UNKNOWN2:
case GB_IO_UNKNOWN3: case GB_IO_UNKNOWN3:
return gb->is_cgb? gb->io_registers[addr & 0xFF] : 0xFF; return gb->is_cgb? gb->io_registers[addr & 0xFF] : 0xFF;
@ -516,21 +514,18 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return; return;
case GB_IO_DMA: case GB_IO_DMA:
if (value <= 0xE0) { if (gb->dma_steps_left) {
if (gb->dma_steps_left) { /* This is not correct emulation, since we're not really delaying the second DMA.
/* This is not correct emulation, since we're not really delaying the second DMA. One write that should have happened in the first DMA will not happen. However,
One write that should have happened in the first DMA will not happen. However, since that byte will be overwritten by the second DMA before it can actually be
since that byte will be overwritten by the second DMA before it can actually be read, it doesn't actually matter. */
read, it doesn't actually matter. */ gb->is_dma_restarting = true;
gb->is_dma_restarting = true;
}
gb->dma_cycles = -7;
gb->dma_current_dest = 0;
gb->dma_current_src = value << 8;
gb->dma_steps_left = 0xa0;
} }
/* else { what? } */ gb->dma_cycles = -7;
gb->dma_current_dest = 0;
gb->dma_current_src = value << 8;
gb->dma_steps_left = 0xa0;
gb->io_registers[GB_IO_DMA] = value;
return; return;
case GB_IO_SVBK: case GB_IO_SVBK:
if (!gb->cgb_mode) { if (!gb->cgb_mode) {
@ -708,7 +703,14 @@ void GB_dma_run(GB_gameboy_t *gb)
/* Todo: measure this value */ /* Todo: measure this value */
gb->dma_cycles -= 4; gb->dma_cycles -= 4;
gb->dma_steps_left--; gb->dma_steps_left--;
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src); if (gb->dma_current_src < 0xe000) {
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src);
}
else {
/* Todo: Not correct on the CGB */
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000);
}
/* dma_current_src must be the correct value during GB_read_memory */ /* dma_current_src must be the correct value during GB_read_memory */
gb->dma_current_src++; gb->dma_current_src++;
if (!gb->dma_steps_left) { if (!gb->dma_steps_left) {

View File

@ -161,7 +161,7 @@ CFLAGS += -Wall -D__LIBRETRO__ $(fpic) $(INCFLAGS) -std=gnu11 -D_GNU_SOURCE -D
all: $(TARGET) all: $(TARGET)
$(CORE_DIR)/libretro/%_boot.c: $(CORE_DIR)/BootROMs/prebuilt/%_boot.bin $(CORE_DIR)/libretro/%_boot.c: $(CORE_DIR)/build/bin/BootROMs/%_boot.bin
echo "/* AUTO-GENERATED */" > $@ echo "/* AUTO-GENERATED */" > $@
echo "const unsigned char $(notdir $(@:%.c=%))[] = {" >> $@ echo "const unsigned char $(notdir $(@:%.c=%))[] = {" >> $@
ifneq ($(findstring Haiku,$(shell uname -s)),) ifneq ($(findstring Haiku,$(shell uname -s)),)

View File

@ -1,4 +1,5 @@
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
@ -81,7 +82,7 @@ static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb; static retro_input_state_t input_state_cb;
static unsigned emulated_devices = 1; static unsigned emulated_devices = 1;
static unsigned pre_init = 1; static bool initialized = false;
static unsigned screen_layout = 0; static unsigned screen_layout = 0;
static unsigned audio_out = 0; static unsigned audio_out = 0;
@ -116,6 +117,25 @@ static void fallback_log(enum retro_log_level level, const char *fmt, ...)
static struct retro_rumble_interface rumble; static struct retro_rumble_interface rumble;
static void extract_basename(char *buf, const char *path, size_t size)
{
const char *base = strrchr(path, '/');
if (!base)
base = strrchr(path, '\\');
if (!base)
base = path;
if (*base == '\\' || *base == '/')
base++;
strncpy(buf, base, size - 1);
buf[size - 1] = '\0';
char *ext = strrchr(buf, '.');
if (ext)
*ext = '\0';
}
static void GB_update_keys_status(GB_gameboy_t *gb, unsigned port) static void GB_update_keys_status(GB_gameboy_t *gb, unsigned port)
{ {
@ -201,40 +221,43 @@ static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
static retro_environment_t environ_cb; static retro_environment_t environ_cb;
static const struct retro_variable vars[] = { /* variables for single cart mode */
{ "sameboy_dual", "Dual Game Boy Mode (restart); disabled|enabled" }, static const struct retro_variable vars_single[] = {
{ "sameboy_color_correction_mode", "Color Correction; off|correct curves|emulate hardware|preserve brightness" }, { "sameboy_dual", "Single cart dual mode (reload); disabled|enabled" },
{ "sameboy_high_pass_filter_mode", "High Pass Filter; off|accurate|remove dc offset" }, { "sameboy_color_correction_mode", "Color correction; off|correct curves|emulate hardware|preserve brightness" },
{ "sameboy_model", "Emulated Model; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, { "sameboy_high_pass_filter_mode", "High-pass filter; off|accurate|remove dc offset" },
{ "sameboy_model", "Emulated model (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" },
{ NULL } { NULL }
}; };
static const struct retro_variable vars_sameboy_dual[] = { /* variables for single cart dual gameboy mode */
{ "sameboy_dual", "Dual Game Boy Mode (restart); disabled|enabled" }, static const struct retro_variable vars_single_dual[] = {
{ "sameboy_link", "Link Cable Emulation; enabled|disabled" }, { "sameboy_dual", "Single cart dual mode (reload); disabled|enabled" },
{ "sameboy_link", "Link cable emulation; enabled|disabled" },
/*{ "sameboy_ir", "Infrared Sensor Emulation; disabled|enabled" },*/ /*{ "sameboy_ir", "Infrared Sensor Emulation; disabled|enabled" },*/
{ "sameboy_screen_layout", "Screen Layout; top-down|left-right" }, { "sameboy_screen_layout", "Screen layout; top-down|left-right" },
{ "sameboy_audio_output", "Audio output; Game Boy #1|Game Boy #2" }, { "sameboy_audio_output", "Audio output; Game Boy #1|Game Boy #2" },
{ "sameboy_model_1", "Emulated Model for Game Boy #1; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, { "sameboy_model_1", "Emulated model for Game Boy #1 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" },
{ "sameboy_model_2", "Emulated Model for Game Boy #2; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, { "sameboy_model_2", "Emulated model for Game Boy #2 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" },
{ "sameboy_color_correction_mode_1", "Color Correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" }, { "sameboy_color_correction_mode_1", "Color correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" },
{ "sameboy_color_correction_mode_2", "Color Correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" }, { "sameboy_color_correction_mode_2", "Color correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" },
{ "sameboy_high_pass_filter_mode_1", "High Pass Filter for Game Boy #1; off|accurate|remove dc offset" }, { "sameboy_high_pass_filter_mode_1", "High-pass filter for Game Boy #1; off|accurate|remove dc offset" },
{ "sameboy_high_pass_filter_mode_2", "High Pass Filter for Game Boy #2; off|accurate|remove dc offset" }, { "sameboy_high_pass_filter_mode_2", "High-pass filter for Game Boy #2; off|accurate|remove dc offset" },
{ NULL } { NULL }
}; };
static const struct retro_variable vars_link_dual[] = { /* variables for dual cart dual gameboy mode */
{ "sameboy_link", "Link Cable Emulation; enabled|disabled" }, static const struct retro_variable vars_dual[] = {
{ "sameboy_link", "Link cable emulation; enabled|disabled" },
/*{ "sameboy_ir", "Infrared Sensor Emulation; disabled|enabled" },*/ /*{ "sameboy_ir", "Infrared Sensor Emulation; disabled|enabled" },*/
{ "sameboy_screen_layout", "Screen Layout; top-down|left-right" }, { "sameboy_screen_layout", "Screen layout; top-down|left-right" },
{ "sameboy_audio_output", "Audio output; Game Boy #1|Game Boy #2" }, { "sameboy_audio_output", "Audio output; Game Boy #1|Game Boy #2" },
{ "sameboy_model_1", "Emulated Model for Game Boy #1; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, { "sameboy_model_1", "Emulated model for Game Boy #1 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" },
{ "sameboy_model_2", "Emulated Model for Game Boy #2; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, { "sameboy_model_2", "Emulated model for Game Boy #2 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" },
{ "sameboy_color_correction_mode_1", "Color Correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" }, { "sameboy_color_correction_mode_1", "Color correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" },
{ "sameboy_color_correction_mode_2", "Color Correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" }, { "sameboy_color_correction_mode_2", "Color correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" },
{ "sameboy_high_pass_filter_mode_1", "High Pass Filter for Game Boy #1; off|accurate|remove dc offset" }, { "sameboy_high_pass_filter_mode_1", "High-pass filter for Game Boy #1; off|accurate|remove dc offset" },
{ "sameboy_high_pass_filter_mode_2", "High Pass Filter for Game Boy #2; off|accurate|remove dc offset" }, { "sameboy_high_pass_filter_mode_2", "High-pass filter for Game Boy #2; off|accurate|remove dc offset" },
{ NULL } { NULL }
}; };
@ -418,10 +441,6 @@ static void check_variables(bool link)
else else
new_model = MODEL_AUTO; new_model = MODEL_AUTO;
if (GB_is_inited(&gameboy[0]) && new_model != model[0]) {
model[0] = new_model;
init_for_current_model();
}
model[0] = new_model; model[0] = new_model;
} }
} }
@ -492,11 +511,7 @@ static void check_variables(bool link)
new_model = MODEL_AGB; new_model = MODEL_AGB;
else else
new_model = MODEL_AUTO; new_model = MODEL_AUTO;
if (GB_is_inited(&gameboy[0]) && new_model != model[0]) {
model[0] = new_model;
init_for_current_model();
}
model[0] = new_model; model[0] = new_model;
} }
@ -513,11 +528,7 @@ static void check_variables(bool link)
new_model = MODEL_AGB; new_model = MODEL_AGB;
else else
new_model = MODEL_AUTO; new_model = MODEL_AUTO;
if (GB_is_inited(&gameboy[1]) && new_model != model[1]) {
model[1] = new_model;
init_for_current_model();
}
model[1] = new_model; model[1] = new_model;
} }
@ -715,9 +726,10 @@ void retro_reset(void)
void retro_run(void) void retro_run(void)
{ {
bool updated = false; bool updated = false;
if (pre_init) if (!initialized)
geometry_updated = false; geometry_updated = false;
if (geometry_updated) { if (geometry_updated) {
@ -727,8 +739,6 @@ void retro_run(void)
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry); environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry);
} }
pre_init = 0;
if (!frame_buf) if (!frame_buf)
return; return;
@ -769,17 +779,19 @@ void retro_run(void)
video_cb(frame_buf_copy, VIDEO_WIDTH * emulated_devices, VIDEO_HEIGHT, VIDEO_WIDTH * emulated_devices * sizeof(uint32_t)); video_cb(frame_buf_copy, VIDEO_WIDTH * emulated_devices, VIDEO_HEIGHT, VIDEO_WIDTH * emulated_devices * sizeof(uint32_t));
} }
initialized = true;
} }
bool retro_load_game(const struct retro_game_info *info) bool retro_load_game(const struct retro_game_info *info)
{ {
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars); environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_single);
check_variables(false); check_variables(false);
if (sameboy_dual) if (sameboy_dual)
{ {
emulated_devices = 2; emulated_devices = 2;
mode = MODE_SINGLE_GAME_DUAL; mode = MODE_SINGLE_GAME_DUAL;
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_sameboy_dual); environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_single_dual);
check_variables(true); check_variables(true);
} }
else else
@ -821,11 +833,36 @@ bool retro_load_game(const struct retro_game_info *info)
log_cb(RETRO_LOG_INFO, "Rumble environment not supported\n"); log_cb(RETRO_LOG_INFO, "Rumble environment not supported\n");
check_variables(emulated_devices == 2 ? true : false); check_variables(emulated_devices == 2 ? true : false);
/* hack: use upstream's file based I/O for Game Boy 2 battery in single cart mode */
if (mode == MODE_SINGLE_GAME_DUAL)
{
char path[PATH_MAX];
char file[PATH_MAX];
extract_basename(file, retro_game_path, sizeof(file));
snprintf (path, sizeof(path), "%s%c%s.srm.2", retro_save_directory, slash, file);
log_cb(RETRO_LOG_INFO, "Loading battery for Game Boy 2 from: %s\n", path);
GB_load_battery(&gameboy[1], path);
}
return true; return true;
} }
void retro_unload_game(void) void retro_unload_game(void)
{ {
/* hack: use upstream's file based I/O for Game Boy 2 battery in single cart mode */
if (mode == MODE_SINGLE_GAME_DUAL)
{
char path[PATH_MAX];
char file[PATH_MAX];
extract_basename(file, retro_game_path, sizeof(file));
snprintf (path, sizeof(path), "%s%c%s.srm.2", retro_save_directory, slash, file);
log_cb(RETRO_LOG_INFO, "Saving battery for Game Boy 2 to: %s\n", path);
GB_save_battery(&gameboy[1], path);
}
for (int i = 0; i < emulated_devices; i++) for (int i = 0; i < emulated_devices; i++)
GB_free(&gameboy[i]); GB_free(&gameboy[i]);
} }
@ -846,7 +883,7 @@ bool retro_load_game_special(unsigned type, const struct retro_game_info *info,
else else
return false; /* all other types are unhandled for now */ return false; /* all other types are unhandled for now */
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_link_dual); environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_dual);
check_variables(true); check_variables(true);
frame_buf = (uint32_t*)malloc(emulated_devices * VIDEO_PIXELS * sizeof(uint32_t)); frame_buf = (uint32_t*)malloc(emulated_devices * VIDEO_PIXELS * sizeof(uint32_t));
@ -897,18 +934,21 @@ size_t retro_serialize_size(void)
bool retro_serialize(void *data, size_t size) bool retro_serialize(void *data, size_t size)
{ {
if (pre_init == 1)
if (!initialized)
return false; return false;
void* save_data[2]; void* save_data[2];
size_t state_size[2]; size_t state_size[2];
size_t offset = 0;
for (int i = 0; i < emulated_devices; i++) for (int i = 0; i < emulated_devices; i++)
{ {
state_size[i] = GB_get_save_state_size(&gameboy[i]); state_size[i] = GB_get_save_state_size(&gameboy[i]);
save_data[i] = (uint8_t*)malloc(state_size[i]); save_data[i] = (uint8_t*)malloc(state_size[i]);
GB_save_state_to_buffer(&gameboy[i], (uint8_t*) save_data[i]); GB_save_state_to_buffer(&gameboy[i], (uint8_t*) save_data[i]);
memcpy(data + (state_size[i] * i), save_data[i], state_size[i]); memcpy(data + offset, save_data[i], state_size[i]);
offset += state_size[i];
free(save_data[i]); free(save_data[i]);
} }
@ -973,7 +1013,7 @@ void *retro_get_memory_data(unsigned type)
} }
} }
break; break;
case MODE_DUAL_GAME: /* todo: hook up other memory types */ case MODE_DUAL_GAME:
{ {
switch (type) switch (type)
{ {
@ -989,6 +1029,18 @@ void *retro_get_memory_data(unsigned type)
else else
data = NULL; data = NULL;
break; break;
case RETRO_MEMORY_GAMEBOY_1_RTC:
if(gameboy[0].cartridge_type->has_battery)
data = &gameboy[0].rtc_real;
else
data = NULL;
break;
case RETRO_MEMORY_GAMEBOY_2_RTC:
if(gameboy[1].cartridge_type->has_battery)
data = &gameboy[1].rtc_real;
else
data = NULL;
break;
default: default:
break; break;
} }
@ -1033,7 +1085,7 @@ size_t retro_get_memory_size(unsigned type)
} }
} }
break; break;
case MODE_DUAL_GAME: /* todo: hook up other memory types */ case MODE_DUAL_GAME:
{ {
switch (type) switch (type)
{ {
@ -1049,8 +1101,16 @@ size_t retro_get_memory_size(unsigned type)
else else
size = 0; size = 0;
break; break;
case RETRO_MEMORY_GAMEBOY_1_RTC:
if(gameboy[0].cartridge_type->has_battery)
size = sizeof (gameboy[0].rtc_real);
break;
case RETRO_MEMORY_GAMEBOY_2_RTC:
if(gameboy[1].cartridge_type->has_battery)
size = sizeof (gameboy[1].rtc_real);
break;
default: default:
break;; break;
} }
} }
break; break;