1 /* FCE Ultra - NES/Famicom Emulator
\r
3 * Copyright notice for this file:
\r
4 * Copyright (C) 2002 Xodnizel
\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
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
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
30 #include "general.h"
\r
36 #include "netplay.h"
\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
46 static DECLFR(FDSRead4030);
\r
47 static DECLFR(FDSRead4031);
\r
48 static DECLFR(FDSRead4032);
\r
49 static DECLFR(FDSRead4033);
\r
51 static DECLFW(FDSWrite);
\r
53 static DECLFW(FDSWaveWrite);
\r
54 static DECLFR(FDSWaveRead);
\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
64 #define FDSRAM GameMemBlock
\r
65 #define CHRRAM (GameMemBlock+32768)
\r
67 static uint8 FDSRegs[6];
\r
68 static int32 IRQLatch,IRQCount;
\r
70 static void FDSClose(void);
\r
72 static uint8 FDSBIOS[8192];
\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
77 static uint8 *diskdata[8]={0,0,0,0,0,0,0,0};
\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
92 case GI_CLOSE: FDSClose();break;
\r
93 case GI_POWER: FDSInit();break;
\r
97 static void FDSStateRestore(int version)
\r
101 setmirror(((FDSRegs[5]&8)>>3)^1);
\r
103 if(version >= 9810)
\r
104 for(x=0;x<TotalSides;x++)
\r
107 for(b=0; b<65500; b++)
\r
108 diskdata[x][b] ^= diskdatao[x][b];
\r
114 void FDSSoundReset(void);
\r
115 void FDSSoundStateAdd(void);
\r
116 static void RenderSound(void);
\r
117 //static void RenderSoundHQ(void);
\r
119 static void FDSInit(void)
\r
121 memset(FDSRegs,0,sizeof(FDSRegs));
\r
122 writeskip=DiskPtr=DiskSeekIRQ=0;
\r
125 setprg8r(0,0xe000,0); // BIOS
\r
126 setprg32r(1,0x6000,0); // 32KB RAM
\r
127 setchr8(0); // 8KB CHR RAM
\r
130 GameStateRestore=FDSStateRestore;
\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
137 SetWriteHandler(0x4020,0x4025,FDSWrite);
\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
149 void FCEU_FDSInsert(void)
\r
153 FCEU_DispMessage("Not FDS; can't eject disk.");
\r
158 FCEU_DispMessage("Disk %d Side %s Inserted",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
163 FCEU_DispMessage("Disk %d Side %s Ejected",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
168 void FCEU_FDSEject(void)
\r
173 void FCEU_FDSSelect(void)
\r
177 FCEU_DispMessage("Not FDS; can't select disk.");
\r
182 FCEU_DispMessage("Eject disk before selecting.");
\r
185 SelectDisk=((SelectDisk+1)%TotalSides)&3;
\r
186 FCEU_DispMessage("Disk %d Side %c Selected",SelectDisk>>1,(SelectDisk&1)?'B':'A');
\r
189 static void FP_FASTAPASS(1) FDSFix(int a)
\r
191 if((IRQa&2) && IRQCount)
\r
199 IRQCount=IRQLatch=0;
\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
214 if(FDSRegs[5]&0x80)
\r
216 X6502_IRQBegin(FCEU_IQEXT2);
\r
222 static DECLFR(FDSRead4030)
\r
227 if(X.IRQlow&FCEU_IQEXT) ret|=1;
\r
228 if(X.IRQlow&FCEU_IQEXT2) ret|=2;
\r
232 X6502_IRQEnd(FCEU_IQEXT);
\r
233 X6502_IRQEnd(FCEU_IQEXT2);
\r
238 static DECLFR(FDSRead4031)
\r
243 z=diskdata[InDisk][DiskPtr];
\r
246 if(DiskPtr<64999) DiskPtr++;
\r
248 X6502_IRQEnd(FCEU_IQEXT2);
\r
253 static DECLFR(FDSRead4032)
\r
261 if(InDisk==255 || !(FDSRegs[5]&1) || (FDSRegs[5]&2))
\r
266 static DECLFR(FDSRead4033)
\r
268 return 0x80; // battery
\r
271 static DECLFW(FDSRAMWrite)
\r
273 (FDSRAM-0x6000)[A]=V;
\r
276 static DECLFR(FDSBIOSRead)
\r
278 return (FDSBIOS-0xE000)[A];
\r
281 static DECLFR(FDSRAMRead)
\r
283 return (FDSRAM-0x6000)[A];
\r
286 /* Begin FDS sound */
\r
288 #define FDSClock (1789772.7272727272727272/2)
\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
298 int32 clockcount; // Counter to divide frequency by 8.
\r
299 uint8 b8shiftreg88; // Modulation register.
\r
300 uint8 amplitude[2]; // Current amplitudes.
\r
304 uint8 mwave[0x20]; // Modulation waveform
\r
305 uint8 cwave[0x40]; // Game-defined waveform(carrier)
\r
309 static FDSSOUND fdso;
\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
321 void FDSSoundStateAdd(void)
\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
328 AddExState(&b8shiftreg88,1,0,"B88");
\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
338 static DECLFR(FDSSRead)
\r
342 case 0x0:return(amplitude[0]|(X.DB&0xC0));
\r
343 case 0x2:return(amplitude[1]|(X.DB&0xC0));
\r
348 static DECLFW(FDSSWrite)
\r
350 if(FSettings.SndRate)
\r
353 if(FSettings.soundq>=1)
\r
363 case 0x4: if(V&0x80)
\r
364 amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F);
\r
366 case 0x5://printf("$%04x:$%02x\n",A,V);
\r
368 case 0x7: b17latch76=0;SPSG[0x5]=0;//printf("$%04x:$%02x\n",A,V);
\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
377 //if(A>=0x7 && A!=0x8 && A<=0xF)
\r
378 //if(A==0xA || A==0x9)
\r
379 //printf("$%04x:$%02x\n",A,V);
\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
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
392 static void DoEnv()
\r
397 if(!(SPSG[x<<2]&0x80) && !(SPSG[0x3]&0x40))
\r
399 static int counto[2]={0,0};
\r
403 if(!(SPSG[x<<2]&0x80))
\r
405 if(SPSG[x<<2]&0x40)
\r
407 if(amplitude[x]<0x3F)
\r
416 counto[x]=(SPSG[x<<2]&0x3F);
\r
423 static DECLFR(FDSWaveRead)
\r
425 return(fdso.cwave[A&0x3f]|(X.DB&0xC0));
\r
428 static DECLFW(FDSWaveWrite)
\r
430 //printf("$%04x:$%02x, %d\n",A,V,SPSG[0x9]&0x80);
\r
432 fdso.cwave[A&0x3f]=V&0x3F;
\r
436 static INLINE void ClockRise(void)
\r
442 b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));
\r
443 b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76;
\r
445 if(!(SPSG[0x7]&0x80))
\r
447 int t=fdso.mwave[(b17latch76>>13)&0x1F]&7;
\r
448 int t2=amplitude[1];
\r
454 adj -= (t2 * ((4 - (t&3) ) ));
\r
456 adj += (t2 * ( (t&3) ));
\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
474 // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF;
\r
475 b24adder66=(b24latch68+b19shiftreg60)&0x1FFFFFF;
\r
478 static INLINE void ClockFall(void)
\r
480 //if(!(SPSG[0x7]&0x80))
\r
482 if((b8shiftreg88&1)) // || clockcount==7)
\r
483 b24latch68=b24adder66;
\r
485 clockcount=(clockcount+1)&7;
\r
488 static INLINE int32 FDSDoSound(void)
\r
490 fdso.count+=fdso.cycles;
\r
491 if(fdso.count>=((int64)1<<40))
\r
494 fdso.count-=(int64)1<<40;
\r
498 if(fdso.envcount<=0)
\r
500 fdso.envcount+=SPSG[0xA]*3;
\r
504 if(fdso.count>=32768) goto dogk;
\r
506 // Might need to emulate applying the amplitude to the waveform a bit better...
\r
508 int k=amplitude[0];
\r
510 return (fdso.cwave[b24latch68>>19]*k)*4/((SPSG[0x9]&0x3)+2);
\r
514 static int32 FBC=0;
\r
516 static void RenderSound(void)
\r
522 end=(SOUNDTS<<16)/soundtsinc;
\r
527 if(!(SPSG[0x9]&0x80))
\r
528 for(x=start;x<end;x++)
\r
530 uint32 t=FDSDoSound();
\r
533 Wave[x>>4]+=t; //(t>>2)-(t>>3); //>>3;
\r
538 static void RenderSoundHQ(void)
\r
542 if(!(SPSG[0x9]&0x80))
\r
543 for(x=FBC;x<SOUNDTS;x++)
\r
545 uint32 t=FDSDoSound();
\r
547 WaveHi[x]+=t; //(t<<2)-(t<<1);
\r
553 static void HQSync(int32 ts)
\r
558 void FDSSound(int c)
\r
565 static DECLFR(FDSBIOSPatch)
\r
569 X.X=FDSRead4031(0x4031);
\r
570 FDSWrite(0x4024,X.A);
\r
582 static void FDS_ESI(void)
\r
584 if(FSettings.SndRate)
\r
587 if(FSettings.soundq>=1)
\r
589 fdso.cycles=(int64)1<<39;
\r
594 fdso.cycles=((int64)1<<40)*FDSClock;
\r
595 fdso.cycles/=FSettings.SndRate *16;
\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
604 //SetReadHandler(0xE7A3,0xE7A3,FDSBIOSPatch);
\r
607 void FDSSoundReset(void)
\r
609 memset(&fdso,0,sizeof(fdso));
\r
611 GameExpSound.HiSync=HQSync;
\r
612 GameExpSound.HiFill=0;//RenderSoundHQ;
\r
613 GameExpSound.Fill=FDSSound;
\r
614 GameExpSound.RChange=FDS_ESI;
\r
617 static DECLFW(FDSWrite)
\r
619 //extern int scanline;
\r
620 //FCEU_printf("$%04x:$%02x, %d\n",A,V,scanline);
\r
624 X6502_IRQEnd(FCEU_IQEXT);
\r
627 // printf("$%04x:$%02x\n",A,V);
\r
630 X6502_IRQEnd(FCEU_IQEXT);
\r
633 // printf("$%04x:$%02x\n",A,V);
\r
636 X6502_IRQEnd(FCEU_IQEXT);
\r
639 // printf("$%04x:$%02x\n",A,V);
\r
643 if(InDisk!=255 && !(FDSRegs[5]&0x4) && (FDSRegs[3]&0x1))
\r
645 if(DiskPtr>=0 && DiskPtr<65500)
\r
647 if(writeskip) writeskip--;
\r
648 else if(DiskPtr>=2)
\r
651 diskdata[InDisk][DiskPtr-2]=V;
\r
657 X6502_IRQEnd(FCEU_IQEXT2);
\r
662 if(FDSRegs[5]&0x40 && !(V&0x10))
\r
667 if(DiskPtr<0) DiskPtr=0;
\r
669 if(!(V&0x4)) writeskip=2;
\r
670 if(V&2) {DiskPtr=0;DiskSeekIRQ=200;}
\r
671 if(V&0x40) DiskSeekIRQ=200;
\r
673 setmirror(((V>>3)&1)^1);
\r
679 static void FreeFDSMemory(void)
\r
683 for(x=0;x<TotalSides;x++)
\r
691 static int SubLoad(int fp)
\r
693 struct md5_context md5;
\r
697 FCEU_fread(header,16,1,fp);
\r
699 if(memcmp(header,"FDS\x1a",4))
\r
701 if(!(memcmp(header+1,"*NINTENDO-HVC*",14)))
\r
704 t=FCEU_fgetsize(fp);
\r
707 TotalSides=t/65500;
\r
708 FCEU_fseek(fp,0,SEEK_SET);
\r
714 TotalSides=header[4];
\r
718 if(TotalSides>8) TotalSides=8;
\r
719 if(TotalSides<1) TotalSides=1;
\r
721 for(x=0;x<TotalSides;x++)
\r
723 diskdata[x]=(uint8 *)FCEU_malloc(65500);
\r
727 for(zol=0;zol<x;zol++)
\r
728 free(diskdata[zol]);
\r
731 FCEU_fread(diskdata[x],1,65500,fp);
\r
732 md5_update(&md5,diskdata[x],65500);
\r
734 md5_finish(&md5,FCEUGameInfo.MD5);
\r
738 static void PreSave(void)
\r
743 for(x=0;x<TotalSides;x++)
\r
746 for(b=0; b<65500; b++)
\r
747 diskdata[x][b] ^= diskdatao[x][b];
\r
751 static void PostSave(void)
\r
756 for(x=0;x<TotalSides;x++)
\r
760 for(b=0; b<65500; b++)
\r
761 diskdata[x][b] ^= diskdatao[x][b];
\r
766 int FDSLoad(const char *name, int fp)
\r
772 FCEU_fseek(fp,0,SEEK_SET);
\r
778 fn = FCEU_MakeFName(FCEUMKF_FDSROM,0,0);
\r
780 if(!(zp=FCEUD_UTF8fopen(fn,"rb")))
\r
782 FCEU_PrintError("FDS BIOS ROM image missing!");
\r
790 if(fread(FDSBIOS,1,8192,zp)!=8192)
\r
794 FCEU_PrintError("Error reading FDS BIOS ROM image.");
\r
803 char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
806 for(x=0;x<TotalSides;x++)
\r
808 diskdatao[x]=(uint8 *)FCEU_malloc(65500);
\r
809 memcpy(diskdatao[x],diskdata[x],65500);
\r
812 if((tp=FCEU_fopen(fn,"rb")))
\r
817 FCEU_PrintError("Error reading auxillary FDS file.");
\r
822 DiskWritten=1; /* For save state handling. */
\r
827 FCEUGameInfo.type=GIT_FDS;
\r
828 GameInterface=FDSGI;
\r
833 ResetExState(PreSave,PostSave);
\r
834 FDSSoundStateAdd();
\r
836 for(x=0;x<TotalSides;x++)
\r
839 sprintf(temp,"DDT%d",x);
\r
840 AddExState(diskdata[x],65500,0,temp);
\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
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
862 FCEU_printf(" Sides: %d\n\n",TotalSides);
\r
864 FCEUI_SetVidSystem(0);
\r
869 void FDSClose(void)
\r
873 char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
875 if(!DiskWritten) return;
\r
877 if(!(fp=FCEUD_UTF8fopen(fn,"wb")))
\r
884 for(x=0;x<TotalSides;x++)
\r
886 if(fwrite(diskdata[x],1,65500,fp)!=65500)
\r
888 FCEU_PrintError("Error saving FDS image!");
\r