mmuhack, 6502 tweaks, fskip
[fceu.git] / fds.c
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 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "types.h"
26 #include "x6502.h"
27 #include "version.h"
28 #include "fce.h"
29 #include "fds.h"
30 #include "svga.h"
31 #include "sound.h"
32 #include "general.h"
33 #include "state.h"
34 #include "file.h"
35 #include "memory.h"
36 #include "cart.h"
37
38 /*      TODO:  Add code to put a delay in between the time a disk is inserted
39         and the when it can be successfully read/written to.  This should
40         prevent writes to wrong places OR add code to prevent disk ejects
41         when the virtual motor is on(mmm...virtual motor).
42 */
43
44 static DECLFR(FDSRead4030);
45 static DECLFR(FDSRead4031);
46 static DECLFR(FDSRead4032);
47 static DECLFR(FDSRead4033);
48 static DECLFW(FDSWrite4020);
49 static DECLFW(FDSWrite4021);
50 static DECLFW(FDSWrite4022);
51 static DECLFW(FDSWrite4023);
52 static DECLFW(FDSWrite4024);
53 static DECLFW(FDSWrite4025);
54 static DECLFW(FDSWaveWrite);
55 static DECLFR(FDSWaveRead);
56
57 static DECLFR(FDSSRead);
58 static DECLFW(FDSSWrite);
59 static DECLFR(FDSBIOSRead);
60 static DECLFR(FDSRAMRead);
61 static DECLFW(FDSRAMWrite);
62 static void FDSInit(void);
63 static void FDSReset(void);
64 static void FP_FASTAPASS(1) FDSFix(int a);
65
66 #define FDSRAM GameMemBlock
67 #define mapbyte1 (GameMemBlock+32768)
68 #define mapbyte2 (GameMemBlock+32768+8)
69 #define mapbyte3 (GameMemBlock+32768+16)
70 #define mapbyte4 (GameMemBlock+32768+24)        // 8 bytes
71 #define CHRRAM   (GameMemBlock+32768+32+64)
72
73 #define IRQLatch       (*(int32*)(CHRRAM+8192))
74 #define IRQCount       (*(int32*)(CHRRAM+8192+4))
75 #define IRQa           (*(CHRRAM+8192+8))
76
77 static void FDSClose(void);
78 static uint8 header[16];
79 #define writeskip mapbyte2[0]
80
81 static char FDSSaveName[2048];
82
83 uint8 FDSBIOS[8192];
84 uint8 *diskdata[4]={0,0,0,0};
85
86 #define SideWrite mapbyte2[1]
87 #define DiskPtr (*(uint32*)(mapbyte2+4))
88 #define dsr0 mapbyte2[2]
89 #define dsr1 mapbyte2[3]
90
91 #define DC_INC          1
92
93 #define DiskSeekIRQ (*(int32*)(mapbyte3+4))
94 #define SelectDisk mapbyte3[0]
95 #define InDisk     mapbyte3[1]
96
97 static void FDSReset(void)
98 {
99         memset(mapbyte1,0,8);
100         memset(mapbyte2,0,8);
101         memset(mapbyte3+4,0,4);
102         memset(mapbyte4,0,8);
103 }
104
105 void FDSGI(int h)
106 {
107  switch(h)
108  {
109   case GI_CLOSE: FDSClose();break;
110   case GI_POWER: FDSReset();FDSInit();break;
111  }
112 }
113
114 static void FDSStateRestore(int version)
115
116  setmirror(((mapbyte1[5]&8)>>3)^1);
117 }
118
119 void FDSSound();
120 void FDSSoundReset(void);
121 void FDSSoundStateAdd(void);
122 static void RenderSound(void);
123
124 static void FDSInit(void)
125 {
126  dsr0=0;
127  dsr1=0x41;
128  setmirror(1);
129  if(InDisk!=255)
130   dsr1&=0xFE;
131
132  setprg8r(0,0xe000,0);          // BIOS
133  setprg32r(1,0x6000,0);         // 32KB RAM
134  setchr8(0);           // 8KB CHR RAM
135
136  MapIRQHook=FDSFix;
137  GameStateRestore=FDSStateRestore;
138
139  SetReadHandler(0x4030,0x4030,FDSRead4030);
140  SetReadHandler(0x4031,0x4031,FDSRead4031);
141  SetReadHandler(0x4032,0x4032,FDSRead4032);
142  SetReadHandler(0x4033,0x4033,FDSRead4033);
143
144  SetWriteHandler(0x4020,0x4020,FDSWrite4020); 
145  SetWriteHandler(0x4021,0x4021,FDSWrite4021);
146  SetWriteHandler(0x4022,0x4022,FDSWrite4022);
147  SetWriteHandler(0x4023,0x4023,FDSWrite4023);
148  SetWriteHandler(0x4024,0x4024,FDSWrite4024);
149  SetWriteHandler(0x4025,0x4025,FDSWrite4025);
150
151  SetWriteHandler(0x6000,0xdfff,FDSRAMWrite);
152  SetReadHandler(0x6000,0xdfff,FDSRAMRead);
153  SetReadHandler(0xE000,0xFFFF,FDSBIOSRead);
154
155  IRQCount=IRQLatch=IRQa=0;
156
157  FDSSoundReset();
158 }
159
160 void FDSControl(int what)
161 {
162  switch(what)
163  {
164   case FDS_IDISK:dsr1&=0xFE;
165                  if(InDisk==255)
166                  {
167                   FCEU_DispMessage("Disk %d Side %s Inserted",
168                   SelectDisk>>1,(SelectDisk&1)?"B":"A");
169                   InDisk=SelectDisk;
170                  }
171                  else
172                   FCEU_DispMessage("Jamming disks is a BAD IDEA");
173                  break;
174   case FDS_EJECT:
175                  if(InDisk!=255)
176                   FCEU_DispMessage("Disk Ejected");
177                  else
178                   FCEU_DispMessage("Cannot Eject Air");
179                  dsr1|=1;
180                  InDisk=255;
181                  break;
182   case FDS_SELECT:
183                   if(InDisk!=255)
184                   {
185                    FCEU_DispMessage("Eject disk before selecting.");
186                    break;
187                   }
188                   SelectDisk=((SelectDisk+1)%header[4])&3;
189                   FCEU_DispMessage("Disk %d Side %s Selected",
190                         SelectDisk>>1,(SelectDisk&1)?"B":"A");
191                   break;
192  }
193 }
194
195 static void FP_FASTAPASS(1) FDSFix(int a)
196 {
197  if(IRQa)
198  {
199   IRQCount-=a;
200   if(IRQCount<=0)
201   {
202    IRQa=0;
203    dsr0|=1;
204    dsr0&=~2;
205    IRQCount=0xFFFF;
206    X6502_IRQBegin(FCEU_IQEXT);
207   }
208  }
209  if(DiskSeekIRQ>0) 
210  {
211   DiskSeekIRQ-=a;
212   if(DiskSeekIRQ<=0)
213   {
214    if(mapbyte1[5]&0x80)
215    {
216     dsr0&=~1;
217     dsr0|=2;
218     TriggerIRQ();
219    }
220   }
221  }
222 }
223
224 void DiskControl(int which)
225 {
226 if(mapbyte1[5]&1)
227  {
228   switch(which)
229   {
230   case DC_INC:
231                if(DiskPtr<64999) DiskPtr++;
232                //DiskSeekIRQ=160+100;
233                //DiskSeekIRQ=140;
234                //DiskSeekIRQ=160;
235                 DiskSeekIRQ=150;
236                break;
237   }
238  }
239 }
240 static DECLFR(FDSRead4030)
241 {
242         X6502_IRQEnd(FCEU_IQEXT);
243         return dsr0;
244 }
245
246 static DECLFR(FDSRead4031)
247 {
248         static uint8 z=0;
249         if(InDisk!=255)
250         {
251          z=diskdata[InDisk][DiskPtr];
252          DiskControl(DC_INC);
253         }
254         return z;
255 }
256
257 static DECLFR(FDSRead4032)
258 {
259         return dsr1;
260 }
261
262 static DECLFR(FDSRead4033)
263 {
264         return 0x80; // battery
265 }
266
267 static DECLFW(FDSRAMWrite)
268 {
269  (FDSRAM-0x6000)[A]=V;
270 }
271
272 static DECLFR(FDSBIOSRead)
273 {
274  return (FDSBIOS-0xE000)[A];
275 }
276
277 static DECLFR(FDSRAMRead)
278 {
279  return (FDSRAM-0x6000)[A];
280 }
281
282 /* Begin FDS sound */
283
284 #define FDSClock (1789772.7272727272727272/8)
285
286 typedef struct {
287         int64 cycles;           // Cycles per PCM sample
288         int64 count;            // Cycle counter
289         int64 envcount;         // Envelope cycle counter
290         uint32 b19shiftreg60;
291         uint32 b24adder66;
292         uint32 b24latch68;
293         uint32 b17latch76;
294         int32 clockcount;       // Counter to divide frequency by 8.
295         uint8 b8shiftreg88;     // Modulation register.
296         uint8 amplitude[2];     // Current amplitudes.
297         uint8 mwave[0x20];      // Modulation waveform
298         uint8 cwave[0x40];      // Game-defined waveform(carrier)
299         uint8 SPSG[0xB];
300 } FDSSOUND;
301
302 static FDSSOUND fdso;
303
304 #define SPSG    fdso.SPSG
305 #define b19shiftreg60   fdso.b19shiftreg60
306 #define b24adder66      fdso.b24adder66
307 #define b24latch68      fdso.b24latch68
308 #define b17latch76      fdso.b17latch76
309 #define b8shiftreg88    fdso.b8shiftreg88
310 #define clockcount      fdso.clockcount
311 #define amplitude       fdso.amplitude
312
313 void FDSSoundStateAdd(void)
314 {
315  AddExState(fdso.cwave,64,0,"WAVE");
316  AddExState(fdso.mwave,32,0,"MWAV");
317  AddExState(amplitude,2,0,"AMPL");
318  AddExState(SPSG,0xB,0,"SPSG");
319
320  AddExState(&b8shiftreg88,1,0,"B88");
321
322  AddExState(&clockcount, 4, 1, "CLOC");
323  AddExState(&b19shiftreg60,4,1,"B60");
324  AddExState(&b24adder66,4,1,"B66");
325  AddExState(&b24latch68,4,1,"B68");
326  AddExState(&b17latch76,4,1,"B76");
327
328 }
329
330 static DECLFR(FDSSRead)
331 {
332  switch(A&0xF)
333  {
334   case 0x0:return(amplitude[0]|(X.DB&0xC0));
335   case 0x2:return(amplitude[1]|(X.DB&0xC0));
336  }
337  return(X.DB);
338 }
339
340 static DECLFW(FDSSWrite)
341 {
342  if(FSettings.SndRate)
343   RenderSound();
344  A-=0x4080;
345  switch(A)
346  {
347   case 0x0: 
348   case 0x4:
349             if(!(V&0x80))
350             {
351               // if(V&0x40) amplitude[(A&0xF)>>2]=0;
352               // else amplitude[(A&0xF)>>2]=0x3F;
353             }
354             else 
355              amplitude[(A&0xF)>>2]=V&0x3F;
356             break;
357   case 0x7: b17latch76=0;SPSG[0x5]=0;break;
358   case 0x8:
359            //printf("%d:$%02x\n",SPSG[0x5],V);
360            fdso.mwave[SPSG[0x5]&0x1F]=V&0x7;
361            SPSG[0x5]=(SPSG[0x5]+1)&0x1F;
362            break;
363  }
364  //if(A>=0x7 && A!=0x8 && A<=0xF)
365  //if(A==0xA || A==0x9) printf("$%04x:$%02x\n",A,V);
366  SPSG[A]=V;
367 }
368
369 // $4080 - Fundamental wave amplitude data register 92
370 // $4082 - Fundamental wave frequency data register 58
371 // $4083 - Same as $4082($4083 is the upper 4 bits).
372
373 // $4084 - Modulation amplitude data register 78
374 // $4086 - Modulation frequency data register 72
375 // $4087 - Same as $4086($4087 is the upper 4 bits)
376
377
378 static void DoEnv()
379 {
380  int x;
381
382  for(x=0;x<2;x++)
383   if(!(SPSG[x<<2]&0x80) && !(SPSG[0x9+x]&0x80))
384   {
385    static int counto[2]={0,0};
386
387    if(counto[x]<=0)
388    {
389     if(SPSG[x<<2]&0x40)
390     {
391      if(amplitude[x]<0x3F)
392       amplitude[x]++;
393     }
394     else
395     {
396      if(amplitude[x]>0)
397       amplitude[x]--;
398     }
399     counto[x]=(SPSG[x<<2]&0x3F);
400    }
401    else
402     counto[x]--;
403   }
404 }
405
406 static DECLFR(FDSWaveRead)
407 {
408  return(fdso.cwave[A&0x3f]|(X.DB&0xC0));
409 }
410
411 static DECLFW(FDSWaveWrite)
412 {
413  if(SPSG[0x9]&0x80)
414   fdso.cwave[A&0x3f]=V&0x3F;
415 }
416
417 static INLINE void ClockRise(void)
418 {
419  if(!clockcount)
420  {
421   b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));
422   b17latch76=(SPSG[0x6]|((SPSG[0x07]&0x3)<<8))+b17latch76;
423
424   if(!(SPSG[0x7]&0x80))
425   {
426    b8shiftreg88=(amplitude[1]*((fdso.mwave[(b17latch76>>11)&0x1F]&7)));
427    //b8shiftreg88=((fdso.mwave[(b17latch76>>11)&0x1F]&7))|(amplitude[1]<<3);
428   }
429   else
430   { b8shiftreg88=0;}
431  }
432  else
433  {
434   b19shiftreg60<<=1;  
435   b8shiftreg88>>=1;
436  }
437  b24adder66=(b24latch68+b19shiftreg60)&0xFFFFFF;
438 }
439
440 static INLINE void ClockFall(void)
441 {
442 // if(!(SPSG[0x7]&0x80))
443  {
444   if(!(b8shiftreg88&1))
445    b24latch68=b24adder66;
446  }
447  clockcount=(clockcount+1)&7;
448 }
449
450 static INLINE int32 FDSDoSound(void)
451 {
452  fdso.count+=fdso.cycles;
453  if(fdso.count>=((int64)1<<40))
454  {
455   dogk:
456   fdso.count-=(int64)1<<40;
457   ClockRise();
458   ClockFall();
459  }
460  if(fdso.count>=32768) goto dogk;
461
462  fdso.envcount-=fdso.cycles;
463  if(fdso.envcount<=0)
464  {
465   // Fix this?
466   fdso.envcount+=((int64)1<<40)*FDSClock/1024;
467   DoEnv();
468  }
469
470  // Might need to emulate applying the amplitude to the waveform a bit better...
471  return (fdso.cwave[b24latch68>>18]*amplitude[0]);
472 }
473
474 static int32 FBC=0;
475
476 static void RenderSound(void)
477 {
478  int32 end, start;
479  int32 x;
480
481  start=FBC;
482  end=(timestamp<<16)/soundtsinc;
483  if(end<=start)
484   return;
485  FBC=end;
486
487  if(!(SPSG[0x9]&0x80))
488   for(x=start;x<end;x++)
489   {
490    uint32 t=FDSDoSound();
491    Wave[x>>4]+=t>>3;
492   }
493 }
494
495 void FDSSound(int c)
496 {
497   RenderSound();
498   FBC=c;
499 }
500
501 static void FDS_ESI(void)
502 {
503  if(FSettings.SndRate)
504  {
505   fdso.cycles=((int64)1<<40)*FDSClock;
506   fdso.cycles/=FSettings.SndRate OVERSAMPLE;
507  }
508 //  fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate OVERSAMPLE);
509  SetReadHandler(0x4040,0x407f,FDSWaveRead);
510  SetWriteHandler(0x4040,0x407f,FDSWaveWrite);
511  SetWriteHandler(0x4080,0x408A,FDSSWrite);
512  SetReadHandler(0x4090,0x4092,FDSSRead);
513 }
514
515 void FDSSoundReset(void)
516 {
517  memset(&fdso,0,sizeof(fdso));
518  FDS_ESI();
519  GameExpSound.Fill=FDSSound;
520  GameExpSound.RChange=FDS_ESI;
521 }
522
523
524 static DECLFW(FDSWrite4020)
525 {
526         X6502_IRQEnd(FCEU_IQEXT);
527         IRQLatch&=0xFF00;
528         IRQLatch|=V;
529         mapbyte1[0]=V;
530 }
531 static DECLFW(FDSWrite4021)
532 {
533         X6502_IRQEnd(FCEU_IQEXT);
534         IRQLatch&=0xFF;
535         IRQLatch|=V<<8;
536         mapbyte1[1]=V;
537 }
538 static DECLFW(FDSWrite4022)
539 {
540         X6502_IRQEnd(FCEU_IQEXT);
541         IRQCount=IRQLatch;
542         IRQa=V&2;
543         mapbyte1[2]=V;
544 }
545 static DECLFW(FDSWrite4023)
546 {
547         mapbyte1[3]=V;
548 }
549 static DECLFW(FDSWrite4024)
550 {
551         if(InDisk!=255 && !(mapbyte1[5]&0x4) && mapbyte1[3]&0x1)
552         {
553          if(DiskPtr>=0 && DiskPtr<65000)
554          {
555           if(writeskip) writeskip--;
556           else if(DiskPtr>=2)
557           {
558            SideWrite|=1<<InDisk;
559            diskdata[InDisk][DiskPtr-2]=V;
560           }
561          }
562         }
563 }
564 static DECLFW(FDSWrite4025)
565 {
566         if(InDisk!=255)
567         {
568          if(!(V&0x40))
569          {
570           if(mapbyte1[5]&0x40 && !(V&0x10))
571           {
572            DiskSeekIRQ=200;
573            DiskPtr-=2;
574           }
575           if(DiskPtr<0) DiskPtr=0;
576          }
577          if(!(V&0x4)) writeskip=2;
578          if(V&2) {DiskPtr=0;DiskSeekIRQ=200;}
579          if(V&0x40) DiskSeekIRQ=200;
580         }
581         mapbyte1[5]=V;
582         setmirror(((V>>3)&1)^1);
583 }
584 static void FreeFDSMemory(void)
585 {
586  int x;
587
588  for(x=0;x<header[4];x++)
589   if(diskdata[x])
590   {
591    free(diskdata[x]);
592    diskdata[x]=0;
593   }
594 }
595
596 int FDSLoad(char *name, int fp)
597 {
598  FILE *zp;
599  int x;
600
601  FCEU_fseek(fp,0,SEEK_SET);
602  FCEU_fread(header,16,1,fp);
603
604  if(memcmp(header,"FDS\x1a",4)) 
605  {
606   if(!(memcmp(header+1,"*NINTENDO-HVC*",14)))
607   {
608    long t;   
609    t=FCEU_fgetsize(fp);
610    if(t<65500)
611     t=65500;
612    header[4]=t/65500;
613    header[0]=0;
614    FCEU_fseek(fp,0,SEEK_SET);
615   }
616   else
617    return 0; 
618  } 
619
620  if(header[4]>4) header[4]=4;
621  if(!header[4]) header[4]|=1;
622  for(x=0;x<header[4];x++)
623  {
624   diskdata[x]=FCEU_malloc(65500);
625   if(!diskdata[x]) 
626   {
627    int zol;
628    for(zol=0;zol<x;zol++)
629     free(diskdata[zol]);
630    return 0;
631   }
632   FCEU_fread(diskdata[x],1,65500,fp);
633  }
634
635  if(!(zp=fopen(FCEU_MakeFName(FCEUMKF_FDSROM,0,0),"rb"))) 
636  {
637   FCEU_PrintError("FDS BIOS ROM image missing!");
638   FreeFDSMemory();
639   return 0;
640  }
641
642  if(fread(FDSBIOS,1,8192,zp)!=8192)
643  {
644   fclose(zp);
645   FreeFDSMemory();
646   FCEU_PrintError("Error reading FDS BIOS ROM image");
647   return 0;
648  }
649
650  fclose(zp);
651
652  FCEUGameInfo.type=GIT_FDS;
653  strcpy(FDSSaveName,name);
654  GameInterface=FDSGI;
655
656  SelectDisk=0;
657  InDisk=255;
658
659  ResetExState();
660  FDSSoundStateAdd();
661
662  for(x=0;x<header[4];x++)
663  {
664   char temp[5];
665   sprintf(temp,"DDT%d",x);
666   AddExState(diskdata[x],65500,0,temp);
667  }
668
669  AddExState(FDSRAM,32768,0,"FDSR");
670  AddExState(mapbyte1,32,0,"MPBY");
671  AddExState(CHRRAM,8192,0,"CHRR");
672  AddExState(&IRQCount, 4, 1, "IRQC");
673  AddExState(&IRQLatch, 4, 1, "IQL1");
674  AddExState(&IRQa, 1, 0, "IRQA");
675
676
677  ResetCartMapping();
678
679  SetupCartCHRMapping(0,CHRRAM,8192,1);
680  SetupCartMirroring(0,0,0);
681
682  return 1;
683 }
684
685 void FDSClose(void)
686 {
687   int fp;
688   int x;
689
690   fp=FCEU_fopen(FDSSaveName,"wb");
691   if(!fp) return;
692
693   if(header[0])                 // Does it have a 16-byte FWNES-style header?
694    if(FCEU_fwrite(header,1,16,fp)!=16) // Write it out.  Should be nicer than fseek()'ing if the file is compressed.  Hopefully...
695     goto fdswerr;
696
697   for(x=0;x<header[4];x++)
698   {
699    if(FCEU_fwrite(diskdata[x],1,65500,fp)!=65500) 
700    {
701     fdswerr:
702     FCEU_PrintError("Error writing FDS image \"%s\"!",FDSSaveName);
703     FCEU_fclose(fp);
704     return;
705    }
706   }
707   FreeFDSMemory();
708   FCEU_fclose(fp);
709 }