diff --git a/Makefile.common b/Makefile.common index 79ae145..85e1948 100644 --- a/Makefile.common +++ b/Makefile.common @@ -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 \ diff --git a/common.h b/common.h index 09b7f51..ba5cfc5 100644 --- a/common.h +++ b/common.h @@ -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 #include "cpu.h" #include "gba_memory.h" +#include "savestate.h" #include "video.h" #include "input.h" #include "sound.h" diff --git a/cpu.c b/cpu.c index 0cb0754..6fbecf0 100644 --- a/cpu.c +++ b/cpu.c @@ -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); +} + + diff --git a/cpu.h b/cpu.h index cc36f26..3b44530 100644 --- a/cpu.h +++ b/cpu.h @@ -20,6 +20,7 @@ #ifndef CPU_H #define CPU_H +#include #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); diff --git a/gba_memory.c b/gba_memory.c index b061ecb..e37c262 100644 --- a/gba_memory.c +++ b/gba_memory.c @@ -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; diff --git a/gba_memory.h b/gba_memory.h index 92199af..bb8e0f9 100644 --- a/gba_memory.h +++ b/gba_memory.h @@ -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 diff --git a/input.c b/input.c index e030596..f74b161 100644 --- a/input.c +++ b/input.c @@ -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); +} + + diff --git a/input.h b/input.h index 72c7e1e..cc6b3e0 100644 --- a/input.h +++ b/input.h @@ -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 diff --git a/libretro.c b/libretro.c index f31235b..37da99d 100644 --- a/libretro.c +++ b/libretro.c @@ -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) diff --git a/main.c b/main.c index de56043..0d7b346 100644 --- a/main.c +++ b/main.c @@ -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); +} diff --git a/main.h b/main.h index 669bb9e..1ca400b 100644 --- a/main.h +++ b/main.h @@ -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); diff --git a/savestate.c b/savestate.c new file mode 100644 index 0000000..61d579d --- /dev/null +++ b/savestate.c @@ -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); +} + + diff --git a/savestate.h b/savestate.h new file mode 100644 index 0000000..2f98102 --- /dev/null +++ b/savestate.h @@ -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 + diff --git a/sound.c b/sound.c index 46cdc19..6529663 100644 --- a/sound.c +++ b/sound.c @@ -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" diff --git a/sound.h b/sound.h index f8b2ef2..4dcd38f 100644 --- a/sound.h +++ b/sound.h @@ -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); diff --git a/video.c b/video.c index 691e51f..e4a3797 100644 --- a/video.c +++ b/video.c @@ -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) \