SGB save states
This commit is contained in:
parent
634a54c046
commit
2f2b792edf
@ -132,10 +132,10 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
|
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 or black (if LCD is on in stop mode) */
|
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
||||||
if (gb->sgb_screen_buffer) {
|
if (gb->sgb) {
|
||||||
uint8_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ? 0 : 0xFF;
|
uint8_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ? 0 : 0xFF;
|
||||||
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
||||||
gb ->sgb_screen_buffer[i] = color;
|
gb->sgb->screen_buffer[i] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -386,8 +386,8 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
if (!gb->cgb_mode) {
|
if (!gb->cgb_mode) {
|
||||||
pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
|
pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
|
||||||
}
|
}
|
||||||
if (gb->sgb_screen_buffer) {
|
if (gb->sgb) {
|
||||||
gb->sgb_screen_buffer[gb->position_in_line + gb->current_line * WIDTH] = pixel;
|
gb->sgb->screen_buffer[gb->position_in_line + gb->current_line * WIDTH] = pixel;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
||||||
@ -400,8 +400,8 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
/* Todo: Verify access timings */
|
/* Todo: Verify access timings */
|
||||||
pixel = ((gb->io_registers[oam_fifo_item->palette? GB_IO_OBP1 : GB_IO_OBP0] >> (pixel << 1)) & 3);
|
pixel = ((gb->io_registers[oam_fifo_item->palette? GB_IO_OBP1 : GB_IO_OBP0] >> (pixel << 1)) & 3);
|
||||||
}
|
}
|
||||||
if (gb->sgb_screen_buffer) {
|
if (gb->sgb) {
|
||||||
gb->sgb_screen_buffer[gb->position_in_line + gb->current_line * WIDTH] =pixel;
|
gb->sgb->screen_buffer[gb->position_in_line + gb->current_line * WIDTH] =pixel;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
||||||
|
17
Core/gb.c
17
Core/gb.c
@ -139,8 +139,8 @@ void GB_free(GB_gameboy_t *gb)
|
|||||||
if (gb->breakpoints) {
|
if (gb->breakpoints) {
|
||||||
free(gb->breakpoints);
|
free(gb->breakpoints);
|
||||||
}
|
}
|
||||||
if (gb->sgb_screen_buffer) {
|
if (gb->sgb) {
|
||||||
free(gb->sgb_screen_buffer);
|
free(gb->sgb);
|
||||||
}
|
}
|
||||||
#ifndef DISABLE_DEBUGGER
|
#ifndef DISABLE_DEBUGGER
|
||||||
GB_debugger_clear_symbols(gb);
|
GB_debugger_clear_symbols(gb);
|
||||||
@ -643,17 +643,18 @@ void GB_reset(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
gb->accessed_oam_row = -1;
|
gb->accessed_oam_row = -1;
|
||||||
|
|
||||||
gb->sgb_player_count = 1;
|
|
||||||
|
|
||||||
if (GB_is_sgb(gb)) {
|
if (GB_is_sgb(gb)) {
|
||||||
if (!gb->sgb_screen_buffer) {
|
if (!gb->sgb) {
|
||||||
gb->sgb_screen_buffer = malloc(160 * 144);
|
gb->sgb = malloc(sizeof(*gb->sgb));
|
||||||
}
|
}
|
||||||
|
gb->sgb->player_count = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (gb->sgb_screen_buffer) {
|
if (gb->sgb) {
|
||||||
free(gb->sgb_screen_buffer);
|
free(gb->sgb);
|
||||||
gb->sgb_screen_buffer = NULL;
|
gb->sgb = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
Core/gb.h
19
Core/gb.h
@ -52,7 +52,6 @@ typedef union {
|
|||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
||||||
// GB_MODEL_DMG_0 = 0x000,
|
// GB_MODEL_DMG_0 = 0x000,
|
||||||
// GB_MODEL_DMG_A = 0x001,
|
// GB_MODEL_DMG_A = 0x001,
|
||||||
GB_MODEL_DMG_B = 0x002,
|
GB_MODEL_DMG_B = 0x002,
|
||||||
@ -467,21 +466,6 @@ struct GB_gameboy_internal_s {
|
|||||||
bool lyc_interrupt_line;
|
bool lyc_interrupt_line;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Super Game Boy state, only dumped/loaded for relevant models */
|
|
||||||
GB_SECTION(sgb,
|
|
||||||
uint8_t sgb_command[16 * 7];
|
|
||||||
uint16_t sgb_command_write_index;
|
|
||||||
bool sgb_ready_for_pulse;
|
|
||||||
bool sgb_ready_for_write;
|
|
||||||
bool sgb_ready_for_stop;
|
|
||||||
bool sgb_disable_commands;
|
|
||||||
|
|
||||||
/* Screen buffer */
|
|
||||||
uint8_t *sgb_screen_buffer;
|
|
||||||
/* Multiplayer Input */
|
|
||||||
uint8_t sgb_player_count, sgb_current_player;
|
|
||||||
);
|
|
||||||
|
|
||||||
/* 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 */
|
/* This data is reserved on reset and must come last in the struct */
|
||||||
GB_SECTION(unsaved,
|
GB_SECTION(unsaved,
|
||||||
@ -578,6 +562,9 @@ struct GB_gameboy_internal_s {
|
|||||||
} *rewind_sequences; // lasts about 4 seconds
|
} *rewind_sequences; // lasts about 4 seconds
|
||||||
size_t rewind_pos;
|
size_t rewind_pos;
|
||||||
|
|
||||||
|
/* SGB - saved and allocated optionally */
|
||||||
|
GB_sgb_t *sgb;
|
||||||
|
|
||||||
/* Misc */
|
/* Misc */
|
||||||
bool turbo;
|
bool turbo;
|
||||||
bool turbo_dont_skip;
|
bool turbo_dont_skip;
|
||||||
|
@ -11,10 +11,11 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
||||||
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
|
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
|
||||||
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||||
|
uint8_t current_player = gb->sgb? gb->sgb->current_player : 0;
|
||||||
switch (key_selection) {
|
switch (key_selection) {
|
||||||
case 3:
|
case 3:
|
||||||
if (gb->sgb_player_count > 1) {
|
if (gb->sgb && gb->sgb->player_count > 1) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= 0xF - gb->sgb_current_player;
|
gb->io_registers[GB_IO_JOYP] |= 0xF - current_player;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Nothing is wired, all up */
|
/* Nothing is wired, all up */
|
||||||
@ -25,7 +26,7 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
case 2:
|
case 2:
|
||||||
/* Direction keys */
|
/* Direction keys */
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[gb->sgb_current_player][i]) << i;
|
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i]) << i;
|
||||||
}
|
}
|
||||||
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
|
/* 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)) {
|
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
|
||||||
@ -39,13 +40,13 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
case 1:
|
case 1:
|
||||||
/* Other keys */
|
/* Other keys */
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[gb->sgb_current_player][i + 4]) << i;
|
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i + 4]) << i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[gb->sgb_current_player][i] || gb->keys[gb->sgb_current_player][i + 4])) << i;
|
gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[current_player][i] || gb->keys[current_player][i + 4])) << i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -36,6 +36,10 @@ int GB_save_state(GB_gameboy_t *gb, const char *path)
|
|||||||
if (!DUMP_SECTION(gb, f, rtc )) goto error;
|
if (!DUMP_SECTION(gb, f, rtc )) goto error;
|
||||||
if (!DUMP_SECTION(gb, f, video )) goto error;
|
if (!DUMP_SECTION(gb, f, video )) goto error;
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb)) {
|
||||||
|
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
|
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
|
||||||
goto error;
|
goto error;
|
||||||
@ -69,6 +73,7 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
|
|||||||
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
|
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
|
||||||
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
|
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
|
||||||
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
|
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
|
||||||
|
+ (GB_is_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
|
||||||
+ gb->mbc_ram_size
|
+ gb->mbc_ram_size
|
||||||
+ gb->ram_size
|
+ gb->ram_size
|
||||||
+ gb->vram_size;
|
+ gb->vram_size;
|
||||||
@ -100,6 +105,10 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
|
|||||||
DUMP_SECTION(gb, buffer, rtc );
|
DUMP_SECTION(gb, buffer, rtc );
|
||||||
DUMP_SECTION(gb, buffer, video );
|
DUMP_SECTION(gb, buffer, video );
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb)) {
|
||||||
|
buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
|
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
|
||||||
buffer_write(gb->ram, gb->ram_size, &buffer);
|
buffer_write(gb->ram, gb->ram_size, &buffer);
|
||||||
@ -133,27 +142,32 @@ static bool read_section(FILE *f, void *dest, uint32_t size)
|
|||||||
static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
|
static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
|
||||||
{
|
{
|
||||||
if (gb->magic != save->magic) {
|
if (gb->magic != save->magic) {
|
||||||
GB_log(gb, "File is not a save state, or is from an incompatible operating system.\n");
|
GB_log(gb, "The file is not a save state, or is from an incompatible operating system.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->version != save->version) {
|
if (gb->version != save->version) {
|
||||||
GB_log(gb, "Save state is for a different version of SameBoy.\n");
|
GB_log(gb, "The save state is for a different version of SameBoy.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->mbc_ram_size < save->mbc_ram_size) {
|
if (gb->mbc_ram_size < save->mbc_ram_size) {
|
||||||
GB_log(gb, "Save state has non-matching MBC RAM size.\n");
|
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->ram_size != save->ram_size) {
|
if (gb->ram_size != save->ram_size) {
|
||||||
GB_log(gb, "Save state has non-matching RAM size. Try changing emulated model.\n");
|
GB_log(gb, "The save state has non-matching RAM size. Try changing the emulated model.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->vram_size != save->vram_size) {
|
if (gb->vram_size != save->vram_size) {
|
||||||
GB_log(gb, "Save state has non-matching VRAM size. Try changing emulated model.\n");
|
GB_log(gb, "The save state has non-matching VRAM size. Try changing the emulated model.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb) != GB_is_sgb(save)) {
|
||||||
|
GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_sgb(save)? "" : "not ");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +204,10 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb)) {
|
||||||
|
if (!read_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
||||||
|
}
|
||||||
|
|
||||||
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
|
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) {
|
if (fread(gb->mbc_ram, 1, save.mbc_ram_size, f) != save.mbc_ram_size) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
@ -289,6 +307,10 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb)) {
|
||||||
|
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb))) return -1;
|
||||||
|
}
|
||||||
|
|
||||||
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
|
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) {
|
if (buffer_read(gb->mbc_ram, save.mbc_ram_size, &buffer, &length) != save.mbc_ram_size) {
|
||||||
return -1;
|
return -1;
|
||||||
|
106
Core/sgb.c
106
Core/sgb.c
@ -1,4 +1,4 @@
|
|||||||
#include "sgb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MLT_REQ = 0x11,
|
MLT_REQ = 0x11,
|
||||||
@ -15,41 +15,41 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
|
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
|
||||||
|
|
||||||
|
|
||||||
if ((gb->sgb_command[0] & 0xF1) == 0xF1) {
|
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
||||||
uint8_t checksum = 0;
|
uint8_t checksum = 0;
|
||||||
for (unsigned i = 2; i < 0x10; i++) {
|
for (unsigned i = 2; i < 0x10; i++) {
|
||||||
checksum += gb->sgb_command[i];
|
checksum += gb->sgb->command[i];
|
||||||
}
|
}
|
||||||
if (checksum != gb->sgb_command[1]) {
|
if (checksum != gb->sgb->command[1]) {
|
||||||
GB_log(gb, "Failed checksum for SGB header command, disabling SGB features\n");
|
GB_log(gb, "Failed checksum for SGB header command, disabling SGB features\n");
|
||||||
gb->sgb_disable_commands = true;
|
gb->sgb->disable_commands = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gb->sgb_command[0] == 0xf9) {
|
if (gb->sgb->command[0] == 0xf9) {
|
||||||
if (gb->sgb_command[0xc] != 3) { // SGB Flag
|
if (gb->sgb->command[0xc] != 3) { // SGB Flag
|
||||||
GB_log(gb, "SGB flag is not 0x03, disabling SGB features\n");
|
GB_log(gb, "SGB flag is not 0x03, disabling SGB features\n");
|
||||||
gb->sgb_disable_commands = true;
|
gb->sgb->disable_commands = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (gb->sgb_command[0] == 0xfb) {
|
else if (gb->sgb->command[0] == 0xfb) {
|
||||||
if (gb->sgb_command[0x3] != 0x33) { // Old licensee code
|
if (gb->sgb->command[0x3] != 0x33) { // Old licensee code
|
||||||
GB_log(gb, "Old licensee code is not 0x33, disabling SGB features\n");
|
GB_log(gb, "Old licensee code is not 0x33, disabling SGB features\n");
|
||||||
gb->sgb_disable_commands = true;
|
gb->sgb->disable_commands = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (gb->sgb_command[0] >> 3) {
|
switch (gb->sgb->command[0] >> 3) {
|
||||||
case MLT_REQ:
|
case MLT_REQ:
|
||||||
gb->sgb_player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb_command[1] & 3];
|
gb->sgb->player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb->command[1] & 3];
|
||||||
gb->sgb_current_player = gb->sgb_player_count - 1;
|
gb->sgb->current_player = gb->sgb->player_count - 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
GB_log(gb, "Unimplemented SGB command %x: ", gb->sgb_command[0] >> 3);
|
GB_log(gb, "Unimplemented SGB command %x: ", gb->sgb->command[0] >> 3);
|
||||||
for (unsigned i = 0; i < gb->sgb_command_write_index / 8; i++) {
|
for (unsigned i = 0; i < gb->sgb->command_write_index / 8; i++) {
|
||||||
GB_log(gb, "%02x ", gb->sgb_command[i]);
|
GB_log(gb, "%02x ", gb->sgb->command[i]);
|
||||||
}
|
}
|
||||||
GB_log(gb, "\n");
|
GB_log(gb, "\n");
|
||||||
;
|
;
|
||||||
@ -60,66 +60,66 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||||
{
|
{
|
||||||
if (!GB_is_sgb(gb)) return;
|
if (!GB_is_sgb(gb)) return;
|
||||||
if (gb->sgb_disable_commands) return;
|
if (gb->sgb->disable_commands) return;
|
||||||
if (gb->sgb_command_write_index >= sizeof(gb->sgb_command) * 8) return;
|
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) return;
|
||||||
|
|
||||||
uint16_t command_size = (gb->sgb_command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
|
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
|
||||||
if ((gb->sgb_command[0] & 0xF1) == 0xF1) {
|
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
||||||
command_size = SGB_PACKET_SIZE * 8;
|
command_size = SGB_PACKET_SIZE * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ((value >> 4) & 3) {
|
switch ((value >> 4) & 3) {
|
||||||
case 3:
|
case 3:
|
||||||
gb->sgb_ready_for_pulse = true;
|
gb->sgb->ready_for_pulse = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // Zero
|
case 2: // Zero
|
||||||
if (!gb->sgb_ready_for_pulse || !gb->sgb_ready_for_write) return;
|
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
|
||||||
if (gb->sgb_ready_for_stop) {
|
if (gb->sgb->ready_for_stop) {
|
||||||
if (gb->sgb_command_write_index == command_size) {
|
if (gb->sgb->command_write_index == command_size) {
|
||||||
command_ready(gb);
|
command_ready(gb);
|
||||||
gb->sgb_command_write_index = 0;
|
gb->sgb->command_write_index = 0;
|
||||||
memset(gb->sgb_command, 0, sizeof(gb->sgb_command));
|
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
|
||||||
}
|
}
|
||||||
gb->sgb_ready_for_pulse = false;
|
gb->sgb->ready_for_pulse = false;
|
||||||
gb->sgb_ready_for_write = false;
|
gb->sgb->ready_for_write = false;
|
||||||
gb->sgb_ready_for_stop = false;
|
gb->sgb->ready_for_stop = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->sgb_command_write_index++;
|
gb->sgb->command_write_index++;
|
||||||
gb->sgb_ready_for_pulse = false;
|
gb->sgb->ready_for_pulse = false;
|
||||||
if (((gb->sgb_command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||||
gb->sgb_ready_for_stop = true;
|
gb->sgb->ready_for_stop = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: // One
|
case 1: // One
|
||||||
if (!gb->sgb_ready_for_pulse || !gb->sgb_ready_for_write) return;
|
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
|
||||||
if (gb->sgb_ready_for_stop) {
|
if (gb->sgb->ready_for_stop) {
|
||||||
GB_log(gb, "Corrupt SGB command.\n");
|
GB_log(gb, "Corrupt SGB command.\n");
|
||||||
gb->sgb_ready_for_pulse = false;
|
gb->sgb->ready_for_pulse = false;
|
||||||
gb->sgb_ready_for_write = false;
|
gb->sgb->ready_for_write = false;
|
||||||
gb->sgb_command_write_index = 0;
|
gb->sgb->command_write_index = 0;
|
||||||
memset(gb->sgb_command, 0, sizeof(gb->sgb_command));
|
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->sgb_command[gb->sgb_command_write_index / 8] |= 1 << (gb->sgb_command_write_index & 7);
|
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
|
||||||
gb->sgb_command_write_index++;
|
gb->sgb->command_write_index++;
|
||||||
gb->sgb_ready_for_pulse = false;
|
gb->sgb->ready_for_pulse = false;
|
||||||
if (((gb->sgb_command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||||
gb->sgb_ready_for_stop = true;
|
gb->sgb->ready_for_stop = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
if (!gb->sgb_ready_for_pulse) return;
|
if (!gb->sgb->ready_for_pulse) return;
|
||||||
gb->sgb_ready_for_pulse = false;
|
gb->sgb->ready_for_pulse = false;
|
||||||
gb->sgb_ready_for_write = true;
|
gb->sgb->ready_for_write = true;
|
||||||
gb->sgb_ready_for_pulse = false;
|
gb->sgb->ready_for_pulse = false;
|
||||||
if (gb->sgb_player_count > 1 && (value & 0x30) != (gb->io_registers[GB_IO_JOYP] & 0x30)) {
|
if (gb->sgb->player_count > 1 && (value & 0x30) != (gb->io_registers[GB_IO_JOYP] & 0x30)) {
|
||||||
gb->sgb_current_player++;
|
gb->sgb->current_player++;
|
||||||
gb->sgb_current_player &= gb->sgb_player_count - 1;
|
gb->sgb->current_player &= gb->sgb->player_count - 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t *output = &gb->screen[48 + 39 * 256];
|
uint32_t *output = &gb->screen[48 + 39 * 256];
|
||||||
uint8_t *input = gb->sgb_screen_buffer;
|
uint8_t *input = gb->sgb->screen_buffer;
|
||||||
for (unsigned y = 0; y < 144; y++) {
|
for (unsigned y = 0; y < 144; y++) {
|
||||||
for (unsigned x = 0; x < 160; x++) {
|
for (unsigned x = 0; x < 160; x++) {
|
||||||
*(output++) = colors[*(input++) & 3];
|
*(output++) = colors[*(input++) & 3];
|
||||||
|
19
Core/sgb.h
19
Core/sgb.h
@ -1,10 +1,27 @@
|
|||||||
#ifndef sgb_h
|
#ifndef sgb_h
|
||||||
#define sgb_h
|
#define sgb_h
|
||||||
#include "gb.h"
|
#include "gb_struct_def.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
|
typedef struct {
|
||||||
|
uint8_t command[16 * 7];
|
||||||
|
uint16_t command_write_index;
|
||||||
|
bool ready_for_pulse;
|
||||||
|
bool ready_for_write;
|
||||||
|
bool ready_for_stop;
|
||||||
|
bool disable_commands;
|
||||||
|
|
||||||
|
/* Screen buffer */
|
||||||
|
uint8_t screen_buffer[160 * 144];
|
||||||
|
/* Multiplayer Input */
|
||||||
|
uint8_t player_count, current_player;
|
||||||
|
} GB_sgb_t;
|
||||||
|
|
||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
||||||
void GB_sgb_render(GB_gameboy_t *gb);
|
void GB_sgb_render(GB_gameboy_t *gb);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user