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
151 // asm code needs pages to be set again..
\r
152 for (page=12; page<28; page++) // 0x6000-0xdfff 32K RAM
\r
153 Page[page]=FDSRAM - (page<<11) + ((page-12)<<11);
\r
154 for (; page<32; page++) // 0xe000-0xffff 8K BIOS
\r
155 Page[page]=FDSBIOS - (page<<11) + ((page-28)<<11);
\r
160 void FCEU_FDSInsert(void)
\r
164 FCEU_DispMessage("Not FDS; can't eject disk.");
\r
169 FCEU_DispMessage("Disk %d Side %s Inserted",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
174 FCEU_DispMessage("Disk %d Side %s Ejected",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
179 void FCEU_FDSEject(void)
\r
184 void FCEU_FDSSelect(void)
\r
188 FCEU_DispMessage("Not FDS; can't select disk.");
\r
193 FCEU_DispMessage("Eject disk before selecting.");
\r
196 SelectDisk=((SelectDisk+1)%TotalSides)&3;
\r
197 FCEU_DispMessage("Disk %d Side %c Selected",SelectDisk>>1,(SelectDisk&1)?'B':'A');
\r
200 static void FP_FASTAPASS(1) FDSFix(int a)
\r
202 if(IRQCount && (IRQa&2))
\r
210 IRQCount=IRQLatch=0;
\r
214 //IRQCount=IRQLatch; //0xFFFF;
\r
215 X6502_IRQBegin(FCEU_IQEXT);
\r
216 //printf("IRQ: %d\n",timestamp);
\r
217 // printf("IRQ: %d\n",scanline);
\r
225 if(FDSRegs[5]&0x80)
\r
227 X6502_IRQBegin(FCEU_IQEXT2);
\r
233 static DECLFR(FDSRead4030)
\r
239 if(X.IRQlow&FCEU_IQEXT) ret|=1;
\r
240 if(X.IRQlow&FCEU_IQEXT2) ret|=2;
\r
242 if((nes_registers[4]>>8)&FCEU_IQEXT) ret|=1;
\r
243 if((nes_registers[4]>>8)&FCEU_IQEXT2) ret|=2;
\r
248 X6502_IRQEnd(FCEU_IQEXT);
\r
249 X6502_IRQEnd(FCEU_IQEXT2);
\r
254 static DECLFR(FDSRead4031)
\r
259 z=diskdata[InDisk][DiskPtr];
\r
262 if(DiskPtr<64999) DiskPtr++;
\r
264 X6502_IRQEnd(FCEU_IQEXT2);
\r
269 static DECLFR(FDSRead4032)
\r
277 if(InDisk==255 || !(FDSRegs[5]&1) || (FDSRegs[5]&2))
\r
282 static DECLFR(FDSRead4033)
\r
284 return 0x80; // battery
\r
287 static DECLFW(FDSRAMWrite)
\r
289 (FDSRAM-0x6000)[A]=V;
\r
292 static DECLFR(FDSBIOSRead)
\r
294 return (FDSBIOS-0xE000)[A];
\r
297 static DECLFR(FDSRAMRead)
\r
299 return (FDSRAM-0x6000)[A];
\r
302 /* Begin FDS sound */
\r
304 #define FDSClock (1789772.7272727272727272/2)
\r
307 int64 cycles; // Cycles per PCM sample
\r
308 int64 count; // Cycle counter
\r
309 int64 envcount; // Envelope cycle counter
\r
310 uint32 b19shiftreg60;
\r
314 int32 clockcount; // Counter to divide frequency by 8.
\r
315 uint8 b8shiftreg88; // Modulation register.
\r
316 uint8 amplitude[2]; // Current amplitudes.
\r
320 uint8 mwave[0x20]; // Modulation waveform
\r
321 uint8 cwave[0x40]; // Game-defined waveform(carrier)
\r
325 static FDSSOUND fdso;
\r
327 #define SPSG fdso.SPSG
\r
328 #define b19shiftreg60 fdso.b19shiftreg60
\r
329 #define b24adder66 fdso.b24adder66
\r
330 #define b24latch68 fdso.b24latch68
\r
331 #define b17latch76 fdso.b17latch76
\r
332 #define b8shiftreg88 fdso.b8shiftreg88
\r
333 #define clockcount fdso.clockcount
\r
334 #define amplitude fdso.amplitude
\r
335 #define speedo fdso.speedo
\r
337 void FDSSoundStateAdd(void)
\r
339 AddExState(fdso.cwave,64,0,"WAVE");
\r
340 AddExState(fdso.mwave,32,0,"MWAV");
\r
341 AddExState(amplitude,2,0,"AMPL");
\r
342 AddExState(SPSG,0xB,0,"SPSG");
\r
344 AddExState(&b8shiftreg88,1,0,"B88");
\r
346 AddExState(&clockcount, 4, 1, "CLOC");
\r
347 AddExState(&b19shiftreg60,4,1,"B60");
\r
348 AddExState(&b24adder66,4,1,"B66");
\r
349 AddExState(&b24latch68,4,1,"B68");
\r
350 AddExState(&b17latch76,4,1,"B76");
\r
354 static DECLFR(FDSSRead)
\r
358 case 0x0:return(amplitude[0]|(X.DB&0xC0));
\r
359 case 0x2:return(amplitude[1]|(X.DB&0xC0));
\r
364 static DECLFW(FDSSWrite)
\r
366 if(FSettings.SndRate)
\r
369 if(FSettings.soundq>=1)
\r
379 case 0x4: if(V&0x80)
\r
380 amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F);
\r
382 case 0x5://printf("$%04x:$%02x\n",A,V);
\r
384 case 0x7: b17latch76=0;SPSG[0x5]=0;//printf("$%04x:$%02x\n",A,V);
\r
388 // printf("%d:$%02x, $%02x\n",SPSG[0x5],V,b17latch76);
\r
389 fdso.mwave[SPSG[0x5]&0x1F]=V&0x7;
\r
390 SPSG[0x5]=(SPSG[0x5]+1)&0x1F;
\r
393 //if(A>=0x7 && A!=0x8 && A<=0xF)
\r
394 //if(A==0xA || A==0x9)
\r
395 //printf("$%04x:$%02x\n",A,V);
\r
399 // $4080 - Fundamental wave amplitude data register 92
\r
400 // $4082 - Fundamental wave frequency data register 58
\r
401 // $4083 - Same as $4082($4083 is the upper 4 bits).
\r
403 // $4084 - Modulation amplitude data register 78
\r
404 // $4086 - Modulation frequency data register 72
\r
405 // $4087 - Same as $4086($4087 is the upper 4 bits)
\r
408 static void DoEnv()
\r
413 if(!(SPSG[x<<2]&0x80) && !(SPSG[0x3]&0x40))
\r
415 static int counto[2]={0,0};
\r
419 if(!(SPSG[x<<2]&0x80))
\r
421 if(SPSG[x<<2]&0x40)
\r
423 if(amplitude[x]<0x3F)
\r
432 counto[x]=(SPSG[x<<2]&0x3F);
\r
439 static DECLFR(FDSWaveRead)
\r
441 return(fdso.cwave[A&0x3f]|(X.DB&0xC0));
\r
444 static DECLFW(FDSWaveWrite)
\r
446 //printf("$%04x:$%02x, %d\n",A,V,SPSG[0x9]&0x80);
\r
448 fdso.cwave[A&0x3f]=V&0x3F;
\r
452 static INLINE void ClockRise(void)
\r
458 b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));
\r
459 b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76;
\r
461 if(!(SPSG[0x7]&0x80))
\r
463 int t=fdso.mwave[(b17latch76>>13)&0x1F]&7;
\r
464 int t2=amplitude[1];
\r
470 adj -= (t2 * ((4 - (t&3) ) ));
\r
472 adj += (t2 * ( (t&3) ));
\r
475 if(adj > 0x7F) adj = 0x7F;
\r
476 if(adj < -0x80) adj = -0x80;
\r
477 //if(adj) printf("%d ",adj);
\r
478 b8shiftreg88=0x80 + adj;
\r
490 // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF;
\r
491 b24adder66=(b24latch68+b19shiftreg60)&0x1FFFFFF;
\r
494 static INLINE void ClockFall(void)
\r
496 //if(!(SPSG[0x7]&0x80))
\r
498 if((b8shiftreg88&1)) // || clockcount==7)
\r
499 b24latch68=b24adder66;
\r
501 clockcount=(clockcount+1)&7;
\r
504 static INLINE int32 FDSDoSound(int32 mul)
\r
506 fdso.count+=fdso.cycles;
\r
507 if(fdso.count>=((int64)1<<40))
\r
510 fdso.count-=(int64)1<<40;
\r
514 if(fdso.envcount<=0)
\r
516 fdso.envcount+=SPSG[0xA]*3;
\r
520 if(fdso.count>=32768) goto dogk;
\r
522 // Might need to emulate applying the amplitude to the waveform a bit better...
\r
525 int k=amplitude[0];
\r
527 return (fdso.cwave[b24latch68>>19]*k)*4/((SPSG[0x9]&0x3)+2);
\r
530 return (fdso.cwave[b24latch68>>19]*mul)>>16;
\r
533 static int32 FBC=0;
\r
535 static void RenderSound(void)
\r
542 end=(SOUNDTS<<16)/soundtsinc;
\r
547 // hack for performance, might produce bad results..
\r
551 switch (SPSG[0x9]&0x3)
\r
553 default:mul = (4<<16)/2;
\r
554 case 1: mul = (4<<16)/3;
\r
555 case 2: mul = (4<<16)/4;
\r
556 case 3: mul = (4<<16)/5;
\r
559 int k=amplitude[0];
\r
564 if(!(SPSG[0x9]&0x80))
\r
565 for(x=start;x<end;x++)
\r
567 uint32 t=FDSDoSound(mul);
\r
570 Wave[x>>4]+=t; //(t>>2)-(t>>3); //>>3;
\r
575 static void RenderSoundHQ(void)
\r
579 if(!(SPSG[0x9]&0x80))
\r
580 for(x=FBC;x<SOUNDTS;x++)
\r
582 uint32 t=FDSDoSound();
\r
584 WaveHi[x]+=t; //(t<<2)-(t<<1);
\r
590 static void HQSync(int32 ts)
\r
595 void FDSSound(int c)
\r
602 static DECLFR(FDSBIOSPatch)
\r
606 X.X=FDSRead4031(0x4031);
\r
607 FDSWrite(0x4024,X.A);
\r
619 static void FDS_ESI(void)
\r
621 if(FSettings.SndRate)
\r
624 if(FSettings.soundq>=1)
\r
626 fdso.cycles=(int64)1<<39;
\r
631 fdso.cycles=((int64)1<<40)*FDSClock;
\r
632 fdso.cycles/=FSettings.SndRate *16;
\r
635 // fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate *16);
\r
636 SetReadHandler(0x4040,0x407f,FDSWaveRead);
\r
637 SetWriteHandler(0x4040,0x407f,FDSWaveWrite);
\r
638 SetWriteHandler(0x4080,0x408A,FDSSWrite);
\r
639 SetReadHandler(0x4090,0x4092,FDSSRead);
\r
641 //SetReadHandler(0xE7A3,0xE7A3,FDSBIOSPatch);
\r
644 void FDSSoundReset(void)
\r
646 memset(&fdso,0,sizeof(fdso));
\r
648 GameExpSound.HiSync=HQSync;
\r
649 GameExpSound.HiFill=0;//RenderSoundHQ;
\r
650 GameExpSound.Fill=FDSSound;
\r
651 GameExpSound.RChange=FDS_ESI;
\r
654 static DECLFW(FDSWrite)
\r
656 //extern int scanline;
\r
657 //FCEU_printf("$%04x:$%02x, %d\n",A,V,scanline);
\r
661 X6502_IRQEnd(FCEU_IQEXT);
\r
664 // printf("$%04x:$%02x\n",A,V);
\r
667 X6502_IRQEnd(FCEU_IQEXT);
\r
670 // printf("$%04x:$%02x\n",A,V);
\r
673 X6502_IRQEnd(FCEU_IQEXT);
\r
676 // printf("$%04x:$%02x\n",A,V);
\r
680 if(InDisk!=255 && !(FDSRegs[5]&0x4) && (FDSRegs[3]&0x1))
\r
682 if(DiskPtr>=0 && DiskPtr<65500)
\r
684 if(writeskip) writeskip--;
\r
685 else if(DiskPtr>=2)
\r
688 diskdata[InDisk][DiskPtr-2]=V;
\r
694 X6502_IRQEnd(FCEU_IQEXT2);
\r
699 if(FDSRegs[5]&0x40 && !(V&0x10))
\r
704 if(DiskPtr<0) DiskPtr=0;
\r
706 if(!(V&0x4)) writeskip=2;
\r
707 if(V&2) {DiskPtr=0;DiskSeekIRQ=200;}
\r
708 if(V&0x40) DiskSeekIRQ=200;
\r
710 setmirror(((V>>3)&1)^1);
\r
716 static void FreeFDSMemory(void)
\r
720 for(x=0;x<TotalSides;x++)
\r
728 static int SubLoad(int fp)
\r
730 struct md5_context md5;
\r
734 FCEU_fread(header,16,1,fp);
\r
736 if(memcmp(header,"FDS\x1a",4))
\r
738 if(!(memcmp(header+1,"*NINTENDO-HVC*",14)))
\r
741 t=FCEU_fgetsize(fp);
\r
744 TotalSides=t/65500;
\r
745 FCEU_fseek(fp,0,SEEK_SET);
\r
751 TotalSides=header[4];
\r
755 if(TotalSides>8) TotalSides=8;
\r
756 if(TotalSides<1) TotalSides=1;
\r
758 for(x=0;x<TotalSides;x++)
\r
760 diskdata[x]=(uint8 *)FCEU_malloc(65500);
\r
764 for(zol=0;zol<x;zol++)
\r
765 free(diskdata[zol]);
\r
768 FCEU_fread(diskdata[x],1,65500,fp);
\r
769 md5_update(&md5,diskdata[x],65500);
\r
771 md5_finish(&md5,FCEUGameInfo.MD5);
\r
775 static void PreSave(void)
\r
780 for(x=0;x<TotalSides;x++)
\r
783 for(b=0; b<65500; b++)
\r
784 diskdata[x][b] ^= diskdatao[x][b];
\r
788 static void PostSave(void)
\r
793 for(x=0;x<TotalSides;x++)
\r
797 for(b=0; b<65500; b++)
\r
798 diskdata[x][b] ^= diskdatao[x][b];
\r
803 int FDSLoad(const char *name, int fp)
\r
809 FCEU_fseek(fp,0,SEEK_SET);
\r
815 fn = FCEU_MakeFName(FCEUMKF_FDSROM,0,0);
\r
817 if(!(zp=FCEUD_UTF8fopen(fn,"rb")))
\r
819 FCEU_PrintError("FDS BIOS ROM image missing!");
\r
822 LoadGameLastError = 10;
\r
828 if(fread(FDSBIOS,1,8192,zp)!=8192)
\r
832 FCEU_PrintError("Error reading FDS BIOS ROM image.");
\r
833 LoadGameLastError = 10;
\r
842 char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
845 for(x=0;x<TotalSides;x++)
\r
847 diskdatao[x]=(uint8 *)FCEU_malloc(65500);
\r
848 memcpy(diskdatao[x],diskdata[x],65500);
\r
851 if((tp=FCEU_fopen(fn,"rb")))
\r
856 FCEU_PrintError("Error reading auxillary FDS file.");
\r
858 LoadGameLastError = 11;
\r
862 DiskWritten=1; /* For save state handling. */
\r
867 FCEUGameInfo.type=GIT_FDS;
\r
868 GameInterface=FDSGI;
\r
873 ResetExState(PreSave,PostSave);
\r
874 FDSSoundStateAdd();
\r
876 for(x=0;x<TotalSides;x++)
\r
879 sprintf(temp,"DDT%d",x);
\r
880 AddExState(diskdata[x],65500,0,temp);
\r
883 AddExState(FDSRAM,32768,0,"FDSR");
\r
884 AddExState(FDSRegs,sizeof(FDSRegs),0,"FREG");
\r
885 AddExState(CHRRAM,8192,0,"CHRR");
\r
886 AddExState(&IRQCount, 4, 1, "IRQC");
\r
887 AddExState(&IRQLatch, 4, 1, "IQL1");
\r
888 AddExState(&IRQa, 1, 0, "IRQA");
\r
889 AddExState(&writeskip,1,0,"WSKI");
\r
890 AddExState(&DiskPtr,4,1,"DPTR");
\r
891 AddExState(&DiskSeekIRQ,4,1,"DSIR");
\r
892 AddExState(&SelectDisk,1,0,"SELD");
\r
893 AddExState(&InDisk,1,0,"INDI");
\r
894 AddExState(&DiskWritten,1,0,"DSKW");
\r
896 ResetCartMapping();
\r
897 SetupCartCHRMapping(0,CHRRAM,8192,1);
\r
898 SetupCartMirroring(0,0,0);
\r
899 memset(CHRRAM,0,8192);
\r
900 memset(FDSRAM,0,32768);
\r
902 FCEU_printf(" Sides: %d\n\n",TotalSides);
\r
904 FCEUI_SetVidSystem(0);
\r
909 void FDSClose(void)
\r
913 char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
915 if(!DiskWritten) return;
\r
917 if(!(fp=FCEUD_UTF8fopen(fn,"wb")))
\r
924 for(x=0;x<TotalSides;x++)
\r
926 if(fwrite(diskdata[x],1,65500,fp)!=65500)
\r
928 FCEU_PrintError("Error saving FDS image!");
\r