palette change to reflect .15 code
[fceu.git] / fds.c
1 /* FCE Ultra - NES/Famicom Emulator\r
2  *\r
3  * Copyright notice for this file:\r
4  *  Copyright (C) 2002 Xodnizel\r
5  *\r
6  * This program is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  */\r
20 \r
21 #include <stdio.h>\r
22 #include <stdlib.h>\r
23 #include <string.h>\r
24 \r
25 #include "types.h"\r
26 #include "x6502.h"\r
27 #include "fce.h"\r
28 #include "fds.h"\r
29 #include "sound.h"\r
30 #include "general.h"\r
31 #include "state.h"\r
32 #include "file.h"\r
33 #include "memory.h"\r
34 #include "cart.h"\r
35 #include "md5.h"\r
36 #include "netplay.h"\r
37 \r
38 #include "svga.h"\r
39 \r
40 /*  TODO:  Add code to put a delay in between the time a disk is inserted\r
41         and the when it can be successfully read/written to.  This should\r
42         prevent writes to wrong places OR add code to prevent disk ejects\r
43         when the virtual motor is on(mmm...virtual motor).\r
44 */\r
45 \r
46 static DECLFR(FDSRead4030);\r
47 static DECLFR(FDSRead4031);\r
48 static DECLFR(FDSRead4032);\r
49 static DECLFR(FDSRead4033);\r
50 \r
51 static DECLFW(FDSWrite);\r
52 \r
53 static DECLFW(FDSWaveWrite);\r
54 static DECLFR(FDSWaveRead);\r
55 \r
56 static DECLFR(FDSSRead);\r
57 static DECLFW(FDSSWrite);\r
58 static DECLFR(FDSBIOSRead);\r
59 static DECLFR(FDSRAMRead);\r
60 static DECLFW(FDSRAMWrite);\r
61 static void FDSInit(void);\r
62 static void FP_FASTAPASS(1) FDSFix(int a);\r
63 \r
64 #define FDSRAM GameMemBlock\r
65 #define CHRRAM (GameMemBlock+32768)\r
66 \r
67 static uint8 FDSRegs[6];\r
68 static int32 IRQLatch,IRQCount;\r
69 static uint8 IRQa;\r
70 static void FDSClose(void);\r
71 \r
72 static uint8 FDSBIOS[8192];\r
73 \r
74 /* Original disk data backup, to help in creating save states. */\r
75 static uint8 *diskdatao[8]={0,0,0,0,0,0,0,0};\r
76 \r
77 static uint8 *diskdata[8]={0,0,0,0,0,0,0,0};\r
78 \r
79 static unsigned int TotalSides;\r
80 static uint8 DiskWritten=0;    /* Set to 1 if disk was written to. */\r
81 static uint8 writeskip;\r
82 static uint32 DiskPtr;\r
83 static int32 DiskSeekIRQ;\r
84 static uint8 SelectDisk,InDisk;\r
85 \r
86 #define DC_INC    1\r
87 \r
88 void FDSGI(int h)\r
89 {\r
90  switch(h)\r
91  {\r
92   case GI_CLOSE: FDSClose();break;\r
93   case GI_POWER: FDSInit();break;\r
94  }\r
95 }\r
96 \r
97 static void FDSStateRestore(int version)\r
98 {\r
99  int x;\r
100 \r
101  setmirror(((FDSRegs[5]&8)>>3)^1);\r
102 \r
103  if(version >= 9810)\r
104   for(x=0;x<TotalSides;x++)\r
105   {\r
106    int b;\r
107    for(b=0; b<65500; b++)\r
108     diskdata[x][b] ^= diskdatao[x][b];\r
109   }\r
110 \r
111 }\r
112 \r
113 void FDSSound();\r
114 void FDSSoundReset(void);\r
115 void FDSSoundStateAdd(void);\r
116 static void RenderSound(void);\r
117 //static void RenderSoundHQ(void);\r
118 \r
119 static void FDSInit(void)\r
120 {\r
121  memset(FDSRegs,0,sizeof(FDSRegs));\r
122  writeskip=DiskPtr=DiskSeekIRQ=0;\r
123  setmirror(1);\r
124 \r
125  setprg8r(0,0xe000,0);    // BIOS\r
126  setprg32r(1,0x6000,0);   // 32KB RAM\r
127  setchr8(0);     // 8KB CHR RAM\r
128 \r
129  MapIRQHook=FDSFix;\r
130  GameStateRestore=FDSStateRestore;\r
131 \r
132  SetReadHandler(0x4030,0x4030,FDSRead4030);\r
133  SetReadHandler(0x4031,0x4031,FDSRead4031);\r
134  SetReadHandler(0x4032,0x4032,FDSRead4032);\r
135  SetReadHandler(0x4033,0x4033,FDSRead4033);\r
136 \r
137  SetWriteHandler(0x4020,0x4025,FDSWrite);\r
138 \r
139  SetWriteHandler(0x6000,0xdfff,FDSRAMWrite);\r
140  SetReadHandler(0x6000,0xdfff,FDSRAMRead);\r
141  SetReadHandler(0xE000,0xFFFF,FDSBIOSRead);\r
142  IRQCount=IRQLatch=IRQa=0;\r
143 \r
144  FDSSoundReset();\r
145  InDisk=0;\r
146  SelectDisk=0;\r
147 }\r
148 \r
149 void FCEU_FDSInsert(void)\r
150 {\r
151         if(TotalSides==0)\r
152         {\r
153         FCEU_DispMessage("Not FDS; can't eject disk.");\r
154                 return;\r
155         }\r
156         if(InDisk==255)\r
157         {\r
158          FCEU_DispMessage("Disk %d Side %s Inserted",SelectDisk>>1,(SelectDisk&1)?"B":"A");\r
159          InDisk=SelectDisk;\r
160         }\r
161         else\r
162         {\r
163          FCEU_DispMessage("Disk %d Side %s Ejected",SelectDisk>>1,(SelectDisk&1)?"B":"A");\r
164          InDisk=255;\r
165         }\r
166 }\r
167 /*\r
168 void FCEU_FDSEject(void)\r
169 {\r
170          InDisk=255;\r
171 }\r
172 */\r
173 void FCEU_FDSSelect(void)\r
174 {\r
175         if(TotalSides==0)\r
176         {\r
177           FCEU_DispMessage("Not FDS; can't select disk.");\r
178           return;\r
179         }\r
180         if(InDisk!=255)\r
181         {\r
182          FCEU_DispMessage("Eject disk before selecting.");\r
183          return;\r
184         }\r
185             SelectDisk=((SelectDisk+1)%TotalSides)&3;\r
186             FCEU_DispMessage("Disk %d Side %c Selected",SelectDisk>>1,(SelectDisk&1)?'B':'A');\r
187 }\r
188 \r
189 static void FP_FASTAPASS(1) FDSFix(int a)\r
190 {\r
191  if((IRQa&2) && IRQCount)\r
192  {\r
193   IRQCount-=a;\r
194   if(IRQCount<=0)\r
195   {\r
196    if(!(IRQa&1))\r
197    {\r
198     IRQa&=~2;\r
199     IRQCount=IRQLatch=0;\r
200    }\r
201    else\r
202     IRQCount=IRQLatch;\r
203    //IRQCount=IRQLatch; //0xFFFF;\r
204    X6502_IRQBegin(FCEU_IQEXT);\r
205    //printf("IRQ: %d\n",timestamp);\r
206 //   printf("IRQ: %d\n",scanline);\r
207   }\r
208  }\r
209  if(DiskSeekIRQ>0)\r
210  {\r
211   DiskSeekIRQ-=a;\r
212   if(DiskSeekIRQ<=0)\r
213   {\r
214    if(FDSRegs[5]&0x80)\r
215    {\r
216     X6502_IRQBegin(FCEU_IQEXT2);\r
217    }\r
218   }\r
219  }\r
220 }\r
221 \r
222 static DECLFR(FDSRead4030)\r
223 {\r
224         uint8 ret=0;\r
225 \r
226         /* Cheap hack. */\r
227         if(X.IRQlow&FCEU_IQEXT) ret|=1;\r
228         if(X.IRQlow&FCEU_IQEXT2) ret|=2;\r
229 \r
230         if(!fceuindbg)\r
231         {\r
232          X6502_IRQEnd(FCEU_IQEXT);\r
233          X6502_IRQEnd(FCEU_IQEXT2);\r
234         }\r
235         return ret;\r
236 }\r
237 \r
238 static DECLFR(FDSRead4031)\r
239 {\r
240         static uint8 z=0;\r
241         if(InDisk!=255)\r
242         {\r
243          z=diskdata[InDisk][DiskPtr];\r
244          if(!fceuindbg)\r
245          {\r
246           if(DiskPtr<64999) DiskPtr++;\r
247           DiskSeekIRQ=150;\r
248           X6502_IRQEnd(FCEU_IQEXT2);\r
249          }\r
250         }\r
251         return z;\r
252 }\r
253 static DECLFR(FDSRead4032)\r
254 {\r
255         uint8 ret;\r
256 \r
257         ret=X.DB&~7;\r
258         if(InDisk==255)\r
259          ret|=5;\r
260 \r
261         if(InDisk==255 || !(FDSRegs[5]&1) || (FDSRegs[5]&2))\r
262          ret|=2;\r
263         return ret;\r
264 }\r
265 \r
266 static DECLFR(FDSRead4033)\r
267 {\r
268         return 0x80; // battery\r
269 }\r
270 \r
271 static DECLFW(FDSRAMWrite)\r
272 {\r
273  (FDSRAM-0x6000)[A]=V;\r
274 }\r
275 \r
276 static DECLFR(FDSBIOSRead)\r
277 {\r
278  return (FDSBIOS-0xE000)[A];\r
279 }\r
280 \r
281 static DECLFR(FDSRAMRead)\r
282 {\r
283  return (FDSRAM-0x6000)[A];\r
284 }\r
285 \r
286 /* Begin FDS sound */\r
287 \r
288 #define FDSClock (1789772.7272727272727272/2)\r
289 \r
290 typedef struct {\r
291   int64 cycles;     // Cycles per PCM sample\r
292   int64 count;    // Cycle counter\r
293   int64 envcount;    // Envelope cycle counter\r
294         uint32 b19shiftreg60;\r
295         uint32 b24adder66;\r
296         uint32 b24latch68;\r
297         uint32 b17latch76;\r
298   int32 clockcount;  // Counter to divide frequency by 8.\r
299   uint8 b8shiftreg88;  // Modulation register.\r
300   uint8 amplitude[2];  // Current amplitudes.\r
301         uint8 speedo[2];\r
302         uint8 mwcount;\r
303         uint8 mwstart;\r
304         uint8 mwave[0x20];      // Modulation waveform\r
305         uint8 cwave[0x40];      // Game-defined waveform(carrier)\r
306         uint8 SPSG[0xB];\r
307 } FDSSOUND;\r
308 \r
309 static FDSSOUND fdso;\r
310 \r
311 #define  SPSG  fdso.SPSG\r
312 #define b19shiftreg60  fdso.b19shiftreg60\r
313 #define b24adder66  fdso.b24adder66\r
314 #define b24latch68  fdso.b24latch68\r
315 #define b17latch76  fdso.b17latch76\r
316 #define b8shiftreg88  fdso.b8shiftreg88\r
317 #define clockcount  fdso.clockcount\r
318 #define amplitude  fdso.amplitude\r
319 #define speedo    fdso.speedo\r
320 \r
321 void FDSSoundStateAdd(void)\r
322 {\r
323  AddExState(fdso.cwave,64,0,"WAVE");\r
324  AddExState(fdso.mwave,32,0,"MWAV");\r
325  AddExState(amplitude,2,0,"AMPL");\r
326  AddExState(SPSG,0xB,0,"SPSG");\r
327 \r
328  AddExState(&b8shiftreg88,1,0,"B88");\r
329 \r
330  AddExState(&clockcount, 4, 1, "CLOC");\r
331  AddExState(&b19shiftreg60,4,1,"B60");\r
332  AddExState(&b24adder66,4,1,"B66");\r
333  AddExState(&b24latch68,4,1,"B68");\r
334  AddExState(&b17latch76,4,1,"B76");\r
335 \r
336 }\r
337 \r
338 static DECLFR(FDSSRead)\r
339 {\r
340  switch(A&0xF)\r
341  {\r
342   case 0x0:return(amplitude[0]|(X.DB&0xC0));\r
343   case 0x2:return(amplitude[1]|(X.DB&0xC0));\r
344  }\r
345  return(X.DB);\r
346 }\r
347 \r
348 static DECLFW(FDSSWrite)\r
349 {\r
350  if(FSettings.SndRate)\r
351  {\r
352 #if 0\r
353   if(FSettings.soundq>=1)\r
354    RenderSoundHQ();\r
355   else\r
356 #endif\r
357    RenderSound();\r
358  }\r
359  A-=0x4080;\r
360  switch(A)\r
361  {\r
362   case 0x0:\r
363   case 0x4: if(V&0x80)\r
364              amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F);\r
365             break;\r
366   case 0x5://printf("$%04x:$%02x\n",A,V);\r
367                 break;\r
368   case 0x7: b17latch76=0;SPSG[0x5]=0;//printf("$%04x:$%02x\n",A,V);\r
369                 break;\r
370   case 0x8:\r
371            b17latch76=0;\r
372         //   printf("%d:$%02x, $%02x\n",SPSG[0x5],V,b17latch76);\r
373            fdso.mwave[SPSG[0x5]&0x1F]=V&0x7;\r
374            SPSG[0x5]=(SPSG[0x5]+1)&0x1F;\r
375            break;\r
376  }\r
377  //if(A>=0x7 && A!=0x8 && A<=0xF)\r
378  //if(A==0xA || A==0x9)\r
379  //printf("$%04x:$%02x\n",A,V);\r
380  SPSG[A]=V;\r
381 }\r
382 \r
383 // $4080 - Fundamental wave amplitude data register 92\r
384 // $4082 - Fundamental wave frequency data register 58\r
385 // $4083 - Same as $4082($4083 is the upper 4 bits).\r
386 \r
387 // $4084 - Modulation amplitude data register 78\r
388 // $4086 - Modulation frequency data register 72\r
389 // $4087 - Same as $4086($4087 is the upper 4 bits)\r
390 \r
391 \r
392 static void DoEnv()\r
393 {\r
394  int x;\r
395 \r
396  for(x=0;x<2;x++)\r
397   if(!(SPSG[x<<2]&0x80) && !(SPSG[0x3]&0x40))\r
398   {\r
399    static int counto[2]={0,0};\r
400 \r
401    if(counto[x]<=0)\r
402    {\r
403     if(!(SPSG[x<<2]&0x80))\r
404     {\r
405      if(SPSG[x<<2]&0x40)\r
406      {\r
407       if(amplitude[x]<0x3F)\r
408        amplitude[x]++;\r
409      }\r
410      else\r
411      {\r
412       if(amplitude[x]>0)\r
413        amplitude[x]--;\r
414      }\r
415     }\r
416     counto[x]=(SPSG[x<<2]&0x3F);\r
417    }\r
418    else\r
419     counto[x]--;\r
420   }\r
421 }\r
422 \r
423 static DECLFR(FDSWaveRead)\r
424 {\r
425  return(fdso.cwave[A&0x3f]|(X.DB&0xC0));\r
426 }\r
427 \r
428 static DECLFW(FDSWaveWrite)\r
429 {\r
430  //printf("$%04x:$%02x, %d\n",A,V,SPSG[0x9]&0x80);\r
431  if(SPSG[0x9]&0x80)\r
432   fdso.cwave[A&0x3f]=V&0x3F;\r
433 }\r
434 \r
435 static int ta;\r
436 static INLINE void ClockRise(void)\r
437 {\r
438  if(!clockcount)\r
439  {\r
440   ta++;\r
441 \r
442   b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));\r
443   b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76;\r
444 \r
445   if(!(SPSG[0x7]&0x80))\r
446   {\r
447    int t=fdso.mwave[(b17latch76>>13)&0x1F]&7;\r
448    int t2=amplitude[1];\r
449    int adj = 0;\r
450 \r
451    if((t&3))\r
452    {\r
453     if((t&4))\r
454      adj -= (t2 * ((4 - (t&3) ) ));\r
455     else\r
456      adj += (t2 * ( (t&3) ));\r
457    }\r
458    adj *= 2;\r
459    if(adj > 0x7F) adj = 0x7F;\r
460    if(adj < -0x80) adj = -0x80;\r
461    //if(adj) printf("%d ",adj);\r
462    b8shiftreg88=0x80 + adj;\r
463   }\r
464   else\r
465   {\r
466    b8shiftreg88=0x80;\r
467   }\r
468  }\r
469  else\r
470  {\r
471   b19shiftreg60<<=1;\r
472   b8shiftreg88>>=1;\r
473  }\r
474 // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF;\r
475  b24adder66=(b24latch68+b19shiftreg60)&0x1FFFFFF;\r
476 }\r
477 \r
478 static INLINE void ClockFall(void)\r
479 {\r
480  //if(!(SPSG[0x7]&0x80))\r
481  {\r
482   if((b8shiftreg88&1)) // || clockcount==7)\r
483    b24latch68=b24adder66;\r
484  }\r
485  clockcount=(clockcount+1)&7;\r
486 }\r
487 \r
488 static INLINE int32 FDSDoSound(void)\r
489 {\r
490  fdso.count+=fdso.cycles;\r
491  if(fdso.count>=((int64)1<<40))\r
492  {\r
493   dogk:\r
494   fdso.count-=(int64)1<<40;\r
495   ClockRise();\r
496   ClockFall();\r
497   fdso.envcount--;\r
498   if(fdso.envcount<=0)\r
499   {\r
500    fdso.envcount+=SPSG[0xA]*3;\r
501    DoEnv();\r
502   }\r
503  }\r
504  if(fdso.count>=32768) goto dogk;\r
505 \r
506  // Might need to emulate applying the amplitude to the waveform a bit better...\r
507  {\r
508   int k=amplitude[0];\r
509   if(k>0x20) k=0x20;\r
510   return (fdso.cwave[b24latch68>>19]*k)*4/((SPSG[0x9]&0x3)+2);\r
511  }\r
512 }\r
513 \r
514 static int32 FBC=0;\r
515 \r
516 static void RenderSound(void)\r
517 {\r
518  int32 end, start;\r
519  int32 x;\r
520 \r
521  start=FBC;\r
522  end=(SOUNDTS<<16)/soundtsinc;\r
523  if(end<=start)\r
524   return;\r
525  FBC=end;\r
526 \r
527  if(!(SPSG[0x9]&0x80))\r
528   for(x=start;x<end;x++)\r
529   {\r
530    uint32 t=FDSDoSound();\r
531    t+=t>>1;\r
532    t>>=4;\r
533    Wave[x>>4]+=t; //(t>>2)-(t>>3); //>>3;\r
534   }\r
535 }\r
536 \r
537 #if 0\r
538 static void RenderSoundHQ(void)\r
539 {\r
540  int32 x;\r
541 \r
542  if(!(SPSG[0x9]&0x80))\r
543   for(x=FBC;x<SOUNDTS;x++)\r
544   {\r
545    uint32 t=FDSDoSound();\r
546    t+=t>>1;\r
547    WaveHi[x]+=t; //(t<<2)-(t<<1);\r
548   }\r
549  FBC=SOUNDTS;\r
550 }\r
551 #endif\r
552 \r
553 static void HQSync(int32 ts)\r
554 {\r
555  FBC=ts;\r
556 }\r
557 \r
558 void FDSSound(int c)\r
559 {\r
560   RenderSound();\r
561   FBC=c;\r
562 }\r
563 \r
564 /*\r
565 static DECLFR(FDSBIOSPatch)\r
566 {\r
567  if(FDSRegs[5]&0x4)\r
568  {\r
569   X.X=FDSRead4031(0x4031);\r
570   FDSWrite(0x4024,X.A);\r
571   X.A=X.X;\r
572   return(0x60);\r
573  }\r
574  else\r
575  {\r
576   return(0x58);\r
577   //puts("Write");\r
578  }\r
579 }\r
580 */\r
581 \r
582 static void FDS_ESI(void)\r
583 {\r
584  if(FSettings.SndRate)\r
585  {\r
586 #if 0\r
587   if(FSettings.soundq>=1)\r
588   {\r
589    fdso.cycles=(int64)1<<39;\r
590   }\r
591   else\r
592 #endif\r
593   {\r
594    fdso.cycles=((int64)1<<40)*FDSClock;\r
595    fdso.cycles/=FSettings.SndRate *16;\r
596   }\r
597  }\r
598 //  fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate *16);\r
599  SetReadHandler(0x4040,0x407f,FDSWaveRead);\r
600  SetWriteHandler(0x4040,0x407f,FDSWaveWrite);\r
601  SetWriteHandler(0x4080,0x408A,FDSSWrite);\r
602  SetReadHandler(0x4090,0x4092,FDSSRead);\r
603 \r
604  //SetReadHandler(0xE7A3,0xE7A3,FDSBIOSPatch);\r
605 }\r
606 \r
607 void FDSSoundReset(void)\r
608 {\r
609  memset(&fdso,0,sizeof(fdso));\r
610  FDS_ESI();\r
611  GameExpSound.HiSync=HQSync;\r
612  GameExpSound.HiFill=0;//RenderSoundHQ;\r
613  GameExpSound.Fill=FDSSound;\r
614  GameExpSound.RChange=FDS_ESI;\r
615 }\r
616 \r
617 static DECLFW(FDSWrite)\r
618 {\r
619  //extern int scanline;\r
620  //FCEU_printf("$%04x:$%02x, %d\n",A,V,scanline);\r
621  switch(A)\r
622  {\r
623   case 0x4020:\r
624         X6502_IRQEnd(FCEU_IQEXT);\r
625         IRQLatch&=0xFF00;\r
626         IRQLatch|=V;\r
627 //  printf("$%04x:$%02x\n",A,V);\r
628         break;\r
629   case 0x4021:\r
630         X6502_IRQEnd(FCEU_IQEXT);\r
631         IRQLatch&=0xFF;\r
632         IRQLatch|=V<<8;\r
633 //  printf("$%04x:$%02x\n",A,V);\r
634         break;\r
635   case 0x4022:\r
636         X6502_IRQEnd(FCEU_IQEXT);\r
637         IRQCount=IRQLatch;\r
638         IRQa=V&3;\r
639 //  printf("$%04x:$%02x\n",A,V);\r
640         break;\r
641   case 0x4023:break;\r
642   case 0x4024:\r
643         if(InDisk!=255 && !(FDSRegs[5]&0x4) && (FDSRegs[3]&0x1))\r
644         {\r
645          if(DiskPtr>=0 && DiskPtr<65500)\r
646          {\r
647           if(writeskip) writeskip--;\r
648           else if(DiskPtr>=2)\r
649           {\r
650            DiskWritten=1;\r
651            diskdata[InDisk][DiskPtr-2]=V;\r
652           }\r
653          }\r
654         }\r
655         break;\r
656   case 0x4025:\r
657         X6502_IRQEnd(FCEU_IQEXT2);\r
658         if(InDisk!=255)\r
659         {\r
660          if(!(V&0x40))\r
661          {\r
662           if(FDSRegs[5]&0x40 && !(V&0x10))\r
663           {\r
664            DiskSeekIRQ=200;\r
665            DiskPtr-=2;\r
666           }\r
667           if(DiskPtr<0) DiskPtr=0;\r
668          }\r
669          if(!(V&0x4)) writeskip=2;\r
670          if(V&2) {DiskPtr=0;DiskSeekIRQ=200;}\r
671          if(V&0x40) DiskSeekIRQ=200;\r
672         }\r
673         setmirror(((V>>3)&1)^1);\r
674         break;\r
675  }\r
676  FDSRegs[A&7]=V;\r
677 }\r
678 \r
679 static void FreeFDSMemory(void)\r
680 {\r
681  int x;\r
682 \r
683  for(x=0;x<TotalSides;x++)\r
684   if(diskdata[x])\r
685   {\r
686    free(diskdata[x]);\r
687    diskdata[x]=0;\r
688   }\r
689 }\r
690 \r
691 static int SubLoad(int fp)\r
692 {\r
693  struct md5_context md5;\r
694  uint8 header[16];\r
695  int x;\r
696 \r
697  FCEU_fread(header,16,1,fp);\r
698 \r
699  if(memcmp(header,"FDS\x1a",4))\r
700  {\r
701   if(!(memcmp(header+1,"*NINTENDO-HVC*",14)))\r
702   {\r
703    long t;\r
704    t=FCEU_fgetsize(fp);\r
705    if(t<65500)\r
706     t=65500;\r
707    TotalSides=t/65500;\r
708    FCEU_fseek(fp,0,SEEK_SET);\r
709   }\r
710   else\r
711    return(0);\r
712  }\r
713  else\r
714   TotalSides=header[4];\r
715 \r
716  md5_starts(&md5);\r
717 \r
718  if(TotalSides>8) TotalSides=8;\r
719  if(TotalSides<1) TotalSides=1;\r
720 \r
721  for(x=0;x<TotalSides;x++)\r
722  {\r
723   diskdata[x]=(uint8 *)FCEU_malloc(65500);\r
724   if(!diskdata[x])\r
725   {\r
726    int zol;\r
727    for(zol=0;zol<x;zol++)\r
728     free(diskdata[zol]);\r
729    return 0;\r
730   }\r
731   FCEU_fread(diskdata[x],1,65500,fp);\r
732   md5_update(&md5,diskdata[x],65500);\r
733  }\r
734  md5_finish(&md5,FCEUGameInfo.MD5);\r
735  return(1);\r
736 }\r
737 \r
738 static void PreSave(void)\r
739 {\r
740  int x;\r
741 \r
742  //if(DiskWritten)\r
743   for(x=0;x<TotalSides;x++)\r
744   {\r
745    int b;\r
746    for(b=0; b<65500; b++)\r
747     diskdata[x][b] ^= diskdatao[x][b];\r
748   }\r
749 }\r
750 \r
751 static void PostSave(void)\r
752 {\r
753  int x;\r
754 \r
755  //if(DiskWritten)\r
756   for(x=0;x<TotalSides;x++)\r
757   {\r
758    int b;\r
759 \r
760    for(b=0; b<65500; b++)\r
761     diskdata[x][b] ^= diskdatao[x][b];\r
762   }\r
763 \r
764 }\r
765 \r
766 int FDSLoad(const char *name, int fp)\r
767 {\r
768  FILE *zp;\r
769  int x;\r
770  char *fn;\r
771 \r
772  FCEU_fseek(fp,0,SEEK_SET);\r
773 \r
774  if(!SubLoad(fp))\r
775   return(0);\r
776 \r
777 \r
778  fn = FCEU_MakeFName(FCEUMKF_FDSROM,0,0);\r
779 \r
780  if(!(zp=FCEUD_UTF8fopen(fn,"rb")))\r
781  {\r
782   FCEU_PrintError("FDS BIOS ROM image missing!");\r
783   FreeFDSMemory();\r
784   free(fn);\r
785   return 0;\r
786  }\r
787 \r
788  free(fn);\r
789 \r
790  if(fread(FDSBIOS,1,8192,zp)!=8192)\r
791  {\r
792   fclose(zp);\r
793   FreeFDSMemory();\r
794   FCEU_PrintError("Error reading FDS BIOS ROM image.");\r
795   return 0;\r
796  }\r
797 \r
798  fclose(zp);\r
799 \r
800 \r
801  {\r
802   int tp;\r
803   char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);\r
804 \r
805   int x;\r
806   for(x=0;x<TotalSides;x++)\r
807   {\r
808    diskdatao[x]=(uint8 *)FCEU_malloc(65500);\r
809    memcpy(diskdatao[x],diskdata[x],65500);\r
810   }\r
811 \r
812   if((tp=FCEU_fopen(fn,"rb")))\r
813   {\r
814    FreeFDSMemory();\r
815    if(!SubLoad(tp))\r
816    {\r
817     FCEU_PrintError("Error reading auxillary FDS file.");\r
818     free(fn);\r
819     return(0);\r
820    }\r
821    FCEU_fclose(tp);\r
822    DiskWritten=1;  /* For save state handling. */\r
823   }\r
824   free(fn);\r
825  }\r
826 \r
827  FCEUGameInfo.type=GIT_FDS;\r
828  GameInterface=FDSGI;\r
829 \r
830  SelectDisk=0;\r
831  InDisk=255;\r
832 \r
833  ResetExState(PreSave,PostSave);\r
834  FDSSoundStateAdd();\r
835 \r
836  for(x=0;x<TotalSides;x++)\r
837  {\r
838   char temp[5];\r
839   sprintf(temp,"DDT%d",x);\r
840   AddExState(diskdata[x],65500,0,temp);\r
841  }\r
842 \r
843  AddExState(FDSRAM,32768,0,"FDSR");\r
844  AddExState(FDSRegs,sizeof(FDSRegs),0,"FREG");\r
845  AddExState(CHRRAM,8192,0,"CHRR");\r
846  AddExState(&IRQCount, 4, 1, "IRQC");\r
847  AddExState(&IRQLatch, 4, 1, "IQL1");\r
848  AddExState(&IRQa, 1, 0, "IRQA");\r
849  AddExState(&writeskip,1,0,"WSKI");\r
850  AddExState(&DiskPtr,4,1,"DPTR");\r
851  AddExState(&DiskSeekIRQ,4,1,"DSIR");\r
852  AddExState(&SelectDisk,1,0,"SELD");\r
853  AddExState(&InDisk,1,0,"INDI");\r
854  AddExState(&DiskWritten,1,0,"DSKW");\r
855 \r
856  ResetCartMapping();\r
857  SetupCartCHRMapping(0,CHRRAM,8192,1);\r
858  SetupCartMirroring(0,0,0);\r
859  memset(CHRRAM,0,8192);\r
860  memset(FDSRAM,0,32768);\r
861 \r
862  FCEU_printf(" Sides: %d\n\n",TotalSides);\r
863 \r
864  FCEUI_SetVidSystem(0);\r
865 \r
866  return 1;\r
867 }\r
868 \r
869 void FDSClose(void)\r
870 {\r
871  FILE *fp;\r
872  int x;\r
873  char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);\r
874 \r
875  if(!DiskWritten) return;\r
876 \r
877  if(!(fp=FCEUD_UTF8fopen(fn,"wb")))\r
878  {\r
879   free(fn);\r
880   return;\r
881  }\r
882  free(fn);\r
883 \r
884  for(x=0;x<TotalSides;x++)\r
885  {\r
886   if(fwrite(diskdata[x],1,65500,fp)!=65500)\r
887   {\r
888    FCEU_PrintError("Error saving FDS image!");\r
889    fclose(fp);\r
890    return;\r
891   }\r
892  }\r
893  FreeFDSMemory();\r
894  fclose(fp);\r
895 }\r