FSkip behaviour changed, RefreshLine_PPU_hook optimized
[fceu.git] / state.c
CommitLineData
c62d2810 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21/* TODO: Add (better) file io error checking */
22/* TODO: Change save state file format. */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
92e249b1 27#ifdef GP2X
28#include <unistd.h>
29#endif
c62d2810 30
31#include "types.h"
32#include "x6502.h"
33#include "version.h"
34#include "fce.h"
35#include "sound.h"
36#define INESPRIV // Take this out when old save state support is removed in a future version.
37#include "ines.h"
38#include "svga.h"
39#include "endian.h"
40#include "fds.h"
41#include "general.h"
42#include "state.h"
43#include "memory.h"
5232c20c 44#include "ppu.h"
c62d2810 45
d97315ac 46static void (*SPreSave)(void) = 0;
47static void (*SPostSave)(void) = 0;
48
49#define SFMDATA_SIZE (64)
50static SFORMAT SFMDATA[SFMDATA_SIZE];
c62d2810 51static int SFEXINDEX;
52static int stateversion;
53
890e37ba 54extern SFORMAT FCEUPPU_STATEINFO[]; // 3
55extern SFORMAT FCEUCTRL_STATEINFO[]; // 4
c62d2810 56
890e37ba 57SFORMAT SFCPU[]={ // 1
c62d2810 58 { &X.PC, 2|RLSB, "PC\0"},
59 { &X.A, 1, "A\0\0"},
60 { &X.P, 1, "P\0\0"},
61 { &X.X, 1, "X\0\0"},
62 { &X.Y, 1, "Y\0\0"},
63 { &X.S, 1, "S\0\0"},
890e37ba 64 { RAM, 0x800, "RAM"},
65 { 0, }
c62d2810 66};
67
890e37ba 68SFORMAT SFCPUC[]={ // 2
c62d2810 69 { &X.jammed, 1, "JAMM"},
70 { &X.IRQlow, 1, "IRQL"},
71 { &X.tcount, 4|RLSB, "ICoa"},
72 { &X.count, 4|RLSB, "ICou"},
73 { &timestamp, 4|RLSB, "TIME"},
890e37ba 74 { &timestampbase, 8|RLSB, "TMEB"},
75 // from 0.98.15
76 { &timestampbase, sizeof(timestampbase) | RLSB, "TSBS"}, // size seems to match?
77 { &X.mooPI, 1, "MooP"}, // alternative to the "quick and dirty hack"
78 // TODO: IQLB?
79 { 0, }
c62d2810 80};
81
890e37ba 82extern uint16 TempAddrT,RefreshAddrT;
c62d2810 83
c62d2810 84
890e37ba 85SFORMAT SFSND[]={
c62d2810 86 { &fhcnt, 4|RLSB,"FHCN"},
87 { &fcnt, 1, "FCNT"},
88 { PSG, 14, "PSG"},
89 { &PSG[0x15], 1, "P15"},
90 { &PSG[0x17], 1, "P17"},
91 { decvolume, 3, "DECV"},
92 { &sqnon, 1, "SQNO"},
93 { &nreg, 2|RLSB, "NREG"},
94 { &trimode, 1, "TRIM"},
95 { &tricoop, 1, "TRIC"},
96 { sweepon, 2, "SWEE"},
97 { &curfreq[0], 4|RLSB,"CRF1"},
98 { &curfreq[1], 4|RLSB,"CRF2"},
99 { SweepCount, 2,"SWCT"},
100 { DecCountTo1, 3,"DCT1"},
101 { &PCMBitIndex, 1,"PBIN"},
102 { &PCMAddressIndex, 4|RLSB, "PAIN"},
890e37ba 103 { &PCMSizeIndex, 4|RLSB, "PSIN"},
104 { 0, }
c62d2810 105};
106
107
890e37ba 108
109int WriteStateChunk(FILE *st, int type, SFORMAT *sf)
c62d2810 110{
890e37ba 111 int bsize, count;
c62d2810 112 int x;
113
890e37ba 114 count = x = 0;
115 while (sf[x++].v) count++;
116
c62d2810 117 fputc(type,st);
22f08d95 118
c62d2810 119 for(x=bsize=0;x<count;x++)
120 bsize+=sf[x].s&(~RLSB);
121 bsize+=count<<3;
122 write32(bsize,st);
123 for(x=0;x<count;x++)
124 {
125 fwrite(sf[x].desc,1,4,st);
126 write32(sf[x].s&(~RLSB),st);
127 #ifdef LSB_FIRST
128 fwrite((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
129 #else
130 {
131 int z;
132 if(sf[x].s&RLSB)
133 {
134 for(z=(sf[x].s&(~RLSB))-1;z>=0;z--)
135 {
136 fputc(*(uint8*)sf[x].v,st);
137 }
138 }
139 else
140 fwrite((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
141 }
142 #endif
143 }
144 return (bsize+5);
145}
146
890e37ba 147int ReadStateChunk(FILE *st, SFORMAT *sf, int size)
c62d2810 148{
149 uint8 tmpyo[16];
890e37ba 150 int bsize, count;
c62d2810 151 int x;
152
890e37ba 153 // recalculate count ourselves
154 count = x = 0;
155 while (sf[x++].v) count++;
156
c62d2810 157 for(x=bsize=0;x<count;x++)
158 bsize+=sf[x].s&(~RLSB);
159 if(stateversion>=53)
160 bsize+=count<<3;
161 else
162 {
163 if(bsize!=size)
164 {
165 fseek(st,size,SEEK_CUR);
166 return 0;
167 }
168 }
169
170 if(stateversion<56)
171 memcpy(tmpyo,mapbyte3,16);
172
173 if(stateversion>=53)
174 {
175 int temp;
176 temp=ftell(st);
177
178 while(ftell(st)<temp+size)
179 {
180 int tsize;
181 char toa[4];
182
183 if(fread(toa,1,4,st)<=0)
184 return 0;
185 read32(&tsize,st);
186
187 for(x=0;x<count;x++)
188 {
189 if(!memcmp(toa,sf[x].desc,4))
190 {
191 if(tsize!=(sf[x].s&(~RLSB)))
890e37ba 192 {
193 printf("ReadStateChunk: sect \"%c%c%c%c\" has wrong size\n", toa[0], toa[1], toa[2], toa[3]);
c62d2810 194 goto nkayo;
890e37ba 195 }
c62d2810 196 #ifndef LSB_FIRST
197 if(sf[x].s&RLSB)
198 {
199 int z;
22f08d95 200 for(z=(sf[x].s&(~RLSB))-1;z>=0;z--)
c62d2810 201 *(uint8*)sf[x].v=fgetc(st);
202 }
203 else
204 #endif
205 {
206 fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
207 }
208 goto bloo;
209 }
210 }
890e37ba 211 printf("ReadStateChunk: sect \"%c%c%c%c\" not handled\n", toa[0], toa[1], toa[2], toa[3]);
c62d2810 212 nkayo:
213 fseek(st,tsize,SEEK_CUR);
214 bloo:;
215 } // while(...)
216 } // >=53
217 else
218 {
219 for(x=0;x<count;x++)
220 {
221 #ifdef LSB_FIRST
222 fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
223 #else
224 int z;
225 if(sf[x].s&RLSB)
226 for(z=(sf[x].s&(~RLSB))-1;z>=0;z--)
227 {
228 *(uint8*)sf[x].v=fgetc(st);
229 }
230 else
231 fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
232 #endif
233 }
234 }
235 if(stateversion<56)
236 {
237 for(x=0;x<16;x++)
238 #ifdef LSB_FIRST
239 mapbyte1[x]=mapbyte1[x<<1];
240 #else
241 mapbyte1[x]=mapbyte1[(x<<1)+1];
242 #endif
243 memcpy(mapbyte3,tmpyo,16);
244 }
245 return 1;
246}
247
890e37ba 248static int ReadStateChunks(FILE *st)
c62d2810 249{
250 int t;
251 uint32 size;
252 int ret=1;
253
254for(;;)
255 {
256 t=fgetc(st);
257 if(t==EOF) break;
258 if(!read32(&size,st)) break;
890e37ba 259
260 // printf("ReadStateChunks: chunk %i\n", t);
c62d2810 261 switch(t)
262 {
890e37ba 263 case 1:if(!ReadStateChunk(st,SFCPU,size)) ret=0;
92e249b1 264#ifdef ASM_6502
265 asmcpu_unpack();
266#endif
267 break;
890e37ba 268 case 2:if(!ReadStateChunk(st,SFCPUC,size)) ret=0;
c62d2810 269 else
270 {
271 X.mooPI=X.P; // Quick and dirty hack.
272 }
273 break;
890e37ba 274 case 3:if(!ReadStateChunk(st,FCEUPPU_STATEINFO,size)) ret=0;break;
275 case 4:if(!ReadStateChunk(st,FCEUCTRL_STATEINFO,size)) ret=0;break;
276 case 5:if(!ReadStateChunk(st,SFSND,size)) ret=0;break;
277 case 0x10:if(!ReadStateChunk(st,SFMDATA,size)) ret=0;break;
278 default:printf("ReadStateChunks: unknown chunk: %i\n", t);
279 if(fseek(st,size,SEEK_CUR)<0) goto endo;break;
c62d2810 280 }
281 }
282 endo:
283 return ret;
284}
285
286
287int CurrentState=0;
288extern int geniestage;
289void SaveState(void)
290{
291 FILE *st=NULL;
971a1d07 292 char *fname;
c62d2810 293
294 TempAddrT=TempAddr;
295 RefreshAddrT=RefreshAddr;
296
297 if(geniestage==1)
298 {
299 FCEU_DispMessage("Cannot save FCS in GG screen.");
300 return;
301 }
302
971a1d07 303 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
304 st=fopen(fname,"wb");
305 free(fname);
c62d2810 306
307 if(st!=NULL)
308 {
309 static uint32 totalsize;
310 static uint8 header[16]="FCS";
311 memset(header+4,0,13);
312 header[3]=VERSION_NUMERIC;
313 fwrite(header,1,16,st);
314
92e249b1 315#ifdef ASM_6502
316 asmcpu_pack();
317#endif
890e37ba 318 totalsize=WriteStateChunk(st,1,SFCPU);
319 totalsize+=WriteStateChunk(st,2,SFCPUC);
320 totalsize+=WriteStateChunk(st,3,FCEUPPU_STATEINFO);
321 totalsize+=WriteStateChunk(st,4,FCEUCTRL_STATEINFO);
322 totalsize+=WriteStateChunk(st,5,SFSND);
d97315ac 323
324
325 if(SPreSave) SPreSave();
890e37ba 326 totalsize+=WriteStateChunk(st,0x10,SFMDATA);
d97315ac 327 if(SPostSave) SPostSave();
22f08d95 328
c62d2810 329 fseek(st,4,SEEK_SET);
330 write32(totalsize,st);
331 SaveStateStatus[CurrentState]=1;
332 fclose(st);
92e249b1 333#ifdef GP2X
334 sync();
335#endif
c62d2810 336 FCEU_DispMessage("State %d saved.",CurrentState);
337 }
338 else
339 FCEU_DispMessage("State %d save error.",CurrentState);
340}
341
342static int LoadStateOld(FILE *st);
5a2aa426 343int FCEUSS_LoadFP(FILE *st, int make_backup)
c62d2810 344{
345 int x;
c62d2810 346 if(st!=NULL)
347 {
348 uint8 header[16];
349
350 fread(&header,1,16,st);
351 if(memcmp(header,"FCS",3))
352 {
22f08d95 353 fseek(st,0,SEEK_SET);
c62d2810 354 if(!LoadStateOld(st))
355 goto lerror;
356 goto okload;
357 }
358 stateversion=header[3];
359 if(stateversion<53)
360 FixOldSaveStateSFreq();
361 x=ReadStateChunks(st);
362 if(GameStateRestore) GameStateRestore(header[3]);
363 if(x)
364 {
365 okload:
366 TempAddr=TempAddrT;
367 RefreshAddr=RefreshAddrT;
368
369 SaveStateStatus[CurrentState]=1;
370 FCEU_DispMessage("State %d loaded.",CurrentState);
371 SaveStateStatus[CurrentState]=1;
372 }
373 else
374 {
375 SaveStateStatus[CurrentState]=1;
376 FCEU_DispMessage("Error(s) reading state %d!",CurrentState);
377 }
378 }
379 else
380 {
381 lerror:
382 FCEU_DispMessage("State %d load error.",CurrentState);
383 SaveStateStatus[CurrentState]=0;
5a2aa426 384 return 0;
385 }
386 return 1;
387}
388
389void LoadState(void)
390{
391 FILE *st=NULL;
971a1d07 392 char *fname;
5a2aa426 393
394 if(geniestage==1)
395 {
396 FCEU_DispMessage("Cannot load FCS in GG screen.");
397 return;
398 }
399
971a1d07 400 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
401 st=fopen(fname,"rb");
402 free(fname);
403
5a2aa426 404 if (st)
405 {
406 FCEUSS_LoadFP(st, 0);
407 fclose(st);
c62d2810 408 }
971a1d07 409 else
410 {
411 FCEU_DispMessage("State %d load error (no file).",CurrentState);
412 SaveStateStatus[CurrentState]=0;
413 }
c62d2810 414}
415
416char SaveStateStatus[10];
971a1d07 417#if 0 // leaks memory
c62d2810 418void CheckStates(void)
419{
420 FILE *st=NULL;
421 int ssel;
422
22f08d95 423 if(SaveStateStatus[0]==(char)-1)
c62d2810 424 for(ssel=0;ssel<10;ssel++)
425 {
426 st=fopen(FCEU_MakeFName(FCEUMKF_STATE,ssel,0),"rb");
427 if(st)
428 {
429 SaveStateStatus[ssel]=1;
430 fclose(st);
431 }
432 else
433 SaveStateStatus[ssel]=0;
434 }
435}
971a1d07 436#endif
c62d2810 437
438void SaveStateRefresh(void)
439{
440 SaveStateStatus[0]=-1;
441}
442
d97315ac 443void ResetExState(void (*PreSave)(void), void (*PostSave)(void))
c62d2810 444{
445 int x;
446 for(x=0;x<SFEXINDEX;x++)
d97315ac 447 {
448 if(SFMDATA[x].desc)
449 free(SFMDATA[x].desc);
450 }
451 SPreSave = PreSave;
452 SPostSave = PostSave;
c62d2810 453 SFEXINDEX=0;
454}
455
d97315ac 456
c62d2810 457void AddExState(void *v, uint32 s, int type, char *desc)
458{
d97315ac 459 if(desc)
c62d2810 460 {
d97315ac 461 SFMDATA[SFEXINDEX].desc=(char *)FCEU_malloc(5);
c62d2810 462 strcpy(SFMDATA[SFEXINDEX].desc,desc);
c62d2810 463 }
d97315ac 464 else
465// SFMDATA[SFEXINDEX].desc=0;
466 return; // do not support recursive save structures
467 SFMDATA[SFEXINDEX].v=v;
468 SFMDATA[SFEXINDEX].s=s;
469 if(type) SFMDATA[SFEXINDEX].s|=RLSB;
470 if(SFEXINDEX<SFMDATA_SIZE-1)
471 SFEXINDEX++;
472 else
473 {
474 static int once=1;
475 if(once)
476 {
477 once=0;
478 FCEU_PrintError("Error in AddExState: SFEXINDEX overflow.\nSomebody made SFMDATA_SIZE too small.");
479 }
480 }
481 SFMDATA[SFEXINDEX].v=0; // End marker.
c62d2810 482}
483
d97315ac 484
c62d2810 485/* Old state loading code follows */
486
487uint8 *StateBuffer;
488unsigned int intostate;
489
490static void afread(void *ptr, size_t _size, size_t _nelem)
491{
492 memcpy(ptr,StateBuffer+intostate,_size*_nelem);
493 intostate+=_size*_nelem;
494}
495
496
497static void areadlower8of16(int8 *d)
498{
499#ifdef LSB_FIRST
500 *d=StateBuffer[intostate++];
501#else
502 d[1]=StateBuffer[intostate++];
503#endif
504}
505
506
507static void areadupper8of16(int8 *d)
508{
509#ifdef LSB_FIRST
510 d[1]=StateBuffer[intostate++];
511#else
512 *d=StateBuffer[intostate++];
513#endif
514}
515
516
517static void aread16(int8 *d)
518{
519#ifdef LSB_FIRST
520 *d=StateBuffer[intostate++];
521 d[1]=StateBuffer[intostate++];
522#else
523 d[1]=StateBuffer[intostate++];
524 *d=StateBuffer[intostate++];
525#endif
526}
527
528
529static void aread32(int8 *d)
530{
531#ifdef LSB_FIRST
532 *d=StateBuffer[intostate++];
533 d[1]=StateBuffer[intostate++];
534 d[2]=StateBuffer[intostate++];
535 d[3]=StateBuffer[intostate++];
536#else
537 d[3]=StateBuffer[intostate++];
538 d[2]=StateBuffer[intostate++];
539 d[1]=StateBuffer[intostate++];
540 *d=StateBuffer[intostate++];
541#endif
542}
543
544static int LoadStateOld(FILE *st)
545{
546 int x;
547 int32 nada;
548 uint8 version;
549 nada=0;
22f08d95 550
890e37ba 551 printf("LoadStateOld\n");
552
c62d2810 553 StateBuffer=FCEU_malloc(59999);
554 if(StateBuffer==NULL)
555 return 0;
556 if(!fread(StateBuffer,59999,1,st))
557 {
558 fclose(st);
559 free(StateBuffer);
560 return 0;
561 }
562
563 intostate=0;
564
565 {
566 uint8 a[2];
567 afread(&a[0],1,1);
568 afread(&a[1],1,1);
569 X.PC=a[0]|(a[1]<<8);
570 }
571 afread(&X.A,1,1);
572 afread(&X.P,1,1);
573 afread(&X.X,1,1);
574 afread(&X.Y,1,1);
575 afread(&X.S,1,1);
576 afread(&version,1,1);
577 afread(&nada,1,1);
578 afread(&nada,1,1);
579 afread(&nada,1,1);
580 afread(&nada,1,1);
581 aread32((int8 *)&X.count);
582 afread(&nada,1,1);
583 afread(&nada,1,1);
584 afread(&nada,1,1);
585 afread(&nada,1,1);
586 aread32((int8 *)&nada);
587 afread(&nada,1,1);
588 afread(&nada,1,1);
589 afread(&nada,1,1);
590 afread(&nada,1,1);
22f08d95 591
c62d2810 592 for(x=0;x<8;x++)
593 areadupper8of16((int8 *)&CHRBankList[x]);
594 afread(PRGBankList,4,1);
595 for(x=0;x<8;x++)
596 areadlower8of16((int8 *)&CHRBankList[x]);
597 afread(CHRRAM,1,0x2000);
598 afread(NTARAM,1,0x400);
599 afread(ExtraNTARAM,1,0x400);
600 afread(NTARAM+0x400,1,0x400);
601 afread(ExtraNTARAM+0x400,1,0x400);
602
603 for(x=0;x<0xF00;x++)
604 afread(&nada,1,1);
605 afread(PALRAM,1,0x20);
606 for(x=0;x<256-32;x++)
607 afread(&nada,1,1);
608 for(x=0x00;x<0x20;x++)
609 PALRAM[x]&=0x3f;
610 afread(PPU,1,4);
611 afread(SPRAM,1,0x100);
612 afread(WRAM,1,8192);
613 afread(RAM,1,0x800);
614 aread16((int8 *)&scanline);
615 aread16((int8 *)&RefreshAddr);
616 afread(&VRAMBuffer,1,1);
22f08d95 617
c62d2810 618 afread(&IRQa,1,1);
619 aread32((int8 *)&IRQCount);
620 aread32((int8 *)&IRQLatch);
621 afread(&Mirroring,1,1);
622 afread(PSG,1,0x17);
623 PSG[0x11]&=0x7F;
624 afread(MapperExRAM,1,193);
625 if(version>=31)
626 PSG[0x17]=MapperExRAM[115];
627 else
628 PSG[0x17]|=0x40;
629 PSG[0x15]&=0xF;
630 sqnon=PSG[0x15];
631
632 X.IRQlow=0;
633 afread(&nada,1,1);
634 afread(&nada,1,1);
635 afread(&nada,1,1);
636 afread(&nada,1,1);
637 afread(&nada,1,1);
638 afread(&nada,1,1);
639 afread(&XOffset,1,1);
640 PPUCHRRAM=0;
641 for(x=0;x<8;x++)
642 {
643 nada=0;
644 afread(&nada,1,1);
22f08d95 645 PPUCHRRAM|=(nada?1:0)<<x;
c62d2810 646 }
22f08d95 647
c62d2810 648 afread(mapbyte1,1,8);
649 afread(mapbyte2,1,8);
650 afread(mapbyte3,1,8);
651 afread(mapbyte4,1,8);
652 for(x=0;x<4;x++)
653 aread16((int8 *)&nada);
22f08d95 654
c62d2810 655 PPUNTARAM=0;
656 for(x=0;x<4;x++)
657 {
658 nada=0;
659 aread16((int8 *)&nada);
660 PPUNTARAM|=((nada&0x800)?0:1)<<x;
661 }
662 afread(MapperExRAM,1,32768);
663 afread(&vtoggle,1,1);
664 aread16((int8 *)&TempAddrT);
665 aread16((int8 *)&RefreshAddrT);
22f08d95 666
c62d2810 667 if(GameStateRestore) GameStateRestore(version);
668 free(StateBuffer);
669 FixOldSaveStateSFreq();
670 X.mooPI=X.P;
671 return 1;
672}
673