mirror of
https://github.com/libretro/parallel-dc.git
synced 2026-11-07 12:37:53 -10:00
A game waiting on the beam reads SPG_STATUS hundreds of times a line, and each read found the next line due and divided twice to place the beam. spg_beam() now keeps what it found - the line, the pending event's end, the cycles that bound the line - and answers a read on the same line, aimed by the same event, with a subtraction. Aiming the event, a timing change or a state loaded starts it afresh. The headless disc runs 3000 frames in 11.5 s, from 17.8 s. tools/pvr/spg_beam_test.sh makes every read twice, kept and afresh, straight and with every frame run ahead, and fails where they differ.
1862 lines
56 KiB
C++
1862 lines
56 KiB
C++
// serialize.cpp : save states
|
|
|
|
#include "types.h"
|
|
#include "hw/aica/dsp.h"
|
|
#include "hw/aica/aica.h"
|
|
#include "hw/aica/sgc_if.h"
|
|
#include "hw/arm7/arm7.h"
|
|
#include "hw/holly/sb_mem.h"
|
|
#include "hw/holly/sb.h"
|
|
#include "hw/flashrom/flashrom.h"
|
|
#include "hw/mem/_vmem.h"
|
|
#include "hw/gdrom/gdromv3.h"
|
|
#include "hw/maple/maple_devs.h"
|
|
#include "hw/maple/maple_cfg.h"
|
|
#include "hw/pvr/spg.h"
|
|
#include "hw/pvr/Renderer_if.h"
|
|
#include "hw/pvr/ta_structs.h"
|
|
#include "hw/sh4/sh4_interrupts.h"
|
|
#include "hw/sh4/sh4_sched.h"
|
|
#include "hw/sh4/sh4_mmr.h"
|
|
#include "hw/sh4/modules/mmu.h"
|
|
#include "imgread/common.h"
|
|
#include "reios/reios.h"
|
|
#include "reios/gdrom_hle.h"
|
|
#include <map>
|
|
#include <set>
|
|
#include "rend/gles/gles.h"
|
|
#include "hw/sh4/dyna/blockmanager.h"
|
|
#include "hw/sh4/dyna/ngen.h"
|
|
#include "hw/naomi/naomi.h"
|
|
#include "hw/naomi/naomi_cart.h"
|
|
#include "hw/sh4/sh4_cache.h"
|
|
#include "hw/bba/bba.h"
|
|
|
|
#define LIBRETRO_SKIP(size) do { if (*data) *(u8**)data += (size); *total_size += (size); } while (false)
|
|
|
|
/*
|
|
* search for "maybe" to find items that were left out that may be needed
|
|
*/
|
|
|
|
//./core/hw/arm7/arm_mem.cpp
|
|
extern bool aica_interr;
|
|
extern u32 aica_reg_L;
|
|
extern bool e68k_out;
|
|
extern u32 e68k_reg_L;
|
|
extern u32 e68k_reg_M;
|
|
|
|
//./core/hw/arm7/arm7.cpp
|
|
extern DECL_ALIGN(8) reg_pair arm_Reg[RN_ARM_REG_SLOTS];
|
|
extern bool armIrqEnable;
|
|
extern bool armFiqEnable;
|
|
extern int armMode;
|
|
extern bool Arm7Enabled;
|
|
|
|
//./core/hw/aica/dsp.o
|
|
extern DECL_ALIGN(4096) dsp_t dsp;
|
|
|
|
//./core/hw/aica/aica.o
|
|
extern AicaTimer timers[3];
|
|
|
|
//./core/hw/aica/aica_if.o
|
|
extern VArray2 aica_ram;
|
|
extern u32 VREG;//video reg =P
|
|
extern u32 ARMRST;//arm reset reg
|
|
extern u32 rtc_EN;
|
|
extern u32 RealTimeClock;
|
|
extern int dma_sched_id;
|
|
|
|
//./core/hw/aica/aica_mem.o
|
|
extern u8 aica_reg[0x8000];
|
|
|
|
//./core/hw/aica/sgc_if.o
|
|
struct ChannelEx;
|
|
#define AicaChannel ChannelEx
|
|
//special handling
|
|
//extern AicaChannel AicaChannel::Chans[64];
|
|
#define Chans AicaChannel::Chans
|
|
#define CDDA_SIZE (2352/2)
|
|
extern s16 cdda_sector[CDDA_SIZE];
|
|
extern u32 cdda_index;
|
|
|
|
//./core/hw/holly/sb.o
|
|
extern Array<RegisterStruct> sb_regs;
|
|
extern u32 SB_ISTNRM;
|
|
extern u32 SB_FFST_rc;
|
|
extern u32 SB_FFST;
|
|
|
|
//./core/hw/holly/sb_mem.o
|
|
extern SRamChip sys_nvmem_sram;
|
|
extern DCFlashChip sys_nvmem_flash;
|
|
|
|
//./core/hw/pvr/elan_host.o
|
|
#include "hw/pvr/elan.h"
|
|
#include "hw/pvr/elan_host.h"
|
|
#include "hw/holly/holly_intc.h"
|
|
|
|
//./core/hw/gdrom/gdromv3.o
|
|
extern int gdrom_sched;
|
|
extern int gd_swap_schid;
|
|
extern signed int sns_asc;
|
|
extern signed int sns_ascq;
|
|
extern signed int sns_key;
|
|
extern packet_cmd_t packet_cmd;
|
|
extern u32 set_mode_offset;
|
|
extern read_params_t read_params ;
|
|
extern packet_cmd_t packet_cmd;
|
|
//Buffer for sector reads [dma]
|
|
extern read_buff_t read_buff ;
|
|
//pio buffer
|
|
extern pio_buff_t pio_buff ;
|
|
extern u32 set_mode_offset;
|
|
extern ata_cmd_t ata_cmd ;
|
|
extern cdda_t cdda ;
|
|
extern gd_states gd_state;
|
|
extern DiscType gd_disk_type;
|
|
extern u32 data_write_mode;
|
|
//Registers
|
|
extern u32 DriveSel;
|
|
extern GD_ErrRegT Error;
|
|
extern GD_InterruptReasonT IntReason;
|
|
extern GD_FeaturesT Features;
|
|
extern GD_SecCountT SecCount;
|
|
extern GD_SecNumbT SecNumber;
|
|
extern GD_StatusT GDStatus;
|
|
extern ByteCount_t GDByteCount ;
|
|
extern int GDROM_TICK;
|
|
|
|
//./core/hw/maple/maple_devs.o
|
|
extern char EEPROM[0x100];
|
|
extern bool EEPROM_loaded;
|
|
|
|
//./core/hw/maple/maple_if.o
|
|
//needs special handler
|
|
extern maple_device* MapleDevices[4][6];
|
|
extern int maple_sched;
|
|
#include "hw/maple/maple_if.h"
|
|
extern bool maple_ddt_pending_reset;
|
|
|
|
#ifdef ENABLE_MODEM
|
|
//./core/hw/modem/modem.cpp
|
|
extern int modem_sched;
|
|
#endif
|
|
|
|
|
|
//./core/hw/pvr/Renderer_if.o
|
|
extern bool pend_rend;
|
|
extern u32 fb_w_cur;
|
|
|
|
//./core/hw/pvr/pvr_mem.o
|
|
extern u32 YUV_tempdata[512/4];//512 bytes
|
|
extern u32 YUV_dest;
|
|
extern u32 YUV_blockcount;
|
|
extern u32 YUV_x_curr;
|
|
extern u32 YUV_y_curr;
|
|
extern u32 YUV_x_size;
|
|
extern u32 YUV_y_size;
|
|
|
|
//./core/hw/pvr/pvr_regs.o
|
|
extern u8 pvr_regs[pvr_RegSize];
|
|
|
|
//./core/hw/pvr/spg.o
|
|
extern u32 in_vblank;
|
|
extern u32 clc_pvr_scanline;
|
|
extern u32 pvr_cur_scanline;
|
|
extern int render_end_schid;
|
|
extern int vblank_schid;
|
|
|
|
//./core/hw/pvr/ta.o
|
|
extern u8 ta_fsm[2049]; //[2048] stores the current state
|
|
extern u32 ta_fsm_cl;
|
|
|
|
//./core/hw/pvr/ta_vtx.o
|
|
|
|
//./core/rend/TexCache.o
|
|
extern VArray2 vram;
|
|
|
|
//./core/hw/sh4/sh4_mmr.o
|
|
extern Array<u8> OnChipRAM;
|
|
extern Array<RegisterStruct> CCN; //CCN : 14 registers
|
|
extern Array<RegisterStruct> UBC; //UBC : 9 registers
|
|
extern Array<RegisterStruct> BSC; //BSC : 18 registers
|
|
extern Array<RegisterStruct> DMAC; //DMAC : 17 registers
|
|
extern Array<RegisterStruct> CPG; //CPG : 5 registers
|
|
extern Array<RegisterStruct> RTC; //RTC : 16 registers
|
|
extern Array<RegisterStruct> INTC; //INTC : 4 registers
|
|
extern Array<RegisterStruct> TMU; //TMU : 12 registers
|
|
extern Array<RegisterStruct> SCI; //SCI : 8 registers
|
|
extern Array<RegisterStruct> SCIF; //SCIF : 10 registers
|
|
|
|
//./core/hw/sh4/sh4_mem.o
|
|
extern VArray2 mem_b;
|
|
|
|
//./core/hw/sh4/sh4_interrupts.o
|
|
extern DECL_ALIGN(64) u16 InterruptEnvId[32];
|
|
extern DECL_ALIGN(64) u32 InterruptBit[32];
|
|
extern DECL_ALIGN(64) u32 InterruptLevelBit[16];
|
|
extern u32 interrupt_vpend; // Vector of pending interrupts
|
|
extern u32 interrupt_vmask; // Vector of masked interrupts (-1 inhibits all interrupts)
|
|
extern u32 decoded_srimask; // Vector of interrupts allowed by SR.IMSK (-1 inhibits all interrupts)
|
|
|
|
//./core/hw/sh4/sh4_core_regs.o
|
|
extern Sh4RCB* p_sh4rcb;
|
|
|
|
//./core/hw/sh4/sh4_sched.o
|
|
extern u64 sh4_sched_ffb;
|
|
extern std::vector<sched_list> sch_list;
|
|
|
|
//./core/hw/sh4/interpr/sh4_interpreter.o
|
|
extern int aica_schid;
|
|
extern int rtc_schid;
|
|
|
|
//./core/hw/sh4/modules/serial.o
|
|
extern SCIF_SCFSR2_type SCIF_SCFSR2;
|
|
extern SCIF_SCSCR2_type SCIF_SCSCR2;
|
|
|
|
//./core/hw/sh4/modules/bsc.o
|
|
extern BSC_PDTRA_type BSC_PDTRA;
|
|
|
|
//./core/hw/sh4/modules/tmu.o
|
|
extern u32 tmu_shift[3];
|
|
extern u32 tmu_mask[3];
|
|
extern u64 tmu_mask64[3];
|
|
extern u32 old_mode[3];
|
|
extern int tmu_sched[3];
|
|
extern u32 tmu_ch_base[3];
|
|
extern u64 tmu_ch_base64[3];
|
|
|
|
//./core/hw/sh4/modules/ccn.o
|
|
extern u32 CCN_QACR_TR[2];
|
|
|
|
//./core/hw/sh4/modules/mmu.o
|
|
extern u32 ITLB_LRU_USE[64];
|
|
|
|
//./core/imgread/common.o
|
|
extern u32 NullDriveDiscType;
|
|
extern u8 q_subchannel[96];
|
|
|
|
//./core/nullDC.o
|
|
extern unsigned FLASH_SIZE;
|
|
extern unsigned BBSRAM_SIZE;
|
|
extern unsigned BIOS_SIZE;
|
|
extern unsigned RAM_SIZE;
|
|
extern unsigned ARAM_SIZE;
|
|
extern unsigned VRAM_SIZE;
|
|
extern unsigned RAM_MASK;
|
|
extern unsigned ARAM_MASK;
|
|
extern unsigned VRAM_MASK;
|
|
|
|
//./core/hw/naomi/naomi.o
|
|
extern u32 GSerialBuffer;
|
|
extern u32 BSerialBuffer;
|
|
extern int GBufPos;
|
|
extern int BBufPos;
|
|
extern int GState;
|
|
extern int BState;
|
|
extern int GOldClk;
|
|
extern int BOldClk;
|
|
extern int BControl;
|
|
extern int BCmd;
|
|
extern int BLastCmd;
|
|
extern int GControl;
|
|
extern int GCmd;
|
|
extern int GLastCmd;
|
|
extern int SerStep;
|
|
extern int SerStep2;
|
|
extern unsigned char BSerial[];
|
|
extern unsigned char GSerial[];
|
|
|
|
/* What each part of the machine has had in a state since V21, in a block of
|
|
* its own at the end: written by X_serialize_v21(), read by
|
|
* X_unserialize_v21() - which answers false if the rest of the state cannot
|
|
* be found after what it read - and, for a state from before V21, set to
|
|
* what such a state implies by X_state_before_v21(). */
|
|
#define V21_PART(x) \
|
|
void x##_serialize_v21(void **data, unsigned int *total_size); \
|
|
bool x##_unserialize_v21(void **data, unsigned int *total_size); \
|
|
void x##_state_before_v21(void)
|
|
V21_PART(aica);
|
|
V21_PART(gdrom);
|
|
V21_PART(maple);
|
|
V21_PART(naomi);
|
|
V21_PART(modem);
|
|
V21_PART(reios);
|
|
V21_PART(gdhle);
|
|
V21_PART(nbios);
|
|
V21_PART(pvr);
|
|
#undef V21_PART
|
|
// and since V22: which way the MMU is done, and the translations Windows CE's way of it keeps (fastmmu.cpp)
|
|
void mmu_serialize_v22(void **data, unsigned int *total_size);
|
|
bool mmu_unserialize_v22(void **data, unsigned int *total_size);
|
|
void mmu_state_before_v22(void);
|
|
// and whether the game has written to the framebuffer since it was last shown (Renderer_if.cpp)
|
|
void pvr_serialize_v22(void **data, unsigned int *total_size);
|
|
void pvr_unserialize_v22(void **data, unsigned int *total_size);
|
|
void pvr_state_before_v22(void);
|
|
/* and since V23: the NAOMI 2 geometry processor's clipping planes
|
|
* (elan_host.cpp), the NAOMI's communication board and the I/O board's
|
|
* program on its way (naomi.cpp), and the libretro layer's mouse motion
|
|
* not yet read and sound not yet sent (libretro.cpp) */
|
|
void pvr_serialize_v23(void **data, unsigned int *total_size);
|
|
bool pvr_unserialize_v23(void **data, unsigned int *total_size);
|
|
void pvr_state_before_v23(void);
|
|
void naomi_serialize_v23(void **data, unsigned int *total_size);
|
|
bool naomi_unserialize_v23(void **data, unsigned int *total_size);
|
|
void naomi_state_before_v23(void);
|
|
void lr_serialize_v23(void **data, unsigned int *total_size);
|
|
bool lr_unserialize_v23(void **data, unsigned int *total_size);
|
|
void lr_state_before_v23(void);
|
|
/* and since V24: the network device in use and what the modem or the
|
|
* Broadband Adapter holds (netstate.cpp), the VMU's beep (vmu_beep.cpp),
|
|
* the NAOMI's serial port and the card readers, touchscreen and hopper on
|
|
* it (naomi_periph.h), and a System SP's own hardware (systemsp.cpp) */
|
|
void net_serialize_v24(void **data, unsigned int *total_size);
|
|
bool net_unserialize_v24(void **data, unsigned int *total_size);
|
|
void net_state_before_v24(void);
|
|
void vmu_beep_serialize_v24(void **data, unsigned int *total_size);
|
|
bool vmu_beep_unserialize_v24(void **data, unsigned int *total_size);
|
|
void vmu_beep_state_before_v24(void);
|
|
void naomi_periph_serialize_v24(void **data, unsigned int *total_size);
|
|
bool naomi_periph_unserialize_v24(void **data, unsigned int *total_size);
|
|
void naomi_periph_state_before_v24(void);
|
|
void systemsp_serialize_v24(void **data, unsigned int *total_size);
|
|
bool systemsp_unserialize_v24(void **data, unsigned int *total_size);
|
|
void systemsp_state_before_v24(void);
|
|
/* and since V25: the dinokich IC card reader of a System SP (systemsp.cpp),
|
|
* what a Mushiking I/O board holds back (maple_devs.cpp), and the DIMM
|
|
* board of a NAOMI GD-ROM game (gdcartridge.cpp) */
|
|
void systemsp_serialize_v25(void **data, unsigned int *total_size);
|
|
bool systemsp_unserialize_v25(void **data, unsigned int *total_size);
|
|
void systemsp_state_before_v25(void);
|
|
void maple_jvs_serialize_v25(void **data, unsigned int *total_size);
|
|
bool maple_jvs_unserialize_v25(void **data, unsigned int *total_size);
|
|
void maple_jvs_state_before_v25(void);
|
|
void naomi_gd_serialize_v25(void **data, unsigned int *total_size);
|
|
bool naomi_gd_unserialize_v25(void **data, unsigned int *total_size);
|
|
void naomi_gd_state_before_v25(void);
|
|
/* and since V26: the NAOMI's two X76F100 serial flash chips, the main
|
|
* board's and the ROM board's (naomi.cpp) */
|
|
void naomi_serial_serialize_v26(void **data, unsigned int *total_size);
|
|
bool naomi_serial_unserialize_v26(void **data, unsigned int *total_size);
|
|
void naomi_serial_state_before_v26(void);
|
|
/* and since V27: what the racing and other I/O boards keep (maple_devs.cpp),
|
|
* the MIDI force feedback board of the driving games (midiffb.cpp), and
|
|
* F355's printer status (sb_mem.cpp) */
|
|
void maple_jvs_serialize_v27(void **data, unsigned int *total_size);
|
|
bool maple_jvs_unserialize_v27(void **data, unsigned int *total_size);
|
|
void maple_jvs_state_before_v27(void);
|
|
void midiffb_serialize_v27(void **data, unsigned int *total_size);
|
|
bool midiffb_unserialize_v27(void **data, unsigned int *total_size);
|
|
void midiffb_state_before_v27(void);
|
|
void naomi_printer_serialize_v27(void **data, unsigned int *total_size);
|
|
bool naomi_printer_unserialize_v27(void **data, unsigned int *total_size);
|
|
void naomi_printer_state_before_v27(void);
|
|
/* and since V28: a light gun's transfer owed its end at vblank-out
|
|
* (spg.cpp) and how many renders ago each TA context was last used
|
|
* (ta_ctx.cpp) */
|
|
void pvr_serialize_v28(void **data, unsigned int *total_size);
|
|
bool pvr_unserialize_v28(void **data, unsigned int *total_size);
|
|
void pvr_state_before_v28(void);
|
|
/* and since V29: SB_ADST, the AICA DMA's start register, which is read
|
|
* through a function and so is not in the register block (aica_if.cpp) */
|
|
void aica_sb_serialize_v29(void **data, unsigned int *total_size);
|
|
bool aica_sb_unserialize_v29(void **data, unsigned int *total_size);
|
|
void aica_sb_state_before_v29(void);
|
|
/* and since V30: the cards the NAOMI's card readers and IC card readers
|
|
* keep out of the reader, as the game last wrote them (card_reader.cpp) */
|
|
void naomi_cards_serialize_v30(void **data, unsigned int *total_size);
|
|
bool naomi_cards_unserialize_v30(void **data, unsigned int *total_size);
|
|
void naomi_cards_state_before_v30(void);
|
|
/* and since V31: the network DIMM board of a NAOMI GD-ROM game
|
|
* (gdcartridge.cpp) */
|
|
void naomi_gd_serialize_v31(void **data, unsigned int *total_size);
|
|
bool naomi_gd_unserialize_v31(void **data, unsigned int *total_size);
|
|
void naomi_gd_state_before_v31(void);
|
|
void tmu_state_loaded(void);
|
|
|
|
/* The modem's part is this long on a Dreamcast (modem_serialize_v21() in
|
|
* modem.cpp: 33 registers, the DSP's 4096 bytes, and 36 more), and a build
|
|
* without the modem has to leave room for it all the same: a state is the
|
|
* same whichever build made it. */
|
|
#define MODEM_V21_SIZE 4165
|
|
|
|
/* The drive's DMA event in a state, on a machine with no drive: there is
|
|
* no such event there and its id is -1. (The id used to start out as 0,
|
|
* which on an arcade board is some other device's event: that one's times
|
|
* were written here, and read back over it.) */
|
|
static sched_list no_event = { NULL, 0, -1, -1 };
|
|
#define GDROM_EVENT (gdrom_sched >= 0 ? sch_list[gdrom_sched] : no_event)
|
|
// (and the modem's, the same way)
|
|
#define MODEM_EVENT (modem_sched >= 0 ? sch_list[modem_sched] : no_event)
|
|
|
|
/*
|
|
* Deserialization input-buffer bounds tracking.
|
|
*
|
|
* The libretro frontend hands retro_unserialize() a buffer and its length.
|
|
* Historically that length was only used to disambiguate two legacy save-state
|
|
* versions and never to bound the reads, so a truncated or malformed state
|
|
* (netplay peer, runahead, on-disk corruption) walked memcpy() straight off the
|
|
* end of the input buffer. dc_unserialize() now publishes the input base and
|
|
* length here; ra_unserialize() refuses to read past the end and latches a
|
|
* failure that propagates to the retro_unserialize() return value.
|
|
*/
|
|
static const u8 *unserialize_base = NULL;
|
|
static size_t unserialize_limit = 0;
|
|
static bool unserialize_failed = false;
|
|
|
|
void ra_unserialize_init(const void *base, size_t limit)
|
|
{
|
|
unserialize_base = (const u8 *)base;
|
|
unserialize_limit = limit;
|
|
unserialize_failed = false;
|
|
}
|
|
|
|
bool ra_unserialize_failed(void)
|
|
{
|
|
return unserialize_failed;
|
|
}
|
|
|
|
/* A state whose contents cannot be this machine's: the load is given up. */
|
|
void ra_unserialize_fail(void)
|
|
{
|
|
unserialize_failed = true;
|
|
}
|
|
|
|
/* (Nothing to copy is nothing to do: a part of the machine this one does
|
|
* not have - the Dreamcast's SRAM - is no memory and a size of 0, and no
|
|
* memory is no pointer to copy from or to, even for none of it.) */
|
|
bool ra_serialize(const void *src, unsigned int src_size, void **dest, unsigned int *total_size)
|
|
{
|
|
if ( *dest != NULL && src_size != 0 )
|
|
{
|
|
memcpy(*dest, src, src_size) ;
|
|
*dest = ((unsigned char*)*dest) + src_size ;
|
|
}
|
|
|
|
*total_size += src_size ;
|
|
return true ;
|
|
}
|
|
|
|
bool ra_unserialize(void *src, unsigned int src_size, void **dest, unsigned int *total_size)
|
|
{
|
|
if ( *dest != NULL && src_size != 0 )
|
|
{
|
|
/* Reject any read that would run past the end of the frontend-supplied
|
|
* input buffer. base==NULL means the limit is unknown (legacy callers);
|
|
* fall back to the original unchecked behavior in that case only. */
|
|
if (unserialize_base != NULL)
|
|
{
|
|
size_t offset = (size_t)((const u8 *)*dest - unserialize_base);
|
|
if (offset > unserialize_limit || src_size > unserialize_limit - offset)
|
|
{
|
|
unserialize_failed = true;
|
|
/* Leave the destination zero-initialized rather than reading
|
|
* out of bounds, and stop consuming input. */
|
|
memset(src, 0, src_size);
|
|
*total_size += src_size ;
|
|
return false ;
|
|
}
|
|
}
|
|
memcpy(src, *dest, src_size) ;
|
|
*dest = ((unsigned char*)*dest) + src_size ;
|
|
}
|
|
|
|
*total_size += src_size ;
|
|
return true ;
|
|
}
|
|
|
|
/* Whether the state being read has @size more bytes at @data. */
|
|
bool ra_unserialize_has(const void *data, size_t size)
|
|
{
|
|
if (data == NULL || unserialize_base == NULL)
|
|
return false;
|
|
|
|
const size_t offset = (size_t)((const u8 *)data - unserialize_base);
|
|
|
|
return offset <= unserialize_limit && size <= unserialize_limit - offset;
|
|
}
|
|
|
|
/* Main memory from a state. With a recompiler it is not copied over
|
|
* blindly: what was compiled from memory that the state does not change is
|
|
* kept (bm_LoadRam()). A state that stops short of it leaves memory zeroed,
|
|
* as any read past the end does, and nothing compiled. */
|
|
static void unserialize_ram(void **data, unsigned int *total_size)
|
|
{
|
|
#if FEAT_SHREC != DYNAREC_NONE
|
|
if (ra_unserialize_has(*data, mem_b.size))
|
|
{
|
|
bm_LoadRam((const u8 *)*data);
|
|
*data = (u8 *)*data + mem_b.size;
|
|
*total_size += mem_b.size;
|
|
return;
|
|
}
|
|
// (no telling what is in memory now: everything is unlocked, and the caller empties the code cache when a load fails)
|
|
bm_Reset();
|
|
#endif
|
|
LIBRETRO_USA(mem_b.data, mem_b.size);
|
|
}
|
|
|
|
#if FEAT_AREC == DYNAREC_JIT
|
|
void FlushCache();
|
|
bool arm7_rec_same_code(const u8 *aram);
|
|
#endif
|
|
|
|
/* Sound memory from a state. What the sound processor's recompiler has made
|
|
* is kept when the state has the same program wherever something was
|
|
* compiled from it: with run-ahead, every time. */
|
|
static void unserialize_aram(void **data, unsigned int *total_size)
|
|
{
|
|
#if FEAT_AREC == DYNAREC_JIT
|
|
if (!ra_unserialize_has(*data, aica_ram.size) || !arm7_rec_same_code((const u8 *)*data))
|
|
FlushCache();
|
|
#endif
|
|
LIBRETRO_USA(aica_ram.data, aica_ram.size);
|
|
}
|
|
|
|
/* Video memory from a state. The textures made from it are kept where the
|
|
* state has the same in their pages (vram_load()). */
|
|
static void unserialize_vram(void **data, unsigned int *total_size)
|
|
{
|
|
if (ra_unserialize_has(*data, vram.size))
|
|
{
|
|
vram_load((const u8 *)*data);
|
|
*data = (u8 *)*data + vram.size;
|
|
*total_size += vram.size;
|
|
return;
|
|
}
|
|
// (not all of it is there: the textures are made again from whatever this leaves)
|
|
KillTex = true;
|
|
LIBRETRO_USA(vram.data, vram.size);
|
|
}
|
|
|
|
bool register_serialize(Array<RegisterStruct>& regs,void **data, unsigned int *total_size )
|
|
{
|
|
int i = 0 ;
|
|
|
|
for ( i = 0 ; i < regs.Size ; i++ )
|
|
{
|
|
/* A register that is read through a function keeps the function's
|
|
* address where the others keep their value. That is not state:
|
|
* register_unserialize() reads it into a dummy. Writing its low
|
|
* half out all the same put a host address, different in every
|
|
* process, into the state for each such register; write zero. */
|
|
u32 value = (regs.data[i].flags & REG_RF) ? 0 : regs.data[i].data32 ;
|
|
|
|
LIBRETRO_S(regs.data[i].flags) ;
|
|
LIBRETRO_S(value) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
bool register_unserialize(Array<RegisterStruct>& regs,void **data, unsigned int *total_size, u32 force_size = 0)
|
|
{
|
|
int i = 0 ;
|
|
u32 dummy = 0 ;
|
|
|
|
if (force_size == 0)
|
|
force_size = regs.Size;
|
|
for ( i = 0 ; i < force_size ; i++ )
|
|
{
|
|
/* What kind of register it is - a plain value or one read and
|
|
* written through functions, whose addresses are in its place -
|
|
* is this build's to say, not the state's: flags taken from the
|
|
* state made a function's address of a value, or a value of an
|
|
* address. The value is taken when both agree it is one. */
|
|
u32 flags = 0 ;
|
|
LIBRETRO_US(flags) ;
|
|
if ( ! ((flags | regs.data[i].flags) & REG_RF) )
|
|
LIBRETRO_US(regs.data[i].data32) ;
|
|
else
|
|
LIBRETRO_US(dummy) ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
/* Where each part begins in the state dc_serialize() wrote or counted last,
|
|
* in the order written, with the part's name: a field written here by name,
|
|
* or the function that writes a part of its own. Nothing in the core reads
|
|
* it. It is for tools/determinism, which finds it by its symbol and names
|
|
* the part two states that should be the same differ in. Two stores a part,
|
|
* a few hundred parts. */
|
|
#define STATE_PARTS_MAX 384
|
|
extern "C" {
|
|
u32 dc_state_parts;
|
|
u32 dc_state_part_at[STATE_PARTS_MAX];
|
|
const char *dc_state_part_name[STATE_PARTS_MAX];
|
|
}
|
|
|
|
static inline int state_part(const char *name, unsigned int at)
|
|
{
|
|
if (dc_state_parts < STATE_PARTS_MAX)
|
|
{
|
|
dc_state_part_name[dc_state_parts] = name;
|
|
dc_state_part_at[dc_state_parts++] = at;
|
|
}
|
|
return 0;
|
|
}
|
|
#define STATE_PART(name) state_part(name, *total_size)
|
|
/* (in dc_serialize() alone: the parts it writes itself, by name) */
|
|
#undef LIBRETRO_S
|
|
#undef LIBRETRO_SA
|
|
#undef LIBRETRO_SKIP
|
|
#define LIBRETRO_S(v) (STATE_PART(#v), ra_serialize(&(v), sizeof(v), data, total_size))
|
|
#define LIBRETRO_SA(v_arr,num) (STATE_PART(#v_arr), ra_serialize((v_arr), sizeof((v_arr)[0]) * (num), data, total_size))
|
|
/* (what is left out is written as zeros: the same state is the same bytes,
|
|
* whatever the frontend's memory had in it before) */
|
|
#define LIBRETRO_SKIP(size) do { STATE_PART("(nothing)"); if (*data) { memset(*data, 0, (size)); *(u8**)data += (size); } *total_size += (size); } while (false)
|
|
|
|
bool dc_serialize(void **data, unsigned int *total_size)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
serialize_version_enum version = VCUR_LIBRETRO;
|
|
|
|
*total_size = 0 ;
|
|
dc_state_parts = 0;
|
|
|
|
//dc not initialized yet
|
|
if ( p_sh4rcb == NULL )
|
|
return false ;
|
|
|
|
LIBRETRO_S(version) ;
|
|
LIBRETRO_S(aica_interr) ;
|
|
LIBRETRO_S(aica_reg_L) ;
|
|
LIBRETRO_S(e68k_out) ;
|
|
LIBRETRO_S(e68k_reg_L) ;
|
|
LIBRETRO_S(e68k_reg_M) ;
|
|
|
|
LIBRETRO_SA(arm_Reg,RN_ARM_REG_COUNT);
|
|
LIBRETRO_S(armIrqEnable);
|
|
LIBRETRO_S(armFiqEnable);
|
|
LIBRETRO_S(armMode);
|
|
LIBRETRO_S(Arm7Enabled);
|
|
|
|
/* The DSP's code as this machine's recompiler made it is no part of the
|
|
* machine, and is not the same on another (nor where the core was loaded
|
|
* at another address): zeros in its place. A load skips it. */
|
|
LIBRETRO_SKIP(sizeof(dsp.DynCode));
|
|
STATE_PART("dsp"), ra_serialize((u8 *)&dsp + sizeof(dsp.DynCode), sizeof(dsp) - sizeof(dsp.DynCode), data, total_size);
|
|
|
|
for ( i = 0 ; i < 3 ; i++)
|
|
{
|
|
LIBRETRO_S(timers[i].c_step);
|
|
LIBRETRO_S(timers[i].m_step);
|
|
}
|
|
|
|
LIBRETRO_SA(aica_ram.data,aica_ram.size) ;
|
|
LIBRETRO_S(VREG);
|
|
LIBRETRO_S(ARMRST);
|
|
LIBRETRO_S(rtc_EN);
|
|
|
|
LIBRETRO_SA(aica_reg,0x8000);
|
|
|
|
STATE_PART("channel_serialize"), channel_serialize(data, total_size) ;
|
|
|
|
libCore_CDDA_Detach();
|
|
LIBRETRO_SA(cdda_sector,CDDA_SIZE);
|
|
LIBRETRO_S(cdda_index);
|
|
|
|
STATE_PART("sb_regs"), register_serialize(sb_regs, data, total_size) ;
|
|
LIBRETRO_S(SB_ISTNRM);
|
|
LIBRETRO_S(SB_FFST_rc);
|
|
LIBRETRO_S(SB_FFST);
|
|
|
|
LIBRETRO_S(sys_nvmem_sram.size);
|
|
LIBRETRO_S(sys_nvmem_sram.mask);
|
|
LIBRETRO_SA(sys_nvmem_sram.data,sys_nvmem_sram.size);
|
|
|
|
LIBRETRO_S(sys_nvmem_flash.size);
|
|
LIBRETRO_S(sys_nvmem_flash.mask);
|
|
LIBRETRO_S(sys_nvmem_flash.state);
|
|
LIBRETRO_SA(sys_nvmem_flash.data,sys_nvmem_flash.size);
|
|
|
|
LIBRETRO_S(GD_HardwareInfo) ;
|
|
|
|
|
|
LIBRETRO_S(sns_asc);
|
|
LIBRETRO_S(sns_ascq);
|
|
LIBRETRO_S(sns_key);
|
|
|
|
LIBRETRO_S(packet_cmd);
|
|
LIBRETRO_S(set_mode_offset);
|
|
LIBRETRO_S(read_params);
|
|
LIBRETRO_S(packet_cmd);
|
|
LIBRETRO_S(pio_buff);
|
|
LIBRETRO_S(set_mode_offset);
|
|
LIBRETRO_S(ata_cmd);
|
|
LIBRETRO_S(cdda);
|
|
LIBRETRO_S(gd_state);
|
|
LIBRETRO_S(gd_disk_type);
|
|
LIBRETRO_S(data_write_mode);
|
|
LIBRETRO_S(DriveSel);
|
|
LIBRETRO_S(Error);
|
|
|
|
LIBRETRO_S(IntReason);
|
|
LIBRETRO_S(Features);
|
|
LIBRETRO_S(SecCount);
|
|
LIBRETRO_S(SecNumber);
|
|
LIBRETRO_S(GDStatus);
|
|
LIBRETRO_S(GDByteCount);
|
|
LIBRETRO_S(GDROM_TICK);
|
|
|
|
|
|
LIBRETRO_SA(EEPROM,0x100);
|
|
LIBRETRO_S(EEPROM_loaded);
|
|
|
|
|
|
LIBRETRO_S(maple_ddt_pending_reset);
|
|
|
|
STATE_PART("mcfg_SerializeDevices"), mcfg_SerializeDevices(data, total_size);
|
|
|
|
LIBRETRO_SA(YUV_tempdata,512/4);
|
|
LIBRETRO_S(YUV_dest);
|
|
LIBRETRO_S(YUV_blockcount);
|
|
LIBRETRO_S(YUV_x_curr);
|
|
LIBRETRO_S(YUV_y_curr);
|
|
LIBRETRO_S(YUV_x_size);
|
|
LIBRETRO_S(YUV_y_size);
|
|
|
|
LIBRETRO_SA(pvr_regs,pvr_RegSize);
|
|
|
|
LIBRETRO_S(in_vblank);
|
|
LIBRETRO_S(clc_pvr_scanline);
|
|
LIBRETRO_S(pvr_cur_scanline);
|
|
LIBRETRO_S(fb_w_cur);
|
|
|
|
LIBRETRO_S(ta_fsm[2048]);
|
|
LIBRETRO_S(ta_fsm_cl);
|
|
|
|
STATE_PART("SerializeTAContext"), SerializeTAContext(data, total_size);
|
|
|
|
LIBRETRO_SA(vram.data, vram.size);
|
|
|
|
LIBRETRO_SA(OnChipRAM.data,OnChipRAM_SIZE);
|
|
|
|
STATE_PART("CCN"), register_serialize(CCN, data, total_size) ;
|
|
STATE_PART("UBC"), register_serialize(UBC, data, total_size) ;
|
|
STATE_PART("BSC"), register_serialize(BSC, data, total_size) ;
|
|
STATE_PART("DMAC"), register_serialize(DMAC, data, total_size) ;
|
|
STATE_PART("CPG"), register_serialize(CPG, data, total_size) ;
|
|
STATE_PART("RTC"), register_serialize(RTC, data, total_size) ;
|
|
STATE_PART("INTC"), register_serialize(INTC, data, total_size) ;
|
|
STATE_PART("TMU"), register_serialize(TMU, data, total_size) ;
|
|
STATE_PART("SCI"), register_serialize(SCI, data, total_size) ;
|
|
STATE_PART("SCIF"), register_serialize(SCIF, data, total_size) ;
|
|
STATE_PART("icache.Serialize"), icache.Serialize(data, total_size);
|
|
STATE_PART("ocache.Serialize"), ocache.Serialize(data, total_size);
|
|
|
|
LIBRETRO_SA(mem_b.data, mem_b.size);
|
|
|
|
LIBRETRO_SA(InterruptEnvId,32);
|
|
LIBRETRO_SA(InterruptBit,32);
|
|
LIBRETRO_SA(InterruptLevelBit,16);
|
|
LIBRETRO_S(interrupt_vpend);
|
|
LIBRETRO_S(interrupt_vmask);
|
|
LIBRETRO_S(decoded_srimask);
|
|
|
|
|
|
//default to nommu_full
|
|
i = 3 ;
|
|
if ( do_sqw_nommu == &do_sqw_nommu_area_3)
|
|
i = 0 ;
|
|
else if (do_sqw_nommu == &do_sqw_nommu_area_3_nonvmem)
|
|
i = 1 ;
|
|
else if (do_sqw_nommu==(sqw_fp*)&TAWriteSQ)
|
|
i = 2 ;
|
|
else if (do_sqw_nommu==&do_sqw_nommu_full)
|
|
i = 3 ;
|
|
|
|
LIBRETRO_S(i) ;
|
|
|
|
LIBRETRO_SA((*p_sh4rcb).sq_buffer,64/8);
|
|
|
|
//store these before unserializing and then restore after
|
|
//void *getptr = &((*p_sh4rcb).cntx.sr.GetFull) ;
|
|
//void *setptr = &((*p_sh4rcb).cntx.sr.SetFull) ;
|
|
LIBRETRO_S((*p_sh4rcb).cntx);
|
|
|
|
|
|
LIBRETRO_S(sh4_sched_ffb);
|
|
|
|
LIBRETRO_S(sch_list[aica_schid].tag) ;
|
|
LIBRETRO_S(sch_list[aica_schid].start) ;
|
|
LIBRETRO_S(sch_list[aica_schid].end) ;
|
|
|
|
LIBRETRO_S(sch_list[rtc_schid].tag) ;
|
|
LIBRETRO_S(sch_list[rtc_schid].start) ;
|
|
LIBRETRO_S(sch_list[rtc_schid].end) ;
|
|
|
|
LIBRETRO_S(GDROM_EVENT.tag) ;
|
|
LIBRETRO_S(GDROM_EVENT.start) ;
|
|
LIBRETRO_S(GDROM_EVENT.end) ;
|
|
|
|
LIBRETRO_S(sch_list[maple_sched].tag) ;
|
|
LIBRETRO_S(sch_list[maple_sched].start) ;
|
|
LIBRETRO_S(sch_list[maple_sched].end) ;
|
|
|
|
LIBRETRO_S(sch_list[dma_sched_id].tag) ;
|
|
LIBRETRO_S(sch_list[dma_sched_id].start) ;
|
|
LIBRETRO_S(sch_list[dma_sched_id].end) ;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
LIBRETRO_S(sch_list[tmu_sched[i]].tag) ;
|
|
LIBRETRO_S(sch_list[tmu_sched[i]].start) ;
|
|
LIBRETRO_S(sch_list[tmu_sched[i]].end) ;
|
|
}
|
|
|
|
LIBRETRO_S(sch_list[render_end_schid].tag) ;
|
|
LIBRETRO_S(sch_list[render_end_schid].start) ;
|
|
LIBRETRO_S(sch_list[render_end_schid].end) ;
|
|
|
|
LIBRETRO_S(sch_list[vblank_schid].tag) ;
|
|
LIBRETRO_S(sch_list[vblank_schid].start) ;
|
|
LIBRETRO_S(sch_list[vblank_schid].end) ;
|
|
|
|
LIBRETRO_S(settings.network.EmulateBBA);
|
|
#ifdef ENABLE_MODEM
|
|
if (settings.network.EmulateBBA)
|
|
{
|
|
STATE_PART("bba_Serialize"), bba_Serialize(data, total_size);
|
|
}
|
|
else
|
|
{
|
|
LIBRETRO_S(MODEM_EVENT.tag) ;
|
|
LIBRETRO_S(MODEM_EVENT.start) ;
|
|
LIBRETRO_S(MODEM_EVENT.end) ;
|
|
}
|
|
#else
|
|
LIBRETRO_S(i);
|
|
LIBRETRO_S(i);
|
|
LIBRETRO_S(i);
|
|
#endif
|
|
|
|
LIBRETRO_S(SCIF_SCFSR2);
|
|
LIBRETRO_S(SCIF_SCSCR2);
|
|
LIBRETRO_S(BSC_PDTRA);
|
|
|
|
LIBRETRO_SA(tmu_shift,3);
|
|
LIBRETRO_SA(tmu_mask,3);
|
|
LIBRETRO_SA(tmu_mask64,3);
|
|
LIBRETRO_SA(old_mode,3);
|
|
LIBRETRO_SA(tmu_ch_base,3);
|
|
LIBRETRO_SA(tmu_ch_base64,3);
|
|
|
|
LIBRETRO_SA(CCN_QACR_TR,2);
|
|
|
|
LIBRETRO_S(UTLB);
|
|
LIBRETRO_S(ITLB);
|
|
LIBRETRO_S(sq_remap);
|
|
LIBRETRO_S(ITLB_LRU_USE);
|
|
|
|
LIBRETRO_S(NullDriveDiscType);
|
|
LIBRETRO_SA(q_subchannel,96);
|
|
|
|
|
|
LIBRETRO_S(FLASH_SIZE);
|
|
LIBRETRO_S(BBSRAM_SIZE);
|
|
LIBRETRO_S(BIOS_SIZE);
|
|
LIBRETRO_S(RAM_SIZE);
|
|
LIBRETRO_S(ARAM_SIZE);
|
|
LIBRETRO_S(VRAM_SIZE);
|
|
LIBRETRO_S(RAM_MASK);
|
|
LIBRETRO_S(ARAM_MASK);
|
|
LIBRETRO_S(VRAM_MASK);
|
|
|
|
LIBRETRO_S(GSerialBuffer);
|
|
LIBRETRO_S(BSerialBuffer);
|
|
LIBRETRO_S(GBufPos);
|
|
LIBRETRO_S(BBufPos);
|
|
LIBRETRO_S(GState);
|
|
LIBRETRO_S(BState);
|
|
LIBRETRO_S(GOldClk);
|
|
LIBRETRO_S(BOldClk);
|
|
LIBRETRO_S(BControl);
|
|
LIBRETRO_S(BCmd);
|
|
LIBRETRO_S(BLastCmd);
|
|
LIBRETRO_S(GControl);
|
|
LIBRETRO_S(GCmd);
|
|
LIBRETRO_S(GLastCmd);
|
|
LIBRETRO_S(SerStep);
|
|
LIBRETRO_S(SerStep2);
|
|
LIBRETRO_SA(BSerial,69);
|
|
LIBRETRO_SA(GSerial,69);
|
|
LIBRETRO_S(reg_dimm_command);
|
|
LIBRETRO_S(reg_dimm_offsetl);
|
|
LIBRETRO_S(reg_dimm_parameterl);
|
|
LIBRETRO_S(reg_dimm_parameterh);
|
|
LIBRETRO_S(reg_dimm_status);
|
|
LIBRETRO_SKIP(1); // NaomiDataRead
|
|
|
|
LIBRETRO_S(settings.dreamcast.broadcast);
|
|
LIBRETRO_S(settings.dreamcast.cable);
|
|
LIBRETRO_S(settings.dreamcast.region);
|
|
|
|
if (CurrentCartridge != NULL)
|
|
STATE_PART("CurrentCartridge->Serialize"), CurrentCartridge->Serialize(data, total_size);
|
|
STATE_PART("gd_hle_state.Serialize"), gd_hle_state.Serialize(data, total_size);
|
|
settings.network.EmulateBBA = false;
|
|
|
|
/* V14: the AICA RTC. Its per-second increment is already deterministic
|
|
* (driven by the SH4 scheduler), but the initial value is seeded from the
|
|
* host clock and was never serialized, so netplay peers and runahead
|
|
* rollbacks diverged whenever a game read the clock. Carry it in the state
|
|
* so it is synced along with everything else. */
|
|
LIBRETRO_S(RealTimeClock);
|
|
|
|
/* V18: the answers of a Maple transfer that is under way, which are not
|
|
* in the game's memory until it is over. */
|
|
LIBRETRO_S(maple_out_used);
|
|
LIBRETRO_SA(maple_out, MAPLE_OUT_WORDS);
|
|
|
|
/* V19: the drive still looking at a disc that has just been put in */
|
|
{
|
|
/* (a machine with no drive has no such event: its id is -1, and
|
|
* what was written here was whatever is in memory before the list) */
|
|
int swap_start = gd_swap_schid >= 0 ? sch_list[gd_swap_schid].start : -1;
|
|
int swap_end = gd_swap_schid >= 0 ? sch_list[gd_swap_schid].end : -1;
|
|
LIBRETRO_S(swap_start);
|
|
LIBRETRO_S(swap_end);
|
|
}
|
|
|
|
/* V20: what a NAOMI 2 has that a NAOMI does not - the second PowerVR's
|
|
* interrupts, and the geometry processor with its memory. */
|
|
if (settings.System == DC_PLATFORM_NAOMI2 && elan_ram)
|
|
{
|
|
elan_state_t elan;
|
|
elan_get_state(&elan);
|
|
LIBRETRO_S(SB_ISTNRM1);
|
|
LIBRETRO_S(elan);
|
|
LIBRETRO_SA(elan_ram, ELAN_RAM_SIZE);
|
|
LIBRETRO_S(sch_list[elan_schid].start);
|
|
LIBRETRO_S(sch_list[elan_schid].end);
|
|
}
|
|
|
|
/* V21: what each part of the machine was missing from a state. Every
|
|
* part is the same size each time for the machine that is running;
|
|
* the tile accelerator's, which is not, is last and is counted at its
|
|
* largest. */
|
|
STATE_PART("aica_serialize_v21"), aica_serialize_v21(data, total_size);
|
|
STATE_PART("gdrom_serialize_v21"), gdrom_serialize_v21(data, total_size);
|
|
STATE_PART("maple_serialize_v21"), maple_serialize_v21(data, total_size);
|
|
STATE_PART("naomi_serialize_v21"), naomi_serialize_v21(data, total_size);
|
|
#ifdef ENABLE_MODEM
|
|
STATE_PART("modem_serialize_v21"), modem_serialize_v21(data, total_size);
|
|
#else
|
|
if (settings.System == DC_PLATFORM_DREAMCAST)
|
|
{
|
|
static const u8 no_modem[MODEM_V21_SIZE] = { 0 };
|
|
LIBRETRO_SA(no_modem, MODEM_V21_SIZE);
|
|
}
|
|
#endif
|
|
STATE_PART("reios_serialize_v21"), reios_serialize_v21(data, total_size);
|
|
STATE_PART("gdhle_serialize_v21"), gdhle_serialize_v21(data, total_size);
|
|
STATE_PART("nbios_serialize_v21"), nbios_serialize_v21(data, total_size);
|
|
STATE_PART("pvr_serialize_v21"), pvr_serialize_v21(data, total_size);
|
|
// V22
|
|
STATE_PART("pvr_serialize_v22"), pvr_serialize_v22(data, total_size);
|
|
STATE_PART("mmu_serialize_v22"), mmu_serialize_v22(data, total_size);
|
|
// V23
|
|
STATE_PART("pvr_serialize_v23"), pvr_serialize_v23(data, total_size);
|
|
STATE_PART("naomi_serialize_v23"), naomi_serialize_v23(data, total_size);
|
|
STATE_PART("lr_serialize_v23"), lr_serialize_v23(data, total_size);
|
|
// V24
|
|
STATE_PART("net_serialize_v24"), net_serialize_v24(data, total_size);
|
|
STATE_PART("vmu_beep_serialize_v24"), vmu_beep_serialize_v24(data, total_size);
|
|
STATE_PART("naomi_periph_serialize_v24"), naomi_periph_serialize_v24(data, total_size);
|
|
STATE_PART("systemsp_serialize_v24"), systemsp_serialize_v24(data, total_size);
|
|
// V25
|
|
STATE_PART("systemsp_serialize_v25"), systemsp_serialize_v25(data, total_size);
|
|
STATE_PART("maple_jvs_serialize_v25"), maple_jvs_serialize_v25(data, total_size);
|
|
STATE_PART("naomi_gd_serialize_v25"), naomi_gd_serialize_v25(data, total_size);
|
|
// V26
|
|
STATE_PART("naomi_serial_serialize_v26"), naomi_serial_serialize_v26(data, total_size);
|
|
// V27
|
|
STATE_PART("maple_jvs_serialize_v27"), maple_jvs_serialize_v27(data, total_size);
|
|
STATE_PART("midiffb_serialize_v27"), midiffb_serialize_v27(data, total_size);
|
|
STATE_PART("naomi_printer_serialize_v27"), naomi_printer_serialize_v27(data, total_size);
|
|
// V28
|
|
STATE_PART("pvr_serialize_v28"), pvr_serialize_v28(data, total_size);
|
|
// V29
|
|
STATE_PART("aica_sb_serialize_v29"), aica_sb_serialize_v29(data, total_size);
|
|
// V30
|
|
STATE_PART("naomi_cards_serialize_v30"), naomi_cards_serialize_v30(data, total_size);
|
|
// V31
|
|
STATE_PART("naomi_gd_serialize_v31"), naomi_gd_serialize_v31(data, total_size);
|
|
|
|
return true ;
|
|
}
|
|
|
|
#undef LIBRETRO_S
|
|
#undef LIBRETRO_SA
|
|
#undef LIBRETRO_SKIP
|
|
#define LIBRETRO_S(v) ra_serialize(&(v), sizeof(v), data, total_size)
|
|
#define LIBRETRO_SA(v_arr,num) ra_serialize((v_arr), sizeof((v_arr)[0]) * (num), data, total_size)
|
|
#define LIBRETRO_SKIP(size) do { if (*data) *(u8**)data += (size); *total_size += (size); } while (false)
|
|
|
|
/* The scheduler's events from a state. One that is waiting was asked for
|
|
* at most a second ahead (sh4_sched_request()), and is called once its time
|
|
* has come. An event from a bad state that would come later than that, or
|
|
* whose time has gone by, is not called until the clock comes round to it -
|
|
* some 21 seconds - and nothing it drives (a frame, sound) happens till
|
|
* then: it is made due now. A state as this build saves it has none such. */
|
|
static void sched_events_loaded(void)
|
|
{
|
|
const u32 now = sh4_sched_now();
|
|
|
|
for (size_t i = 0; i < sch_list.size(); i++)
|
|
{
|
|
sched_list &ev = sch_list[i];
|
|
|
|
if (ev.end == -1)
|
|
continue;
|
|
const u32 asked = (u32)ev.end - (u32)ev.start;
|
|
const u32 gone = now - (u32)ev.start;
|
|
if (asked <= SH4_MAIN_CLOCK && gone <= asked)
|
|
continue;
|
|
ev.start = (int)now;
|
|
ev.end = (int)now;
|
|
if (ev.end == -1)
|
|
ev.end++;
|
|
}
|
|
}
|
|
|
|
bool dc_unserialize(void **data, unsigned int *total_size, size_t actual_data_size)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
u8 dummy ;
|
|
int dummy_int ;
|
|
serialize_version_enum version = V1 ;
|
|
|
|
*total_size = 0 ;
|
|
|
|
/* Publish the input buffer bounds so ra_unserialize() can reject reads
|
|
* past the end of a truncated or malformed state. *data points at the
|
|
* start of the frontend-supplied buffer on entry. */
|
|
ra_unserialize_init(*data, actual_data_size);
|
|
|
|
/* A state has the whole of the machine's three memories in it: one
|
|
* that is smaller than those is not this machine's (or is cut short),
|
|
* and is turned down before any of it has been put in place. */
|
|
if (actual_data_size < (size_t)RAM_SIZE + VRAM_SIZE + ARAM_SIZE)
|
|
{
|
|
ra_unserialize_fail();
|
|
return false;
|
|
}
|
|
|
|
LIBRETRO_US(version) ;
|
|
|
|
//This normally isn't necessary - but we need some way to differentiate between save states
|
|
//that were created after the format change and before the new format had a new version saved in it
|
|
if (version == V1 && actual_data_size != 48324799 && actual_data_size != 48855967)
|
|
version = V2 ;
|
|
/* A state of a version this build has not heard of - a later build's -
|
|
* is laid out in a way it cannot know. Nothing has been put in place
|
|
* yet. */
|
|
if ((u32)version > (u32)VCUR_LIBRETRO)
|
|
{
|
|
ra_unserialize_fail();
|
|
return false;
|
|
}
|
|
|
|
LIBRETRO_US(aica_interr) ;
|
|
LIBRETRO_US(aica_reg_L) ;
|
|
LIBRETRO_US(e68k_out) ;
|
|
LIBRETRO_US(e68k_reg_L) ;
|
|
LIBRETRO_US(e68k_reg_M) ;
|
|
|
|
LIBRETRO_USA(arm_Reg,RN_ARM_REG_COUNT);
|
|
// (the sound CPU fetches whole words: an address that is not one's is never in its pc)
|
|
arm_Reg[R15_ARM_NEXT].I &= ~3;
|
|
LIBRETRO_US(armIrqEnable);
|
|
LIBRETRO_US(armFiqEnable);
|
|
LIBRETRO_US(armMode);
|
|
LIBRETRO_US(Arm7Enabled);
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_SKIP(256); // cpuBitsSet
|
|
LIBRETRO_SKIP(1); // intState
|
|
LIBRETRO_SKIP(1); // stopState
|
|
LIBRETRO_SKIP(1); // holdState
|
|
}
|
|
|
|
/* The first thing in it is the code a DSP recompiler makes where it
|
|
* keeps it there: this program's, with its addresses in it, and not
|
|
* the one that saved the state's. It stays as it is; the rest is the
|
|
* DSP's state. (Whether it still fits the program is the recompiler's
|
|
* to find out: dsp.dyndirty is set after every load.) */
|
|
LIBRETRO_SKIP(sizeof(dsp.DynCode));
|
|
LIBRETRO_USA((u8 *)&dsp + sizeof(dsp.DynCode), sizeof(dsp) - sizeof(dsp.DynCode));
|
|
|
|
for ( i = 0 ; i < 3 ; i++)
|
|
{
|
|
LIBRETRO_US(timers[i].c_step);
|
|
LIBRETRO_US(timers[i].m_step);
|
|
}
|
|
|
|
|
|
unserialize_aram(data, total_size);
|
|
LIBRETRO_US(VREG);
|
|
LIBRETRO_US(ARMRST);
|
|
LIBRETRO_US(rtc_EN);
|
|
if (version < V3)
|
|
LIBRETRO_US(dummy_int); //dma_sched_id
|
|
|
|
LIBRETRO_USA(aica_reg,0x8000);
|
|
/* A timer counts down from its prescaler's step, 1 << md, to 1: none
|
|
* other is ever in it, and 0 had it count for 2^32 samples. */
|
|
for ( i = 0 ; i < 3 ; i++)
|
|
{
|
|
timers[i].m_step = 1 << timers[i].data->md;
|
|
if (timers[i].c_step < 1 || (u32)timers[i].c_step > timers[i].m_step)
|
|
timers[i].c_step = timers[i].m_step;
|
|
}
|
|
|
|
|
|
if (version < V7)
|
|
{
|
|
LIBRETRO_SKIP(4 * 16); // volume_lut
|
|
LIBRETRO_SKIP(4 * 256 + 768); // tl_lut. Due to a previous bug this is not 4 * (256 + 768)
|
|
LIBRETRO_SKIP(4 * 64); // AEG_ATT_SPS
|
|
LIBRETRO_SKIP(4 * 64); // AEG_DSR_SPS
|
|
LIBRETRO_SKIP(2); // pl
|
|
LIBRETRO_SKIP(2); // pr
|
|
}
|
|
channel_unserialize(data, total_size, version);
|
|
|
|
libCore_CDDA_Detach();
|
|
LIBRETRO_USA(cdda_sector,CDDA_SIZE);
|
|
LIBRETRO_US(cdda_index);
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_SKIP(4 * 64); // mxlr
|
|
LIBRETRO_SKIP(4); // samples_gen
|
|
}
|
|
|
|
register_unserialize(sb_regs, data, total_size) ;
|
|
LIBRETRO_US(SB_ISTNRM);
|
|
LIBRETRO_US(SB_FFST_rc);
|
|
LIBRETRO_US(SB_FFST);
|
|
|
|
|
|
/* size/mask describe the live, already-allocated nvmem buffers. Read the
|
|
* stored values into temporaries and only copy up to the real allocation,
|
|
* so a corrupt state whose size exceeds the buffer can't overflow data[]
|
|
* (and can't clobber the live size/mask either). */
|
|
{
|
|
u32 stored_size, stored_mask;
|
|
LIBRETRO_US(stored_size);
|
|
LIBRETRO_US(stored_mask);
|
|
u32 copy = stored_size < sys_nvmem_sram.size ? stored_size : sys_nvmem_sram.size;
|
|
LIBRETRO_USA(sys_nvmem_sram.data, copy);
|
|
if (stored_size > copy)
|
|
LIBRETRO_SKIP(stored_size - copy);
|
|
}
|
|
|
|
{
|
|
u32 stored_size, stored_mask;
|
|
LIBRETRO_US(stored_size);
|
|
LIBRETRO_US(stored_mask);
|
|
LIBRETRO_US(sys_nvmem_flash.state);
|
|
u32 copy = stored_size < sys_nvmem_flash.size ? stored_size : sys_nvmem_flash.size;
|
|
LIBRETRO_USA(sys_nvmem_flash.data, copy);
|
|
if (stored_size > copy)
|
|
LIBRETRO_SKIP(stored_size - copy);
|
|
}
|
|
|
|
|
|
LIBRETRO_US(GD_HardwareInfo) ;
|
|
|
|
if (version < V3)
|
|
LIBRETRO_US(dummy_int); //gdrom_sched
|
|
LIBRETRO_US(sns_asc);
|
|
LIBRETRO_US(sns_ascq);
|
|
LIBRETRO_US(sns_key);
|
|
|
|
LIBRETRO_US(packet_cmd);
|
|
LIBRETRO_US(set_mode_offset);
|
|
LIBRETRO_US(read_params);
|
|
LIBRETRO_US(packet_cmd);
|
|
// read_buff
|
|
read_buff.cache_size = 0;
|
|
if (version < V9)
|
|
LIBRETRO_SKIP(4 + 4 + 2352 * 8192);
|
|
LIBRETRO_US(pio_buff);
|
|
LIBRETRO_US(set_mode_offset);
|
|
LIBRETRO_US(ata_cmd);
|
|
LIBRETRO_US(cdda);
|
|
/* The status used to be a flag, playing or not, with whatever followed
|
|
* it in its word: a state from then has something here that is none of
|
|
* the four states, and is read as the flag. This was done to every
|
|
* state, and made a drive that was paused, or had played to the end,
|
|
* play. */
|
|
if ((u32)cdda.status > (u32)cdda_t::Terminated)
|
|
cdda.status = ((u32)cdda.status & 0xFF) ? cdda_t::Playing : cdda_t::NoInfo;
|
|
// the drive keeps four bits of repeat count (15: for ever)
|
|
cdda.repeats &= 0xF;
|
|
LIBRETRO_US(gd_state);
|
|
LIBRETRO_US(gd_disk_type);
|
|
LIBRETRO_US(data_write_mode);
|
|
LIBRETRO_US(DriveSel);
|
|
LIBRETRO_US(Error);
|
|
LIBRETRO_US(IntReason);
|
|
LIBRETRO_US(Features);
|
|
LIBRETRO_US(SecCount);
|
|
LIBRETRO_US(SecNumber);
|
|
LIBRETRO_US(GDStatus);
|
|
LIBRETRO_US(GDByteCount);
|
|
LIBRETRO_US(GDROM_TICK);
|
|
gdrom_state_loaded();
|
|
|
|
|
|
LIBRETRO_USA(EEPROM,0x100);
|
|
{
|
|
// (a flag is 0 or 1, whatever byte the state has for it)
|
|
u8 flag = 0;
|
|
LIBRETRO_US(flag);
|
|
EEPROM_loaded = flag != 0;
|
|
}
|
|
|
|
if ( version == V1 )
|
|
{
|
|
//V1 saved NaomiState which was a struct of 3 u8 variables
|
|
LIBRETRO_US(dummy);
|
|
LIBRETRO_US(dummy);
|
|
LIBRETRO_US(dummy);
|
|
}
|
|
|
|
{
|
|
u8 flag = 0;
|
|
LIBRETRO_US(flag);
|
|
maple_ddt_pending_reset = flag != 0;
|
|
}
|
|
|
|
if ( version == V1 )
|
|
{
|
|
for (i = 0 ; i < 4 ; i++)
|
|
for (j = 0 ; j < 6 ; j++)
|
|
if ( MapleDevices[i][j] != 0 )
|
|
{
|
|
MapleDeviceType devtype = MapleDevices[i][j]->get_device_type() ;
|
|
mcfg_DestroyDevice(i,j) ;
|
|
mcfg_Create(devtype, i, j);
|
|
MapleDevices[i][j]->maple_unserialize(data, total_size) ;
|
|
}
|
|
}
|
|
else
|
|
mcfg_UnserializeDevices(data, total_size);
|
|
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_SKIP(4); // FrameCOunt
|
|
LIBRETRO_SKIP(1); // pend_rend
|
|
}
|
|
pend_rend = false;
|
|
|
|
LIBRETRO_USA(YUV_tempdata,512/4);
|
|
LIBRETRO_US(YUV_dest);
|
|
LIBRETRO_US(YUV_blockcount);
|
|
LIBRETRO_US(YUV_x_curr);
|
|
LIBRETRO_US(YUV_y_curr);
|
|
LIBRETRO_US(YUV_x_size);
|
|
LIBRETRO_US(YUV_y_size);
|
|
|
|
|
|
|
|
if (version < V9)
|
|
LIBRETRO_SKIP(1); // fog_needs_update
|
|
LIBRETRO_USA(pvr_regs,pvr_RegSize);
|
|
|
|
|
|
LIBRETRO_US(in_vblank);
|
|
LIBRETRO_US(clc_pvr_scanline);
|
|
/* pvr_cur_scanline: current raster line. Not saved before V15, where the
|
|
* post-load CalculateSync() reset it to 0; keep that behaviour for old
|
|
* states so their (approximate) load path is unchanged. */
|
|
if (version >= V15)
|
|
LIBRETRO_US(pvr_cur_scanline);
|
|
else
|
|
pvr_cur_scanline = 0;
|
|
// (the line counter has ten bits; lines are counted up to this one from wherever the beam is)
|
|
if (pvr_cur_scanline > 1023)
|
|
pvr_cur_scanline = 0;
|
|
fb_w_cur = 1;
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_US(i); // pvr_numscanlines
|
|
LIBRETRO_US(i); // prv_cur_scanline
|
|
LIBRETRO_US(i); // vblk_cnt
|
|
LIBRETRO_US(i); // Line_Cycles
|
|
LIBRETRO_US(i); // Frame_Cycles
|
|
if (version < V3)
|
|
{
|
|
LIBRETRO_US(dummy_int); //render_end_schid
|
|
LIBRETRO_US(dummy_int); //vblank_schid
|
|
LIBRETRO_US(dummy_int); //time_sync
|
|
}
|
|
LIBRETRO_SKIP(8); // speed_load_mspdf
|
|
LIBRETRO_US(dummy_int); // mips_counter
|
|
LIBRETRO_SKIP(8); // full_rps
|
|
|
|
LIBRETRO_SKIP(4 * 256); // ta_type_lut
|
|
LIBRETRO_SKIP(2048); // ta_fsm
|
|
}
|
|
if (version >= V12)
|
|
LIBRETRO_US(fb_w_cur);
|
|
else
|
|
fb_w_cur = 1;
|
|
LIBRETRO_US(ta_fsm[2048]);
|
|
LIBRETRO_US(ta_fsm_cl);
|
|
// (the state the list is in is one of eight, and it is an index into the table of what follows each)
|
|
if (ta_fsm[2048] > 7)
|
|
ta_fsm[2048] = 0;
|
|
if (ta_fsm_cl > 7)
|
|
ta_fsm_cl = 7;
|
|
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_SKIP(1); // pal_needs_update
|
|
LIBRETRO_SKIP(4 * 4); // _pal_rev_256
|
|
LIBRETRO_SKIP(4 * 64); // _pal_rev_16
|
|
LIBRETRO_SKIP(4 * 4); // pal_rev_256
|
|
LIBRETRO_SKIP(4 * 64); // pal_rev_16
|
|
LIBRETRO_SKIP(4 * 65536 * 3); // decoded_colors
|
|
LIBRETRO_US(i); // tileclip_val
|
|
LIBRETRO_SKIP(65536); // f32_su8_tbl
|
|
LIBRETRO_US(i); // FaceBaseColor
|
|
LIBRETRO_US(i); // FaceOffsColor
|
|
LIBRETRO_US(i); // SFaceBaseColor
|
|
LIBRETRO_US(i); // SFaceOffsColor
|
|
|
|
|
|
LIBRETRO_US(i); // palette_index
|
|
LIBRETRO_US(dummy); // KillTex
|
|
LIBRETRO_SKIP(4 * 1024); // palette16_ram
|
|
LIBRETRO_SKIP(4 * 1024); // palette32_ram
|
|
LIBRETRO_SKIP(4 * 1024 * 8 * 2); // detwiddle
|
|
}
|
|
if (version >= V10)
|
|
UnserializeTAContext(data, total_size, version); // (the state's version, not this build's: a state of before V12 has no render passes to read)
|
|
|
|
unserialize_vram(data, total_size);
|
|
|
|
LIBRETRO_USA(OnChipRAM.data,OnChipRAM_SIZE);
|
|
|
|
register_unserialize(CCN, data, total_size, version < V4 ? 16 : 0) ;
|
|
register_unserialize(UBC, data, total_size) ;
|
|
register_unserialize(BSC, data, total_size) ;
|
|
register_unserialize(DMAC, data, total_size) ;
|
|
register_unserialize(CPG, data, total_size) ;
|
|
register_unserialize(RTC, data, total_size) ;
|
|
register_unserialize(INTC, data, total_size, version < V4 ? 4 : 0) ;
|
|
register_unserialize(TMU, data, total_size) ;
|
|
register_unserialize(SCI, data, total_size) ;
|
|
register_unserialize(SCIF, data, total_size) ;
|
|
if (version >= V9)
|
|
icache.Unserialize(data, total_size);
|
|
else
|
|
icache.Reset(true);
|
|
if (version >= V10)
|
|
ocache.Unserialize(data, total_size);
|
|
else
|
|
ocache.Reset(true);
|
|
|
|
unserialize_ram(data, total_size);
|
|
|
|
if (version < V9)
|
|
{
|
|
u16 dum16;
|
|
LIBRETRO_US(dum16); // IRLPriority
|
|
}
|
|
LIBRETRO_USA(InterruptEnvId,32);
|
|
LIBRETRO_USA(InterruptBit,32);
|
|
LIBRETRO_USA(InterruptLevelBit,16);
|
|
LIBRETRO_US(interrupt_vpend);
|
|
LIBRETRO_US(interrupt_vmask);
|
|
LIBRETRO_US(decoded_srimask);
|
|
|
|
LIBRETRO_US(i) ;
|
|
if ( i == 0 )
|
|
do_sqw_nommu = &do_sqw_nommu_area_3 ;
|
|
else if ( i == 1 )
|
|
do_sqw_nommu = &do_sqw_nommu_area_3_nonvmem ;
|
|
else if ( i == 2 )
|
|
do_sqw_nommu = (sqw_fp*)&TAWriteSQ ;
|
|
else if ( i == 3 )
|
|
do_sqw_nommu = &do_sqw_nommu_full ;
|
|
|
|
LIBRETRO_USA((*p_sh4rcb).sq_buffer,64/8);
|
|
|
|
LIBRETRO_US((*p_sh4rcb).cntx);
|
|
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_US(dummy_int); // old_rm
|
|
LIBRETRO_US(dummy_int); // old_dn
|
|
}
|
|
|
|
LIBRETRO_US(sh4_sched_ffb);
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_SKIP(4); // sh4_sched_intr
|
|
if (version < V3)
|
|
LIBRETRO_US(dummy_int); // sh4_sched_next_id
|
|
}
|
|
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(sch_list[aica_schid].start) ;
|
|
LIBRETRO_US(sch_list[aica_schid].end) ;
|
|
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(sch_list[rtc_schid].start) ;
|
|
LIBRETRO_US(sch_list[rtc_schid].end) ;
|
|
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(GDROM_EVENT.start) ;
|
|
LIBRETRO_US(GDROM_EVENT.end) ;
|
|
no_event.tag = 0;
|
|
no_event.start = no_event.end = -1;
|
|
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(sch_list[maple_sched].start) ;
|
|
LIBRETRO_US(sch_list[maple_sched].end) ;
|
|
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(sch_list[dma_sched_id].start) ;
|
|
LIBRETRO_US(sch_list[dma_sched_id].end) ;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(sch_list[tmu_sched[i]].start) ;
|
|
LIBRETRO_US(sch_list[tmu_sched[i]].end) ;
|
|
}
|
|
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(sch_list[render_end_schid].start) ;
|
|
LIBRETRO_US(sch_list[render_end_schid].end) ;
|
|
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(sch_list[vblank_schid].start) ;
|
|
LIBRETRO_US(sch_list[vblank_schid].end) ;
|
|
spg_beam_forget();
|
|
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_US(dummy_int); // sch_list[time_sync].tag
|
|
LIBRETRO_US(dummy_int); // sch_list[time_sync].start
|
|
LIBRETRO_US(dummy_int); // sch_list[time_sync].end
|
|
}
|
|
|
|
{
|
|
/* Whether the machine has a broadband adapter in place of the
|
|
* modem. This build has none: a state that says it has one has
|
|
* the adapter's contents next, which nothing here can take, and
|
|
* the flag left set had every access to the modem go to an adapter
|
|
* that was never set up. */
|
|
u8 bba = 0;
|
|
if (version >= V13)
|
|
LIBRETRO_US(bba);
|
|
settings.network.EmulateBBA = false;
|
|
if (bba != 0)
|
|
{
|
|
ra_unserialize_fail();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( version >= V2 )
|
|
{
|
|
#ifdef ENABLE_MODEM
|
|
LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state
|
|
LIBRETRO_US(MODEM_EVENT.start) ;
|
|
LIBRETRO_US(MODEM_EVENT.end) ;
|
|
no_event.tag = 0;
|
|
no_event.start = no_event.end = -1;
|
|
#else
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int);
|
|
#endif
|
|
}
|
|
|
|
if (version < V3)
|
|
{
|
|
LIBRETRO_US(dummy_int); //aica_sched
|
|
LIBRETRO_US(dummy_int); //rtc_sched
|
|
}
|
|
|
|
LIBRETRO_US(SCIF_SCFSR2);
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_SKIP(1); // SCIF_SCFRDR2
|
|
LIBRETRO_US(dummy_int); // SCIF_SCFDR2
|
|
}
|
|
else if (version >= V11)
|
|
LIBRETRO_US(SCIF_SCSCR2);
|
|
LIBRETRO_US(BSC_PDTRA);
|
|
|
|
LIBRETRO_USA(tmu_shift,3);
|
|
LIBRETRO_USA(tmu_mask,3);
|
|
LIBRETRO_USA(tmu_mask64,3);
|
|
LIBRETRO_USA(old_mode,3);
|
|
if (version < V3)
|
|
{
|
|
//tmu_sched*3
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int);
|
|
}
|
|
LIBRETRO_USA(tmu_ch_base,3);
|
|
LIBRETRO_USA(tmu_ch_base64,3);
|
|
|
|
LIBRETRO_USA(CCN_QACR_TR,2);
|
|
// not the routine the state names, which may be from before each queue went by its own register
|
|
CCN_QACR_select();
|
|
|
|
if (version < V6)
|
|
{
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
LIBRETRO_US(UTLB[i].Address);
|
|
LIBRETRO_US(UTLB[i].Data);
|
|
}
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
LIBRETRO_US(ITLB[i].Address);
|
|
LIBRETRO_US(ITLB[i].Data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LIBRETRO_US(UTLB);
|
|
LIBRETRO_US(ITLB);
|
|
}
|
|
if (version >= V11)
|
|
LIBRETRO_US(sq_remap);
|
|
{
|
|
/* Which ITLB entry each LRU value replaces: a table this build
|
|
* works out once (MMU_init), not state. One from the state could
|
|
* point past the four entries. */
|
|
u32 lru_use[64];
|
|
LIBRETRO_USA(lru_use, 64);
|
|
}
|
|
|
|
LIBRETRO_US(NullDriveDiscType);
|
|
LIBRETRO_USA(q_subchannel,96);
|
|
|
|
{
|
|
/* How much memory of each kind the machine has, and the masks for
|
|
* it. These are not state: they are what the machine that is
|
|
* running was set up with, and its memory is that large whatever
|
|
* a state says. Taking them from the state had every access
|
|
* masked for a size the memory may not have. A state made on a
|
|
* machine with other main, video or sound memory is another
|
|
* machine's, with all that came before this laid out otherwise,
|
|
* and is not loaded. */
|
|
unsigned sizes[9];
|
|
LIBRETRO_USA(sizes, 9);
|
|
if (sizes[3] != RAM_SIZE || sizes[4] != ARAM_SIZE || sizes[5] != VRAM_SIZE)
|
|
{
|
|
ra_unserialize_fail();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_US(dummy_int); // naomi_updates
|
|
if (version < V4)
|
|
{
|
|
LIBRETRO_US(dummy_int); // RomPioOffset
|
|
LIBRETRO_US(dummy_int); // DmaOffset
|
|
LIBRETRO_US(dummy_int); // DmaCount
|
|
}
|
|
LIBRETRO_US(dummy_int); // BoardID
|
|
}
|
|
LIBRETRO_US(GSerialBuffer);
|
|
LIBRETRO_US(BSerialBuffer);
|
|
LIBRETRO_US(GBufPos);
|
|
LIBRETRO_US(BBufPos);
|
|
LIBRETRO_US(GState);
|
|
LIBRETRO_US(BState);
|
|
LIBRETRO_US(GOldClk);
|
|
LIBRETRO_US(BOldClk);
|
|
LIBRETRO_US(BControl);
|
|
LIBRETRO_US(BCmd);
|
|
LIBRETRO_US(BLastCmd);
|
|
LIBRETRO_US(GControl);
|
|
LIBRETRO_US(GCmd);
|
|
LIBRETRO_US(GLastCmd);
|
|
LIBRETRO_US(SerStep);
|
|
LIBRETRO_US(SerStep2);
|
|
LIBRETRO_USA(BSerial,69);
|
|
LIBRETRO_USA(GSerial,69);
|
|
LIBRETRO_US(reg_dimm_command);
|
|
LIBRETRO_US(reg_dimm_offsetl);
|
|
LIBRETRO_US(reg_dimm_parameterl);
|
|
LIBRETRO_US(reg_dimm_parameterh);
|
|
LIBRETRO_US(reg_dimm_status);
|
|
LIBRETRO_SKIP(1); // NaomiDataRead
|
|
if (version < V4)
|
|
{
|
|
LIBRETRO_US(dummy_int); // NAOMI_ROM_OFFSETH
|
|
LIBRETRO_US(dummy_int); // NAOMI_ROM_OFFSETL
|
|
LIBRETRO_US(dummy_int); // NAOMI_ROM_DATA
|
|
LIBRETRO_US(dummy_int); // NAOMI_DMA_OFFSETH
|
|
LIBRETRO_US(dummy_int); // NAOMI_DMA_OFFSETL
|
|
LIBRETRO_US(dummy_int); // NAOMI_DMA_COUNT
|
|
LIBRETRO_US(dummy_int); // NAOMI_BOARDID_WRITE
|
|
LIBRETRO_US(dummy_int); // NAOMI_BOARDID_READ
|
|
LIBRETRO_US(dummy_int); // NAOMI_COMM_OFFSET
|
|
LIBRETRO_US(dummy_int); // NAOMI_COMM_DATA
|
|
}
|
|
|
|
if (version < V9)
|
|
{
|
|
LIBRETRO_US(dummy_int); // cycle_counter
|
|
LIBRETRO_US(dummy_int); // idxnxx
|
|
|
|
#if FEAT_SHREC != DYNAREC_NONE
|
|
LIBRETRO_SKIP(sizeof(state_t)); // state
|
|
LIBRETRO_US(dummy_int); // div_som_reg1
|
|
LIBRETRO_US(dummy_int); // div_som_reg2
|
|
LIBRETRO_US(dummy_int); // div_som_reg3
|
|
|
|
LIBRETRO_US(dummy_int); // LastAddr
|
|
LIBRETRO_US(dummy_int); // LastAddr_min
|
|
LIBRETRO_SKIP(1024); // block_hash
|
|
#endif
|
|
|
|
// RegisterRead, RegisterWrite
|
|
for (int i = 0; i < sh4_reg_count; i++)
|
|
{
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int);
|
|
}
|
|
LIBRETRO_US(dummy_int); // fallback_blocks
|
|
LIBRETRO_US(dummy_int); // total_blocks
|
|
LIBRETRO_US(dummy_int); // REMOVED_OPS
|
|
|
|
if (version < V5)
|
|
{
|
|
LIBRETRO_US(dummy_int); // u16 kcode[4]
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int); // u8 rt[4]
|
|
LIBRETRO_US(dummy_int); // u8 lt[4]
|
|
LIBRETRO_US(dummy_int); // u32 vks[4]
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int);
|
|
LIBRETRO_US(dummy_int); // s8 joyx[4]
|
|
LIBRETRO_US(dummy_int); // s8 joyy[4]
|
|
}
|
|
}
|
|
if (version >= V3)
|
|
{
|
|
LIBRETRO_US(settings.dreamcast.broadcast);
|
|
LIBRETRO_US(settings.dreamcast.cable);
|
|
LIBRETRO_US(settings.dreamcast.region);
|
|
}
|
|
|
|
if (version >= V4 && CurrentCartridge != NULL)
|
|
{
|
|
CurrentCartridge->Unserialize(data, total_size);
|
|
}
|
|
if (version >= V7)
|
|
gd_hle_state.Unserialize(data, total_size);
|
|
|
|
if (version >= V14)
|
|
LIBRETRO_US(RealTimeClock);
|
|
|
|
/* Before V18 a transfer's answers were in memory from its start, and
|
|
* so they are in such a state: there is nothing waiting. */
|
|
maple_out_used = 0;
|
|
if (version >= V18)
|
|
{
|
|
LIBRETRO_US(maple_out_used);
|
|
LIBRETRO_USA(maple_out, MAPLE_OUT_WORDS);
|
|
if (maple_out_used > MAPLE_OUT_WORDS)
|
|
maple_out_used = 0;
|
|
}
|
|
|
|
// Before V19 a disc put in was ready at once: there is never a look under way
|
|
{
|
|
/* (on a machine with no drive there is no such event, its id is
|
|
* -1, and this was written before the start of the list) */
|
|
int swap_start = -1;
|
|
int swap_end = -1;
|
|
if (version >= V19)
|
|
{
|
|
LIBRETRO_US(swap_start);
|
|
LIBRETRO_US(swap_end);
|
|
}
|
|
if (gd_swap_schid >= 0)
|
|
{
|
|
if (version >= V19)
|
|
sch_list[gd_swap_schid].start = swap_start;
|
|
sch_list[gd_swap_schid].end = swap_end;
|
|
}
|
|
}
|
|
|
|
if (version >= V20 && settings.System == DC_PLATFORM_NAOMI2 && elan_ram)
|
|
{
|
|
elan_state_t elan;
|
|
LIBRETRO_US(SB_ISTNRM1);
|
|
LIBRETRO_US(elan);
|
|
LIBRETRO_USA(elan_ram, ELAN_RAM_SIZE);
|
|
LIBRETRO_US(sch_list[elan_schid].start);
|
|
LIBRETRO_US(sch_list[elan_schid].end);
|
|
elan_set_state(&elan);
|
|
}
|
|
|
|
if (version >= V21)
|
|
{
|
|
bool ok = aica_unserialize_v21(data, total_size);
|
|
ok = ok && gdrom_unserialize_v21(data, total_size);
|
|
ok = ok && maple_unserialize_v21(data, total_size);
|
|
ok = ok && naomi_unserialize_v21(data, total_size);
|
|
#ifdef ENABLE_MODEM
|
|
ok = ok && modem_unserialize_v21(data, total_size);
|
|
#else
|
|
if (settings.System == DC_PLATFORM_DREAMCAST)
|
|
LIBRETRO_SKIP(MODEM_V21_SIZE);
|
|
#endif
|
|
ok = ok && reios_unserialize_v21(data, total_size);
|
|
ok = ok && gdhle_unserialize_v21(data, total_size);
|
|
ok = ok && nbios_unserialize_v21(data, total_size);
|
|
ok = ok && pvr_unserialize_v21(data, total_size);
|
|
if (!ok)
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
{
|
|
aica_state_before_v21();
|
|
gdrom_state_before_v21();
|
|
maple_state_before_v21();
|
|
naomi_state_before_v21();
|
|
#ifdef ENABLE_MODEM
|
|
modem_state_before_v21();
|
|
#endif
|
|
reios_state_before_v21();
|
|
gdhle_state_before_v21();
|
|
nbios_state_before_v21();
|
|
pvr_state_before_v21();
|
|
}
|
|
if (version >= V22)
|
|
{
|
|
pvr_unserialize_v22(data, total_size);
|
|
if (!mmu_unserialize_v22(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
{
|
|
pvr_state_before_v22();
|
|
mmu_state_before_v22();
|
|
}
|
|
if (version >= V23)
|
|
{
|
|
if (!pvr_unserialize_v23(data, total_size) || !naomi_unserialize_v23(data, total_size)
|
|
|| !lr_unserialize_v23(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
{
|
|
pvr_state_before_v23();
|
|
naomi_state_before_v23();
|
|
lr_state_before_v23();
|
|
}
|
|
if (version >= V24)
|
|
{
|
|
if (!net_unserialize_v24(data, total_size)
|
|
|| !vmu_beep_unserialize_v24(data, total_size)
|
|
|| !naomi_periph_unserialize_v24(data, total_size)
|
|
|| !systemsp_unserialize_v24(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
{
|
|
net_state_before_v24();
|
|
vmu_beep_state_before_v24();
|
|
naomi_periph_state_before_v24();
|
|
systemsp_state_before_v24();
|
|
}
|
|
if (version >= V25)
|
|
{
|
|
if (!systemsp_unserialize_v25(data, total_size)
|
|
|| !maple_jvs_unserialize_v25(data, total_size)
|
|
|| !naomi_gd_unserialize_v25(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
{
|
|
systemsp_state_before_v25();
|
|
maple_jvs_state_before_v25();
|
|
naomi_gd_state_before_v25();
|
|
}
|
|
if (version >= V26)
|
|
{
|
|
if (!naomi_serial_unserialize_v26(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
naomi_serial_state_before_v26();
|
|
if (version >= V27)
|
|
{
|
|
if (!maple_jvs_unserialize_v27(data, total_size)
|
|
|| !midiffb_unserialize_v27(data, total_size)
|
|
|| !naomi_printer_unserialize_v27(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
{
|
|
maple_jvs_state_before_v27();
|
|
midiffb_state_before_v27();
|
|
naomi_printer_state_before_v27();
|
|
}
|
|
if (version >= V28)
|
|
{
|
|
if (!pvr_unserialize_v28(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
pvr_state_before_v28();
|
|
if (version >= V29)
|
|
{
|
|
if (!aica_sb_unserialize_v29(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
aica_sb_state_before_v29();
|
|
if (version >= V30)
|
|
{
|
|
if (!naomi_cards_unserialize_v30(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
naomi_cards_state_before_v30();
|
|
if (version >= V31)
|
|
{
|
|
if (!naomi_gd_unserialize_v31(data, total_size))
|
|
ra_unserialize_fail();
|
|
}
|
|
else
|
|
naomi_gd_state_before_v31();
|
|
sched_events_loaded();
|
|
/* A sound DMA is under way only while its end is to come: a start is
|
|
* not taken while one is, and SB_ADST set with no end on its way would
|
|
* have none taken again. */
|
|
if ((SB_ADST & 1) && !sh4_sched_is_scheduled(dma_sched_id))
|
|
SB_ADST = 0;
|
|
/* The timers: what follows from their registers is worked out again,
|
|
* for a state of any version, and their events are asked for anew. */
|
|
tmu_state_loaded();
|
|
|
|
/* Fail the whole load if any read was rejected for running past the end
|
|
* of the input buffer. Callers (retro_unserialize) then bail cleanly
|
|
* instead of continuing on partially-garbage state. */
|
|
return !ra_unserialize_failed() ;
|
|
}
|