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