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