/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "types.h" #include "x6502.h" #include "fceu.h" #include "fds.h" #include "sound.h" #include "file.h" #include "utils/md5.h" #include "utils/memory.h" #include "state.h" #include "file.h" #include "cart.h" #include "netplay.h" #include "driver.h" #include "movie.h" #include #include #include // 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). extern int disableBatteryLoading; bool isFDS = false; //flag for determining if a FDS game is loaded, movie.cpp needs this 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 void FDSInit(void); static void FDSClose(void); static void FDSFix(int a); static uint8 FDSRegs[6]; static int32 IRQLatch, IRQCount; static uint8 IRQa; static uint8 *FDSRAM = NULL; static uint32 FDSRAMSize; static uint8 *FDSBIOS = NULL; static uint32 FDSBIOSsize; static uint8 *CHRRAM = NULL; static uint32 CHRRAMSize; /* 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 int TotalSides; //mbg merge 7/17/06 - unsignedectomy static uint8 DiskWritten = 0; /* Set to 1 if disk was written to. */ static uint8 writeskip; static int32 DiskPtr; static int32 DiskSeekIRQ; static uint8 SelectDisk, InDisk; /* 4024(w), 4025(w), 4031(r) by dink(fbneo) */ enum FDS_DiskBlockIDs { DSK_INIT = 0, DSK_VOLUME, DSK_FILECNT, DSK_FILEHDR, DSK_FILEDATA }; static uint8 mapperFDS_control; // 4025(w) control register static uint16 mapperFDS_filesize; // size of file being read/written static uint8 mapperFDS_block; // block-id of current block static uint16 mapperFDS_blockstart; // start-address of current block static uint16 mapperFDS_blocklen; // length of current block static uint16 mapperFDS_diskaddr; // current address relative to blockstart static uint8 mapperFDS_diskaccess; // disk needs to be accessed at least once before writing #define fds_disk() (diskdata[InDisk][mapperFDS_blockstart + mapperFDS_diskaddr]) #define mapperFDS_diskinsert (InDisk != 255) #define DC_INC 1 void FDSGI(GI h) { switch (h) { case GI_CLOSE: FDSClose(); break; case GI_POWER: FDSInit(); break; // Unhandled Cases case GI_RESETM2: case GI_RESETSAVE: break; } } static void FDSStateRestore(int version) { int x; setmirror(((FDSRegs[5] & 8) >> 3) ^ 1); if (version >= 9810) for (x = 0; x < TotalSides; x++) { int b; for (b = 0; b < 65500; b++) diskdata[x][b] ^= diskdatao[x][b]; } } void FDSSound(); void FDSSoundReset(void); void FDSSoundStateAdd(void); static void RenderSound(void); static void RenderSoundHQ(void); static void FDSInit(void) { memset(FDSRegs, 0, sizeof(FDSRegs)); writeskip = DiskPtr = DiskSeekIRQ = 0; setmirror(1); setprg8(0xE000, 0); // BIOS setprg32r(1, 0x6000, 0); // 32KB RAM setchr8(0); // 8KB CHR RAM MapIRQHook = FDSFix; GameStateRestore = FDSStateRestore; SetReadHandler(0x4030, 0x4030, FDSRead4030); SetReadHandler(0x4031, 0x4031, FDSRead4031); SetReadHandler(0x4032, 0x4032, FDSRead4032); SetReadHandler(0x4033, 0x4033, FDSRead4033); SetWriteHandler(0x4020, 0x4025, FDSWrite); SetWriteHandler(0x6000, 0xDFFF, CartBW); SetReadHandler(0x6000, 0xFFFF, CartBR); IRQCount = IRQLatch = IRQa = 0; FDSSoundReset(); InDisk = 0; SelectDisk = 0; mapperFDS_control = 0; mapperFDS_filesize = 0; mapperFDS_block = 0; mapperFDS_blockstart = 0; mapperFDS_blocklen = 0; mapperFDS_diskaddr = 0; mapperFDS_diskaccess = 0; } void FCEU_FDSInsert(void) { if (TotalSides == 0) { FCEU_DispMessage("Not FDS; can't eject disk.", 0); return; } if (FCEUI_EmulationPaused()) EmulationPaused |= EMULATIONPAUSED_FA; if (FCEUMOV_Mode(MOVIEMODE_RECORD)) FCEUMOV_AddCommand(FCEUNPCMD_FDSINSERT); if (InDisk == 255) { FCEU_DispMessage("Disk %d Side %s Inserted", 0, SelectDisk >> 1, (SelectDisk & 1) ? "B" : "A"); InDisk = SelectDisk; } else { FCEU_DispMessage("Disk %d Side %s Ejected", 0, SelectDisk >> 1, (SelectDisk & 1) ? "B" : "A"); InDisk = 255; } } /* void FCEU_FDSEject(void) { InDisk=255; } */ void FCEU_FDSSelect(void) { if (TotalSides == 0) { FCEU_DispMessage("Not FDS; can't select disk.", 0); return; } if (InDisk != 255) { FCEU_DispMessage("Eject disk before selecting.", 0); return; } if (FCEUI_EmulationPaused()) EmulationPaused |= EMULATIONPAUSED_FA; if (FCEUMOV_Mode(MOVIEMODE_RECORD)) FCEUMOV_AddCommand(FCEUNPCMD_FDSSELECT); SelectDisk = ((SelectDisk + 1) % TotalSides) & 3; FCEU_DispMessage("Disk %d Side %c Selected", 0, SelectDisk >> 1, (SelectDisk & 1) ? 'B' : 'A'); } #define IRQ_Repeat 0x01 #define IRQ_Enabled 0x02 static void FDSFix(int a) { if (IRQa & IRQ_Enabled) { IRQCount -= a; if (IRQCount <= 0) { IRQCount = IRQLatch; /* Puff Puff Golf notes: Game freezes while music playing ingame after inserting Disk Side B. IRQ is usually fired at scanline 169 and 183 for music to work. At some point after inserting disk B, an IRQ is fired at scanline 174 which will just freeze game while music plays. If you ignore triggering IRQ altogether, game plays but no music */ X6502_IRQBegin(FCEU_IQEXT); if (!(IRQa & IRQ_Repeat)) { IRQa &= ~IRQ_Enabled; } } } 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 ret = 0; ret = 0xff; if (mapperFDS_diskinsert && mapperFDS_control & 0x04) { mapperFDS_diskaccess = 1; ret = 0; switch (mapperFDS_block) { case DSK_FILEHDR: if (mapperFDS_diskaddr < mapperFDS_blocklen) { ret = fds_disk(); switch (mapperFDS_diskaddr) { case 13: mapperFDS_filesize = ret; break; case 14: mapperFDS_filesize |= ret << 8; //char fdsfile[10]; //strncpy(fdsfile, (char*)&diskdata[InDisk][mapperFDS_blockstart + 3], 8); //printf("Read file: %s (size: %d)\n"), fdsfile, mapperFDS_filesize); break; } mapperFDS_diskaddr++; } break; default: if (mapperFDS_diskaddr < mapperFDS_blocklen) { ret = fds_disk(); mapperFDS_diskaddr++; } break; } DiskSeekIRQ = 150; X6502_IRQEnd(FCEU_IQEXT2); } return ret; } 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 } /* 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; break; case 0x7: b17latch76 = 0; SPSG[0x5] = 0; break; case 0x8: b17latch76 = 0; fdso.mwave[SPSG[0x5] & 0x1F] = V & 0x7; SPSG[0x5] = (SPSG[0x5] + 1) & 0x1F; break; } 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) { 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; b8shiftreg88 = 0x80 + adj; } else { b8shiftreg88 = 0x80; } } else { b19shiftreg60 <<= 1; b8shiftreg88 >>= 1; } b24adder66 = (b24latch68 + b19shiftreg60) & 0x1FFFFFF; } static INLINE void ClockFall(void) { if ((b8shiftreg88 & 1)) 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 < end; x++) { uint32 t = FDSDoSound(); t += t >> 1; t >>= 4; Wave[x >> 4] += t; //(t>>2)-(t>>3); //>>3; } } static void RenderSoundHQ(void) { uint32 x; //mbg merge 7/17/06 - made this unsigned if (!(SPSG[0x9] & 0x80)) for (x = FBC; x < SOUNDTS; x++) { uint32 t = FDSDoSound(); t += t >> 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 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; } } SetReadHandler(0x4040, 0x407f, FDSWaveRead); SetWriteHandler(0x4040, 0x407f, FDSWaveWrite); SetWriteHandler(0x4080, 0x408A, FDSSWrite); SetReadHandler(0x4090, 0x4092, FDSSRead); } 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) { switch (A) { case 0x4020: IRQLatch &= 0xFF00; IRQLatch |= V; break; case 0x4021: IRQLatch &= 0xFF; IRQLatch |= V << 8; break; case 0x4022: if (FDSRegs[3] & 1) { IRQa = V & 0x03; if (IRQa & IRQ_Enabled) { IRQCount = IRQLatch; } else { X6502_IRQEnd(FCEU_IQEXT); } } break; case 0x4023: if (!(V & 0x01)) { IRQa &= ~IRQ_Enabled; X6502_IRQEnd(FCEU_IQEXT); X6502_IRQEnd(FCEU_IQEXT2); } break; case 0x4024: if (mapperFDS_diskinsert && ~mapperFDS_control & 0x04) { if (mapperFDS_diskaccess == 0) { mapperFDS_diskaccess = 1; break; } switch (mapperFDS_block) { case DSK_FILEHDR: if (mapperFDS_diskaddr < mapperFDS_blocklen) { fds_disk() = V; DiskWritten = 1; switch (mapperFDS_diskaddr) { case 13: mapperFDS_filesize = V; break; case 14: mapperFDS_filesize |= V << 8; //char fdsfile[10]; //strncpy(fdsfile, (char*)&diskdata[InDisk][mapperFDS_blockstart + 3], 8); //printf("Write file: %s (size: %d)\n"), fdsfile, mapperFDS_filesize); break; } mapperFDS_diskaddr++; } break; default: if (mapperFDS_diskaddr < mapperFDS_blocklen) { fds_disk() = V; DiskWritten = 1; mapperFDS_diskaddr++; } break; } } break; case 0x4025: X6502_IRQEnd(FCEU_IQEXT2); if (mapperFDS_diskinsert) { if (V & 0x40 && ~mapperFDS_control & 0x40) { mapperFDS_diskaccess = 0; DiskSeekIRQ = 150; // blockstart - address of block on disk // diskaddr - address relative to blockstart // _block -> _blockID ? mapperFDS_blockstart += mapperFDS_diskaddr; mapperFDS_diskaddr = 0; mapperFDS_block++; if (mapperFDS_block > DSK_FILEDATA) mapperFDS_block = DSK_FILEHDR; switch (mapperFDS_block) { case DSK_VOLUME: mapperFDS_blocklen = 0x38; break; case DSK_FILECNT: mapperFDS_blocklen = 0x02; break; case DSK_FILEHDR: mapperFDS_blocklen = 0x10; break; case DSK_FILEDATA: // mapperFDS_blocklen = 0x01 + mapperFDS_filesize; break; } } if (V & 0x02) { // transfer reset mapperFDS_block = DSK_INIT; mapperFDS_blockstart = 0; mapperFDS_blocklen = 0; mapperFDS_diskaddr = 0; DiskSeekIRQ = 150; } if (V & 0x40) { // turn on motor DiskSeekIRQ = 150; } } mapperFDS_control = V; setmirror(((V >> 3) & 1) ^ 1); break; } FDSRegs[A & 7] = V; } static void FreeFDSMemory(void) { int x; for (x = 0; x < TotalSides; x++) if (diskdata[x]) { free(diskdata[x]); diskdata[x] = 0; } } static int SubLoad(FCEUFILE *fp) { struct md5_context md5; uint8 header[16]; int x; FCEU_fseek(fp, 0, SEEK_SET); FCEU_fread(header, 16, 1, fp); if (memcmp(header, "FDS\x1a", 4)) { if (!(memcmp(header + 1, "*NINTENDO-HVC*", 14))) { long t; t = FCEU_fgetsize(fp); if (t < 65500) t = 65500; TotalSides = t / 65500; FCEU_fseek(fp, 0, SEEK_SET); } else return 1; } else TotalSides = header[4]; md5_starts(&md5); if (TotalSides > 8) TotalSides = 8; if (TotalSides < 1) TotalSides = 1; for (x = 0; x < TotalSides; x++) { if ((diskdata[x] = (uint8*)FCEU_malloc(65500)) == NULL) return 2; FCEU_fread(diskdata[x], 1, 65500, fp); md5_update(&md5, diskdata[x], 65500); } md5_finish(&md5, GameInfo->MD5.data); return 0; } static void PreSave(void) { int x; for (x = 0; x < TotalSides; x++) { int b; for (b = 0; b < 65500; b++) diskdata[x][b] ^= diskdatao[x][b]; } } static void PostSave(void) { int x; for (x = 0; x < TotalSides; x++) { int b; for (b = 0; b < 65500; b++) diskdata[x][b] ^= diskdatao[x][b]; } } int FDSLoad(const char *name, FCEUFILE *fp) { FILE *zp; int x; // try to load FDS image first FreeFDSMemory(); int load_result = SubLoad(fp); switch (load_result) { case 1: FreeFDSMemory(); return LOADER_INVALID_FORMAT; case 2: FreeFDSMemory(); FCEU_PrintError("Unable to allocate memory."); return LOADER_HANDLED_ERROR; } // load FDS BIOS next char *fn = strdup(FCEU_MakeFName(FCEUMKF_FDSROM, 0, 0).c_str()); if (!(zp = FCEUD_UTF8fopen(fn, "rb"))) { FCEU_PrintError("FDS BIOS ROM image missing: %s", FCEU_MakeFName(FCEUMKF_FDSROM, 0, 0).c_str()); free(fn); FreeFDSMemory(); return LOADER_HANDLED_ERROR; } free(fn); fseek(zp, 0L, SEEK_END); if (ftell(zp) != 8192) { fclose(zp); FreeFDSMemory(); FCEU_PrintError("FDS BIOS ROM image incompatible: %s", FCEU_MakeFName(FCEUMKF_FDSROM, 0, 0).c_str()); return LOADER_HANDLED_ERROR; } fseek(zp, 0L, SEEK_SET); ResetCartMapping(); if(FDSBIOS) free(FDSBIOS); FDSBIOS = NULL; if(FDSRAM) free(FDSRAM); FDSRAM = NULL; if(CHRRAM) free(CHRRAM); CHRRAM = NULL; FDSBIOSsize = 8192; FDSBIOS = (uint8*)FCEU_gmalloc(FDSBIOSsize); SetupCartPRGMapping(0, FDSBIOS, FDSBIOSsize, 0); if (fread(FDSBIOS, 1, FDSBIOSsize, zp) != FDSBIOSsize) { if(FDSBIOS) free(FDSBIOS); FDSBIOS = NULL; fclose(zp); FreeFDSMemory(); FCEU_PrintError("Error reading FDS BIOS ROM image."); return LOADER_HANDLED_ERROR; } fclose(zp); if (!disableBatteryLoading) { FCEUFILE *tp; char *fn = strdup(FCEU_MakeFName(FCEUMKF_FDS, 0, 0).c_str()); int x; for (x = 0; x < TotalSides; x++) { diskdatao[x] = (uint8*)FCEU_malloc(65500); memcpy(diskdatao[x], diskdata[x], 65500); } if ((tp = FCEU_fopen(fn, 0, "rb", 0))) { FCEU_printf("Disk was written. Auxiliary FDS file open \"%s\".\n",fn); FreeFDSMemory(); if (SubLoad(tp)) { FCEU_PrintError("Error reading auxiliary FDS file."); if(FDSBIOS) free(FDSBIOS); FDSBIOS = NULL; free(fn); FreeFDSMemory(); return LOADER_HANDLED_ERROR; } FCEU_fclose(tp); DiskWritten = 1; /* For save state handling. */ } free(fn); } extern char LoadedRomFName[2048]; strcpy(LoadedRomFName, name); //For the debugger list GameInfo->type = GIT_FDS; GameInterface = FDSGI; isFDS = true; SelectDisk = 0; InDisk = 255; ResetExState(PreSave, PostSave); FDSSoundStateAdd(); for (x = 0; x < TotalSides; x++) { char temp[5]; sprintf(temp, "DDT%d", x); AddExState(diskdata[x], 65500, 0, temp); } AddExState(FDSRegs, sizeof(FDSRegs), 0, "FREG"); AddExState(&IRQCount, 4, 1, "IRQC"); AddExState(&IRQLatch, 4, 1, "IQL1"); AddExState(&IRQa, 1, 0, "IRQA"); AddExState(&writeskip, 1, 0, "WSKI"); AddExState(&DiskPtr, 4, 1, "DPTR"); AddExState(&DiskSeekIRQ, 4, 1, "DSIR"); AddExState(&SelectDisk, 1, 0, "SELD"); AddExState(&InDisk, 1, 0, "INDI"); AddExState(&DiskWritten, 1, 0, "DSKW"); AddExState(&mapperFDS_control, 1, 0, "CTRG"); AddExState(&mapperFDS_filesize, 2, 1, "FLSZ"); AddExState(&mapperFDS_block, 1, 0, "BLCK"); AddExState(&mapperFDS_blockstart, 2, 1, "BLKS"); AddExState(&mapperFDS_blocklen, 2, 1, "BLKL"); AddExState(&mapperFDS_diskaddr, 2, 1, "DADR"); AddExState(&mapperFDS_diskaccess, 1, 0, "DACC"); CHRRAMSize = 8192; CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSize); SetupCartCHRMapping(0, CHRRAM, CHRRAMSize, 1); AddExState(CHRRAM, CHRRAMSize, 0, "CHRR"); FDSRAMSize = 32768; FDSRAM = (uint8*)FCEU_gmalloc(FDSRAMSize); SetupCartPRGMapping(1, FDSRAM, FDSRAMSize, 1); AddExState(FDSRAM, FDSRAMSize, 0, "FDSR"); SetupCartMirroring(0, 0, 0); FCEU_printf(" Sides: %d\n\n", TotalSides); FCEUI_SetVidSystem(0); return LOADER_OK; } void FDSClose(void) { FILE *fp; int x; isFDS = false; if (!DiskWritten) return; const std::string &fn = FCEU_MakeFName(FCEUMKF_FDS, 0, 0); if (!(fp = FCEUD_UTF8fopen(fn.c_str(), "wb"))) { return; } for (x = 0; x < TotalSides; x++) { if (fwrite(diskdata[x], 1, 65500, fp) != 65500) { FCEU_PrintError("Error saving FDS image!"); fclose(fp); return; } } for (x = 0; x < TotalSides; x++) if (diskdatao[x]) { free(diskdatao[x]); diskdatao[x] = 0; } FreeFDSMemory(); if(FDSBIOS) free(FDSBIOS); FDSBIOS = NULL; if(FDSRAM) free(FDSRAM); FDSRAM = NULL; if(CHRRAM) free(CHRRAM); CHRRAM = NULL; fclose(fp); }