New savestate implementation

This uses BSON as savestate format, to allow external tools to parse it
(so that we can add proper test of the states). The BSON is not 100%
correct according to spec (no ordered keys) but can be parsed by most
libraries.

This fixes also a bug in the savestate palette color recalculation that
was wrongly overwritting the original palette (which could cause some
problems on some games).

Also fixes some potential issues by serializing some more stuff and
cleans up unused stuff.

Testing shows that states look good and there's only minor differences
in audio ticks, related to buffer sizes (since buffer flushes are
de-synced from video frames due to different frequency).
This commit is contained in:
David Guillen Fandos 2021-08-24 17:15:27 +02:00
parent 40b3cd51ec
commit 7068cbc95b
16 changed files with 644 additions and 216 deletions

View File

@ -5,6 +5,7 @@ SOURCES_ASM := $(CORE_DIR)/bios_data.S
SOURCES_C := $(CORE_DIR)/main.c \
$(CORE_DIR)/cpu.c \
$(CORE_DIR)/gba_memory.c \
$(CORE_DIR)/savestate.c \
$(CORE_DIR)/video.c \
$(CORE_DIR)/input.c \
$(CORE_DIR)/sound.c \

View File

@ -77,13 +77,13 @@
#ifdef USE_BGR_FORMAT
#define convert_palette(value) \
value = ((value & 0x7FE0) << 1) | (value & 0x1F)
(((value & 0x7FE0) << 1) | (value & 0x1F))
#elif defined(USE_XBGR1555_FORMAT)
#define convert_palette(value) \
value = (value & 0x7FFF)
(value & 0x7FFF)
#else
#define convert_palette(value) \
value = ((value & 0x1F) << 11) | ((value & 0x03E0) << 1) | (value >> 10)
(((value & 0x1F) << 11) | ((value & 0x03E0) << 1) | (value >> 10))
#endif
#define GBA_SCREEN_WIDTH (240)
@ -153,6 +153,7 @@ typedef u32 fixed8_24;
#include <stdarg.h>
#include "cpu.h"
#include "gba_memory.h"
#include "savestate.h"
#include "video.h"
#include "input.h"
#include "sound.h"

29
cpu.c
View File

@ -3774,13 +3774,26 @@ void init_cpu(void)
reg_mode[MODE_SUPERVISOR][5] = 0x03007FE0;
}
#define cpu_savestate_builder(type) \
void cpu_##type##_savestate(void) \
{ \
state_mem_##type(reg, 4 * REG_IGNORE); \
state_mem_##type##_array(spsr); \
state_mem_##type##_array(reg_mode); \
bool cpu_read_savestate(const u8 *src)
{
const u8 *cpudoc = bson_find_key(src, "cpu");
return bson_read_int32(cpudoc, "bus-value", &bios_read_protect) &&
bson_read_int32_array(cpudoc, "regs", reg, REG_IGNORE) &&
bson_read_int32_array(cpudoc, "spsr", spsr, 6) &&
bson_read_int32_array(cpudoc, "regmod", (u32*)reg_mode, 7*7);
}
cpu_savestate_builder(read)
cpu_savestate_builder(write)
unsigned cpu_write_savestate(u8 *dst)
{
u8 *wbptr, *startp = dst;
bson_start_document(dst, "cpu", wbptr);
bson_write_int32array(dst, "regs", reg, REG_IGNORE);
bson_write_int32array(dst, "spsr", spsr, 6);
bson_write_int32array(dst, "regmod", reg_mode, 7*7);
bson_write_int32(dst, "bus-value", bios_read_protect);
bson_finish_document(dst, wbptr);
return (unsigned int)(dst - startp);
}

5
cpu.h
View File

@ -20,6 +20,7 @@
#ifndef CPU_H
#define CPU_H
#include <stdbool.h>
#include "gpsp_config.h"
// System mode and user mode are represented as the same here
@ -118,8 +119,8 @@ void function_cc execute_store_u16(u32 address, u32 source);
void function_cc execute_store_u32(u32 address, u32 source);
u32 function_cc execute_arm_translate(u32 cycles);
void init_translater(void);
void cpu_write_savestate(void);
void cpu_read_savestate(void);
unsigned cpu_write_savestate(u8* dst);
bool cpu_read_savestate(const u8 *src);
u8 function_cc *block_lookup_address_arm(u32 pc);
u8 function_cc *block_lookup_address_thumb(u32 pc);

View File

@ -360,7 +360,7 @@ sram_size_type sram_size = SRAM_SIZE_32KB;
flash_mode_type flash_mode = FLASH_BASE_MODE;
u32 flash_command_position = 0;
u8 *flash_bank_ptr = gamepak_backup;
u32 flash_bank_num; // 0 or 1
flash_device_id_type flash_device_id = FLASH_DEVICE_MACRONIX_64KB;
flash_manufacturer_id_type flash_manufacturer_id =
@ -398,7 +398,10 @@ u8 read_backup(u32 address)
}
}
else
value = flash_bank_ptr[address];
{
u32 fulladdr = address + 64*1024*flash_bank_num;
value = gamepak_backup[fulladdr];
}
return value;
}
@ -656,7 +659,6 @@ static cpu_alert_type trigger_dma(u32 dma_number, u32 value)
u32 dst_address = 0xFFFFFFF & (read_dmareg(REG_DMA0DAD, dma_number) |
(read_dmareg(REG_DMA0DAD + 1, dma_number) << 16));
dma[dma_number].dma_channel = dma_number;
dma[dma_number].source_address = src_address;
dma[dma_number].dest_address = dst_address;
dma[dma_number].source_direction = (dma_increment_type)((value >> 7) & 3);
@ -702,7 +704,7 @@ static cpu_alert_type trigger_dma(u32 dma_number, u32 value)
write_dmareg(REG_DMA0CNT_H, dma_number, value);
if(start_type == DMA_START_IMMEDIATELY)
return dma_transfer(&dma[dma_number]);
return dma_transfer(dma_number);
}
}
else
@ -1431,7 +1433,7 @@ cpu_alert_type function_cc write_io_register32(u32 address, u32 value)
{ \
u32 palette_address = address; \
address16(palette_ram, palette_address) = eswap16(value); \
convert_palette(value); \
value = convert_palette(value); \
address16(palette_ram_converted, palette_address) = value; \
} \
@ -1441,9 +1443,9 @@ cpu_alert_type function_cc write_io_register32(u32 address, u32 value)
u32 value_high = value >> 16; \
u32 value_low = value & 0xFFFF; \
address32(palette_ram, palette_address) = eswap32(value); \
convert_palette(value_high); \
value_high = convert_palette(value_high); \
address16(palette_ram_converted, palette_address + 2) = value_high; \
convert_palette(value_low); \
value_low = convert_palette(value_low); \
address16(palette_ram_converted, palette_address) = value_low; \
} \
@ -1535,7 +1537,8 @@ void function_cc write_backup(u32 address, u32 value)
(flash_mode == FLASH_ERASE_MODE) && (value == 0x30))
{
// Erase sector
memset(flash_bank_ptr + (address & 0xF000), 0xFF, 1024 * 4);
u32 fulladdr = (address & 0xF000) + 64*1024*flash_bank_num;
memset(&gamepak_backup[fulladdr], 0xFF, 1024 * 4);
flash_mode = FLASH_BASE_MODE;
flash_command_position = 0;
}
@ -1545,7 +1548,7 @@ void function_cc write_backup(u32 address, u32 value)
(flash_mode == FLASH_BANKSWITCH_MODE) && (address == 0x0000) &&
(flash_size == FLASH_SIZE_128KB))
{
flash_bank_ptr = gamepak_backup + ((value & 0x01) * (1024 * 64));
flash_bank_num = value & 1;
flash_mode = FLASH_BASE_MODE;
}
else
@ -1553,7 +1556,8 @@ void function_cc write_backup(u32 address, u32 value)
if((flash_command_position == 0) && (flash_mode == FLASH_WRITE_MODE))
{
// Write value to flash ROM
flash_bank_ptr[address] = value;
u32 fulladdr = address + 64*1024*flash_bank_num;
gamepak_backup[fulladdr] = value;
flash_mode = FLASH_BASE_MODE;
}
else
@ -2791,11 +2795,12 @@ cpu_alert_type dma_tf_loop##transfer_size( \
dma_tf_loop_builder(16);
dma_tf_loop_builder(32);
cpu_alert_type dma_transfer(dma_transfer_type *dma)
cpu_alert_type dma_transfer(unsigned dma_chan)
{
u32 length = dma->length;
u32 src_ptr = dma->source_address;
uintptr_t dest_ptr = dma->dest_address;
dma_transfer_type *dmach = &dma[dma_chan];
u32 length = dmach->length;
u32 src_ptr = dmach->source_address;
uintptr_t dest_ptr = dmach->dest_address;
cpu_alert_type ret = CPU_ALERT_NONE;
// Technically this should be done for source and destination, but
@ -2805,49 +2810,49 @@ cpu_alert_type dma_transfer(dma_transfer_type *dma)
{
u32 first_length = ((dest_ptr & 0xFF000000) + 0x1000000) - dest_ptr;
u32 second_length = length - first_length;
dma->length = first_length;
dmach->length = first_length;
dma_transfer(dma);
dma_transfer(dma_chan);
dma->length = length;
dmach->length = length;
length = second_length;
dest_ptr += first_length;
src_ptr += first_length;
}
if (dma->source_direction < 3)
if (dmach->source_direction < 3)
{
const int stridetbl[4] = {1, -1, 0, 1};
int dst_stride = stridetbl[dma->dest_direction];
int src_stride = stridetbl[dma->source_direction];
bool dst_wb = dma->dest_direction < 3;
int dst_stride = stridetbl[dmach->dest_direction];
int src_stride = stridetbl[dmach->source_direction];
bool dst_wb = dmach->dest_direction < 3;
if(dma->length_type == DMA_16BIT)
if(dmach->length_type == DMA_16BIT)
{
src_ptr &= ~0x01;
dest_ptr &= ~0x01;
ret = dma_tf_loop16(src_ptr, dest_ptr, 2 * src_stride, 2 * dst_stride, dst_wb, length, dma);
ret = dma_tf_loop16(src_ptr, dest_ptr, 2 * src_stride, 2 * dst_stride, dst_wb, length, dmach);
}
else
{
src_ptr &= ~0x03;
dest_ptr &= ~0x03;
ret = dma_tf_loop32(src_ptr, dest_ptr, 4 * src_stride, 4 * dst_stride, dst_wb, length, dma);
ret = dma_tf_loop32(src_ptr, dest_ptr, 4 * src_stride, 4 * dst_stride, dst_wb, length, dmach);
}
}
if((dma->repeat_type == DMA_NO_REPEAT) ||
(dma->start_type == DMA_START_IMMEDIATELY))
if((dmach->repeat_type == DMA_NO_REPEAT) ||
(dmach->start_type == DMA_START_IMMEDIATELY))
{
u32 cntrl = read_dmareg(REG_DMA0CNT_H, dma->dma_channel);
write_dmareg(REG_DMA0CNT_H, dma->dma_channel, cntrl & (~0x8000));
dma->start_type = DMA_INACTIVE;
u32 cntrl = read_dmareg(REG_DMA0CNT_H, dma_chan);
write_dmareg(REG_DMA0CNT_H, dma_chan, cntrl & (~0x8000));
dmach->start_type = DMA_INACTIVE;
}
if(dma->irq)
if(dmach->irq)
{
raise_interrupt(IRQ_DMA0 << dma->dma_channel);
raise_interrupt(IRQ_DMA0 << dma_chan);
ret = CPU_ALERT_IRQ;
}
@ -3010,7 +3015,7 @@ void init_memory(void)
sram_size = SRAM_SIZE_32KB;
//flash_size = FLASH_SIZE_64KB;
flash_bank_ptr = gamepak_backup;
flash_bank_num = 0;
flash_command_position = 0;
eeprom_size = EEPROM_512_BYTE;
eeprom_mode = EEPROM_BASE_MODE;
@ -3038,100 +3043,132 @@ void memory_term(void)
}
}
#define savestate_block(type) \
cpu_##type##_savestate(); \
input_##type##_savestate(); \
main_##type##_savestate(); \
memory_##type##_savestate(); \
sound_##type##_savestate();
const u8 *state_mem_read_ptr;
u8 *state_mem_write_ptr;
void gba_load_state(const void* src)
bool memory_read_savestate(const u8 *src)
{
u32 i;
u32 current_color;
int i;
const u8 *memdoc = bson_find_key(src, "memory");
const u8 *bakdoc = bson_find_key(src, "backup");
const u8 *dmadoc = bson_find_key(src, "dma");
if (!memdoc || !bakdoc || !dmadoc)
return false;
state_mem_read_ptr = (u8*)src;
savestate_block(read);
if (!(
bson_read_bytes(memdoc, "iwram", &iwram[0x8000], 0x8000) &&
bson_read_bytes(memdoc, "ewram", ewram, 0x40000) &&
bson_read_bytes(memdoc, "vram", vram, sizeof(vram)) &&
bson_read_bytes(memdoc, "oamram", oam_ram, sizeof(oam_ram)) &&
bson_read_bytes(memdoc, "palram", palette_ram, sizeof(palette_ram)) &&
bson_read_bytes(memdoc, "ioregs", io_registers, sizeof(io_registers)) &&
#ifdef HAVE_DYNAREC
if (dynarec_enable)
init_caches();
#endif
bson_read_int32(bakdoc, "backup-type", &backup_type) &&
bson_read_int32(bakdoc, "sram-size", &sram_size) &&
reg[OAM_UPDATED] = 1;
gbc_sound_update = 1;
bson_read_int32(bakdoc, "flash-mode", &flash_mode) &&
bson_read_int32(bakdoc, "flash-cmd-pos", &flash_command_position) &&
bson_read_int32(bakdoc, "flash-bank-num", &flash_bank_num) &&
bson_read_int32(bakdoc, "flash-dev-id", &flash_device_id) &&
bson_read_int32(bakdoc, "flash-man-id", &flash_manufacturer_id) &&
bson_read_int32(bakdoc, "flash-size", &flash_size) &&
for(i = 0; i < 512; i++)
{
current_color = palette_ram[i];
palette_ram_converted[i] =
convert_palette(current_color);
}
bson_read_int32(bakdoc, "eeprom-size", &eeprom_size) &&
bson_read_int32(bakdoc, "eeprom-mode", &eeprom_mode) &&
bson_read_int32(bakdoc, "eeprom-addr", &eeprom_address) &&
bson_read_int32(bakdoc, "eeprom-counter", &eeprom_counter) &&
video_reload_counters();
bson_read_int32(bakdoc, "rtc-state", &rtc_state) &&
bson_read_int32(bakdoc, "rtc-write-mode", &rtc_write_mode) &&
bson_read_int32(bakdoc, "rtc-cmd", &rtc_command) &&
bson_read_int32(bakdoc, "rtc-status", &rtc_status) &&
bson_read_int32(bakdoc, "rtc-data-byte-cnt", &rtc_data_bytes) &&
bson_read_int32(bakdoc, "rtc-bit-cnt", (u32*)&rtc_bit_count) &&
bson_read_bytes(bakdoc, "rtc-regs", rtc_registers, sizeof(rtc_registers)) &&
bson_read_int32_array(bakdoc, "rtc-data-words", rtc_data,
sizeof(rtc_data)/sizeof(rtc_data[0]))))
return false;
// Oops, these contain raw pointers
for(i = 0; i < 4; i++)
gbc_sound_channel[i].sample_table_idx = 2;
for (i = 0; i < DMA_CHAN_CNT; i++)
{
char tname[2] = {'0' + i, 0};
const u8 *dmastr = bson_find_key(dmadoc, tname);
if (!(
bson_read_int32(dmastr, "src-addr", &dma[i].source_address) &&
bson_read_int32(dmastr, "dst-addr", &dma[i].dest_address) &&
bson_read_int32(dmastr, "src-dir", &dma[i].source_direction) &&
bson_read_int32(dmastr, "dst-dir", &dma[i].dest_direction) &&
bson_read_int32(dmastr, "len", &dma[i].length) &&
bson_read_int32(dmastr, "size", &dma[i].length_type) &&
bson_read_int32(dmastr, "repeat", &dma[i].repeat_type) &&
bson_read_int32(dmastr, "start", &dma[i].start_type) &&
bson_read_int32(dmastr, "dsc", &dma[i].direct_sound_channel) &&
bson_read_int32(dmastr, "irq", &dma[i].irq)))
return false;
}
instruction_count = 0;
reg[CHANGED_PC_STATUS] = 1;
return true;
}
void gba_save_state(void* dst)
unsigned memory_write_savestate(u8 *dst)
{
state_mem_write_ptr = (u8*)dst;
savestate_block(write);
int i;
u8 *wbptr, *wbptr2, *startp = dst;
bson_start_document(dst, "memory", wbptr);
bson_write_bytes(dst, "iwram", &iwram[0x8000], 0x8000);
bson_write_bytes(dst, "ewram", ewram, 0x40000);
bson_write_bytes(dst, "vram", vram, sizeof(vram));
bson_write_bytes(dst, "oamram", oam_ram, sizeof(oam_ram));
bson_write_bytes(dst, "palram", palette_ram, sizeof(palette_ram));
bson_write_bytes(dst, "ioregs", io_registers, sizeof(io_registers));
bson_finish_document(dst, wbptr);
bson_start_document(dst, "backup", wbptr);
bson_write_int32(dst, "backup-type", backup_type);
bson_write_int32(dst, "sram-size", sram_size);
bson_write_int32(dst, "flash-mode", flash_mode);
bson_write_int32(dst, "flash-cmd-pos", flash_command_position);
bson_write_int32(dst, "flash-bank-num", flash_bank_num);
bson_write_int32(dst, "flash-dev-id", flash_device_id);
bson_write_int32(dst, "flash-man-id", flash_manufacturer_id);
bson_write_int32(dst, "flash-size", flash_size);
bson_write_int32(dst, "eeprom-size", eeprom_size);
bson_write_int32(dst, "eeprom-mode", eeprom_mode);
bson_write_int32(dst, "eeprom-addr", eeprom_address);
bson_write_int32(dst, "eeprom-counter", eeprom_counter);
bson_write_int32(dst, "rtc-state", rtc_state);
bson_write_int32(dst, "rtc-write-mode", rtc_write_mode);
bson_write_int32(dst, "rtc-cmd", rtc_command);
bson_write_int32(dst, "rtc-status", rtc_status);
bson_write_int32(dst, "rtc-data-byte-cnt", rtc_data_bytes);
bson_write_int32(dst, "rtc-bit-cnt", rtc_bit_count);
bson_write_bytes(dst, "rtc-regs", rtc_registers, sizeof(rtc_registers));
bson_write_int32array(dst, "rtc-data-words", rtc_data,
sizeof(rtc_data)/sizeof(rtc_data[0]));
bson_finish_document(dst, wbptr);
bson_start_document(dst, "dma", wbptr);
for (i = 0; i < DMA_CHAN_CNT; i++)
{
char tname[2] = {'0' + i, 0};
bson_start_document(dst, tname, wbptr2);
bson_write_int32(dst, "src-addr", dma[i].source_address);
bson_write_int32(dst, "dst-addr", dma[i].dest_address);
bson_write_int32(dst, "src-dir", dma[i].source_direction);
bson_write_int32(dst, "dst-dir", dma[i].dest_direction);
bson_write_int32(dst, "len", dma[i].length);
bson_write_int32(dst, "size", dma[i].length_type);
bson_write_int32(dst, "repeat", dma[i].repeat_type);
bson_write_int32(dst, "start", dma[i].start_type);
bson_write_int32(dst, "dsc", dma[i].direct_sound_channel);
bson_write_int32(dst, "irq", dma[i].irq);
bson_finish_document(dst, wbptr2);
}
bson_finish_document(dst, wbptr);
return (unsigned int)(dst - startp);
}
#define memory_savestate_builder(type) \
void memory_##type##_savestate(void) \
{ \
state_mem_##type##_variable(backup_type); \
state_mem_##type##_variable(sram_size); \
state_mem_##type##_variable(flash_mode); \
state_mem_##type##_variable(flash_command_position); \
state_mem_##type##_pointer(flash_bank_ptr, gamepak_backup); \
state_mem_##type##_variable(flash_device_id); \
state_mem_##type##_variable(flash_manufacturer_id); \
state_mem_##type##_variable(flash_size); \
state_mem_##type##_variable(eeprom_size); \
state_mem_##type##_variable(eeprom_mode); \
state_mem_##type##_variable(eeprom_address); \
state_mem_##type##_variable(eeprom_counter); \
state_mem_##type##_variable(rtc_state); \
state_mem_##type##_variable(rtc_write_mode); \
state_mem_##type##_array(rtc_registers); \
state_mem_##type##_variable(rtc_command); \
state_mem_##type##_array(rtc_data); \
state_mem_##type##_variable(rtc_status); \
state_mem_##type##_variable(rtc_data_bytes); \
state_mem_##type##_variable(rtc_bit_count); \
state_mem_##type##_array(dma); \
\
state_mem_##type(iwram + 0x8000, 0x8000); \
state_mem_##type(ewram, 0x40000); \
state_mem_##type(vram, 0x18000); \
state_mem_##type(oam_ram, 0x400); \
state_mem_##type(palette_ram, 0x400); \
state_mem_##type(io_registers, 0x400); \
\
/* This should not happen anymore :P */ \
if((flash_bank_ptr < gamepak_backup) || \
(flash_bank_ptr > (&gamepak_backup[sizeof(gamepak_backup)])))\
flash_bank_ptr = gamepak_backup; \
}
memory_savestate_builder(read)
memory_savestate_builder(write)
static s32 load_gamepak_raw(const char *name)
{
unsigned i, j;

View File

@ -23,6 +23,8 @@
#include "libretro.h"
extern int use_libretro_save_method;
#define DMA_CHAN_CNT 4
typedef enum
{
DMA_START_IMMEDIATELY = 0,
@ -67,7 +69,6 @@ typedef enum
typedef struct
{
u32 dma_channel;
u32 source_address;
u32 dest_address;
u32 length;
@ -213,7 +214,7 @@ extern char gamepak_code[5];
extern char gamepak_maker[3];
extern char gamepak_filename[512];
cpu_alert_type dma_transfer(dma_transfer_type *dma);
cpu_alert_type dma_transfer(unsigned dma_chan);
u8 *memory_region(u32 address, u32 *memory_limit);
u32 load_gamepak(const struct retro_game_info* info, const char *name);
u32 load_backup(char *name);
@ -227,7 +228,7 @@ u8 *load_gamepak_page(u32 physical_index);
extern u32 oam_update;
extern u32 gbc_sound_update;
extern u32 gbc_sound_wave_update;
extern dma_transfer_type dma[4];
extern dma_transfer_type dma[DMA_CHAN_CNT];
extern u8 open_gba_bios_rom[1024*16];
extern u32 bios_read_protect;
@ -247,9 +248,6 @@ extern u32 reg[64];
extern flash_device_id_type flash_device_id;
extern const u8 *state_mem_read_ptr;
extern u8 *state_mem_write_ptr;
typedef enum
{
BACKUP_SRAM,
@ -306,12 +304,6 @@ extern eeprom_size_type eeprom_size;
extern u8 gamepak_backup[1024 * 128];
static inline void state_mem_write(const void* src, size_t size)
{
memcpy(state_mem_write_ptr, src, size);
state_mem_write_ptr += size;
}
// Page sticky bit routines
extern u32 gamepak_sticky_bit[1024/32];
static inline void touch_gamepak_page(u32 physical_index)
@ -327,33 +319,7 @@ static inline void clear_gamepak_stickybits(void)
memset(gamepak_sticky_bit, 0, sizeof(gamepak_sticky_bit));
}
/* this is an upper limit, ToDo : calculate the exact state size */
#define GBA_STATE_MEM_SIZE (512*1024)
#define state_mem_write_array(array) state_mem_write(array, sizeof(array))
#define state_mem_write_variable(variable) state_mem_write(&variable, sizeof(variable))
#define state_mem_write_pointer(ptr, base) { \
u32 offset = ((u8*)ptr) - ((u8*)base); \
state_mem_write(&offset, sizeof(offset)); \
}
static inline void state_mem_read(void* dst, size_t size)
{
memcpy(dst, state_mem_read_ptr, size);
state_mem_read_ptr += size;
}
#define state_mem_read_array(array) state_mem_read(array, sizeof(array))
#define state_mem_read_variable(variable) state_mem_read(&variable, sizeof(variable))
#define state_mem_read_pointer(ptr, base) { \
u32 offset; \
state_mem_read(&offset, sizeof(offset)); \
ptr = (typeof(ptr))(((u8*)base) + offset); \
}
void memory_write_savestate(void);
void memory_read_savestate(void);
void gba_load_state(const void *src);
void gba_save_state(void *dst);
unsigned memory_write_savestate(u8 *dst);
bool memory_read_savestate(const u8*src);
#endif

22
input.c
View File

@ -133,11 +133,21 @@ u32 update_input(void)
return 0;
}
#define input_savestate_builder(type) \
void input_##type##_savestate(void) \
{ \
state_mem_##type##_variable(old_key); \
bool input_read_savestate(const u8 *src)
{
const u8 *p = bson_find_key(src, "input");
if (p)
return bson_read_int32(p, "prevkey", &old_key);
return false;
}
input_savestate_builder(read)
input_savestate_builder(write)
unsigned input_write_savestate(u8 *dst)
{
u8 *wbptr1, *startp = dst;
bson_start_document(dst, "input", wbptr1);
bson_write_int32(dst, "prevkey", old_key);
bson_finish_document(dst, wbptr1);
return (unsigned int)(dst - startp);
}

View File

@ -75,7 +75,7 @@ extern unsigned turbo_b_counter;
void init_input(void);
u32 update_input(void);
void input_write_savestate(void);
void input_read_savestate(void);
unsigned input_write_savestate(u8* dst);
bool input_read_savestate(const u8 *src);
#endif

View File

@ -664,9 +664,7 @@ bool retro_unserialize(const void* data, size_t size)
if (size != GBA_STATE_MEM_SIZE)
return false;
gba_load_state(data);
return true;
return gba_load_state(data);
}
void retro_cheat_reset(void)

71
main.c
View File

@ -152,7 +152,7 @@ u32 update_gba(void)
for(i = 0; i < 4; i++)
{
if(dma[i].start_type == DMA_START_HBLANK)
dma_transfer(&dma[i]);
dma_transfer(i);
}
}
@ -183,7 +183,7 @@ u32 update_gba(void)
for(i = 0; i < 4; i++)
{
if(dma[i].start_type == DMA_START_VBLANK)
dma_transfer(&dma[i]);
dma_transfer(i);
}
}
else
@ -279,16 +279,65 @@ void change_ext(const char *src, char *buffer, const char *extension)
strcpy(dot_position, extension);
}
#define main_savestate_builder(type) \
void main_##type##_savestate(void) \
{ \
state_mem_##type##_variable(cpu_ticks); \
state_mem_##type##_variable(execute_cycles); \
state_mem_##type##_variable(video_count); \
state_mem_##type##_array(timer); \
bool main_read_savestate(const u8 *src)
{
int i;
const u8 *p1 = bson_find_key(src, "emu");
const u8 *p2 = bson_find_key(src, "timers");
if (!p1 || !p2)
return false;
execute_cycles = 123;
if (!(bson_read_int32(p1, "cpu-ticks", &cpu_ticks) &&
bson_read_int32(p1, "exec-cycles", &execute_cycles) &&
bson_read_int32(p1, "video-count", (u32*)&video_count)))
return false;
for (i = 0; i < 4; i++)
{
char tname[2] = {'0' + i, 0};
const u8 *p = bson_find_key(p2, tname);
if (!(
bson_read_int32(p, "count", (u32*)&timer[i].count) &&
bson_read_int32(p, "reload", &timer[i].reload) &&
bson_read_int32(p, "prescale", &timer[i].prescale) &&
bson_read_int32(p, "freq-step", &timer[i].frequency_step) &&
bson_read_int32(p, "dsc", &timer[i].direct_sound_channels) &&
bson_read_int32(p, "irq", &timer[i].irq) &&
bson_read_int32(p, "status", &timer[i].status)))
return false;
}
return true;
}
main_savestate_builder(read)
main_savestate_builder(write)
unsigned main_write_savestate(u8* dst)
{
int i;
u8 *wbptr, *wbptr2, *startp = dst;
bson_start_document(dst, "emu", wbptr);
bson_write_int32(dst, "cpu-ticks", cpu_ticks);
bson_write_int32(dst, "exec-cycles", execute_cycles);
bson_write_int32(dst, "video-count", video_count);
bson_finish_document(dst, wbptr);
bson_start_document(dst, "timers", wbptr);
for (i = 0; i < 4; i++)
{
char tname[2] = {'0' + i, 0};
bson_start_document(dst, tname, wbptr2);
bson_write_int32(dst, "count", timer[i].count);
bson_write_int32(dst, "reload", timer[i].reload);
bson_write_int32(dst, "prescale", timer[i].prescale);
bson_write_int32(dst, "freq-step", timer[i].frequency_step);
bson_write_int32(dst, "dsc", timer[i].direct_sound_channels);
bson_write_int32(dst, "irq", timer[i].irq);
bson_write_int32(dst, "status", timer[i].status);
bson_finish_document(dst, wbptr2);
}
bson_finish_document(dst, wbptr);
return (unsigned int)(dst - startp);
}

4
main.h
View File

@ -92,8 +92,8 @@ void reset_gba(void);
void init_main(void);
void game_name_ext(char *src, char *buffer, char *extension);
void main_write_savestate(void);
void main_read_savestate(void);
unsigned main_write_savestate(u8* ptr);
bool main_read_savestate(const u8 *src);
u32 file_length(FILE *fp);

153
savestate.c Normal file
View File

@ -0,0 +1,153 @@
#include "common.h"
const u8 *state_mem_read_ptr;
u8 *state_mem_write_ptr;
const u8* bson_find_key(const u8 *srcp, const char *key)
{
unsigned keyl = strlen(key) + 1;
unsigned doclen = bson_read_u32(srcp);
const u8* p = &srcp[4];
while (*p != 0 && (p - srcp) < doclen) {
u8 tp = *p;
unsigned tlen = strlen((char*)&p[1]) + 1;
if (keyl == tlen && !memcmp(key, &p[1], tlen))
return &p[tlen + 1];
p += 1 + tlen;
if (tp == 3 || tp == 4)
p += bson_read_u32(p);
else if (tp == 5)
p += bson_read_u32(p) + 1 + 4;
else if (tp == 0x10)
p += 4;
}
return NULL;
}
bool bson_read_int32(const u8 *srcp, const char *key, u32* value)
{
const u8* p = srcp ? bson_find_key(srcp, key) : NULL;
if (!p)
return false;
*value = bson_read_u32(p);
return true;
}
bool bson_read_int32_array(const u8 *srcp, const char *key, u32* value, unsigned cnt)
{
const u8* p = srcp ? bson_find_key(srcp, key) : NULL;
if (p) {
unsigned arrsz = bson_read_u32(p);
p += 4;
if (arrsz < 5)
return false;
arrsz = (arrsz - 5) >> 3;
while (arrsz--) {
p += 4; // type and name
*value++ = bson_read_u32(p);
p += 4; // value
}
return true;
}
*value = bson_read_u32(p);
return false;
}
bool bson_read_bytes(const u8 *srcp, const char *key, void* buffer, unsigned cnt)
{
const u8* p = srcp ? bson_find_key(srcp, key) : NULL;
if (p) {
unsigned bufsz = bson_read_u32(p);
if (bufsz != cnt)
return false;
// Skip byte array type and size
memcpy(buffer, &p[5], cnt);
return true;
}
return false;
}
bool gba_load_state(const void* src)
{
u32 i, tmp;
u8* srcptr = (u8*)src;
u32 docsize = bson_read_u32(srcptr);
if (docsize != GBA_STATE_MEM_SIZE)
return false;
if (!bson_read_int32(srcptr, "info-magic", &tmp) || tmp != GBA_STATE_MAGIC)
return false;
if (!bson_read_int32(srcptr, "info-version", &tmp) || tmp != GBA_STATE_VERSION)
return false;
if (!(cpu_read_savestate(srcptr) &&
input_read_savestate(srcptr) &&
main_read_savestate(srcptr) &&
memory_read_savestate(srcptr) &&
sound_read_savestate(srcptr)))
{
// TODO: reset state instead! Should revert instead??
return false;
}
// Generate converted palette (since it is not saved)
for(i = 0; i < 512; i++)
{
palette_ram_converted[i] = convert_palette(palette_ram[i]);
}
video_reload_counters();
// Reset most of the frame state and dynarec state
#ifdef HAVE_DYNAREC
if (dynarec_enable)
init_caches();
#endif
instruction_count = 0;
reg[CHANGED_PC_STATUS] = 1;
reg[COMPLETED_FRAME] = 0;
reg[OAM_UPDATED] = 1;
gbc_sound_update = 1;
return true;
}
void gba_save_state(void* dst)
{
u8 *stptr = (u8*)dst;
u8 *wrptr = (u8*)dst;
// Initial bson size
bson_write_u32(wrptr, 0);
// Add some info fields
bson_write_int32(wrptr, "info-magic", GBA_STATE_MAGIC);
bson_write_int32(wrptr, "info-version", GBA_STATE_VERSION);
wrptr += cpu_write_savestate(wrptr);
wrptr += input_write_savestate(wrptr);
wrptr += main_write_savestate(wrptr);
wrptr += memory_write_savestate(wrptr);
wrptr += sound_write_savestate(wrptr);
// The padding space is pushed into a padding field for easy parsing
{
unsigned padsize = GBA_STATE_MEM_SIZE - (wrptr - stptr);
padsize -= 1 + 9 + 4 + 1 + 1;
*wrptr++ = 0x05; // Byte array
bson_write_cstring(wrptr, "zpadding");
bson_write_u32(wrptr, padsize);
*wrptr++ = 0;
wrptr += padsize;
}
*wrptr++ = 0;
// Update the doc size
bson_write_u32(stptr, wrptr - stptr);
}

76
savestate.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef SAVESTATE_H
#define SAVESTATE_H
#define bson_write_u32(p, value) \
{ \
u32 __tval = (value); \
*p++ = (u8)((__tval)); \
*p++ = (u8)((__tval) >> 8); \
*p++ = (u8)((__tval) >> 16); \
*p++ = (u8)((__tval) >> 24); \
}
#define bson_read_u32(p) \
((p[3] << 24) | (p[2] << 16) | \
(p[1] << 8) | (p[0] << 0))
#define bson_write_cstring(p, value) \
memcpy(p, value, strlen(value)+1); \
p += strlen(value)+1;
#define bson_write_int32(p, key, value) \
*p++ = 0x10; \
bson_write_cstring(p, key); \
bson_write_u32(p, value);
#define bson_write_int32array(p, key, arr, cnt) \
{ \
u32 *arrptr = (u32*)(arr); \
int _n; \
*p++ = 0x4; \
bson_write_cstring(p, key); \
bson_write_u32(p, 5 + (cnt) * 8); \
for (_n = 0; _n < (cnt); _n++) { \
char ak[3] = {'0'+(_n/10), '0'+(_n%10), 0}; \
bson_write_int32(p, ak, arrptr[_n]); \
} \
*p++ = 0; \
}
#define bson_write_bytes(p, key, value, vlen) \
*p++ = 0x05; \
bson_write_cstring(p, key); \
bson_write_u32(p, vlen); \
*p++ = 0; \
memcpy(p, value, vlen); \
p += vlen;
#define bson_start_document(p, key, hdrptr) \
*p++ = 0x03; \
bson_write_cstring(p, key); \
hdrptr = p; \
bson_write_u32(p, 0);
#define bson_finish_document(p, hdrptr) \
{ \
u32 _sz = p - hdrptr + 1; \
*p++ = 0; \
bson_write_u32(hdrptr, _sz); \
}
const u8* bson_find_key(const u8 *srcp, const char *key);
bool bson_read_int32(const u8 *srcp, const char *key, u32* value);
bool bson_read_int32_array(const u8 *srcp, const char *key, u32* value, unsigned cnt);
bool bson_read_bytes(const u8 *srcp, const char *key, void* buffer, unsigned cnt);
/* this is an upper limit, leave room for future (?) stuff */
#define GBA_STATE_MEM_SIZE (416*1024)
#define GBA_STATE_MAGIC 0x06BAC0DE
#define GBA_STATE_VERSION 0x00010000
bool gba_load_state(const void *src);
void gba_save_state(void *dst);
#endif

168
sound.c
View File

@ -29,7 +29,6 @@ u32 sound_on;
static s16 sound_buffer[BUFFER_SIZE];
static u32 sound_buffer_base;
static u32 sound_last_cpu_ticks;
static fixed16_16 gbc_sound_tick_step;
/* Queue 1 sample to the top of the DS FIFO, wrap around circularly */
@ -164,10 +163,10 @@ void sound_timer(fixed8_24 frequency_step, u32 channel)
if(((ds->fifo_top - ds->fifo_base) % 32) <= 16)
{
if(dma[1].direct_sound_channel == channel)
dma_transfer(&dma[1]);
dma_transfer(1);
if(dma[2].direct_sound_channel == channel)
dma_transfer(&dma[2]);
dma_transfer(2);
}
}
@ -567,7 +566,6 @@ void reset_sound(void)
sound_on = 0;
sound_buffer_base = 0;
sound_last_cpu_ticks = 0;
memset(sound_buffer, 0, sizeof(sound_buffer));
for(i = 0; i < 2; i++, ds++)
@ -577,7 +575,6 @@ void reset_sound(void)
ds->fifo_top = 0;
ds->fifo_base = 0;
ds->fifo_fractional = 0;
ds->last_cpu_ticks = 0;
memset(ds->fifo, 0, 32);
}
@ -610,25 +607,152 @@ void init_sound(int need_reset)
reset_sound();
}
#define sound_savestate_builder(type) \
void sound_##type##_savestate(void) \
{ \
state_mem_##type##_variable(sound_on); \
state_mem_##type##_variable(sound_buffer_base); \
state_mem_##type##_variable(sound_last_cpu_ticks); \
state_mem_##type##_variable(gbc_sound_buffer_index); \
state_mem_##type##_variable(gbc_sound_last_cpu_ticks); \
state_mem_##type##_variable(gbc_sound_partial_ticks); \
state_mem_##type##_variable(gbc_sound_master_volume_left); \
state_mem_##type##_variable(gbc_sound_master_volume_right); \
state_mem_##type##_variable(gbc_sound_master_volume); \
state_mem_##type##_array(wave_samples); \
state_mem_##type##_array(direct_sound_channel); \
state_mem_##type##_array(gbc_sound_channel); \
bool sound_read_savestate(const u8 *src)
{
int i;
const u8 *snddoc = bson_find_key(src, "sound");
if (!(
bson_read_int32(snddoc, "on", &sound_on) &&
bson_read_int32(snddoc, "buf-base", &sound_buffer_base) &&
bson_read_int32(snddoc, "gbc-buf-idx", &gbc_sound_buffer_index) &&
bson_read_int32(snddoc, "gbc-last-cpu-ticks", &gbc_sound_last_cpu_ticks) &&
bson_read_int32(snddoc, "gbc-partial-ticks", &gbc_sound_partial_ticks) &&
bson_read_int32(snddoc, "gbc-ms-vol-left", &gbc_sound_master_volume_left) &&
bson_read_int32(snddoc, "gbc-ms-vol-right", &gbc_sound_master_volume_right) &&
bson_read_int32(snddoc, "gbc-ms-vol", &gbc_sound_master_volume) &&
bson_read_bytes(snddoc, "wav-samples", wave_samples, sizeof(wave_samples))))
return false;
for (i = 0; i < 2; i++)
{
direct_sound_struct *ds = &direct_sound_channel[i];
char tn[4] = {'d', 's', '0' + i, 0};
const u8 *sndchan = bson_find_key(snddoc, tn);
if (!(
bson_read_int32(sndchan, "status", &ds->status) &&
bson_read_int32(sndchan, "volume", &ds->volume) &&
bson_read_int32(sndchan, "fifo-base", &ds->fifo_base) &&
bson_read_int32(sndchan, "fifo-top", &ds->fifo_top) &&
bson_read_int32(sndchan, "fifo-frac", &ds->fifo_fractional) &&
bson_read_bytes(sndchan, "fifo-bytes", ds->fifo, sizeof(ds->fifo)) &&
bson_read_int32(sndchan, "buf-idx", &ds->buffer_index)))
return false;
}
for (i = 0; i < 4; i++)
{
gbc_sound_struct *gs = &gbc_sound_channel[i];
char tn[4] = {'g', 's', '0' + i, 0};
const u8 *sndchan = bson_find_key(snddoc, tn);
if (!(
bson_read_int32(sndchan, "status", &gs->status) &&
bson_read_int32(sndchan, "rate", &gs->rate) &&
bson_read_int32(sndchan, "freq-step", &gs->frequency_step) &&
bson_read_int32(sndchan, "sample-idx", &gs->sample_index) &&
bson_read_int32(sndchan, "tick-cnt", &gs->tick_counter) &&
bson_read_int32(sndchan, "volume", &gs->total_volume) &&
bson_read_int32(sndchan, "active", &gs->active_flag) &&
bson_read_int32(sndchan, "enable", &gs->master_enable) &&
bson_read_int32(sndchan, "env-vol0", &gs->envelope_initial_volume) &&
bson_read_int32(sndchan, "env-vol", &gs->envelope_volume) &&
bson_read_int32(sndchan, "env-dir", &gs->envelope_direction) &&
bson_read_int32(sndchan, "env-status", &gs->envelope_status) &&
bson_read_int32(sndchan, "env-step", &gs->envelope_step) &&
bson_read_int32(sndchan, "env-ticks0", &gs->envelope_initial_ticks) &&
bson_read_int32(sndchan, "env-ticks", &gs->envelope_ticks) &&
bson_read_int32(sndchan, "sweep-status", &gs->sweep_status) &&
bson_read_int32(sndchan, "sweep-dir", &gs->sweep_direction) &&
bson_read_int32(sndchan, "sweep-ticks0", &gs->sweep_initial_ticks) &&
bson_read_int32(sndchan, "sweep-ticks", &gs->sweep_ticks) &&
bson_read_int32(sndchan, "sweep-shift", &gs->sweep_shift) &&
bson_read_int32(sndchan, "wav-type", &gs->wave_type) &&
bson_read_int32(sndchan, "wav-bank", &gs->wave_bank) &&
bson_read_int32(sndchan, "wav-vol", &gs->wave_volume) &&
bson_read_int32(sndchan, "len-status", &gs->length_status) &&
bson_read_int32(sndchan, "len-ticks", &gs->length_ticks) &&
bson_read_int32(sndchan, "noise-type", &gs->noise_type) &&
bson_read_int32(sndchan, "sample-tbl", &gs->sample_table_idx)))
return false;
}
return true;
}
sound_savestate_builder(read)
sound_savestate_builder(write)
unsigned sound_write_savestate(u8 *dst)
{
int i;
u8 *wbptr, *startp = dst;
bson_start_document(dst, "sound", wbptr);
bson_write_int32(dst, "on", sound_on);
bson_write_int32(dst, "buf-base", sound_buffer_base);
bson_write_int32(dst, "gbc-buf-idx", gbc_sound_buffer_index);
bson_write_int32(dst, "gbc-last-cpu-ticks", gbc_sound_last_cpu_ticks);
bson_write_int32(dst, "gbc-partial-ticks", gbc_sound_partial_ticks);
bson_write_int32(dst, "gbc-ms-vol-left", gbc_sound_master_volume_left);
bson_write_int32(dst, "gbc-ms-vol-right", gbc_sound_master_volume_right);
bson_write_int32(dst, "gbc-ms-vol", gbc_sound_master_volume);
bson_write_bytes(dst, "wav-samples", wave_samples, sizeof(wave_samples));
for (i = 0; i < 2; i++)
{
u8 *wbptr2;
char tn[4] = {'d', 's', '0' + i, 0};
bson_start_document(dst, tn, wbptr2);
bson_write_int32(dst, "status", direct_sound_channel[i].status);
bson_write_int32(dst, "volume", direct_sound_channel[i].volume);
bson_write_int32(dst, "fifo-base", direct_sound_channel[i].fifo_base);
bson_write_int32(dst, "fifo-top", direct_sound_channel[i].fifo_top);
bson_write_int32(dst, "fifo-frac", direct_sound_channel[i].fifo_fractional);
bson_write_bytes(dst, "fifo-bytes", direct_sound_channel[i].fifo,
sizeof(direct_sound_channel[i].fifo));
bson_write_int32(dst, "buf-idx", direct_sound_channel[i].buffer_index);
bson_finish_document(dst, wbptr2);
}
for (i = 0; i < 4; i++)
{
gbc_sound_struct *gs = &gbc_sound_channel[i];
u8 *wbptr2;
char tn[4] = {'g', 's', '0' + i, 0};
bson_start_document(dst, tn, wbptr2);
bson_write_int32(dst, "status", gs->status);
bson_write_int32(dst, "rate", gs->rate);
bson_write_int32(dst, "freq-step", gs->frequency_step);
bson_write_int32(dst, "sample-idx", gs->sample_index);
bson_write_int32(dst, "tick-cnt", gs->tick_counter);
bson_write_int32(dst, "volume", gs->total_volume);
bson_write_int32(dst, "active", gs->active_flag);
bson_write_int32(dst, "enable", gs->master_enable);
bson_write_int32(dst, "env-vol0", gs->envelope_initial_volume);
bson_write_int32(dst, "env-vol", gs->envelope_volume);
bson_write_int32(dst, "env-dir", gs->envelope_direction);
bson_write_int32(dst, "env-status", gs->envelope_status);
bson_write_int32(dst, "env-step", gs->envelope_step);
bson_write_int32(dst, "env-ticks0", gs->envelope_initial_ticks);
bson_write_int32(dst, "env-ticks", gs->envelope_ticks);
bson_write_int32(dst, "sweep-status", gs->sweep_status);
bson_write_int32(dst, "sweep-dir", gs->sweep_direction);
bson_write_int32(dst, "sweep-ticks0", gs->sweep_initial_ticks);
bson_write_int32(dst, "sweep-ticks", gs->sweep_ticks);
bson_write_int32(dst, "sweep-shift", gs->sweep_shift);
bson_write_int32(dst, "wav-type", gs->wave_type);
bson_write_int32(dst, "wav-bank", gs->wave_bank);
bson_write_int32(dst, "wav-vol", gs->wave_volume);
bson_write_int32(dst, "len-status", gs->length_status);
bson_write_int32(dst, "len-ticks", gs->length_ticks);
bson_write_int32(dst, "noise-type", gs->noise_type);
bson_write_int32(dst, "sample-tbl", gs->sample_table_idx);
bson_finish_document(dst, wbptr2);
}
bson_finish_document(dst, wbptr);
return (unsigned int)(dst - startp);
}
#include "libretro.h"

View File

@ -52,7 +52,6 @@ typedef struct
u32 buffer_index;
direct_sound_status_type status;
direct_sound_volume_type volume;
u32 last_cpu_ticks;
} direct_sound_struct;
typedef enum
@ -114,8 +113,8 @@ void sound_timer(fixed8_24 frequency_step, u32 channel);
void sound_reset_fifo(u32 channel);
void update_gbc_sound(u32 cpu_ticks);
void init_sound(int need_reset);
void sound_write_savestate(void);
void sound_read_savestate(void);
unsigned sound_write_savestate(u8 *dst);
bool sound_read_savestate(const u8 *src);
void render_audio(void);

View File

@ -2584,7 +2584,7 @@ render_scanline_affine_builder(transparent, alpha);
#define bitmap_render_pixel_mode3(alpha_op) \
convert_palette(current_pixel); \
current_pixel = convert_palette(current_pixel); \
*dest_ptr = current_pixel \
#define bitmap_render_pixel_mode4(alpha_op) \