mirror of
https://github.com/ogoguel/activegs-ios.git
synced 2025-01-02 09:30:10 +00:00
1017 lines
20 KiB
C++
1017 lines
20 KiB
C++
/*
|
||
ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS
|
||
Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net
|
||
This code is covered by the GNU GPL licence
|
||
*/
|
||
|
||
#ifdef _WIN32
|
||
# define _WINSOCKAPI_
|
||
#endif
|
||
#include "StdString.h"
|
||
#include "defc.h"
|
||
#include "savestate.h"
|
||
#include "adb.h"
|
||
#include "async_event.h"
|
||
#include "clock.h"
|
||
#include "iwm.h"
|
||
#include "moremem.h"
|
||
#include "paddles.h"
|
||
#include "scc.h"
|
||
#include "sim65816.h"
|
||
#include "sound.h"
|
||
#include "video.h"
|
||
#include "compression.h"
|
||
|
||
#undef debug_printf
|
||
#define debug_printf(X,...)
|
||
|
||
|
||
s_savestate g_savestate;
|
||
|
||
void x_sleep(int _ms)
|
||
{
|
||
#ifdef WIN32
|
||
Sleep(_ms);
|
||
#else
|
||
usleep(_ms);
|
||
#endif
|
||
}
|
||
|
||
int savedState::maxSize=0;
|
||
|
||
savedState::savedState()
|
||
{
|
||
param.vbl=0;
|
||
|
||
}
|
||
|
||
void savedState::release()
|
||
{
|
||
if (isEmpty()) return ;
|
||
|
||
adb.release();
|
||
async_event.release();
|
||
clock.release();
|
||
iwm.release();
|
||
moremem.release();
|
||
paddles.release();
|
||
scc.release();
|
||
sim65816.release();
|
||
sound.release();
|
||
video.release();
|
||
offscreen.release();
|
||
param.vbl = 0;
|
||
}
|
||
|
||
void serialize::release()
|
||
{
|
||
g_compression.remove_job(this);
|
||
|
||
if (rledata)
|
||
{
|
||
x_free(rledata,rlesize,fastalloc);
|
||
rledata = NULL;
|
||
}
|
||
|
||
if (data)
|
||
{
|
||
x_free(data,size,fastalloc);
|
||
data=NULL;
|
||
}
|
||
}
|
||
|
||
void savedState::save(int _fullsave, int _usefastalloc)
|
||
{
|
||
#if (!defined(MAC) || defined(ACTIVEGSPLUGIN) ) && !defined(ACTIVEGS_NOSAVESTATE)
|
||
|
||
release();
|
||
|
||
fastalloc = _usefastalloc;
|
||
|
||
int size = 0;
|
||
|
||
param.dcycs = g_sim65816.g_last_vbl_dcycs;
|
||
param.vbl = g_sim65816.g_vbl_count;
|
||
param.memory_size = g_sim65816.g_mem_size_total ;
|
||
param.rom = 0 ;
|
||
|
||
debug_printf("capturing (%d) vbl:%d dcycs:%f last_vbl_dcys:%f\n",_fullsave,vbl,dcycs,g_sim65816.g_last_vbl_dcycs);
|
||
|
||
fullsave = _fullsave;
|
||
if (_fullsave)
|
||
{
|
||
size += g_adb.out(&adb,fastalloc);
|
||
size += g_async_event.out(&async_event,fastalloc);
|
||
size += g_clock.out(&clock,fastalloc);
|
||
size += g_iwm.out(&iwm,fastalloc);
|
||
size += g_moremem.out(&moremem,fastalloc);
|
||
size += g_paddles.out(&paddles,fastalloc);
|
||
size += g_scc.out(&scc,fastalloc);
|
||
size += g_sim65816.out(&sim65816,fastalloc);
|
||
size += g_sound.out(&sound,fastalloc);
|
||
size += g_video.out(&video,fastalloc);
|
||
}
|
||
|
||
#ifdef VIDEO_SINGLEVLINE
|
||
#define STEP 1
|
||
#else
|
||
#define STEP 2
|
||
#endif
|
||
|
||
// sauvegarde l'offscreen (1 ligne sur 2)
|
||
int wb = g_kimage_offscreen.width_act * ( g_kimage_offscreen.mdepth >> 3 );
|
||
offscreen.size = wb * g_kimage_offscreen.height / STEP ;
|
||
offscreen.fastalloc = fastalloc;
|
||
offscreen.data = (byte*)x_malloc( offscreen.size,fastalloc );
|
||
if (!offscreen.data)
|
||
x_fatal_exit("out of memory error!");
|
||
byte* ptrs = (byte*)g_kimage_offscreen.data_ptr;
|
||
byte* ptrd = (byte*)offscreen.data;
|
||
|
||
|
||
for(int h=0;h<g_kimage_offscreen.height;h+=STEP)
|
||
{
|
||
memcpy(ptrd,ptrs,wb);
|
||
ptrd+=wb;
|
||
ptrs+=wb*STEP;
|
||
}
|
||
size += offscreen.size;
|
||
|
||
if (maxSize<size)
|
||
maxSize=size;
|
||
|
||
// g_compression.add_job(&offscreen);
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
extern void refresh_offscreen();
|
||
|
||
void savedState::display()
|
||
{
|
||
|
||
if (isEmpty()) return ;
|
||
if (offscreen.data==NULL)
|
||
{
|
||
refresh_offscreen();
|
||
refresh_video(1);
|
||
return ;
|
||
}
|
||
|
||
|
||
// static double lastcycs=0.0;
|
||
extern void drawText(Kimage* _dst, const char* text,int _x,int _y);
|
||
|
||
#if !defined(MAC) || defined(ACTIVEGSPLUGIN)
|
||
|
||
offscreen.expand();
|
||
|
||
// g<>n<EFBFBD>re une kimage temporaire
|
||
Kimage temp;
|
||
init_kimage(&temp,0,s_video.g_screen_depth, s_video.g_screen_mdepth);
|
||
temp.width_req = X_A2_WINDOW_WIDTH;
|
||
temp.width_act = X_A2_WINDOW_WIDTH;
|
||
temp.height = X_A2_WINDOW_HEIGHT;
|
||
|
||
int pb = temp.mdepth >> 3;
|
||
|
||
temp.data_ptr = (byte*)malloc(X_A2_WINDOW_WIDTH*X_A2_WINDOW_HEIGHT*pb);
|
||
|
||
#ifdef VIDEO_SINGLEVLINE
|
||
// restore l'offscreen <20> l'identique
|
||
int wb = temp.width_act * pb;
|
||
byte* ptrs = (byte*)offscreen.data;
|
||
byte* ptrd = (byte*)temp.data_ptr;
|
||
|
||
for(int h=0;h<g_kimage_offscreen.height;h++)
|
||
{
|
||
memcpy(ptrd,ptrs,wb);
|
||
ptrd+=wb;
|
||
ptrs+=wb;
|
||
}
|
||
#else
|
||
// restore l'offscreen (1 ligne sur 2 dupliqu<71>e)
|
||
int wb = temp.width_act * pb;
|
||
byte* ptrs = (byte*)offscreen.data;
|
||
byte* ptrd = (byte*)temp.data_ptr;
|
||
|
||
for(int h=0;h<g_kimage_offscreen.height;h+=2)
|
||
{
|
||
memcpy(ptrd,ptrs,wb);
|
||
ptrd+=wb;
|
||
memcpy(ptrd,ptrs,wb);
|
||
ptrd+=wb;
|
||
ptrs+=wb;
|
||
}
|
||
#endif
|
||
// push l'image => pour profiter des effets post prod
|
||
x_push_kimage(&temp,0,0,0,0,temp.width_req,temp.height);
|
||
|
||
offscreen.releaseuncompressed();
|
||
|
||
// lib<69>re le fichier temp
|
||
free(temp.data_ptr );
|
||
|
||
refresh_video(1);
|
||
// double dtime = get_dtime();
|
||
// double delay = dtime - lastcycs;
|
||
// lastcycs = dtime;
|
||
|
||
// debug_printf("displaying frame:%d (%f)\n",vbl,delay);
|
||
#endif
|
||
|
||
}
|
||
|
||
void s_savestate::handleKey(int _key,int _isup)
|
||
{
|
||
#if (!defined(MAC) || defined(ACTIVEGSPLUGIN)) && !defined(ACTIVEGS_NOSAVESTATE)
|
||
if (!_isup && (_key == 0x3B || _key == 0x3C || _key == 0x24 /*RET*/|| _key == 0x60 /*VK_F5*/ ) )
|
||
{
|
||
if (_key == 0x24 || _key == 0x60)
|
||
{
|
||
|
||
r_sim65816.set_state(RUNNING);
|
||
return ;
|
||
}
|
||
|
||
|
||
int next ;
|
||
if (iCurState==-1)
|
||
{
|
||
// TODO A AMELIORER
|
||
iCurState = (MAX_STATE+iNextState -1 )%MAX_STATE;
|
||
}
|
||
|
||
if (_key==0x3B)
|
||
{
|
||
targetStateSens = 0;
|
||
|
||
// v<>rifie qu'il y a encore une clef
|
||
|
||
int next = (MAX_STATE+iCurState -1 )%MAX_STATE;
|
||
while(next != iNextState )
|
||
{
|
||
if (states[next].isEmpty())
|
||
// plus de clef
|
||
break;
|
||
if (states[next].fullsave)
|
||
{
|
||
targetStateSens = -1;
|
||
break;
|
||
}
|
||
next = (MAX_STATE+next -1 )%MAX_STATE;
|
||
|
||
}
|
||
if (!targetStateSens)
|
||
|
||
printf("begin rewind\n");
|
||
|
||
}
|
||
else
|
||
{
|
||
targetStateSens = +1;
|
||
next = (MAX_STATE+iCurState +1 )%MAX_STATE;
|
||
|
||
if (next == iNextState || states[next].isEmpty() )
|
||
{
|
||
targetStateSens = 0;
|
||
iCurState=-1;
|
||
printf("end rewind\n");
|
||
return ;
|
||
}
|
||
}
|
||
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
int s_savestate::getSavedStateVBL()
|
||
{
|
||
return state.param.vbl;
|
||
|
||
}
|
||
|
||
|
||
void s_savestate::saveState(const char* _filename)
|
||
{
|
||
if (r_sim65816.get_state() ==IN_PAUSE)
|
||
{
|
||
// en pause, sauve imm<6D>diatement (sauf si on est en mode rewind
|
||
|
||
if (iCurState!=-1)
|
||
{
|
||
printf("cannot save state if rewind has started");
|
||
return ;
|
||
}
|
||
if (_filename==NULL)
|
||
{
|
||
// save to memory
|
||
state.save(1,0);
|
||
debug_printf("save state @%d \n",state->vbl);
|
||
}
|
||
else
|
||
{
|
||
savedState temp;
|
||
temp.save(1,0);
|
||
temp.writeToDisk(_filename);
|
||
}
|
||
|
||
return ;
|
||
}
|
||
|
||
|
||
stateActionRequired=STATE_SAVE;
|
||
filename = _filename;
|
||
|
||
}
|
||
|
||
|
||
void s_savestate::restoreState(const char* _filename)
|
||
{
|
||
|
||
|
||
if (r_sim65816.get_state() ==IN_PAUSE)
|
||
{
|
||
// en pause, on peut faire le restore imm<6D>diatement (si on est pas en mode rewind)
|
||
|
||
if (iCurState!=-1)
|
||
{
|
||
printf("cannot restore state if rewind has started");
|
||
return ;
|
||
}
|
||
if (_filename==NULL)
|
||
{
|
||
if (!state.isEmpty())
|
||
{
|
||
// on est revenu <20> une position quelconque : le rewind n'a plus de sens
|
||
state.restore();
|
||
reset_rewind();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
savedState temp;
|
||
int ret = temp.loadFromDisk(_filename);
|
||
if (ret)
|
||
{
|
||
temp.restore();
|
||
reset_rewind();
|
||
}
|
||
}
|
||
|
||
return ;
|
||
}
|
||
|
||
// pr<70>pare le save <20> la prochaine boucle
|
||
stateActionRequired=STATE_RESTORE;
|
||
filename = _filename;
|
||
|
||
}
|
||
|
||
|
||
void s_savestate::delete_state(int pos)
|
||
{
|
||
if (states[pos].isEmpty()) return ;
|
||
|
||
states[pos].release();
|
||
|
||
// d<>truit les <20>tat pr<70>sents jusqu'<27> la prochaine clef
|
||
int deleteState=(pos +1 )%MAX_STATE;
|
||
while(deleteState!=pos)
|
||
{
|
||
if (states[deleteState].isEmpty()) break;
|
||
if (states[deleteState].fullsave)
|
||
// on a trouv<75> la prochaine clef : on s'arrete
|
||
break;
|
||
// d<>truit les <20>cran interm<72>diaires
|
||
states[deleteState].release();
|
||
debug_printf("deleting state:%d\n",deleteState);
|
||
deleteState=(deleteState +1 )%MAX_STATE;
|
||
}
|
||
}
|
||
|
||
|
||
void s_savestate::handleState()
|
||
{
|
||
|
||
// double d = get_dtime();
|
||
#if (!defined(MAC) || defined(ACTIVEGSPLUGIN)) && !defined(ACTIVEGS_NOSAVESTATE)
|
||
|
||
if (r_sim65816.should_emulator_terminate())
|
||
{
|
||
printf("ignoring handleState : emulator is quitting...\n");
|
||
return ;
|
||
}
|
||
|
||
iCurState = -1;
|
||
|
||
int b = r_sim65816.get_rewind_enable();
|
||
|
||
int savescreen = ( b && g_sim65816.g_vbl_count >= nextScreenVBL ) ;
|
||
int s = r_sim65816.get_state();
|
||
int fullsave = b && ( g_sim65816.g_vbl_count>=nextStateVBL || s==IN_PAUSE) ;
|
||
|
||
if (savescreen||fullsave)
|
||
{
|
||
nextScreenVBL = g_sim65816.g_vbl_count + VBL_DELAY_BETWEEN_SCREEN ;
|
||
|
||
delete_state(iNextState);
|
||
|
||
// s'assure que l'on a assez de m<>moire libre
|
||
int n = (iNextState +1 )%MAX_STATE;
|
||
while( 1)
|
||
{
|
||
int f = get_free_memory_size();
|
||
if ( f> (int)savedState::maxSize*1.20 )
|
||
break;
|
||
if (!states[n].isEmpty())
|
||
{
|
||
debug_printf("only %dKB remaining : trying to free space\n",f/1024);
|
||
delete_state(n);
|
||
debug_printf("now %dKB!\n",get_free_memory_size()/1204);
|
||
}
|
||
n = (n +1 )%MAX_STATE;
|
||
if (n == iNextState)
|
||
{
|
||
x_fatal_exit("err: out of memory error");
|
||
}
|
||
|
||
}
|
||
|
||
// capture l'<27>tat courant
|
||
states[iNextState].save(fullsave,1);
|
||
|
||
// positionne la prochaine place libre
|
||
iNextState = (iNextState +1 )%MAX_STATE;
|
||
|
||
if (fullsave)
|
||
nextStateVBL = g_sim65816.g_vbl_count+NB_SCREEN_PER_STATE*VBL_DELAY_BETWEEN_SCREEN;
|
||
|
||
}
|
||
else
|
||
g_compression.process_jobs(1);
|
||
|
||
|
||
switch( stateActionRequired)
|
||
{
|
||
case STATE_SAVE:
|
||
// if (filename.IsEmpty())
|
||
if (filename[0]==0)
|
||
|
||
{
|
||
state.save(1,0);
|
||
debug_printf("save state @ \n",state->vbl);
|
||
}
|
||
else
|
||
{
|
||
savedState temp;
|
||
temp.save(1,0);
|
||
temp.writeToDisk(filename);
|
||
}
|
||
|
||
stateActionRequired = STATE_NONE;
|
||
break;
|
||
|
||
case STATE_RESTORE:
|
||
// if (filename.IsEmpty())
|
||
if (filename[0]==0)
|
||
{
|
||
if (!state.isEmpty())
|
||
{
|
||
// on est revenu <20> une position quelconque : le rewind n'a plus de sens
|
||
state.restore();
|
||
reset_rewind();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
savedState temp;
|
||
int ret = temp.loadFromDisk(filename);
|
||
if (ret)
|
||
{
|
||
temp.restore();
|
||
reset_rewind();
|
||
}
|
||
}
|
||
|
||
|
||
stateActionRequired = STATE_NONE;
|
||
break ;
|
||
|
||
case STATE_NONE:
|
||
|
||
|
||
// if (r_sim65816.get_state() == IN_PAUSE)
|
||
if (s == PAUSE_REQUESTED ) // !!!! L'ETAT PEUT AVOIR CHANGE DEPUIS LE DEBUT DE LA FONCTION : NE LE RELIT PAS!
|
||
{
|
||
r_sim65816.set_state(IN_PAUSE);
|
||
printf("in pause (cache=%dMB, maxStateSize=%dMB)\n",cacheSize/(1024*1024),savedState::maxSize/(1024*1024));
|
||
|
||
while(r_sim65816.get_state() == IN_PAUSE || ( iCurState!=-1 && !states[iCurState].isEmpty() && !states[iCurState].fullsave ) )
|
||
{
|
||
if (r_sim65816.should_emulator_terminate())
|
||
{
|
||
printf("aborting PAUSE mode...\n");
|
||
r_sim65816.set_state(RUNNING);
|
||
return;
|
||
}
|
||
x_sleep(1); // 50hz
|
||
|
||
if (targetStateSens)
|
||
{
|
||
double dtime = get_dtime();
|
||
double delaysincelastrefresh = dtime - lastrefreshcycs;
|
||
if (delaysincelastrefresh > VBL_DELAY_BETWEEN_SCREEN/2/60.0)
|
||
{
|
||
if (targetStateSens>0)
|
||
iCurState = (iCurState +1 )%MAX_STATE;
|
||
else
|
||
iCurState = (MAX_STATE+iCurState -1 )%MAX_STATE;
|
||
|
||
states[iCurState].display();
|
||
|
||
if (states[iCurState].fullsave)
|
||
targetStateSens = 0;
|
||
else
|
||
lastrefreshcycs = dtime;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
printf("out pause\n");
|
||
|
||
|
||
if (iCurState!=-1)
|
||
{
|
||
|
||
if (!states[iCurState].isEmpty())
|
||
states[iCurState].restore();
|
||
|
||
int n = (iCurState+1)%MAX_STATE;
|
||
while(n!=iNextState)
|
||
{
|
||
delete_state(n);
|
||
n = ( n + 1 ) % MAX_STATE;
|
||
}
|
||
iNextState = (iCurState+1)%MAX_STATE;
|
||
|
||
nextScreenVBL = g_sim65816.g_vbl_count + VBL_DELAY_BETWEEN_SCREEN;
|
||
nextStateVBL = g_sim65816.g_vbl_count + NB_SCREEN_PER_STATE*VBL_DELAY_BETWEEN_SCREEN;
|
||
|
||
}
|
||
|
||
}
|
||
break;
|
||
}
|
||
#endif
|
||
// double delay = get_dtime() - d;
|
||
// g_graph.add_graph(1,delay,g_sim65816.g_vbl_count);
|
||
|
||
}
|
||
|
||
|
||
#define CHECK_RESTORE_VAR(CLASS,VAR) \
|
||
if ( g_##CLASS.VAR != ((s_##CLASS*)(##CLASS.data))->##VAR) \
|
||
printf("serialization error for var "#CLASS"."#VAR" %d vs. %d",g_##CLASS.VAR,((s_##CLASS*)(##CLASS.data))->##VAR);
|
||
|
||
#define CHECK_RESTORE_CLASS(CLASS) \
|
||
if ( sizeof(s_##CLASS) != ((s_##CLASS*)(##CLASS.data))->_size) \
|
||
printf("serialization error for class "#CLASS" %d vs. %d\n",sizeof(s_##CLASS),((s_##CLASS*)(##CLASS.data))->_size );
|
||
|
||
#define SAVE_VERSION 0x01020304
|
||
|
||
void serialize::save(const char*_name,void* _fileptr)
|
||
{
|
||
|
||
FILE* f= (FILE*)_fileptr;
|
||
fwrite(_name,1,4,f);
|
||
word32 version = SAVE_VERSION;
|
||
fwrite(&version,1,sizeof(version),f);
|
||
fwrite(&size,1,sizeof(size),f);
|
||
fwrite(data,1,size,f);
|
||
|
||
}
|
||
|
||
int serialize::load(const char*_name,void* _fileptr)
|
||
{
|
||
char name[5];
|
||
memset(name,0,5);
|
||
FILE* f= (FILE*)_fileptr;
|
||
fread(name,1,4,f);
|
||
if (strcmp(name,_name))
|
||
{
|
||
printf("#%s save state name mismatch(%s vs %s)\n",_name,_name,name);
|
||
return 0;
|
||
}
|
||
|
||
word32 version = 0;
|
||
fread(&version,1,sizeof(version),f);
|
||
if (version != SAVE_VERSION)
|
||
{
|
||
printf("#%s save state version mismatch(%X vs %X)\n",_name,version,SAVE_VERSION);
|
||
return 0;
|
||
}
|
||
|
||
int nbread = fread(&size,1,sizeof(size),f);
|
||
if (nbread !=4)
|
||
{
|
||
printf("#%s save state size mismatch(%d)\n",_name,size);
|
||
return 0;
|
||
}
|
||
|
||
fastalloc = 0;
|
||
data = (void*)malloc(size);
|
||
nbread = fread(data,1,size,f);
|
||
if (nbread!=size)
|
||
{
|
||
printf("#%s save state data mismatch(%d vs %d)\n",_name,nbread,size);
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
void savedState::writeToDisk(const char* _filename)
|
||
{
|
||
if (isEmpty())
|
||
{
|
||
printf("nothing to save");
|
||
return ;
|
||
}
|
||
if (_filename==NULL)
|
||
{
|
||
printf("no filename");
|
||
return ;
|
||
}
|
||
FILE* f= fopen(_filename,"wb");
|
||
if (f==NULL)
|
||
{
|
||
printf("cannot create file");
|
||
return ;
|
||
}
|
||
|
||
fwrite(¶m,1,sizeof(savedStateParam),f);
|
||
|
||
adb.save("adb ",f);
|
||
async_event.save("asyn",f);
|
||
clock.save("cloc",f);
|
||
iwm.save("iwm ",f);
|
||
moremem.save("more",f);
|
||
paddles.save("padd",f);
|
||
scc.save("scc ",f);
|
||
sim65816.save("sim6",f);
|
||
sound.save("soun",f);
|
||
video.save("vide",f);
|
||
// offscreen.save("offs",f); ne sauve pas l'offscreen
|
||
|
||
|
||
|
||
|
||
fclose(f);
|
||
}
|
||
|
||
int savedState::loadFromDisk(const char* _filename)
|
||
{
|
||
release();
|
||
int ret = loadFromDiskInternal(_filename);
|
||
if (!ret)
|
||
release();
|
||
return ret;
|
||
}
|
||
|
||
int savedState::loadFromDiskInternal(const char* _filename)
|
||
{
|
||
|
||
FILE* f= fopen(_filename,"rb");
|
||
if (f==NULL)
|
||
{
|
||
printf("cannot load %s\n",_filename);
|
||
return 0;
|
||
}
|
||
|
||
int nbread = fread(¶m,1,sizeof(savedStateParam),f);
|
||
if (nbread!=sizeof(savedStateParam))
|
||
{
|
||
printf("could not read savedStateParam\n");
|
||
return 0;
|
||
}
|
||
if (param.size != sizeof(savedStateParam))
|
||
{
|
||
printf("alignement mismatch\n");
|
||
return 0;
|
||
}
|
||
|
||
if (param.version != SAVEDSTATE_SIG)
|
||
{
|
||
printf("invalid sig\n");
|
||
return 0;
|
||
}
|
||
|
||
int ret = adb.load("adb ",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
ret = async_event.load("asyn",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
clock.load("cloc",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
iwm.load("iwm ",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
moremem.load("more",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
paddles.load("padd",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
scc.load("scc ",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
sim65816.load("sim6",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
sound.load("soun",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
video.load("vide",f);
|
||
if (ret==0)
|
||
return 0;
|
||
|
||
offscreen.release();
|
||
/*
|
||
offscreen.load("offs",f);
|
||
if (ret==0)
|
||
return 0;
|
||
*/
|
||
|
||
|
||
return 1;
|
||
}
|
||
|
||
void savedState::restore()
|
||
{
|
||
//writeToDisk("d:\\temp\\test.bin");
|
||
|
||
if (isEmpty())
|
||
{
|
||
printf("nothing to restore!\n");
|
||
return ;
|
||
}
|
||
|
||
#if (!defined(MAC) || defined(ACTIVEGSPLUGIN)) && !defined(ACTIVEGS_NOSAVESTATE)
|
||
|
||
debug_printf("current state :vbl:%d dcycs:%f last_vbl_dcys:%f\n",g_sim65816.g_vbl_count,g_sim65816.g_cur_dcycs,g_sim65816.g_last_vbl_dcycs);
|
||
|
||
#ifdef WIN32
|
||
CHECK_RESTORE_CLASS(sim65816);
|
||
CHECK_RESTORE_VAR(sim65816,g_mem_size_total);
|
||
#endif
|
||
g_adb.in(&adb);
|
||
g_async_event.in(&async_event);
|
||
g_clock.in(&clock);
|
||
g_iwm.in(&iwm);
|
||
g_moremem.in(&moremem);
|
||
g_paddles.in(&paddles);
|
||
g_scc.in(&scc);
|
||
g_sim65816.in(&sim65816);
|
||
g_sound.in(&sound);
|
||
g_video.in(&video);
|
||
|
||
r_sim65816.request_update_emulator_runtime_config();
|
||
display();
|
||
printf("restoring vbl:%d dcycs:%f last_vbl_dcys:%f\n",param.vbl,param.dcycs,g_sim65816.g_last_vbl_dcycs);
|
||
|
||
#endif
|
||
}
|
||
|
||
void s_savestate::init()
|
||
{
|
||
#ifdef WIN32
|
||
MEMORYSTATUS memStatus = {0};
|
||
::GlobalMemoryStatus(&memStatus);
|
||
DWORD dwTotalPhys =memStatus.dwTotalPhys;
|
||
printf("Total physical memory: %u MB\n", dwTotalPhys / (1024*1024) );
|
||
#endif
|
||
|
||
reset_rewind();
|
||
|
||
stateActionRequired=STATE_NONE;
|
||
state.release();
|
||
|
||
#ifdef CACHE_SIZE
|
||
cache = (byte*)malloc(CACHE_SIZE);
|
||
printf("Nb State = %d\n",MAX_STATE);
|
||
printf("Max replay = %fs\n",(float)MAX_STATE/60.0);
|
||
printf("Key duration = %fs\n",(float)(NB_SCREEN_PER_STATE*VBL_DELAY_BETWEEN_SCREEN)/60.0);
|
||
#else
|
||
cache=NULL;
|
||
#endif
|
||
cachefree = 0;
|
||
cachepos = 0;
|
||
cacheSize= 0;
|
||
|
||
|
||
}
|
||
|
||
|
||
void* s_savestate::x_free(void* _data,int _size, int _fastalloc)
|
||
{
|
||
if (!cache || !_fastalloc)
|
||
{
|
||
free(_data);
|
||
return NULL;
|
||
}
|
||
|
||
int pos = (intptr_t)_data - (intptr_t)cache;
|
||
|
||
// xxxx#######xxFREEE....POS#####
|
||
|
||
//printf("freeing :%d (size:%d)\n",pos,_size);
|
||
// POSxxxxxx######xxxxFREEE
|
||
|
||
if (cachepos<=pos || cachepos>cachefree)
|
||
{
|
||
cachepos = pos + _size;
|
||
// printf("newpos:%d\n",cachepos);
|
||
}
|
||
|
||
cacheSize -= _size;
|
||
return NULL;
|
||
}
|
||
|
||
int s_savestate::get_free_memory_size()
|
||
{
|
||
#ifdef CACHE_SIZE
|
||
int p = cachepos;
|
||
int f = cachefree;
|
||
|
||
if (f>=p)
|
||
// #####POS.....FREE#######
|
||
return MAX(p,CACHE_SIZE-f);
|
||
else
|
||
// ....FREE#####POS...
|
||
return p-f;
|
||
#else
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
void* x_free(void* _data,int _size, int _fastalloc)
|
||
{
|
||
return g_savestate.x_free(_data,_size,_fastalloc);
|
||
|
||
}
|
||
|
||
void* s_savestate::x_malloc(int _size, int _fastalloc)
|
||
{
|
||
|
||
if (!cache || !_fastalloc)
|
||
{
|
||
void* p = (void*)malloc(_size);
|
||
if (!p)
|
||
x_fatal_exit("Out of Memory Error : relaunch with rewind mode off");
|
||
cacheSize+=_size;
|
||
return p;
|
||
}
|
||
#ifndef CACHE_SIZE
|
||
// ne doit jamais arriver
|
||
return NULL;
|
||
#else
|
||
int c = cachefree;
|
||
|
||
if (c >= cachepos)
|
||
{
|
||
// ......POS#####################FREE.........
|
||
|
||
if (c+_size > CACHE_SIZE)
|
||
{
|
||
// on boucle
|
||
c = 0;
|
||
// FREEE......POS#####################00000000
|
||
if (c+_size > cachepos)
|
||
{
|
||
printf("x_malloc : out of memory error\n");
|
||
return NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// ok
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// ######FREE......POS##########################
|
||
|
||
if (c+_size < cachepos)
|
||
{
|
||
// il y a de la place entre free & pos = on l'utilise
|
||
// ok
|
||
}
|
||
else
|
||
{
|
||
printf("x_malloc: out of memory error\n");
|
||
return NULL;
|
||
|
||
}
|
||
}
|
||
|
||
cachefree = c+_size;
|
||
cacheSize += _size;
|
||
// printf("alloc size:%d ptr:%d (cachepos:%d cachefree:%d)\n",_size,c,cachepos,cachefree);
|
||
return &cache[c];
|
||
#endif
|
||
}
|
||
|
||
void* x_malloc(int _size, int _fastalloc)
|
||
{
|
||
return g_savestate.x_malloc(_size,_fastalloc);
|
||
|
||
}
|
||
void* x_realloc(void* ptr,int _newsize, int _formersize)
|
||
{
|
||
printf("x_realloc not implemented\n");
|
||
return ptr;
|
||
/*
|
||
void*p =(void*)x_malloc(_newsize);
|
||
memcpy(p,ptr,_newsize);
|
||
x_free(ptr,_formersize);
|
||
return p;
|
||
*/
|
||
}
|
||
|
||
void s_savestate::reset_state()
|
||
{
|
||
state.release();
|
||
}
|
||
|
||
void s_savestate::reset_rewind()
|
||
{
|
||
printf("reset_rewind\n");
|
||
for(int i=0;i<MAX_STATE;i++)
|
||
states[i].release();
|
||
|
||
// reset mempory pointers
|
||
cachefree=0;
|
||
cachepos=0;
|
||
iNextState = 0;
|
||
|
||
// reset recording vbl
|
||
nextScreenVBL = 0;
|
||
nextStateVBL = 0;
|
||
|
||
}
|
||
|
||
void s_savestate::shut()
|
||
{
|
||
|
||
reset_rewind();
|
||
state.release();
|
||
|
||
if (cache)
|
||
{
|
||
free(cache);
|
||
cache=NULL;
|
||
}
|
||
|
||
}
|
||
|
||
s_savestate::s_savestate()
|
||
{
|
||
|
||
stateActionRequired = 0;
|
||
iNextState = 0;
|
||
iCurState = 0 ;
|
||
nextStateVBL= 0;
|
||
nextScreenVBL = 0;
|
||
targetStateSens = 0;
|
||
lastrefreshcycs = 0.0;
|
||
cache = NULL;
|
||
cachepos = 0;
|
||
cachefree = 0;
|
||
cacheSize = 0;
|
||
|
||
|
||
}
|
||
|
||
s_savestate::~s_savestate()
|
||
{
|
||
|
||
}
|
||
|