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
88 void FDSGI(int h, void *param)
\r
92 case GI_CLOSE: FDSClose();break;
\r
93 case GI_POWER: FDSInit();break;
\r
94 case GI_INFOSTRING: sprintf(param, "FDS, Sides: %d", TotalSides);break;
\r
98 static void FDSStateRestore(int version)
\r
102 setmirror(((FDSRegs[5]&8)>>3)^1);
\r
104 if(version >= 9810)
\r
105 for(x=0;x<TotalSides;x++)
\r
108 for(b=0; b<65500; b++)
\r
109 diskdata[x][b] ^= diskdatao[x][b];
\r
115 void FDSSoundReset(void);
\r
116 void FDSSoundStateAdd(void);
\r
117 static void RenderSound(void);
\r
118 //static void RenderSoundHQ(void);
\r
120 static void FDSInit(void)
\r
122 memset(FDSRegs,0,sizeof(FDSRegs));
\r
123 writeskip=DiskPtr=DiskSeekIRQ=0;
\r
126 setprg8r(0,0xe000,0); // BIOS
\r
127 setprg32r(1,0x6000,0); // 32KB RAM
\r
128 setchr8(0); // 8KB CHR RAM
\r
131 GameStateRestore=FDSStateRestore;
\r
133 SetReadHandler(0x4030,0x4030,FDSRead4030);
\r
134 SetReadHandler(0x4031,0x4031,FDSRead4031);
\r
135 SetReadHandler(0x4032,0x4032,FDSRead4032);
\r
136 SetReadHandler(0x4033,0x4033,FDSRead4033);
\r
138 SetWriteHandler(0x4020,0x4025,FDSWrite);
\r
140 SetWriteHandler(0x6000,0xdfff,FDSRAMWrite);
\r
141 SetReadHandler(0x6000,0xdfff,FDSRAMRead);
\r
142 SetReadHandler(0xE000,0xFFFF,FDSBIOSRead);
\r
143 IRQCount=IRQLatch=IRQa=0;
\r
152 // asm code needs pages to be set again..
\r
153 for (page=12; page<28; page++) // 0x6000-0xdfff 32K RAM
\r
154 Page[page]=FDSRAM - (12<<11);
\r
155 for (; page<32; page++) // 0xe000-0xffff 8K BIOS
\r
156 Page[page]=FDSBIOS - (28<<11);
\r
161 void FCEU_FDSInsert(void)
\r
165 FCEU_DispMessage("Not FDS; can't eject disk.");
\r
170 FCEU_DispMessage("Disk %d Side %s Inserted",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
175 FCEU_DispMessage("Disk %d Side %s Ejected",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
180 void FCEU_FDSEject(void)
\r
185 void FCEU_FDSSelect(void)
\r
189 FCEU_DispMessage("Not FDS; can't select disk.");
\r
194 FCEU_DispMessage("Eject disk before selecting.");
\r
197 SelectDisk=((SelectDisk+1)%TotalSides)&3;
\r
198 FCEU_DispMessage("Disk %d Side %c Selected",SelectDisk>>1,(SelectDisk&1)?'B':'A');
\r
201 static void FP_FASTAPASS(1) FDSFix(int a)
\r
203 if(IRQCount && (IRQa&2))
\r
211 IRQCount=IRQLatch=0;
\r
215 //IRQCount=IRQLatch; //0xFFFF;
\r
216 X6502_IRQBegin(FCEU_IQEXT);
\r
217 //printf("IRQ: %d\n",timestamp);
\r
218 // printf("IRQ: %d\n",scanline);
\r
226 if(FDSRegs[5]&0x80)
\r
228 X6502_IRQBegin(FCEU_IQEXT2);
\r
234 static DECLFR(FDSRead4030)
\r
240 if(X.IRQlow&FCEU_IQEXT) ret|=1;
\r
241 if(X.IRQlow&FCEU_IQEXT2) ret|=2;
\r
243 if((nes_registers[4]>>8)&FCEU_IQEXT) ret|=1;
\r
244 if((nes_registers[4]>>8)&FCEU_IQEXT2) ret|=2;
\r
249 X6502_IRQEnd(FCEU_IQEXT);
\r
250 X6502_IRQEnd(FCEU_IQEXT2);
\r
255 static DECLFR(FDSRead4031)
\r
260 z=diskdata[InDisk][DiskPtr];
\r
263 if(DiskPtr<64999) DiskPtr++;
\r
265 X6502_IRQEnd(FCEU_IQEXT2);
\r
270 static DECLFR(FDSRead4032)
\r
278 if(InDisk==255 || !(FDSRegs[5]&1) || (FDSRegs[5]&2))
\r
283 static DECLFR(FDSRead4033)
\r
285 return 0x80; // battery
\r
288 static DECLFW(FDSRAMWrite)
\r
290 (FDSRAM-0x6000)[A]=V;
\r
293 static DECLFR(FDSBIOSRead)
\r
295 return (FDSBIOS-0xE000)[A];
\r
298 static DECLFR(FDSRAMRead)
\r
300 return (FDSRAM-0x6000)[A];
\r
303 /* Begin FDS sound */
\r
305 #define FDSClock (1789772.7272727272727272/2)
\r
308 int64 cycles; // Cycles per PCM sample
\r
309 int64 count; // Cycle counter
\r
310 int64 envcount; // Envelope cycle counter
\r
311 uint32 b19shiftreg60;
\r
315 int32 clockcount; // Counter to divide frequency by 8.
\r
316 uint8 b8shiftreg88; // Modulation register.
\r
317 uint8 amplitude[2]; // Current amplitudes.
\r
321 uint8 mwave[0x20]; // Modulation waveform
\r
322 uint8 cwave[0x40]; // Game-defined waveform(carrier)
\r
326 static FDSSOUND fdso;
\r
328 #define SPSG fdso.SPSG
\r
329 #define b19shiftreg60 fdso.b19shiftreg60
\r
330 #define b24adder66 fdso.b24adder66
\r
331 #define b24latch68 fdso.b24latch68
\r
332 #define b17latch76 fdso.b17latch76
\r
333 #define b8shiftreg88 fdso.b8shiftreg88
\r
334 #define clockcount fdso.clockcount
\r
335 #define amplitude fdso.amplitude
\r
336 #define speedo fdso.speedo
\r
338 void FDSSoundStateAdd(void)
\r
340 AddExState(fdso.cwave,64,0,"WAVE");
\r
341 AddExState(fdso.mwave,32,0,"MWAV");
\r
342 AddExState(amplitude,2,0,"AMPL");
\r
343 AddExState(SPSG,0xB,0,"SPSG");
\r
345 AddExState(&b8shiftreg88,1,0,"B88");
\r
347 AddExState(&clockcount, 4, 1, "CLOC");
\r
348 AddExState(&b19shiftreg60,4,1,"B60");
\r
349 AddExState(&b24adder66,4,1,"B66");
\r
350 AddExState(&b24latch68,4,1,"B68");
\r
351 AddExState(&b17latch76,4,1,"B76");
\r
355 static DECLFR(FDSSRead)
\r
359 case 0x0:return(amplitude[0]|(X.DB&0xC0));
\r
360 case 0x2:return(amplitude[1]|(X.DB&0xC0));
\r
365 static DECLFW(FDSSWrite)
\r
367 if(FSettings.SndRate)
\r
370 if(FSettings.soundq>=1)
\r
380 case 0x4: if(V&0x80)
\r
381 amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F);
\r
383 case 0x5://printf("$%04x:$%02x\n",A,V);
\r
385 case 0x7: b17latch76=0;SPSG[0x5]=0;//printf("$%04x:$%02x\n",A,V);
\r
389 // printf("%d:$%02x, $%02x\n",SPSG[0x5],V,b17latch76);
\r
390 fdso.mwave[SPSG[0x5]&0x1F]=V&0x7;
\r
391 SPSG[0x5]=(SPSG[0x5]+1)&0x1F;
\r
394 //if(A>=0x7 && A!=0x8 && A<=0xF)
\r
395 //if(A==0xA || A==0x9)
\r
396 //printf("$%04x:$%02x\n",A,V);
\r
400 // $4080 - Fundamental wave amplitude data register 92
\r
401 // $4082 - Fundamental wave frequency data register 58
\r
402 // $4083 - Same as $4082($4083 is the upper 4 bits).
\r
404 // $4084 - Modulation amplitude data register 78
\r
405 // $4086 - Modulation frequency data register 72
\r
406 // $4087 - Same as $4086($4087 is the upper 4 bits)
\r
409 static void DoEnv()
\r
414 if(!(SPSG[x<<2]&0x80) && !(SPSG[0x3]&0x40))
\r
416 static int counto[2]={0,0};
\r
420 if(!(SPSG[x<<2]&0x80))
\r
422 if(SPSG[x<<2]&0x40)
\r
424 if(amplitude[x]<0x3F)
\r
433 counto[x]=(SPSG[x<<2]&0x3F);
\r
440 static DECLFR(FDSWaveRead)
\r
442 return(fdso.cwave[A&0x3f]|(X.DB&0xC0));
\r
445 static DECLFW(FDSWaveWrite)
\r
447 //printf("$%04x:$%02x, %d\n",A,V,SPSG[0x9]&0x80);
\r
449 fdso.cwave[A&0x3f]=V&0x3F;
\r
453 static INLINE void ClockRise(void)
\r
459 b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));
\r
460 b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76;
\r
462 if(!(SPSG[0x7]&0x80))
\r
464 int t=fdso.mwave[(b17latch76>>13)&0x1F]&7;
\r
465 int t2=amplitude[1];
\r
471 adj -= (t2 * ((4 - (t&3) ) ));
\r
473 adj += (t2 * ( (t&3) ));
\r
476 if(adj > 0x7F) adj = 0x7F;
\r
477 if(adj < -0x80) adj = -0x80;
\r
478 //if(adj) printf("%d ",adj);
\r
479 b8shiftreg88=0x80 + adj;
\r
491 // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF;
\r
492 b24adder66=(b24latch68+b19shiftreg60)&0x1FFFFFF;
\r
495 static INLINE void ClockFall(void)
\r
497 //if(!(SPSG[0x7]&0x80))
\r
499 if((b8shiftreg88&1)) // || clockcount==7)
\r
500 b24latch68=b24adder66;
\r
502 clockcount=(clockcount+1)&7;
\r
505 static INLINE int32 FDSDoSound(int32 mul)
\r
507 fdso.count+=fdso.cycles;
\r
508 if(fdso.count>=((int64)1<<40))
\r
511 fdso.count-=(int64)1<<40;
\r
515 if(fdso.envcount<=0)
\r
517 fdso.envcount+=SPSG[0xA]*3;
\r
521 if(fdso.count>=32768) goto dogk;
\r
523 // Might need to emulate applying the amplitude to the waveform a bit better...
\r
526 int k=amplitude[0];
\r
528 return (fdso.cwave[b24latch68>>19]*k)*4/((SPSG[0x9]&0x3)+2);
\r
531 return (fdso.cwave[b24latch68>>19]*mul)>>16;
\r
534 static int32 FBC=0;
\r
536 static void RenderSound(void)
\r
543 end=(SOUNDTS<<16)/soundtsinc;
\r
548 // hack for performance, might produce bad results..
\r
552 switch (SPSG[0x9]&0x3)
\r
554 default:mul = (4<<16)/2;
\r
555 case 1: mul = (4<<16)/3;
\r
556 case 2: mul = (4<<16)/4;
\r
557 case 3: mul = (4<<16)/5;
\r
560 int k=amplitude[0];
\r
565 if(!(SPSG[0x9]&0x80))
\r
566 for(x=start;x<end;x++)
\r
568 uint32 t=FDSDoSound(mul);
\r
571 Wave[x>>4]+=t; //(t>>2)-(t>>3); //>>3;
\r
576 static void RenderSoundHQ(void)
\r
580 if(!(SPSG[0x9]&0x80))
\r
581 for(x=FBC;x<SOUNDTS;x++)
\r
583 uint32 t=FDSDoSound();
\r
585 WaveHi[x]+=t; //(t<<2)-(t<<1);
\r
591 static void HQSync(int32 ts)
\r
596 void FDSSound(int c)
\r
603 static DECLFR(FDSBIOSPatch)
\r
607 X.X=FDSRead4031(0x4031);
\r
608 FDSWrite(0x4024,X.A);
\r
620 static void FDS_ESI(void)
\r
622 if(FSettings.SndRate)
\r
625 if(FSettings.soundq>=1)
\r
627 fdso.cycles=(int64)1<<39;
\r
632 fdso.cycles=((int64)1<<40)*FDSClock;
\r
633 fdso.cycles/=FSettings.SndRate *16;
\r
636 // fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate *16);
\r
637 SetReadHandler(0x4040,0x407f,FDSWaveRead);
\r
638 SetWriteHandler(0x4040,0x407f,FDSWaveWrite);
\r
639 SetWriteHandler(0x4080,0x408A,FDSSWrite);
\r
640 SetReadHandler(0x4090,0x4092,FDSSRead);
\r
642 //SetReadHandler(0xE7A3,0xE7A3,FDSBIOSPatch);
\r
645 void FDSSoundReset(void)
\r
647 memset(&fdso,0,sizeof(fdso));
\r
649 GameExpSound.HiSync=HQSync;
\r
650 GameExpSound.HiFill=0;//RenderSoundHQ;
\r
651 GameExpSound.Fill=FDSSound;
\r
652 GameExpSound.RChange=FDS_ESI;
\r
655 static DECLFW(FDSWrite)
\r
657 //extern int scanline;
\r
658 //FCEU_printf("$%04x:$%02x, %d\n",A,V,scanline);
\r
662 X6502_IRQEnd(FCEU_IQEXT);
\r
665 // printf("$%04x:$%02x\n",A,V);
\r
668 X6502_IRQEnd(FCEU_IQEXT);
\r
671 // printf("$%04x:$%02x\n",A,V);
\r
674 X6502_IRQEnd(FCEU_IQEXT);
\r
677 // printf("$%04x:$%02x\n",A,V);
\r
681 if(InDisk!=255 && !(FDSRegs[5]&0x4) && (FDSRegs[3]&0x1))
\r
683 if(DiskPtr>=0 && DiskPtr<65500)
\r
685 if(writeskip) writeskip--;
\r
686 else if(DiskPtr>=2)
\r
689 diskdata[InDisk][DiskPtr-2]=V;
\r
695 X6502_IRQEnd(FCEU_IQEXT2);
\r
700 if(FDSRegs[5]&0x40 && !(V&0x10))
\r
705 if(DiskPtr<0) DiskPtr=0;
\r
707 if(!(V&0x4)) writeskip=2;
\r
708 if(V&2) {DiskPtr=0;DiskSeekIRQ=200;}
\r
709 if(V&0x40) DiskSeekIRQ=200;
\r
711 setmirror(((V>>3)&1)^1);
\r
717 static void FreeFDSMemory(void)
\r
721 for(x=0;x<TotalSides;x++)
\r
729 static int SubLoad(int fp)
\r
731 struct md5_context md5;
\r
735 FCEU_fread(header,16,1,fp);
\r
737 if(memcmp(header,"FDS\x1a",4))
\r
739 if(!(memcmp(header+1,"*NINTENDO-HVC*",14)))
\r
742 t=FCEU_fgetsize(fp);
\r
745 TotalSides=t/65500;
\r
746 FCEU_fseek(fp,0,SEEK_SET);
\r
752 TotalSides=header[4];
\r
756 if(TotalSides>8) TotalSides=8;
\r
757 if(TotalSides<1) TotalSides=1;
\r
759 for(x=0;x<TotalSides;x++)
\r
761 diskdata[x]=(uint8 *)FCEU_malloc(65500);
\r
765 for(zol=0;zol<x;zol++)
\r
766 free(diskdata[zol]);
\r
769 FCEU_fread(diskdata[x],1,65500,fp);
\r
770 md5_update(&md5,diskdata[x],65500);
\r
772 md5_finish(&md5,FCEUGameInfo.MD5);
\r
776 static void PreSave(void)
\r
781 for(x=0;x<TotalSides;x++)
\r
784 for(b=0; b<65500; b++)
\r
785 diskdata[x][b] ^= diskdatao[x][b];
\r
789 static void PostSave(void)
\r
794 for(x=0;x<TotalSides;x++)
\r
798 for(b=0; b<65500; b++)
\r
799 diskdata[x][b] ^= diskdatao[x][b];
\r
804 int FDSLoad(const char *name, int fp)
\r
810 FCEU_fseek(fp,0,SEEK_SET);
\r
816 fn = FCEU_MakeFName(FCEUMKF_FDSROM,0,0);
\r
818 if(!(zp=FCEUD_UTF8fopen(fn,"rb")))
\r
820 FCEU_PrintError("FDS BIOS ROM image missing!");
\r
823 LoadGameLastError = 10;
\r
829 if(fread(FDSBIOS,1,8192,zp)!=8192)
\r
833 FCEU_PrintError("Error reading FDS BIOS ROM image.");
\r
834 LoadGameLastError = 10;
\r
843 char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
846 for(x=0;x<TotalSides;x++)
\r
848 diskdatao[x]=(uint8 *)FCEU_malloc(65500);
\r
849 memcpy(diskdatao[x],diskdata[x],65500);
\r
852 if((tp=FCEU_fopen(fn,"rb")))
\r
857 FCEU_PrintError("Error reading auxillary FDS file.");
\r
859 LoadGameLastError = 11;
\r
863 DiskWritten=1; /* For save state handling. */
\r
868 FCEUGameInfo.type=GIT_FDS;
\r
869 GameInterface=FDSGI;
\r
874 ResetExState(PreSave,PostSave);
\r
875 FDSSoundStateAdd();
\r
877 for(x=0;x<TotalSides;x++)
\r
880 sprintf(temp,"DDT%d",x);
\r
881 AddExState(diskdata[x],65500,0,temp);
\r
884 AddExState(FDSRAM,32768,0,"FDSR");
\r
885 AddExState(FDSRegs,sizeof(FDSRegs),0,"FREG");
\r
886 AddExState(CHRRAM,8192,0,"CHRR");
\r
887 AddExState(&IRQCount, 4, 1, "IRQC");
\r
888 AddExState(&IRQLatch, 4, 1, "IQL1");
\r
889 AddExState(&IRQa, 1, 0, "IRQA");
\r
890 AddExState(&writeskip,1,0,"WSKI");
\r
891 AddExState(&DiskPtr,4,1,"DPTR");
\r
892 AddExState(&DiskSeekIRQ,4,1,"DSIR");
\r
893 AddExState(&SelectDisk,1,0,"SELD");
\r
894 AddExState(&InDisk,1,0,"INDI");
\r
895 AddExState(&DiskWritten,1,0,"DSKW");
\r
897 ResetCartMapping();
\r
898 SetupCartCHRMapping(0,CHRRAM,8192,1);
\r
899 SetupCartMirroring(0,0,0);
\r
900 memset(CHRRAM,0,8192);
\r
901 memset(FDSRAM,0,32768);
\r
903 FCEU_printf(" Sides: %d\n\n",TotalSides);
\r
905 FCEUI_SetVidSystem(0);
\r
910 void FDSClose(void)
\r
914 char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
916 if(!DiskWritten) return;
\r
918 if(!(fp=FCEUD_UTF8fopen(fn,"wb")))
\r
925 for(x=0;x<TotalSides;x++)
\r
927 if(fwrite(diskdata[x],1,65500,fp)!=65500)
\r
929 FCEU_PrintError("Error saving FDS image!");
\r