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
26 #include <unistd.h> // for sync()
\r
34 #include "general.h"
\r
40 #include "netplay.h"
\r
44 /* TODO: Add code to put a delay in between the time a disk is inserted
\r
45 and the when it can be successfully read/written to. This should
\r
46 prevent writes to wrong places OR add code to prevent disk ejects
\r
47 when the virtual motor is on(mmm...virtual motor).
\r
50 static DECLFR(FDSRead4030);
\r
51 static DECLFR(FDSRead4031);
\r
52 static DECLFR(FDSRead4032);
\r
53 static DECLFR(FDSRead4033);
\r
55 static DECLFW(FDSWrite);
\r
57 static DECLFW(FDSWaveWrite);
\r
58 static DECLFR(FDSWaveRead);
\r
60 static DECLFR(FDSSRead);
\r
61 static DECLFW(FDSSWrite);
\r
62 static DECLFR(FDSBIOSRead);
\r
63 static DECLFR(FDSRAMRead);
\r
64 static DECLFW(FDSRAMWrite);
\r
65 static void FDSInit(void);
\r
66 static void FP_FASTAPASS(1) FDSFix(int a);
\r
68 #define FDSRAM GameMemBlock
\r
69 #define CHRRAM (GameMemBlock+32768)
\r
71 static uint8 FDSRegs[6];
\r
72 static int32 IRQLatch,IRQCount;
\r
74 static void FDSClose(void);
\r
76 static uint8 FDSBIOS[8192];
\r
78 /* Original disk data backup, to help in creating save states. */
\r
79 static uint8 *diskdatao[8]={0,0,0,0,0,0,0,0};
\r
81 static uint8 *diskdata[8]={0,0,0,0,0,0,0,0};
\r
83 static unsigned int TotalSides;
\r
84 static uint8 DiskWritten=0; /* Set to 1 if disk was written to. */
\r
85 static uint8 writeskip;
\r
86 static uint32 DiskPtr;
\r
87 static int32 DiskSeekIRQ;
\r
88 static uint8 SelectDisk,InDisk;
\r
92 void FDSGI(int h, void *param)
\r
96 case GI_CLOSE: FDSClose();break;
\r
97 case GI_POWER: FDSInit();break;
\r
98 case GI_INFOSTRING: sprintf(param, "FDS, Sides: %d", TotalSides);break;
\r
102 static void FDSStateRestore(int version)
\r
106 setmirror(((FDSRegs[5]&8)>>3)^1);
\r
108 if(version >= 9810)
\r
109 for(x=0;x<TotalSides;x++)
\r
112 for(b=0; b<65500; b++)
\r
113 diskdata[x][b] ^= diskdatao[x][b];
\r
119 void FDSSoundReset(void);
\r
120 void FDSSoundStateAdd(void);
\r
121 static void RenderSound(void);
\r
122 //static void RenderSoundHQ(void);
\r
124 static void FDSInit(void)
\r
126 memset(FDSRegs,0,sizeof(FDSRegs));
\r
127 writeskip=DiskPtr=DiskSeekIRQ=0;
\r
130 setprg8r(0,0xe000,0); // BIOS
\r
131 setprg32r(1,0x6000,0); // 32KB RAM
\r
132 setchr8(0); // 8KB CHR RAM
\r
135 GameStateRestore=FDSStateRestore;
\r
137 SetReadHandler(0x4030,0x4030,FDSRead4030);
\r
138 SetReadHandler(0x4031,0x4031,FDSRead4031);
\r
139 SetReadHandler(0x4032,0x4032,FDSRead4032);
\r
140 SetReadHandler(0x4033,0x4033,FDSRead4033);
\r
142 SetWriteHandler(0x4020,0x4025,FDSWrite);
\r
144 SetWriteHandler(0x6000,0xdfff,FDSRAMWrite);
\r
145 SetReadHandler(0x6000,0xdfff,FDSRAMRead);
\r
146 SetReadHandler(0xE000,0xFFFF,FDSBIOSRead);
\r
147 IRQCount=IRQLatch=IRQa=0;
\r
156 // asm code needs pages to be set again..
\r
157 for (page=12; page<28; page++) // 0x6000-0xdfff 32K RAM
\r
158 Page[page]=FDSRAM - (12<<11);
\r
159 for (; page<32; page++) // 0xe000-0xffff 8K BIOS
\r
160 Page[page]=FDSBIOS - (28<<11);
\r
165 void FCEU_FDSInsert(void)
\r
169 FCEU_DispMessage("Not FDS; can't eject disk.");
\r
174 FCEU_DispMessage("Disk %d Side %s Inserted",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
179 FCEU_DispMessage("Disk %d Side %s Ejected",SelectDisk>>1,(SelectDisk&1)?"B":"A");
\r
184 void FCEU_FDSEject(void)
\r
189 void FCEU_FDSSelect(void)
\r
193 FCEU_DispMessage("Not FDS; can't select disk.");
\r
198 FCEU_DispMessage("Eject disk before selecting.");
\r
201 SelectDisk=((SelectDisk+1)%TotalSides)&3;
\r
202 FCEU_DispMessage("Disk %d Side %c Selected",SelectDisk>>1,(SelectDisk&1)?'B':'A');
\r
205 static void FP_FASTAPASS(1) FDSFix(int a)
\r
207 if(IRQCount && (IRQa&2))
\r
215 IRQCount=IRQLatch=0;
\r
219 //IRQCount=IRQLatch; //0xFFFF;
\r
220 X6502_IRQBegin(FCEU_IQEXT);
\r
221 //printf("IRQ: %d\n",timestamp);
\r
222 // printf("IRQ: %d\n",scanline);
\r
230 if(FDSRegs[5]&0x80)
\r
232 X6502_IRQBegin(FCEU_IQEXT2);
\r
238 static DECLFR(FDSRead4030)
\r
244 if(X.IRQlow&FCEU_IQEXT) ret|=1;
\r
245 if(X.IRQlow&FCEU_IQEXT2) ret|=2;
\r
247 if((nes_registers[4]>>8)&FCEU_IQEXT) ret|=1;
\r
248 if((nes_registers[4]>>8)&FCEU_IQEXT2) ret|=2;
\r
253 X6502_IRQEnd(FCEU_IQEXT);
\r
254 X6502_IRQEnd(FCEU_IQEXT2);
\r
259 static DECLFR(FDSRead4031)
\r
264 z=diskdata[InDisk][DiskPtr];
\r
267 if(DiskPtr<64999) DiskPtr++;
\r
269 X6502_IRQEnd(FCEU_IQEXT2);
\r
274 static DECLFR(FDSRead4032)
\r
282 if(InDisk==255 || !(FDSRegs[5]&1) || (FDSRegs[5]&2))
\r
287 static DECLFR(FDSRead4033)
\r
289 return 0x80; // battery
\r
292 static DECLFW(FDSRAMWrite)
\r
294 (FDSRAM-0x6000)[A]=V;
\r
297 static DECLFR(FDSBIOSRead)
\r
299 return (FDSBIOS-0xE000)[A];
\r
302 static DECLFR(FDSRAMRead)
\r
304 return (FDSRAM-0x6000)[A];
\r
307 /* Begin FDS sound */
\r
309 #define FDSClock (1789772.7272727272727272/2)
\r
312 int64 cycles; // Cycles per PCM sample
\r
313 int64 count; // Cycle counter
\r
314 int64 envcount; // Envelope cycle counter
\r
315 uint32 b19shiftreg60;
\r
319 int32 clockcount; // Counter to divide frequency by 8.
\r
320 uint8 b8shiftreg88; // Modulation register.
\r
321 uint8 amplitude[2]; // Current amplitudes.
\r
325 uint8 mwave[0x20]; // Modulation waveform
\r
326 uint8 cwave[0x40]; // Game-defined waveform(carrier)
\r
330 static FDSSOUND fdso;
\r
332 #define SPSG fdso.SPSG
\r
333 #define b19shiftreg60 fdso.b19shiftreg60
\r
334 #define b24adder66 fdso.b24adder66
\r
335 #define b24latch68 fdso.b24latch68
\r
336 #define b17latch76 fdso.b17latch76
\r
337 #define b8shiftreg88 fdso.b8shiftreg88
\r
338 #define clockcount fdso.clockcount
\r
339 #define amplitude fdso.amplitude
\r
340 #define speedo fdso.speedo
\r
342 void FDSSoundStateAdd(void)
\r
344 AddExState(fdso.cwave,64,0,"WAVE");
\r
345 AddExState(fdso.mwave,32,0,"MWAV");
\r
346 AddExState(amplitude,2,0,"AMPL");
\r
347 AddExState(SPSG,0xB,0,"SPSG");
\r
349 AddExState(&b8shiftreg88,1,0,"B88");
\r
351 AddExState(&clockcount, 4, 1, "CLOC");
\r
352 AddExState(&b19shiftreg60,4,1,"B60");
\r
353 AddExState(&b24adder66,4,1,"B66");
\r
354 AddExState(&b24latch68,4,1,"B68");
\r
355 AddExState(&b17latch76,4,1,"B76");
\r
359 static DECLFR(FDSSRead)
\r
363 case 0x0:return(amplitude[0]|(X.DB&0xC0));
\r
364 case 0x2:return(amplitude[1]|(X.DB&0xC0));
\r
369 static DECLFW(FDSSWrite)
\r
371 if(FSettings.SndRate)
\r
374 if(FSettings.soundq>=1)
\r
384 case 0x4: if(V&0x80)
\r
385 amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F);
\r
387 case 0x5://printf("$%04x:$%02x\n",A,V);
\r
389 case 0x7: b17latch76=0;SPSG[0x5]=0;//printf("$%04x:$%02x\n",A,V);
\r
393 // printf("%d:$%02x, $%02x\n",SPSG[0x5],V,b17latch76);
\r
394 fdso.mwave[SPSG[0x5]&0x1F]=V&0x7;
\r
395 SPSG[0x5]=(SPSG[0x5]+1)&0x1F;
\r
398 //if(A>=0x7 && A!=0x8 && A<=0xF)
\r
399 //if(A==0xA || A==0x9)
\r
400 //printf("$%04x:$%02x\n",A,V);
\r
404 // $4080 - Fundamental wave amplitude data register 92
\r
405 // $4082 - Fundamental wave frequency data register 58
\r
406 // $4083 - Same as $4082($4083 is the upper 4 bits).
\r
408 // $4084 - Modulation amplitude data register 78
\r
409 // $4086 - Modulation frequency data register 72
\r
410 // $4087 - Same as $4086($4087 is the upper 4 bits)
\r
413 static void DoEnv()
\r
418 if(!(SPSG[x<<2]&0x80) && !(SPSG[0x3]&0x40))
\r
420 static int counto[2]={0,0};
\r
424 if(!(SPSG[x<<2]&0x80))
\r
426 if(SPSG[x<<2]&0x40)
\r
428 if(amplitude[x]<0x3F)
\r
437 counto[x]=(SPSG[x<<2]&0x3F);
\r
444 static DECLFR(FDSWaveRead)
\r
446 return(fdso.cwave[A&0x3f]|(X.DB&0xC0));
\r
449 static DECLFW(FDSWaveWrite)
\r
451 //printf("$%04x:$%02x, %d\n",A,V,SPSG[0x9]&0x80);
\r
453 fdso.cwave[A&0x3f]=V&0x3F;
\r
457 static INLINE void ClockRise(void)
\r
463 b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));
\r
464 b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76;
\r
466 if(!(SPSG[0x7]&0x80))
\r
468 int t=fdso.mwave[(b17latch76>>13)&0x1F]&7;
\r
469 int t2=amplitude[1];
\r
475 adj -= (t2 * ((4 - (t&3) ) ));
\r
477 adj += (t2 * ( (t&3) ));
\r
480 if(adj > 0x7F) adj = 0x7F;
\r
481 if(adj < -0x80) adj = -0x80;
\r
482 //if(adj) printf("%d ",adj);
\r
483 b8shiftreg88=0x80 + adj;
\r
495 // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF;
\r
496 b24adder66=(b24latch68+b19shiftreg60)&0x1FFFFFF;
\r
499 static INLINE void ClockFall(void)
\r
501 //if(!(SPSG[0x7]&0x80))
\r
503 if((b8shiftreg88&1)) // || clockcount==7)
\r
504 b24latch68=b24adder66;
\r
506 clockcount=(clockcount+1)&7;
\r
509 static INLINE int32 FDSDoSound(int32 mul)
\r
511 fdso.count+=fdso.cycles;
\r
512 if(fdso.count>=((int64)1<<40))
\r
515 fdso.count-=(int64)1<<40;
\r
519 if(fdso.envcount<=0)
\r
521 fdso.envcount+=SPSG[0xA]*3;
\r
525 if(fdso.count>=32768) goto dogk;
\r
527 // Might need to emulate applying the amplitude to the waveform a bit better...
\r
530 int k=amplitude[0];
\r
532 return (fdso.cwave[b24latch68>>19]*k)*4/((SPSG[0x9]&0x3)+2);
\r
535 return (fdso.cwave[b24latch68>>19]*mul)>>16;
\r
538 static int32 FBC=0;
\r
540 static void RenderSound(void)
\r
547 end=(SOUNDTS<<16)/soundtsinc;
\r
552 // hack for performance, might produce bad results..
\r
556 switch (SPSG[0x9]&0x3)
\r
558 default:mul = (4<<16)/2;
\r
559 case 1: mul = (4<<16)/3;
\r
560 case 2: mul = (4<<16)/4;
\r
561 case 3: mul = (4<<16)/5;
\r
564 int k=amplitude[0];
\r
569 if(!(SPSG[0x9]&0x80))
\r
570 for(x=start;x<end;x++)
\r
572 uint32 t=FDSDoSound(mul);
\r
575 Wave[x>>4]+=t; //(t>>2)-(t>>3); //>>3;
\r
580 static void RenderSoundHQ(void)
\r
584 if(!(SPSG[0x9]&0x80))
\r
585 for(x=FBC;x<SOUNDTS;x++)
\r
587 uint32 t=FDSDoSound();
\r
589 WaveHi[x]+=t; //(t<<2)-(t<<1);
\r
595 static void HQSync(int32 ts)
\r
600 void FDSSound(int c)
\r
607 static DECLFR(FDSBIOSPatch)
\r
611 X.X=FDSRead4031(0x4031);
\r
612 FDSWrite(0x4024,X.A);
\r
624 static void FDS_ESI(void)
\r
626 if(FSettings.SndRate)
\r
629 if(FSettings.soundq>=1)
\r
631 fdso.cycles=(int64)1<<39;
\r
636 fdso.cycles=((int64)1<<40)*FDSClock;
\r
637 fdso.cycles/=FSettings.SndRate *16;
\r
640 // fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate *16);
\r
641 SetReadHandler(0x4040,0x407f,FDSWaveRead);
\r
642 SetWriteHandler(0x4040,0x407f,FDSWaveWrite);
\r
643 SetWriteHandler(0x4080,0x408A,FDSSWrite);
\r
644 SetReadHandler(0x4090,0x4092,FDSSRead);
\r
646 //SetReadHandler(0xE7A3,0xE7A3,FDSBIOSPatch);
\r
649 void FDSSoundReset(void)
\r
651 memset(&fdso,0,sizeof(fdso));
\r
653 GameExpSound.HiSync=HQSync;
\r
654 GameExpSound.HiFill=0;//RenderSoundHQ;
\r
655 GameExpSound.Fill=FDSSound;
\r
656 GameExpSound.RChange=FDS_ESI;
\r
659 static DECLFW(FDSWrite)
\r
661 //extern int scanline;
\r
662 //FCEU_printf("$%04x:$%02x, %d\n",A,V,scanline);
\r
666 X6502_IRQEnd(FCEU_IQEXT);
\r
669 // printf("$%04x:$%02x\n",A,V);
\r
672 X6502_IRQEnd(FCEU_IQEXT);
\r
675 // printf("$%04x:$%02x\n",A,V);
\r
678 X6502_IRQEnd(FCEU_IQEXT);
\r
681 // printf("$%04x:$%02x\n",A,V);
\r
685 if(InDisk!=255 && !(FDSRegs[5]&0x4) && (FDSRegs[3]&0x1))
\r
687 if(DiskPtr>=0 && DiskPtr<65500)
\r
689 if(writeskip) writeskip--;
\r
690 else if(DiskPtr>=2)
\r
693 diskdata[InDisk][DiskPtr-2]=V;
\r
699 X6502_IRQEnd(FCEU_IQEXT2);
\r
704 if(FDSRegs[5]&0x40 && !(V&0x10))
\r
709 if(DiskPtr<0) DiskPtr=0;
\r
711 if(!(V&0x4)) writeskip=2;
\r
712 if(V&2) {DiskPtr=0;DiskSeekIRQ=200;}
\r
713 if(V&0x40) DiskSeekIRQ=200;
\r
715 setmirror(((V>>3)&1)^1);
\r
721 static void FreeFDSMemory(void)
\r
725 for(x=0;x<TotalSides;x++)
\r
733 static int SubLoad(int fp)
\r
735 struct md5_context md5;
\r
739 FCEU_fread(header,16,1,fp);
\r
741 if(memcmp(header,"FDS\x1a",4))
\r
743 if(!(memcmp(header+1,"*NINTENDO-HVC*",14)))
\r
746 t=FCEU_fgetsize(fp);
\r
749 TotalSides=t/65500;
\r
750 FCEU_fseek(fp,0,SEEK_SET);
\r
756 TotalSides=header[4];
\r
760 if(TotalSides>8) TotalSides=8;
\r
761 if(TotalSides<1) TotalSides=1;
\r
763 for(x=0;x<TotalSides;x++)
\r
765 diskdata[x]=(uint8 *)FCEU_malloc(65500);
\r
769 for(zol=0;zol<x;zol++)
\r
770 free(diskdata[zol]);
\r
773 FCEU_fread(diskdata[x],1,65500,fp);
\r
774 md5_update(&md5,diskdata[x],65500);
\r
776 md5_finish(&md5,FCEUGameInfo.MD5);
\r
780 static void PreSave(void)
\r
785 for(x=0;x<TotalSides;x++)
\r
788 for(b=0; b<65500; b++)
\r
789 diskdata[x][b] ^= diskdatao[x][b];
\r
793 static void PostSave(void)
\r
798 for(x=0;x<TotalSides;x++)
\r
802 for(b=0; b<65500; b++)
\r
803 diskdata[x][b] ^= diskdatao[x][b];
\r
808 int FDSLoad(const char *name, int fp)
\r
814 FCEU_fseek(fp,0,SEEK_SET);
\r
820 fn = FCEU_MakeFName(FCEUMKF_FDSROM,0,0);
\r
822 if(!(zp=FCEUD_UTF8fopen(fn,"rb")))
\r
824 FCEU_PrintError("FDS BIOS ROM image missing!");
\r
827 LoadGameLastError = 10;
\r
833 if(fread(FDSBIOS,1,8192,zp)!=8192)
\r
837 FCEU_PrintError("Error reading FDS BIOS ROM image.");
\r
838 LoadGameLastError = 10;
\r
847 char *fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
850 for(x=0;x<TotalSides;x++)
\r
852 diskdatao[x]=(uint8 *)FCEU_malloc(65500);
\r
853 memcpy(diskdatao[x],diskdata[x],65500);
\r
856 if((tp=FCEU_fopen(fn,"rb")))
\r
861 FCEU_PrintError("Error reading auxillary FDS file.");
\r
863 LoadGameLastError = 11;
\r
867 DiskWritten=1; /* For save state handling. */
\r
872 FCEUGameInfo.type=GIT_FDS;
\r
873 GameInterface=FDSGI;
\r
878 ResetExState(PreSave,PostSave);
\r
879 FDSSoundStateAdd();
\r
881 for(x=0;x<TotalSides;x++)
\r
884 sprintf(temp,"DDT%d",x);
\r
885 AddExState(diskdata[x],65500,0,temp);
\r
888 AddExState(FDSRAM,32768,0,"FDSR");
\r
889 AddExState(FDSRegs,sizeof(FDSRegs),0,"FREG");
\r
890 AddExState(CHRRAM,8192,0,"CHRR");
\r
891 AddExState(&IRQCount, 4, 1, "IRQC");
\r
892 AddExState(&IRQLatch, 4, 1, "IQL1");
\r
893 AddExState(&IRQa, 1, 0, "IRQA");
\r
894 AddExState(&writeskip,1,0,"WSKI");
\r
895 AddExState(&DiskPtr,4,1,"DPTR");
\r
896 AddExState(&DiskSeekIRQ,4,1,"DSIR");
\r
897 AddExState(&SelectDisk,1,0,"SELD");
\r
898 AddExState(&InDisk,1,0,"INDI");
\r
899 AddExState(&DiskWritten,1,0,"DSKW");
\r
901 ResetCartMapping();
\r
902 SetupCartCHRMapping(0,CHRRAM,8192,1);
\r
903 SetupCartMirroring(0,0,0);
\r
904 memset(CHRRAM,0,8192);
\r
905 memset(FDSRAM,0,32768);
\r
907 FCEU_printf(" Sides: %d\n\n",TotalSides);
\r
909 FCEUI_SetVidSystem(0);
\r
914 void FDSClose(void)
\r
922 fn=FCEU_MakeFName(FCEUMKF_FDS,0,0);
\r
924 if(!(fp=FCEUD_UTF8fopen(fn,"wb")))
\r
931 for(x=0;x<TotalSides;x++)
\r
933 if(fwrite(diskdata[x],1,65500,fp)!=65500)
\r
935 FCEU_PrintError("Error saving FDS image!");
\r
947 for(x=0;x<TotalSides;x++)
\r
950 free(diskdatao[x]);
\r