/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2002 Xodnizel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "types.h" #include "x6502.h" #include "fceu.h" #include "fds.h" #include "sound.h" #include "general.h" #include "state.h" #include "file.h" #include "memory.h" #include "cart.h" #include "md5.h" #include "netplay.h" /* TODO: Add code to put a delay in between the time a disk is inserted and the when it can be successfully read/written to. This should prevent writes to wrong places OR add code to prevent disk ejects when the virtual motor is on(mmm...virtual motor). */ static DECLFR(FDSRead4030); static DECLFR(FDSRead4031); static DECLFR(FDSRead4032); static DECLFR(FDSRead4033); static DECLFW(FDSWrite); static DECLFW(FDSWaveWrite); static DECLFR(FDSWaveRead); static DECLFR(FDSSRead); static DECLFW(FDSSWrite); static DECLFR(FDSBIOSRead); static DECLFR(FDSRAMRead); static DECLFW(FDSRAMWrite); static void FDSInit(void); static void FP_FASTAPASS(1) FDSFix(int a); #define FDSRAM GameMemBlock #define CHRRAM (GameMemBlock+32768) static uint8 FDSRegs[6]; static int32 IRQLatch,IRQCount; static uint8 IRQa; static void FDSClose(void); uint8 FDSBIOS[8192]; /* Original disk data backup, to help in creating save states. */ static uint8 *diskdatao[8]={0,0,0,0,0,0,0,0}; static uint8 *diskdata[8]={0,0,0,0,0,0,0,0}; static unsigned int TotalSides; static uint8 DiskWritten=0; /* Set to 1 if disk was written to. */ static uint8 writeskip; static uint32 DiskPtr; static int32 DiskSeekIRQ; static uint8 SelectDisk,InDisk; #define DC_INC 1 void FDSGI(int h) { switch(h) { case GI_CLOSE: FDSClose();break; case GI_POWER: FDSInit();break; } } static void FDSStateRestore(int version) { int x; setmirror(((FDSRegs[5]&8)>>3)^1); if(version >= 9810) for(x=0;x>1,(SelectDisk&1)?"B":"A"); InDisk=SelectDisk; } else { FCEU_DispMessage("Disk %d Side %s Ejected",SelectDisk>>1,(SelectDisk&1)?"B":"A"); InDisk=255; } } void FCEU_FDSEject(void) { InDisk=255; } void FCEU_FDSSelect(void) { if(InDisk!=255) { FCEU_DispMessage("Eject disk before selecting."); return; } SelectDisk=((SelectDisk+1)%TotalSides)&3; FCEU_DispMessage("Disk %d Side %s Selected",SelectDisk>>1,(SelectDisk&1)?"B":"A"); } static void FP_FASTAPASS(1) FDSFix(int a) { if((IRQa&2) && IRQCount) { IRQCount-=a; if(IRQCount<=0) { if(!(IRQa&1)) { IRQa&=~2; IRQCount=IRQLatch=0; } else IRQCount=IRQLatch; //IRQCount=IRQLatch; //0xFFFF; X6502_IRQBegin(FCEU_IQEXT); //printf("IRQ: %d\n",timestamp); // printf("IRQ: %d\n",scanline); } } if(DiskSeekIRQ>0) { DiskSeekIRQ-=a; if(DiskSeekIRQ<=0) { if(FDSRegs[5]&0x80) { X6502_IRQBegin(FCEU_IQEXT2); } } } } static DECLFR(FDSRead4030) { uint8 ret=0; /* Cheap hack. */ if(X.IRQlow&FCEU_IQEXT) ret|=1; if(X.IRQlow&FCEU_IQEXT2) ret|=2; if(!fceuindbg) { X6502_IRQEnd(FCEU_IQEXT); X6502_IRQEnd(FCEU_IQEXT2); } return ret; } static DECLFR(FDSRead4031) { static uint8 z=0; if(InDisk!=255) { z=diskdata[InDisk][DiskPtr]; if(!fceuindbg) { if(DiskPtr<64999) DiskPtr++; DiskSeekIRQ=150; X6502_IRQEnd(FCEU_IQEXT2); } } return z; } static DECLFR(FDSRead4032) { uint8 ret; ret=X.DB&~7; if(InDisk==255) ret|=5; if(InDisk==255 || !(FDSRegs[5]&1) || (FDSRegs[5]&2)) ret|=2; return ret; } static DECLFR(FDSRead4033) { return 0x80; // battery } static DECLFW(FDSRAMWrite) { (FDSRAM-0x6000)[A]=V; } static DECLFR(FDSBIOSRead) { return (FDSBIOS-0xE000)[A]; } static DECLFR(FDSRAMRead) { return (FDSRAM-0x6000)[A]; } /* Begin FDS sound */ #define FDSClock (1789772.7272727272727272/2) typedef struct { int64 cycles; // Cycles per PCM sample int64 count; // Cycle counter int64 envcount; // Envelope cycle counter uint32 b19shiftreg60; uint32 b24adder66; uint32 b24latch68; uint32 b17latch76; int32 clockcount; // Counter to divide frequency by 8. uint8 b8shiftreg88; // Modulation register. uint8 amplitude[2]; // Current amplitudes. uint8 speedo[2]; uint8 mwcount; uint8 mwstart; uint8 mwave[0x20]; // Modulation waveform uint8 cwave[0x40]; // Game-defined waveform(carrier) uint8 SPSG[0xB]; } FDSSOUND; static FDSSOUND fdso; #define SPSG fdso.SPSG #define b19shiftreg60 fdso.b19shiftreg60 #define b24adder66 fdso.b24adder66 #define b24latch68 fdso.b24latch68 #define b17latch76 fdso.b17latch76 #define b8shiftreg88 fdso.b8shiftreg88 #define clockcount fdso.clockcount #define amplitude fdso.amplitude #define speedo fdso.speedo void FDSSoundStateAdd(void) { AddExState(fdso.cwave,64,0,"WAVE"); AddExState(fdso.mwave,32,0,"MWAV"); AddExState(amplitude,2,0,"AMPL"); AddExState(SPSG,0xB,0,"SPSG"); AddExState(&b8shiftreg88,1,0,"B88"); AddExState(&clockcount, 4, 1, "CLOC"); AddExState(&b19shiftreg60,4,1,"B60"); AddExState(&b24adder66,4,1,"B66"); AddExState(&b24latch68,4,1,"B68"); AddExState(&b17latch76,4,1,"B76"); } static DECLFR(FDSSRead) { switch(A&0xF) { case 0x0:return(amplitude[0]|(X.DB&0xC0)); case 0x2:return(amplitude[1]|(X.DB&0xC0)); } return(X.DB); } static DECLFW(FDSSWrite) { if(FSettings.SndRate) { if(FSettings.soundq>=1) RenderSoundHQ(); else RenderSound(); } A-=0x4080; switch(A) { case 0x0: case 0x4: if(V&0x80) amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F); break; case 0x5://printf("$%04x:$%02x\n",A,V); break; case 0x7: b17latch76=0;SPSG[0x5]=0;//printf("$%04x:$%02x\n",A,V); break; case 0x8: b17latch76=0; // printf("%d:$%02x, $%02x\n",SPSG[0x5],V,b17latch76); fdso.mwave[SPSG[0x5]&0x1F]=V&0x7; SPSG[0x5]=(SPSG[0x5]+1)&0x1F; break; } //if(A>=0x7 && A!=0x8 && A<=0xF) //if(A==0xA || A==0x9) //printf("$%04x:$%02x\n",A,V); SPSG[A]=V; } // $4080 - Fundamental wave amplitude data register 92 // $4082 - Fundamental wave frequency data register 58 // $4083 - Same as $4082($4083 is the upper 4 bits). // $4084 - Modulation amplitude data register 78 // $4086 - Modulation frequency data register 72 // $4087 - Same as $4086($4087 is the upper 4 bits) static void DoEnv() { int x; for(x=0;x<2;x++) if(!(SPSG[x<<2]&0x80) && !(SPSG[0x3]&0x40)) { static int counto[2]={0,0}; if(counto[x]<=0) { if(!(SPSG[x<<2]&0x80)) { if(SPSG[x<<2]&0x40) { if(amplitude[x]<0x3F) amplitude[x]++; } else { if(amplitude[x]>0) amplitude[x]--; } } counto[x]=(SPSG[x<<2]&0x3F); } else counto[x]--; } } static DECLFR(FDSWaveRead) { return(fdso.cwave[A&0x3f]|(X.DB&0xC0)); } static DECLFW(FDSWaveWrite) { //printf("$%04x:$%02x, %d\n",A,V,SPSG[0x9]&0x80); if(SPSG[0x9]&0x80) fdso.cwave[A&0x3f]=V&0x3F; } static int ta; static INLINE void ClockRise(void) { if(!clockcount) { ta++; b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8)); b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76; if(!(SPSG[0x7]&0x80)) { int t=fdso.mwave[(b17latch76>>13)&0x1F]&7; int t2=amplitude[1]; int adj = 0; if((t&3)) { if((t&4)) adj -= (t2 * ((4 - (t&3) ) )); else adj += (t2 * ( (t&3) )); } adj *= 2; if(adj > 0x7F) adj = 0x7F; if(adj < -0x80) adj = -0x80; //if(adj) printf("%d ",adj); b8shiftreg88=0x80 + adj; } else { b8shiftreg88=0x80; } } else { b19shiftreg60<<=1; b8shiftreg88>>=1; } // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF; b24adder66=(b24latch68+b19shiftreg60)&0x1FFFFFF; } static INLINE void ClockFall(void) { //if(!(SPSG[0x7]&0x80)) { if((b8shiftreg88&1)) // || clockcount==7) b24latch68=b24adder66; } clockcount=(clockcount+1)&7; } static INLINE int32 FDSDoSound(void) { fdso.count+=fdso.cycles; if(fdso.count>=((int64)1<<40)) { dogk: fdso.count-=(int64)1<<40; ClockRise(); ClockFall(); fdso.envcount--; if(fdso.envcount<=0) { fdso.envcount+=SPSG[0xA]*3; DoEnv(); } } if(fdso.count>=32768) goto dogk; // Might need to emulate applying the amplitude to the waveform a bit better... { int k=amplitude[0]; if(k>0x20) k=0x20; return (fdso.cwave[b24latch68>>19]*k)*4/((SPSG[0x9]&0x3)+2); } } static int32 FBC=0; static void RenderSound(void) { int32 end, start; int32 x; start=FBC; end=(SOUNDTS<<16)/soundtsinc; if(end<=start) return; FBC=end; if(!(SPSG[0x9]&0x80)) for(x=start;x>1; t>>=4; Wave[x>>4]+=t; //(t>>2)-(t>>3); //>>3; } } static void RenderSoundHQ(void) { int32 x; if(!(SPSG[0x9]&0x80)) for(x=FBC;x>1; WaveHi[x]+=t; //(t<<2)-(t<<1); } FBC=SOUNDTS; } static void HQSync(int32 ts) { FBC=ts; } void FDSSound(int c) { RenderSound(); FBC=c; } /* static DECLFR(FDSBIOSPatch) { if(FDSRegs[5]&0x4) { X.X=FDSRead4031(0x4031); FDSWrite(0x4024,X.A); X.A=X.X; return(0x60); } else { return(0x58); //puts("Write"); } } */ static void FDS_ESI(void) { if(FSettings.SndRate) { if(FSettings.soundq>=1) { fdso.cycles=(int64)1<<39; } else { fdso.cycles=((int64)1<<40)*FDSClock; fdso.cycles/=FSettings.SndRate *16; } } // fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate *16); SetReadHandler(0x4040,0x407f,FDSWaveRead); SetWriteHandler(0x4040,0x407f,FDSWaveWrite); SetWriteHandler(0x4080,0x408A,FDSSWrite); SetReadHandler(0x4090,0x4092,FDSSRead); //SetReadHandler(0xE7A3,0xE7A3,FDSBIOSPatch); } void FDSSoundReset(void) { memset(&fdso,0,sizeof(fdso)); FDS_ESI(); GameExpSound.HiSync=HQSync; GameExpSound.HiFill=RenderSoundHQ; GameExpSound.Fill=FDSSound; GameExpSound.RChange=FDS_ESI; } static DECLFW(FDSWrite) { //extern int scanline; //FCEU_printf("$%04x:$%02x, %d\n",A,V,scanline); switch(A) { case 0x4020: X6502_IRQEnd(FCEU_IQEXT); IRQLatch&=0xFF00; IRQLatch|=V; // printf("$%04x:$%02x\n",A,V); break; case 0x4021: X6502_IRQEnd(FCEU_IQEXT); IRQLatch&=0xFF; IRQLatch|=V<<8; // printf("$%04x:$%02x\n",A,V); break; case 0x4022: X6502_IRQEnd(FCEU_IQEXT); IRQCount=IRQLatch; IRQa=V&3; // printf("$%04x:$%02x\n",A,V); break; case 0x4023:break; case 0x4024: if(InDisk!=255 && !(FDSRegs[5]&0x4) && (FDSRegs[3]&0x1)) { if(DiskPtr>=0 && DiskPtr<65500) { if(writeskip) writeskip--; else if(DiskPtr>=2) { DiskWritten=1; diskdata[InDisk][DiskPtr-2]=V; } } } break; case 0x4025: X6502_IRQEnd(FCEU_IQEXT2); if(InDisk!=255) { if(!(V&0x40)) { if(FDSRegs[5]&0x40 && !(V&0x10)) { DiskSeekIRQ=200; DiskPtr-=2; } if(DiskPtr<0) DiskPtr=0; } if(!(V&0x4)) writeskip=2; if(V&2) {DiskPtr=0;DiskSeekIRQ=200;} if(V&0x40) DiskSeekIRQ=200; } setmirror(((V>>3)&1)^1); break; } FDSRegs[A&7]=V; } static void FreeFDSMemory(void) { int x; for(x=0;x8) TotalSides=8; if(TotalSides<1) TotalSides=1; for(x=0;xMD5); return(1); } static void PreSave(void) { int x; //if(DiskWritten) for(x=0;xtype=GIT_FDS; GameInterface=FDSGI; SelectDisk=0; InDisk=255; ResetExState(PreSave,PostSave); FDSSoundStateAdd(); for(x=0;x