redquark-amiberry-rb/src/savestate.cpp

1190 lines
28 KiB
C++
Raw Normal View History

2017-12-04 15:49:40 +01:00
/*
2016-11-30 22:25:43 +01:00
* UAE - The Un*x Amiga Emulator
*
* Save/restore emulator state
*
* (c) 1999-2001 Toni Wilen
*
* see below for ASF-structure
*/
2017-12-04 15:49:40 +01:00
/* Features:
*
* - full CPU state (68000/68010/68020/68030/68040/68060)
* - FPU (68881/68882/68040/68060)
* - full CIA-A and CIA-B state (with all internal registers)
* - saves all custom registers and audio internal state.
* - Chip, Bogo, Fast, Z3 and Picasso96 RAM supported
* - disk drive type, imagefile, track and motor state
* - Kickstart ROM version, address and size is saved. This data is not used during restore yet.
* - Action Replay state is saved
*/
/* Notes:
*
* - blitter state is not saved, blitter is forced to finish immediately if it
* was active
* - disk DMA state is completely saved
* - does not ask for statefile name and description. Currently uses DF0's disk
* image name (".adf" is replaced with ".asf")
* - only Amiga state is restored, harddisk support, autoconfig, expansion boards etc..
* are not saved/restored (and probably never will).
* - use this for saving games that can't be saved to disk
*/
/* Usage :
*
* save:
*
* set savestate_state = STATE_DOSAVE, savestate_filename = "..."
*
* restore:
*
* set savestate_state = STATE_DORESTORE, savestate_filename = "..."
*
*/
2015-05-13 18:47:23 +00:00
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
2017-12-04 15:49:40 +01:00
#include "memory.h"
2015-05-13 18:47:23 +00:00
#include "zfile.h"
#include "autoconf.h"
#include "custom.h"
2017-12-04 15:49:40 +01:00
#include "newcpu.h"
2015-05-13 18:47:23 +00:00
#include "savestate.h"
#include "uae.h"
2015-09-09 21:49:41 +02:00
#include "gui.h"
2015-05-17 07:52:43 +00:00
#include "audio.h"
2015-09-09 21:49:41 +02:00
#include "filesys.h"
2017-12-04 15:49:40 +01:00
#include "disk.h"
#include "threaddep/thread.h"
#include "devices.h"
2015-05-13 18:47:23 +00:00
int savestate_state = 0;
static bool new_blitter = false;
2017-12-04 15:49:40 +01:00
struct zfile *savestate_file;
static int savestate_docompress, savestate_nodialogs;
2015-05-13 18:47:23 +00:00
TCHAR savestate_fname[MAX_DPATH];
2015-05-13 18:47:23 +00:00
2017-12-04 15:49:40 +01:00
static void state_incompatible_warn(void)
2015-09-09 21:49:41 +02:00
{
2017-12-04 15:49:40 +01:00
static int warned;
int dowarn = 0;
int i;
2015-09-09 21:49:41 +02:00
#ifdef BSDSOCKET
2016-11-30 22:25:43 +01:00
if (currprefs.socket_emu)
dowarn = 1;
2015-09-09 21:49:41 +02:00
#endif
#ifdef FILESYS
2017-12-04 15:49:40 +01:00
for(i = 0; i < currprefs.mountitems; i++) {
struct mountedinfo mi;
int type = get_filesys_unitconfig (&currprefs, i, &mi);
if (mi.ismounted && type != FILESYS_VIRTUAL && type != FILESYS_HARDFILE && type != FILESYS_HARDFILE_RDB)
dowarn = 1;
}
2015-09-09 21:49:41 +02:00
#endif
2017-12-04 15:49:40 +01:00
if (!warned && dowarn) {
warned = 1;
notify_user (NUMSG_STATEHD);
}
2015-09-09 21:49:41 +02:00
}
2017-12-04 15:49:40 +01:00
void save_u32_func (uae_u8 **dstp, uae_u32 v)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 *dst = *dstp;
*dst++ = (uae_u8)(v >> 24);
*dst++ = (uae_u8)(v >> 16);
*dst++ = (uae_u8)(v >> 8);
*dst++ = (uae_u8)(v >> 0);
*dstp = dst;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
void save_u64_func (uae_u8 **dstp, uae_u64 v)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
save_u32_func (dstp, (uae_u32)(v >> 32));
save_u32_func (dstp, (uae_u32)v);
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
void save_u16_func (uae_u8 **dstp, uae_u16 v)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 *dst = *dstp;
*dst++ = (uae_u8)(v >> 8);
*dst++ = (uae_u8)(v >> 0);
*dstp = dst;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
void save_u8_func (uae_u8 **dstp, uae_u8 v)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 *dst = *dstp;
*dst++ = v;
*dstp = dst;
}
2017-12-04 15:49:40 +01:00
void save_string_func (uae_u8 **dstp, const TCHAR *from)
{
2017-12-04 15:49:40 +01:00
uae_u8 *dst = *dstp;
2016-11-30 22:25:43 +01:00
char *s, *s2;
2017-12-04 15:49:40 +01:00
s2 = s = uutf8 (from);
2016-11-30 22:25:43 +01:00
while (s && *s)
*dst++ = *s++;
2017-12-04 15:49:40 +01:00
*dst++ = 0;
*dstp = dst;
2016-11-30 22:25:43 +01:00
xfree (s2);
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
void save_path_func (uae_u8 **dstp, const TCHAR *from, int type)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
save_string_func (dstp, from);
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
uae_u32 restore_u32_func (uae_u8 **dstp)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u32 v;
uae_u8 *dst = *dstp;
v = (dst[0] << 24) | (dst[1] << 16) | (dst[2] << 8) | (dst[3]);
*dstp = dst + 4;
return v;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
uae_u64 restore_u64_func (uae_u8 **dstp)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u64 v;
v = restore_u32_func (dstp);
v <<= 32;
v |= restore_u32_func (dstp);
return v;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
uae_u16 restore_u16_func (uae_u8 **dstp)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u16 v;
uae_u8 *dst = *dstp;
v=(dst[0] << 8) | (dst[1]);
*dstp = dst + 2;
return v;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
uae_u8 restore_u8_func (uae_u8 **dstp)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 v;
uae_u8 *dst = *dstp;
v = dst[0];
*dstp = dst + 1;
return v;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
TCHAR *restore_string_func (uae_u8 **dstp)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
int len;
uae_u8 v;
uae_u8 *dst = *dstp;
char *top, *to;
TCHAR *s;
len = strlen((char *)dst) + 1;
top = to = xmalloc (char, len);
do {
v = *dst++;
*top++ = v;
} while(v);
*dstp = dst;
s = utf8u (to);
2016-11-30 22:25:43 +01:00
xfree (to);
return s;
}
2017-12-04 15:49:40 +01:00
TCHAR *restore_path_func (uae_u8 **dstp, int type)
{
TCHAR *newpath;
TCHAR *s;
2017-12-04 15:49:40 +01:00
TCHAR tmp[MAX_DPATH], tmp2[MAX_DPATH];
2016-11-30 22:25:43 +01:00
s = restore_string_func(dstp);
if (s[0] == 0)
return s;
2017-12-04 15:49:40 +01:00
if (zfile_exists (s))
2016-11-30 22:25:43 +01:00
return s;
if (type == SAVESTATE_PATH_HD)
return s;
2017-12-04 15:49:40 +01:00
getfilepart (tmp, sizeof tmp / sizeof (TCHAR), s);
if (zfile_exists (tmp)) {
xfree (s);
return my_strdup (tmp);
2016-11-30 22:25:43 +01:00
}
2017-12-04 15:49:40 +01:00
newpath = NULL;
if (type == SAVESTATE_PATH_FLOPPY)
newpath = currprefs.path_floppy;
else if (type == SAVESTATE_PATH_VDIR || type == SAVESTATE_PATH_HDF)
newpath = currprefs.path_hardfile;
else if (type == SAVESTATE_PATH_CD)
newpath = currprefs.path_cd;
if (newpath != NULL && newpath[0] != 0) {
_tcscpy (tmp2, newpath);
fixtrailing (tmp2);
_tcscat (tmp2, tmp);
if (zfile_exists (tmp2)) {
xfree (s);
return my_strdup (tmp2);
2016-11-30 22:25:43 +01:00
}
2017-12-04 15:49:40 +01:00
}
getpathpart (tmp2, sizeof tmp2 / sizeof (TCHAR), savestate_fname);
2016-11-30 22:25:43 +01:00
_tcscat (tmp2, tmp);
2017-12-04 15:49:40 +01:00
if (zfile_exists (tmp2)) {
2016-11-30 22:25:43 +01:00
xfree (s);
2017-12-04 15:49:40 +01:00
return my_strdup (tmp2);
2016-11-30 22:25:43 +01:00
}
return s;
2015-05-13 18:47:23 +00:00
}
/* read and write IFF-style hunks */
2017-12-04 15:49:40 +01:00
static void save_chunk (struct zfile *f, uae_u8 *chunk, unsigned int len, const TCHAR *name, int compress)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 tmp[8], *dst;
uae_u8 zero[4]= { 0, 0, 0, 0 };
uae_u32 flags;
unsigned int pos;
unsigned int chunklen, len2;
char *s;
if (!chunk)
return;
if (compress < 0) {
zfile_fwrite (chunk, 1, len, f);
return;
}
/* chunk name */
s = ua (name);
zfile_fwrite (s, 1, 4, f);
2016-11-30 22:25:43 +01:00
xfree (s);
2017-12-04 15:49:40 +01:00
pos = zfile_ftell (f);
/* chunk size */
dst = &tmp[0];
chunklen = len + 4 + 4 + 4;
save_u32 (chunklen);
zfile_fwrite (&tmp[0], 1, 4, f);
/* chunk flags */
flags = 0;
dst = &tmp[0];
save_u32 (flags | compress);
zfile_fwrite (&tmp[0], 1, 4, f);
/* chunk data */
if (compress) {
int tmplen = len;
size_t opos;
dst = &tmp[0];
save_u32 (len);
opos = zfile_ftell (f);
zfile_fwrite (&tmp[0], 1, 4, f);
len = zfile_zcompress (f, chunk, len);
if (len > 0) {
zfile_fseek (f, pos, SEEK_SET);
dst = &tmp[0];
save_u32 (len + 4 + 4 + 4 + 4);
zfile_fwrite (&tmp[0], 1, 4, f);
zfile_fseek (f, 0, SEEK_END);
} else {
len = tmplen;
compress = 0;
zfile_fseek (f, opos, SEEK_SET);
dst = &tmp[0];
save_u32 (flags);
zfile_fwrite (&tmp[0], 1, 4, f);
}
}
if (!compress)
zfile_fwrite (chunk, 1, len, f);
/* alignment */
len2 = 4 - (len & 3);
if (len2)
zfile_fwrite (zero, 1, len2, f);
write_log (_T("Chunk '%s' chunk size %u (%u)\n"), name, chunklen, len);
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
static uae_u8 *restore_chunk (struct zfile *f, TCHAR *name, unsigned int *len, unsigned int *totallen, size_t *filepos)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 tmp[6], dummy[4], *mem, *src;
uae_u32 flags;
int len2;
2016-11-30 22:25:43 +01:00
2017-12-04 15:49:40 +01:00
*totallen = 0;
/* chunk name */
zfile_fread (tmp, 1, 4, f);
2016-11-30 22:25:43 +01:00
tmp[4] = 0;
2017-12-04 15:49:40 +01:00
au_copy (name, 5, (char*)tmp);
/* chunk size */
zfile_fread (tmp, 1, 4, f);
src = tmp;
len2 = restore_u32 () - 4 - 4 - 4;
if (len2 < 0)
len2 = 0;
*len = len2;
if (len2 == 0) {
*filepos = zfile_ftell (f);
return 0;
}
/* chunk flags */
zfile_fread (tmp, 1, 4, f);
src = tmp;
flags = restore_u32 ();
*totallen = *len;
if (flags & 1) {
zfile_fread (tmp, 1, 4, f);
src = tmp;
*totallen = restore_u32();
*filepos = zfile_ftell (f) - 4 - 4 - 4;
len2 -= 4;
} else {
*filepos = zfile_ftell (f) - 4 - 4;
}
/* chunk data. RAM contents will be loaded during the reset phase,
no need to malloc multiple megabytes here. */
if (_tcscmp (name, _T("CRAM")) != 0
&& _tcscmp (name, _T("BRAM")) != 0
&& _tcscmp (name, _T("FRAM")) != 0
&& _tcscmp (name, _T("ZRAM")) != 0
&& _tcscmp (name, _T("PRAM")) != 0
&& _tcscmp (name, _T("A3K1")) != 0
2016-11-30 22:25:43 +01:00
&& _tcscmp (name, _T("A3K2")) != 0
&& _tcscmp (name, _T("BORO")) != 0
)
2017-12-04 15:49:40 +01:00
{
/* extra bytes at the end needed to handle old statefiles that now have new fields */
mem = xcalloc (uae_u8, *totallen + 100);
if (!mem)
return NULL;
if (flags & 1) {
zfile_zuncompress (mem, *totallen, f, len2);
} else {
zfile_fread (mem, 1, len2, f);
}
} else {
mem = 0;
zfile_fseek (f, len2, SEEK_CUR);
}
/* alignment */
len2 = 4 - (len2 & 3);
if (len2)
zfile_fread (dummy, 1, len2, f);
return mem;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
void restore_ram (size_t filepos, uae_u8 *memory)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 tmp[8];
uae_u8 *src = tmp;
int size, fullsize;
uae_u32 flags;
if (filepos == 0 || memory == NULL)
return;
zfile_fseek (savestate_file, filepos, SEEK_SET);
zfile_fread (tmp, 1, sizeof tmp, savestate_file);
size = restore_u32();
flags = restore_u32();
size -= 4 + 4 + 4;
if (flags & 1) {
zfile_fread (tmp, 1, 4, savestate_file);
src = tmp;
fullsize = restore_u32();
size -= 4;
zfile_zuncompress (memory, fullsize, savestate_file, size);
} else {
zfile_fread (memory, 1, size, savestate_file);
}
2015-09-09 21:49:41 +02:00
}
2017-12-04 15:49:40 +01:00
static void restore_header (uae_u8 *src)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
TCHAR *emuname, *emuversion, *description;
2016-11-30 22:25:43 +01:00
2017-12-04 15:49:40 +01:00
restore_u32();
emuname = restore_string ();
emuversion = restore_string ();
description = restore_string ();
2016-11-30 22:25:43 +01:00
write_log (_T("Saved with: '%s %s', description: '%s'\n"),
2017-12-04 15:49:40 +01:00
emuname,emuversion,description);
xfree (description);
xfree (emuversion);
xfree (emuname);
2015-05-13 18:47:23 +00:00
}
/* restore all subsystems */
2017-12-04 15:49:40 +01:00
void restore_state (const TCHAR *filename)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
struct zfile *f;
uae_u8 *chunk,*end;
TCHAR name[5];
unsigned int len, totallen;
size_t filepos, filesize;
int z3num, z2num;
2017-02-25 00:38:14 +01:00
2017-12-04 15:49:40 +01:00
chunk = 0;
f = zfile_fopen (filename, _T("rb"), ZFD_NORMAL);
if (!f)
goto error;
zfile_fseek (f, 0, SEEK_END);
filesize = zfile_ftell (f);
zfile_fseek (f, 0, SEEK_SET);
savestate_state = STATE_RESTORE;
chunk = restore_chunk (f, name, &len, &totallen, &filepos);
if (!chunk || _tcsncmp (name, _T("ASF "), 4)) {
2016-11-30 22:25:43 +01:00
write_log (_T("%s is not an AmigaStateFile\n"), filename);
2017-12-04 15:49:40 +01:00
goto error;
}
2016-11-30 22:25:43 +01:00
write_log (_T("STATERESTORE: '%s'\n"), filename);
2017-12-04 15:49:40 +01:00
set_config_changed ();
savestate_file = f;
restore_header (chunk);
xfree (chunk);
devices_restore_start();
z2num = z3num = 0;
2017-12-04 15:49:40 +01:00
for (;;) {
name[0] = 0;
chunk = end = restore_chunk (f, name, &len, &totallen, &filepos);
write_log (_T("Chunk '%s' size %u (%u)\n"), name, len, totallen);
if (!_tcscmp (name, _T("END "))) {
break;
}
if (!_tcscmp (name, _T("CRAM"))) {
restore_cram (totallen, filepos);
continue;
} else if (!_tcscmp (name, _T("BRAM"))) {
restore_bram (totallen, filepos);
continue;
} else if (!_tcscmp (name, _T("A3K1"))) {
restore_a3000lram (totallen, filepos);
2017-02-25 00:38:14 +01:00
continue;
2017-12-04 15:49:40 +01:00
} else if (!_tcscmp (name, _T("A3K2"))) {
restore_a3000hram (totallen, filepos);
2017-02-25 00:38:14 +01:00
continue;
2015-05-13 18:47:23 +00:00
#ifdef AUTOCONFIG
2017-12-04 15:49:40 +01:00
} else if (!_tcscmp (name, _T("FRAM"))) {
restore_fram (totallen, filepos, z2num++);
continue;
} else if (!_tcscmp (name, _T("ZRAM"))) {
restore_zram (totallen, filepos, z3num++);
continue;
} else if (!_tcscmp (name, _T("BORO"))) {
restore_bootrom (totallen, filepos);
continue;
2015-05-13 18:47:23 +00:00
#endif
#ifdef PICASSO96
2017-12-04 15:49:40 +01:00
} else if (!_tcscmp (name, _T("PRAM"))) {
restore_pram (totallen, filepos);
continue;
2015-05-13 18:47:23 +00:00
#endif
2017-12-04 15:49:40 +01:00
} else if (!_tcscmp (name, _T("CPU "))) {
end = restore_cpu (chunk);
} else if (!_tcscmp (name, _T("CPUX")))
end = restore_cpu_extra (chunk);
2015-05-13 18:47:23 +00:00
#ifdef FPUEMU
2017-12-04 15:49:40 +01:00
else if (!_tcscmp (name, _T("FPU ")))
end = restore_fpu (chunk);
2015-05-13 18:47:23 +00:00
#endif
2017-12-04 15:49:40 +01:00
else if (!_tcscmp (name, _T("AGAC")))
end = restore_custom_agacolors (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR0")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (0, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR1")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (1, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR2")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (2, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR3")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (3, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR4")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (4, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR5")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (5, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR6")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (6, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("SPR7")))
2017-12-04 15:49:40 +01:00
end = restore_custom_sprite (7, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("CIAA")))
2017-12-04 15:49:40 +01:00
end = restore_cia (0, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("CIAB")))
2017-12-04 15:49:40 +01:00
end = restore_cia (1, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("CHIP")))
2017-12-04 15:49:40 +01:00
end = restore_custom (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("CINP")))
2017-12-04 15:49:40 +01:00
end = restore_input (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("CHPX")))
2017-12-04 15:49:40 +01:00
end = restore_custom_extra (chunk);
else if (!_tcscmp (name, _T("AUD0")))
end = restore_audio (0, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("AUD1")))
2017-12-04 15:49:40 +01:00
end = restore_audio (1, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("AUD2")))
2017-12-04 15:49:40 +01:00
end = restore_audio (2, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("AUD3")))
2017-12-04 15:49:40 +01:00
end = restore_audio (3, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("BLIT")))
2017-12-04 15:49:40 +01:00
end = restore_blitter (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("BLTX")))
2017-12-04 15:49:40 +01:00
end = restore_blitter_new (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DISK")))
2017-12-04 15:49:40 +01:00
end = restore_floppy (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSK0")))
2017-12-04 15:49:40 +01:00
end = restore_disk (0, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSK1")))
2017-12-04 15:49:40 +01:00
end = restore_disk (1, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSK2")))
2017-12-04 15:49:40 +01:00
end = restore_disk (2, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSK3")))
2017-12-04 15:49:40 +01:00
end = restore_disk (3, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSD0")))
2017-12-04 15:49:40 +01:00
end = restore_disk2 (0, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSD1")))
2017-12-04 15:49:40 +01:00
end = restore_disk2 (1, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSD2")))
2017-12-04 15:49:40 +01:00
end = restore_disk2 (2, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("DSD3")))
2017-12-04 15:49:40 +01:00
end = restore_disk2 (3, chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("KEYB")))
2017-12-04 15:49:40 +01:00
end = restore_keyboard (chunk);
2015-05-13 18:47:23 +00:00
#ifdef AUTOCONFIG
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("EXPA")))
2017-12-04 15:49:40 +01:00
end = restore_expansion (chunk);
2015-05-13 18:47:23 +00:00
#endif
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("ROM ")))
2017-12-04 15:49:40 +01:00
end = restore_rom (chunk);
2015-05-17 07:52:43 +00:00
#ifdef PICASSO96
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("P96 ")))
2017-12-04 15:49:40 +01:00
end = restore_p96 (chunk);
#endif
#ifdef ACTION_REPLAY
else if (!_tcscmp (name, _T("ACTR")))
end = restore_action_replay (chunk);
else if (!_tcscmp (name, _T("HRTM")))
end = restore_hrtmon (chunk);
2015-05-17 07:52:43 +00:00
#endif
2015-05-13 18:47:23 +00:00
#ifdef FILESYS
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("FSYS")))
2017-12-04 15:49:40 +01:00
end = restore_filesys (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("FSYC")))
2017-12-04 15:49:40 +01:00
end = restore_filesys_common (chunk);
2015-09-09 21:49:41 +02:00
#endif
#ifdef CD32
2016-11-30 22:25:43 +01:00
else if (!_tcscmp (name, _T("CD32")))
2017-12-04 15:49:40 +01:00
end = restore_akiko (chunk);
#endif
2017-12-04 15:49:40 +01:00
else if (!_tcscmp (name, _T("GAYL")))
end = restore_gayle (chunk);
else if (!_tcscmp (name, _T("IDE ")))
end = restore_gayle_ide (chunk);
2016-11-30 22:25:43 +01:00
else if (!_tcsncmp (name, _T("CDU"), 3))
2017-12-04 15:49:40 +01:00
end = restore_cd (name[3] - '0', chunk);
else if (!_tcsncmp (name, _T("EXPI"), 4))
end = restore_expansion_info(chunk);
else {
end = chunk + len;
2016-11-30 22:25:43 +01:00
write_log (_T("unknown chunk '%s' size %d bytes\n"), name, len);
2017-12-04 15:49:40 +01:00
}
if (end == NULL)
write_log (_T("Chunk '%s', size %d bytes was not accepted!\n"),
name, len);
else if (totallen != end - chunk)
write_log (_T("Chunk '%s' total size %d bytes but read %ld bytes!\n"),
name, totallen, end - chunk);
xfree (chunk);
}
target_addtorecent (filename, 0);
return;
error:
2017-12-04 15:49:40 +01:00
savestate_state = 0;
savestate_file = 0;
if (chunk)
xfree (chunk);
if (f)
zfile_fclose (f);
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
void savestate_restore_finish (void)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
if (!isrestore ())
return;
zfile_fclose (savestate_file);
savestate_file = 0;
restore_cpu_finish();
restore_audio_finish ();
restore_disk_finish ();
restore_akiko_finish ();
#ifdef PICASSO96
2017-12-04 15:49:40 +01:00
restore_p96_finish ();
#endif
2017-12-04 15:49:40 +01:00
restore_cia_finish ();
2016-11-30 22:25:43 +01:00
savestate_state = 0;
init_hz_normal();
2017-12-04 15:49:40 +01:00
audio_activate ();
2015-05-13 18:47:23 +00:00
}
/* 1=compressed,2=not compressed,3=ram dump,4=audio dump */
2017-12-04 15:49:40 +01:00
void savestate_initsave (const TCHAR *filename, int mode, int nodialogs, bool save)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
if (filename == NULL) {
savestate_fname[0] = 0;
savestate_docompress = 0;
savestate_nodialogs = 0;
return;
}
_tcscpy (savestate_fname, filename);
savestate_docompress = (mode == 1) ? 1 : 0;
savestate_nodialogs = nodialogs;
2016-11-30 22:25:43 +01:00
new_blitter = false;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
static void save_rams (struct zfile *f, int comp)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 *dst;
int len;
dst = save_cram (&len);
save_chunk (f, dst, len, _T("CRAM"), comp);
dst = save_bram (&len);
save_chunk (f, dst, len, _T("BRAM"), comp);
dst = save_a3000lram (&len);
save_chunk (f, dst, len, _T("A3K1"), comp);
dst = save_a3000hram (&len);
save_chunk (f, dst, len, _T("A3K2"), comp);
2015-05-13 18:47:23 +00:00
#ifdef AUTOCONFIG
for (int i = 0; i < MAX_RAM_BOARDS; i++) {
2017-12-04 15:49:40 +01:00
dst = save_fram (&len, i);
save_chunk (f, dst, len, _T("FRAM"), comp);
}
for (int i = 0; i < MAX_RAM_BOARDS; i++) {
2017-12-04 15:49:40 +01:00
dst = save_zram (&len, i);
save_chunk (f, dst, len, _T("ZRAM"), comp);
}
2017-12-04 15:49:40 +01:00
dst = save_bootrom (&len);
save_chunk (f, dst, len, _T("BORO"), comp);
2015-05-13 18:47:23 +00:00
#endif
#ifdef PICASSO96
2017-12-04 15:49:40 +01:00
dst = save_pram (&len);
save_chunk (f, dst, len, _T("PRAM"), comp);
2015-05-13 18:47:23 +00:00
#endif
}
/* Save all subsystems */
2017-12-04 15:49:40 +01:00
static int save_state_internal (struct zfile *f, const TCHAR *description, int comp, bool savepath)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
uae_u8 endhunk[] = { 'E', 'N', 'D', ' ', 0, 0, 0, 8 };
uae_u8 header[1000];
TCHAR tmp[100];
uae_u8 *dst;
TCHAR name[5];
2016-11-30 22:25:43 +01:00
int i, len;
write_log (_T("STATESAVE (%s):\n"), f ? zfile_getname (f) : _T("<internal>"));
2017-12-04 15:49:40 +01:00
dst = header;
save_u32 (0);
save_string(_T("UAE"));
_stprintf (tmp, _T("%d.%d.%d"), UAEMAJOR, UAEMINOR, UAESUBREV);
save_string (tmp);
save_string (description);
save_chunk (f, header, dst-header, _T("ASF "), 0);
dst = save_cpu (&len, 0);
save_chunk (f, dst, len, _T("CPU "), 0);
xfree (dst);
dst = save_cpu_extra (&len, 0);
save_chunk (f, dst, len, _T("CPUX"), 0);
xfree (dst);
2015-05-13 18:47:23 +00:00
#ifdef FPUEMU
2017-12-04 15:49:40 +01:00
dst = save_fpu (&len, 0);
save_chunk (f, dst, len, _T("FPU "), 0);
xfree (dst);
2015-05-13 18:47:23 +00:00
#endif
2017-12-04 15:49:40 +01:00
_tcscpy(name, _T("DSKx"));
for (i = 0; i < 4; i++) {
dst = save_disk (i, &len, 0, savepath);
if (dst) {
name[3] = i + '0';
save_chunk (f, dst, len, name, 0);
xfree (dst);
}
}
2016-11-30 22:25:43 +01:00
_tcscpy(name, _T("DSDx"));
2017-12-04 15:49:40 +01:00
for (i = 0; i < 4; i++) {
dst = save_disk2 (i, &len, 0);
if (dst) {
2016-11-30 22:25:43 +01:00
name[3] = i + '0';
2017-12-04 15:49:40 +01:00
save_chunk (f, dst, len, name, comp);
2016-11-30 22:25:43 +01:00
xfree (dst);
}
}
2017-12-04 15:49:40 +01:00
dst = save_floppy (&len, 0);
save_chunk (f, dst, len, _T("DISK"), 0);
xfree (dst);
2016-11-30 22:25:43 +01:00
2017-12-04 15:49:40 +01:00
dst = save_custom (&len, 0, 0);
save_chunk (f, dst, len, _T("CHIP"), 0);
xfree (dst);
2016-11-30 22:25:43 +01:00
2017-12-04 15:49:40 +01:00
dst = save_custom_extra (&len, 0);
save_chunk (f, dst, len, _T("CHPX"), 0);
2016-11-30 22:25:43 +01:00
xfree (dst);
2017-12-04 15:49:40 +01:00
dst = save_blitter_new (&len, 0);
save_chunk (f, dst, len, _T("BLTX"), 0);
xfree (dst);
if (new_blitter == false) {
dst = save_blitter (&len, 0);
save_chunk (f, dst, len, _T("BLIT"), 0);
xfree (dst);
}
dst = save_input (&len, 0);
save_chunk (f, dst, len, _T("CINP"), 0);
2016-11-30 22:25:43 +01:00
xfree (dst);
2017-12-04 15:49:40 +01:00
dst = save_custom_agacolors (&len, 0);
save_chunk (f, dst, len, _T("AGAC"), 0);
xfree (dst);
2016-11-30 22:25:43 +01:00
_tcscpy (name, _T("SPRx"));
2017-12-04 15:49:40 +01:00
for (i = 0; i < 8; i++) {
dst = save_custom_sprite (i, &len, 0);
name[3] = i + '0';
save_chunk (f, dst, len, name, 0);
xfree (dst);
}
2016-11-30 22:25:43 +01:00
_tcscpy (name, _T("AUDx"));
2017-12-04 15:49:40 +01:00
for (i = 0; i < 4; i++) {
dst = save_audio (i, &len, 0);
name[3] = i + '0';
save_chunk (f, dst, len, name, 0);
xfree (dst);
}
2016-11-30 22:25:43 +01:00
2017-12-04 15:49:40 +01:00
dst = save_cia (0, &len, 0);
save_chunk (f, dst, len, _T("CIAA"), 0);
xfree (dst);
2016-11-30 22:25:43 +01:00
2017-12-04 15:49:40 +01:00
dst = save_cia (1, &len, 0);
save_chunk (f, dst, len, _T("CIAB"), 0);
xfree (dst);
2016-11-30 22:25:43 +01:00
2017-12-04 15:49:40 +01:00
dst = save_keyboard (&len, NULL);
save_chunk (f, dst, len, _T("KEYB"), 0);
xfree (dst);
2015-05-13 18:47:23 +00:00
#ifdef AUTOCONFIG
2017-12-04 15:49:40 +01:00
dst = save_expansion_info(&len, 0);
save_chunk(f, dst, len, _T("EXPI"), 0);
dst = save_expansion (&len, 0);
save_chunk (f, dst, len, _T("EXPA"), 0);
xfree (dst);
2015-05-13 18:47:23 +00:00
#endif
#ifdef PICASSO96
2017-12-04 15:49:40 +01:00
dst = save_p96 (&len, 0);
save_chunk (f, dst, len, _T("P96 "), 0);
#endif
2017-12-04 15:49:40 +01:00
save_rams (f, comp);
dst = save_rom (1, &len, 0);
do {
if (!dst)
break;
save_chunk (f, dst, len, _T("ROM "), 0);
xfree (dst);
} while ((dst = save_rom (0, &len, 0)));
2015-05-13 18:47:23 +00:00
#ifdef CD32
2017-12-04 15:49:40 +01:00
dst = save_akiko (&len, NULL);
save_chunk (f, dst, len, _T("CD32"), 0);
2016-11-30 22:25:43 +01:00
xfree (dst);
#endif
2017-12-04 15:49:40 +01:00
#ifdef ACTION_REPLAY
dst = save_action_replay (&len, NULL);
save_chunk (f, dst, len, _T("ACTR"), comp);
dst = save_hrtmon (&len, NULL);
save_chunk (f, dst, len, _T("HRTM"), comp);
#endif
2015-05-13 18:47:23 +00:00
#ifdef FILESYS
2017-12-04 15:49:40 +01:00
dst = save_filesys_common (&len);
if (dst) {
save_chunk (f, dst, len, _T("FSYC"), 0);
for (i = 0; i < nr_units (); i++) {
dst = save_filesys (i, &len);
if (dst) {
save_chunk (f, dst, len, _T("FSYS"), 0);
xfree (dst);
}
}
}
#endif
dst = save_gayle (&len, NULL);
if (dst) {
save_chunk (f, dst, len, _T("GAYL"), 0);
xfree(dst);
}
for (i = 0; i < 4; i++) {
dst = save_gayle_ide (i, &len, NULL);
if (dst) {
save_chunk (f, dst, len, _T("IDE "), 0);
xfree (dst);
2017-02-25 00:38:14 +01:00
}
}
2015-09-09 21:49:41 +02:00
2017-12-04 15:49:40 +01:00
for (i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
dst = save_cd (i, &len);
if (dst) {
_stprintf (name, _T("CDU%d"), i);
save_chunk (f, dst, len, name, 0);
2016-11-30 22:25:43 +01:00
}
}
2017-12-04 15:49:40 +01:00
zfile_fwrite (endhunk, 1, 8, f);
2017-12-04 15:49:40 +01:00
return 1;
}
2017-12-04 15:49:40 +01:00
int save_state (const TCHAR *filename, const TCHAR *description)
{
2017-12-04 15:49:40 +01:00
struct zfile *f;
int comp = savestate_docompress;
if (!savestate_nodialogs) {
state_incompatible_warn();
if (!save_filesys_cando()) {
gui_message (_T("Filesystem active. Try again later."));
return -1;
}
}
2016-11-30 22:25:43 +01:00
new_blitter = false;
2017-12-04 15:49:40 +01:00
savestate_nodialogs = 0;
custom_prepare_savestate ();
f = zfile_fopen (filename, _T("w+b"), 0);
if (!f)
return 0;
int v = save_state_internal (f, description, comp, true);
2016-11-30 22:25:43 +01:00
if (v)
2017-12-04 15:49:40 +01:00
write_log (_T("Save of '%s' complete\n"), filename);
zfile_fclose (f);
savestate_state = 0;
2016-11-30 22:25:43 +01:00
return v;
2015-05-13 18:47:23 +00:00
}
2017-12-04 15:49:40 +01:00
bool savestate_check (void)
2015-05-13 18:47:23 +00:00
{
2017-12-04 15:49:40 +01:00
if (savestate_state == STATE_DORESTORE) {
2016-11-30 22:25:43 +01:00
savestate_state = STATE_RESTORE;
return true;
}
return false;
2015-05-13 18:47:23 +00:00
}
2015-05-13 18:47:23 +00:00
/*
My (Toni Wilen <twilen@arabuusimiehet.com>)
proposal for Amiga-emulators' state-save format
Feel free to comment...
This is very similar to IFF-fileformat
Every hunk must end to 4 byte boundary,
fill with zero bytes if needed
version 0.8
HUNK HEADER (beginning of every hunk)
hunk name (4 ascii-characters)
hunk size (including header)
2016-11-30 22:25:43 +01:00
hunk flags
2015-05-13 18:47:23 +00:00
bit 0 = chunk contents are compressed with zlib (maybe RAM chunks only?)
HEADER
"ASF " (AmigaStateFile)
2016-11-30 22:25:43 +01:00
2015-05-13 18:47:23 +00:00
statefile version
emulator name ("uae", "fellow" etc..)
emulator version string (example: "0.8.15")
free user writable comment string
CPU
2015-09-09 21:49:41 +02:00
"CPU "
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
CPU model 4 (68000,68010,68020,68030,68040,68060)
CPU typeflags bit 0=EC-model or not, bit 31 = clock rate included
D0-D7 8*4=32
A0-A6 7*4=32
PC 4
2015-05-13 18:47:23 +00:00
unused 4
68000 prefetch (IRC) 2
68000 prefetch (IR) 2
2015-09-09 21:49:41 +02:00
USP 4
ISP 4
SR/CCR 2
flags 4 (bit 0=CPU was HALTed)
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
CPU specific registers
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
68000: SR/CCR is last saved register
68010: save also DFC,SFC and VBR
68020: all 68010 registers and CAAR,CACR and MSP
etc..
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
68010+:
DFC 4
SFC 4
VBR 4
68020+:
CAAR 4
CACR 4
MSP 4
68030+:
AC0 4
AC1 4
ACUSR 2
TT0 4
TT1 4
68040+:
ITT0 4
ITT1 4
DTT0 4
DTT1 4
TCR 4
URP 4
SRP 4
68060:
BUSCR 4
PCR 4
All:
Clock in KHz 4 (only if bit 31 in flags)
4 (spare, only if bit 31 in flags)
2015-05-13 18:47:23 +00:00
FPU (only if used)
"FPU "
2015-09-09 21:49:41 +02:00
FPU model 4 (68881/68882/68040/68060)
FPU typeflags 4 (bit 31 = clock rate included)
FP0-FP7 4+4+2 (80 bits)
FPCR 4
FPSR 4
FPIAR 4
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
Clock in KHz 4 (only if bit 31 in flags)
4 (spare, only if bit 31 in flags)
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
MMU (when and if MMU is supported in future..)
2015-05-13 18:47:23 +00:00
"MMU "
2015-05-13 18:47:23 +00:00
MMU model 4 (68040)
flags 4 (none defined yet)
2015-05-13 18:47:23 +00:00
CUSTOM CHIPS
2015-09-09 21:49:41 +02:00
"CHIP"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
chipset flags 4 OCS=0,ECSAGNUS=1,ECSDENISE=2,AGA=4
ECSAGNUS and ECSDENISE can be combined
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
DFF000-DFF1FF 352 (0x120 - 0x17f and 0x0a0 - 0xdf excluded)
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
sprite registers (0x120 - 0x17f) saved with SPRx chunks
audio registers (0x0a0 - 0xdf) saved with AUDx chunks
2015-05-13 18:47:23 +00:00
AGA COLORS
2015-09-09 21:49:41 +02:00
"AGAC"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
AGA color 8 banks * 32 registers *
registers LONG (XRGB) = 1024
2015-05-13 18:47:23 +00:00
SPRITE
2015-09-09 21:49:41 +02:00
"SPR0" - "SPR7"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
SPRxPT 4
SPRxPOS 2
SPRxCTL 2
SPRxDATA 2
SPRxDATB 2
AGA sprite DATA/DATB 3 * 2 * 2
sprite "armed" status 1
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
sprites maybe armed in non-DMA mode
use bit 0 only, other bits are reserved
2015-05-13 18:47:23 +00:00
AUDIO
2015-09-09 21:49:41 +02:00
"AUD0" "AUD1" "AUD2" "AUD3"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
audio state 1
machine mode
AUDxVOL 1
2015-05-13 18:47:23 +00:00
irq? 1
data_written? 1
2015-09-09 21:49:41 +02:00
internal AUDxLEN 2
AUDxLEN 2
2015-05-13 18:47:23 +00:00
internal AUDxPER 2
AUDxPER 2
2015-09-09 21:49:41 +02:00
internal AUDxLC 4
2015-05-13 18:47:23 +00:00
AUDxLC 4
evtime? 4
BLITTER
2015-09-09 21:49:41 +02:00
"BLIT"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
internal blitter state
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
flags 4
bit 0=blitter active
bit 1=fill carry bit
internal ahold 4
internal bhold 4
internal hsize 2
internal vsize 2
2015-05-13 18:47:23 +00:00
CIA
2015-09-09 21:49:41 +02:00
"CIAA" and "CIAB"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
BFE001-BFEF01 16*1 (CIAA)
BFD000-BFDF00 16*1 (CIAB)
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
internal registers
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
IRQ mask (ICR) 1 BYTE
timer latches 2 timers * 2 BYTES (LO/HI)
latched tod 3 BYTES (LO/MED/HI)
alarm 3 BYTES (LO/MED/HI)
flags 1 BYTE
bit 0=tod latched (read)
bit 1=tod stopped (write)
2015-05-13 18:47:23 +00:00
div10 counter 1 BYTE
FLOPPY DRIVES
2015-09-09 21:49:41 +02:00
"DSK0" "DSK1" "DSK2" "DSK3"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
drive state
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
drive ID-word 4
state 1 (bit 0: motor on, bit 1: drive disabled, bit 2: current id bit)
rw-head track 1
dskready 1
id-mode 1 (ID mode bit number 0-31)
floppy information
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
bits from 4
beginning of track
CRC of disk-image 4 (used during restore to check if image
is correct)
disk-image null-terminated
file name
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
INTERNAL FLOPPY CONTROLLER STATUS
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
"DISK"
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
current DMA word 2
DMA word bit offset 1
WORDSYNC found 1 (no=0,yes=1)
hpos of next bit 1
DSKLENGTH status 0=off,1=written once,2=written twice
unused 2
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
RAM SPACE
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
"xRAM" (CRAM = chip, BRAM = bogo, FRAM = fast, ZRAM = Z3, P96 = RTG RAM, A3K1/A3K2 = MB RAM)
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
start address 4 ("bank"=chip/slow/fast etc..)
of RAM "bank"
RAM "bank" size 4
RAM flags 4 (bit 0 = zlib compressed)
RAM "bank" contents
2015-05-13 18:47:23 +00:00
ROM SPACE
2015-09-09 21:49:41 +02:00
"ROM "
ROM start 4
address
size of ROM 4
ROM type 4 KICK=0
ROM flags 4
ROM version 2
ROM revision 2
ROM CRC 4 see below
ROM-image ID-string null terminated, see below
path to rom image
ROM contents (Not mandatory, use hunk size to check if
this hunk contains ROM data or not)
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
Kickstart ROM:
ID-string is "Kickstart x.x"
ROM version: version in high word and revision in low word
Kickstart ROM version and revision can be found from ROM start
+ 12 (version) and +14 (revision)
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
ROM version and CRC is only meant for emulator to automatically
find correct image from its ROM-directory during state restore.
2015-05-13 18:47:23 +00:00
2015-09-09 21:49:41 +02:00
Usually saving ROM contents is not good idea.
2015-05-13 18:47:23 +00:00
ACTION REPLAY
"ACTR"
Model (1,2,3) 4
path to rom image
RAM space (depends on model)
ROM CRC 4
"CDx "
Flags 4 (bit 0 = scsi, bit 1 = ide, bit 2 = image)
Path (for example image file or drive letter)
2015-05-13 18:47:23 +00:00
END
2015-09-09 21:49:41 +02:00
hunk "END " ends, remember hunk size 8!
2015-05-13 18:47:23 +00:00
EMULATOR SPECIFIC HUNKS
Read only if "emulator name" in header is same as used emulator.
Maybe useful for configuration?
misc:
- save only at position 0,0 before triggering VBLANK interrupt
- all data must be saved in bigendian format
- should we strip all paths from image file names?
*/