From 2c662616c5c5173dc5c82e141e1ce65c0a12544e Mon Sep 17 00:00:00 2001 From: dborth Date: Tue, 15 Sep 2009 08:20:48 +0000 Subject: [PATCH] sync to FCEUX svn --- source/fceultra/boards/bandai.cpp | 14 +-- source/fceultra/boards/mmc5.cpp | 2 +- source/fceultra/cart.cpp | 6 +- source/fceultra/config.cpp | 6 +- source/fceultra/debug.cpp | 16 ++- source/fceultra/fceu.cpp | 44 +++---- source/fceultra/fds.cpp | 57 +++++---- source/fceultra/file.cpp | 6 +- source/fceultra/movie.cpp | 21 +++- source/fceultra/movie.h | 4 + source/fceultra/nsf.cpp | 58 ++++++--- source/fceultra/nsf.h | 2 + source/fceultra/ppu.cpp | 200 +++++++++++++++++++++--------- source/fceultra/ppu.h | 11 +- source/fceultra/state.cpp | 51 ++++---- source/fceultra/types.h | 4 +- source/ngc/fceuload.cpp | 2 +- source/ngc/fceusupport.h | 2 +- 18 files changed, 339 insertions(+), 167 deletions(-) diff --git a/source/fceultra/boards/bandai.cpp b/source/fceultra/boards/bandai.cpp index 8604b3c..7844341 100644 --- a/source/fceultra/boards/bandai.cpp +++ b/source/fceultra/boards/bandai.cpp @@ -25,7 +25,7 @@ static uint8 reg[16], is153; static uint8 IRQa; -static uint16 IRQCount, IRQLatch; +static int16 IRQCount, IRQLatch; static uint8 *WRAM=NULL; static uint32 WRAMSIZE; @@ -35,7 +35,7 @@ static SFORMAT StateRegs[]= {reg, 16, "REGS"}, {&IRQa, 1, "IRQA"}, {&IRQCount, 2, "IRQC"}, - {&IRQLatch, 2, "IRQL"}, + {&IRQLatch, 2, "IRQL"}, // need for Famicom Jump II - Saikyou no 7 Nin (J) [!] {0} }; @@ -75,18 +75,18 @@ static void BandaiSync(void) setprg16(0x8000,reg[8]); setprg16(0xC000,~0); } - switch(reg[9]) + switch(reg[9]&3) { case 0: setmirror(MI_V); break; case 1: setmirror(MI_H); break; case 2: setmirror(MI_0); break; case 3: setmirror(MI_1); break; - } + } } static DECLFW(BandaiWrite) { - A&=0x0F; + A&=0x0F; if(A<0x0A) { reg[A&0x0F]=V; @@ -96,9 +96,9 @@ static DECLFW(BandaiWrite) switch(A) { case 0x0A: X6502_IRQEnd(FCEU_IQEXT); IRQa=V&1; IRQCount=IRQLatch; break; - case 0x0B: IRQLatch&=0xFF00; IRQLatch|=V; break; + case 0x0B: IRQLatch&=0xFF00; IRQLatch|=V; break; case 0x0C: IRQLatch&=0xFF; IRQLatch|=V<<8; break; - case 0x0D: break;// Serial EEPROM control port + case 0x0D: break;// Serial EEPROM control port } } diff --git a/source/fceultra/boards/mmc5.cpp b/source/fceultra/boards/mmc5.cpp index 125e562..1375091 100644 --- a/source/fceultra/boards/mmc5.cpp +++ b/source/fceultra/boards/mmc5.cpp @@ -115,7 +115,7 @@ static void mmc5_PPUWrite(uint32 A, uint8 V) { } } -uint8 mmc5_PPURead(uint32 A) { +uint8 FASTCALL mmc5_PPURead(uint32 A) { if(A<0x2000) { if(ppuphase == PPUPHASE_BG) diff --git a/source/fceultra/cart.cpp b/source/fceultra/cart.cpp index 17da47e..21746e3 100644 --- a/source/fceultra/cart.cpp +++ b/source/fceultra/cart.cpp @@ -99,6 +99,8 @@ void ResetCartMapping(void) { int x; + PPU_ResetHooks(); + for(x=0;x<32;x++) { Page[x]=nothing-x*2048; @@ -293,7 +295,7 @@ void setchr8r(int r, unsigned int V) if(CHRram[r]) PPUCHRRAM|=(255); else - PPUCHRRAM=0; + PPUCHRRAM=0; } void setchr1(unsigned int A, unsigned int V) @@ -437,7 +439,7 @@ static uint8 *GENIEROM=0; void FixGenieMap(void); -// Called when a game(file) is opened successfully. +// Called when a game(file) is opened successfully. void OpenGenie(void) { FILE *fp; diff --git a/source/fceultra/config.cpp b/source/fceultra/config.cpp index cc38811..28d6701 100644 --- a/source/fceultra/config.cpp +++ b/source/fceultra/config.cpp @@ -16,9 +16,11 @@ char *FCEUI_GetAboutString() { const char *aboutTemplate = FCEU_NAME_AND_VERSION "\n\n\ Authors:\n\ -zeromus, adelikat, CaH4e3\n\n\ +zeromus, adelikat,\n\n\ Contributers:\n\ -Acmlm,DWEdit,QFox\n\ +Acmlm,CaH4e3\n\ +DWEdit,QFox\n\ +qeed,Shinydoofy,ugetab\n\ \n\ FCEUX 2.0\n\ mz, nitsujrehtona, Lukas Sabota,\n\ diff --git a/source/fceultra/debug.cpp b/source/fceultra/debug.cpp index 060360f..3b61991 100644 --- a/source/fceultra/debug.cpp +++ b/source/fceultra/debug.cpp @@ -624,10 +624,22 @@ void DebugCycle() { else vblankScanLines = 0; + if (GameInfo->type==GIT_NSF) + { + if ((_PC >= 0x3801) && (_PC <= 0x3824)) return; + } + if (numWPs || dbgstate.step || dbgstate.runline || dbgstate.stepout || watchpoint[64].flags || dbgstate.badopbreak) breakpoint(); if(debug_loggingCD) LogCDData(); + //mbg 6/30/06 - this was commented out when i got here. i dont understand it anyway //if(logging || (hMemView && (EditingMode == 2))) LogInstruction(); - FCEUD_TraceInstruction(); -} \ No newline at end of file + +//This needs to be windows only or else the linux build system will fail since logging is declared in a +//windows source file +#ifdef WIN32 + extern volatile int logging; //UGETAB: This is required to be an extern, because the info isn't set here + if(logging) FCEUD_TraceInstruction(); +#endif +} diff --git a/source/fceultra/fceu.cpp b/source/fceultra/fceu.cpp index 008ea7d..88285b4 100644 --- a/source/fceultra/fceu.cpp +++ b/source/fceultra/fceu.cpp @@ -292,7 +292,7 @@ static void AllocBuffers() void win_AllocBuffers(uint8 **GameMemBlock, uint8 **RAM); win_AllocBuffers(&GameMemBlock, &RAM); -#else +#else GameMemBlock = (uint8*)FCEU_gmalloc(131072); RAM = (uint8*)FCEU_gmalloc(0x800); @@ -304,7 +304,7 @@ static void FreeBuffers() { #ifdef _USE_SHARED_MEMORY_ void win_FreeBuffers(uint8 *GameMemBlock, uint8 *RAM); win_FreeBuffers(GameMemBlock, RAM); -#else +#else FCEU_free(GameMemBlock); FCEU_free(RAM); #endif @@ -314,7 +314,7 @@ static void FreeBuffers() { uint8 PAL=0; static DECLFW(BRAML) -{ +{ RAM[A]=V; #ifdef _S9XLUA_H FCEU_LuaWriteInform(); @@ -361,7 +361,7 @@ void ResetGameLoaded(void) int UNIFLoad(const char *name, FCEUFILE *fp); int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode); int FDSLoad(const char *name, FCEUFILE *fp); -int NSFLoad(FCEUFILE *fp); +int NSFLoad(const char *name, FCEUFILE *fp); //char lastLoadedGameName [2048] = {0,}; // hack for movie WRAM clearing on record from poweron @@ -424,7 +424,7 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode) goto endlseq;*/ if(iNESLoad(name,fp,OverwriteVidMode)) goto endlseq; - if(NSFLoad(fp)) + if(NSFLoad(name,fp)) goto endlseq; if(UNIFLoad(name,fp)) goto endlseq; @@ -612,7 +612,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski r = FCEUPPU_Loop(skip); if (skip != 2) ssize=FlushEmulateSound(); //If skip = 2 we are skipping sound processing - + #ifdef WIN32 //These Windows only dialogs need to be updated only once per frame so they are included here UpdateCheatList(); @@ -631,12 +631,12 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski *SoundBuf=0; *SoundBufSize=0; } - else + else { *SoundBuf=WaveFinal; *SoundBufSize=ssize; } - + if (EmulationPaused&2 && ( !frameAdvanceLagSkip || !lagFlag) ) //Lots of conditions here. EmulationPaused&2 must be true. In addition frameAdvanceLagSkip or lagFlag must be false { @@ -646,12 +646,12 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski if(soundoptions&SO_MUTEFA) //mute the frame advance if the user requested it *SoundBufSize=0; //keep sound muted #endif - + } - + currMovieData.TryDumpIncremental(); - - if (lagFlag) + + if (lagFlag) { lagCounter++; justLagged = true; @@ -663,8 +663,8 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski } void FCEUI_CloseGame(void) -{ - if(!FCEU_IsValidUI(FCEUI_CLOSEGAME)) +{ + if(!FCEU_IsValidUI(FCEUI_CLOSEGAME)) return; CloseGame(); } @@ -701,7 +701,7 @@ void hand(X6502 *X, int type, unsigned int A) } int suppressAddPowerCommand=0; // hack... yeah, I know... -void PowerNES(void) +void PowerNES(void) { //void MapperInit(); //MapperInit(); @@ -740,7 +740,7 @@ void PowerNES(void) extern int disableBatteryLoading; if(disableBatteryLoading) GameInterface(GI_RESETSAVE); - + timestampbase=0; LagCounterReset(); @@ -757,9 +757,9 @@ void FCEU_ResetVidSys(void) int w; if(GameInfo->vidsys==GIV_NTSC) - w=0; + w=0; else if(GameInfo->vidsys==GIV_PAL) - w=1; + w=1; else w=FSettings.PAL; @@ -899,7 +899,7 @@ void UpdateAutosave(void) { if(!EnableAutosave) return; - + char * f; AutosaveCounter = (AutosaveCounter + 1) % 256; if(AutosaveCounter == 0) @@ -1083,7 +1083,7 @@ bool FCEUXLoad(const char *name, FCEUFILE *fp) cart->chrPages = head.VROM_size; - cart->mirroring = (head.ROM_type&1); + cart->mirroring = (head.ROM_type&1); if(head.ROM_type&8) cart->mirroring=2; //skip trainer @@ -1105,7 +1105,7 @@ bool FCEUXLoad(const char *name, FCEUFILE *fp) ResetCartMapping(); SetupCartPRGMapping(0,(uint8*)cart->PRG,cart->prgSize,0); SetupCartCHRMapping(0,(uint8*)cart->CHR,cart->chrSize,0); - + return true; } @@ -1116,4 +1116,4 @@ uint8 FCEU_ReadRomByte(uint32 i) { if(i < 16+PRGsize[0])return PRGptr[0][i-16]; if(i < 16+PRGsize[0]+CHRsize[0])return CHRptr[0][i-16-PRGsize[0]]; return 0; -} +} \ No newline at end of file diff --git a/source/fceultra/fds.cpp b/source/fceultra/fds.cpp index c01d292..6b931ad 100644 --- a/source/fceultra/fds.cpp +++ b/source/fceultra/fds.cpp @@ -35,12 +35,13 @@ #include "cart.h" #include "netplay.h" #include "driver.h" +#include "movie.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). -extern int disableBatteryLoading; +extern int disableBatteryLoading; static DECLFR(FDSRead4030); static DECLFR(FDSRead4031); @@ -68,7 +69,11 @@ static int32 IRQLatch,IRQCount; static uint8 IRQa; static void FDSClose(void); +#ifdef GEKKO uint8 FDSBIOS[8192]; +#else +static uint8 FDSBIOS[8192]; +#endif /* Original disk data backup, to help in creating save states. */ static uint8 *diskdatao[8]={0,0,0,0,0,0,0,0}; @@ -94,7 +99,7 @@ void FDSGI(GI h) } static void FDSStateRestore(int version) -{ +{ int x; setmirror(((FDSRegs[5]&8)>>3)^1); @@ -133,7 +138,7 @@ static void FDSInit(void) SetReadHandler(0x4032,0x4032,FDSRead4032); SetReadHandler(0x4033,0x4033,FDSRead4033); - SetWriteHandler(0x4020,0x4025,FDSWrite); + SetWriteHandler(0x4020,0x4025,FDSWrite); SetWriteHandler(0x6000,0xdfff,FDSRAMWrite); SetReadHandler(0x6000,0xdfff,FDSRAMRead); @@ -147,17 +152,22 @@ static void FDSInit(void) void FCEU_FDSInsert(void) { + if(FCEUI_EmulationPaused()) EmulationPaused |= 2; + + if(FCEUMOV_Mode(MOVIEMODE_RECORD)) + FCEUMOV_AddCommand(FCEUNPCMD_FDSINSERT); + if(TotalSides==0) { - FCEU_DispMessage("Not FDS; can't eject disk."); + FCEU_DispMessage("Not FDS; can't eject disk."); return; } if(InDisk==255) { - FCEU_DispMessage("Disk %d Side %s Inserted",SelectDisk>>1,(SelectDisk&1)?"B":"A"); + FCEU_DispMessage("Disk %d Side %s Inserted",SelectDisk>>1,(SelectDisk&1)?"B":"A"); InDisk=SelectDisk; } - else + else { FCEU_DispMessage("Disk %d Side %s Ejected",SelectDisk>>1,(SelectDisk&1)?"B":"A"); InDisk=255; @@ -171,6 +181,11 @@ InDisk=255; */ void FCEU_FDSSelect(void) { + if(FCEUI_EmulationPaused()) EmulationPaused |= 2; + + if(FCEUMOV_Mode(MOVIEMODE_RECORD)) + FCEUMOV_AddCommand(FCEUNPCMD_FDSSELECT); + if(TotalSides==0) { FCEU_DispMessage("Not FDS; can't select disk."); @@ -198,14 +213,14 @@ static void FDSFix(int a) IRQCount=IRQLatch=0; } else - IRQCount=IRQLatch; + IRQCount=IRQLatch; //IRQCount=IRQLatch; //0xFFFF; X6502_IRQBegin(FCEU_IQEXT); //printf("IRQ: %d\n",timestamp); // printf("IRQ: %d\n",scanline); } } - if(DiskSeekIRQ>0) + if(DiskSeekIRQ>0) { DiskSeekIRQ-=a; if(DiskSeekIRQ<=0) @@ -250,14 +265,14 @@ static DECLFR(FDSRead4031) 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)) + if(InDisk==255 || !(FDSRegs[5]&1) || (FDSRegs[5]&2)) ret|=2; return ret; } @@ -356,7 +371,7 @@ static DECLFW(FDSSWrite) A-=0x4080; switch(A) { - case 0x0: + case 0x0: case 0x4: if(V&0x80) amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F); break; @@ -372,7 +387,7 @@ static DECLFW(FDSSWrite) break; } //if(A>=0x7 && A!=0x8 && A<=0xF) - //if(A==0xA || A==0x9) + //if(A==0xA || A==0x9) //printf("$%04x:$%02x\n",A,V); SPSG[A]=V; } @@ -439,7 +454,7 @@ static INLINE void ClockRise(void) b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8)); b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76; - if(!(SPSG[0x7]&0x80)) + if(!(SPSG[0x7]&0x80)) { int t=fdso.mwave[(b17latch76>>13)&0x1F]&7; int t2=amplitude[1]; @@ -459,13 +474,13 @@ static INLINE void ClockRise(void) b8shiftreg88=0x80 + adj; } else - { + { b8shiftreg88=0x80; } } else { - b19shiftreg60<<=1; + b19shiftreg60<<=1; b8shiftreg88>>=1; } // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF; @@ -495,7 +510,7 @@ dogk: if(fdso.envcount<=0) { fdso.envcount+=SPSG[0xA]*3; - DoEnv(); + DoEnv(); } } if(fdso.count>=32768) goto dogk; @@ -702,7 +717,7 @@ static int SubLoad(FCEUFILE *fp) } else return(0); - } + } else TotalSides=header[4]; @@ -752,7 +767,7 @@ static void PostSave(void) for(b=0; b<65500; b++) diskdata[x][b] ^= diskdatao[x][b]; - } + } } @@ -770,7 +785,7 @@ int FDSLoad(const char *name, FCEUFILE *fp) #ifndef GEKKO fn = strdup(FCEU_MakeFName(FCEUMKF_FDSROM,0,0).c_str()); - if(!(zp=FCEUD_UTF8fopen(fn,"rb"))) + if(!(zp=FCEUD_UTF8fopen(fn,"rb"))) { FCEU_PrintError("FDS BIOS ROM image missing!"); FreeFDSMemory(); @@ -830,7 +845,7 @@ if (!disableBatteryLoading) for(x=0;xcommand_power()) PowerNES(); @@ -980,6 +982,12 @@ void FCEUMOV_AddInputState() if(mr->command_reset()) ResetNES(); + if(mr->command_fds_insert()) + FCEU_FDSInsert(); + + if(mr->command_fds_select()) + FCEU_FDSSelect(); + joyports[0].load(mr); joyports[1].load(mr); } @@ -993,13 +1001,13 @@ void FCEUMOV_AddInputState() } } - //pause the movie at a specified frame + //pause the movie at a specified frame if(FCEUMOV_ShouldPause() && FCEUI_EmulationPaused()==0) { FCEUI_ToggleEmulationPause(); FCEU_DispMessage("Paused at specified movie frame"); } - + } else if(movieMode == MOVIEMODE_RECORD) { @@ -1033,6 +1041,11 @@ void FCEUMOV_AddCommand(int cmd) //NOTE: EMOVIECMD matches FCEUNPCMD_RESET and FCEUNPCMD_POWER //we are lucky (well, I planned it that way) + switch(cmd) { + case FCEUNPCMD_FDSINSERT: cmd = MOVIECMD_FDS_INSERT; break; + case FCEUNPCMD_FDSSELECT: cmd = MOVIECMD_FDS_SELECT; break; + } + _currCommand |= cmd; #endif } @@ -1485,4 +1498,4 @@ bool CheckFileExists(const char* filename) #else return false; #endif -} \ No newline at end of file +} diff --git a/source/fceultra/movie.h b/source/fceultra/movie.h index 9dd5255..6296b2b 100644 --- a/source/fceultra/movie.h +++ b/source/fceultra/movie.h @@ -65,6 +65,8 @@ enum EMOVIECMD { MOVIECMD_RESET = 1, MOVIECMD_POWER = 2, + MOVIECMD_FDS_INSERT = 4, + MOVIECMD_FDS_SELECT = 8 }; EMOVIEMODE FCEUMOV_Mode(); @@ -104,6 +106,8 @@ public: uint8 commands; bool command_reset() { return (commands&MOVIECMD_RESET)!=0; } bool command_power() { return (commands&MOVIECMD_POWER)!=0; } + bool command_fds_insert() { return (commands&MOVIECMD_FDS_INSERT)!=0; } + bool command_fds_select() { return (commands&MOVIECMD_FDS_SELECT)!=0; } void toggleBit(int joy, int bit) { diff --git a/source/fceultra/nsf.cpp b/source/fceultra/nsf.cpp index 5810bd6..7fa2301 100644 --- a/source/fceultra/nsf.cpp +++ b/source/fceultra/nsf.cpp @@ -109,15 +109,19 @@ static DECLFR(NSFROMRead) static int doreset=0; static int NSFNMIFlags; -static uint8 *NSFDATA=0; -static int NSFMaxBank; +uint8 *NSFDATA=0; +int NSFMaxBank; static int NSFSize; static uint8 BSon; +static uint8 BankCounter; + static uint16 PlayAddr; static uint16 InitAddr; static uint16 LoadAddr; +extern char LoadedRomFName[2048]; + NSF_HEADER NSFHeader; //mbg merge 6/29/06 - needs to be global void NSFMMC5_Close(void); @@ -156,11 +160,11 @@ static INLINE void BANKSET(uint32 A, uint32 bank) bank&=NSFMaxBank; if(NSFHeader.SoundChip&4) memcpy(ExWRAM+(A-0x6000),NSFDATA+(bank<<12),4096); - else + else setprg4(A,bank); } -int NSFLoad(FCEUFILE *fp) +int NSFLoad(const char *name, FCEUFILE *fp) { int x; #ifndef GEKKO @@ -188,7 +192,7 @@ int NSFLoad(FCEUFILE *fp) NSFSize=FCEU_fgetsize(fp)-0x80; NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096); - NSFMaxBank=uppow2(NSFMaxBank); + NSFMaxBank=PRGsize[0]=uppow2(NSFMaxBank); if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096))) return 0; @@ -200,6 +204,30 @@ int NSFLoad(FCEUFILE *fp) NSFMaxBank--; BSon=0; + for(x=0;x<8;x++) + { + BSon|=NSFHeader.BankSwitch[x]; + } + + if(BSon==0) + { + BankCounter=0x00; + + if ((NSFHeader.LoadAddressHigh & 0x70) >= 0x70) + { + //Ice Climber, and other F000 base address tunes need this + BSon=0xFF; + } + else { + for(x=(NSFHeader.LoadAddressHigh & 0x70) / 0x10;x<8;x++) + { + NSFHeader.BankSwitch[x]=BankCounter; + BankCounter+=0x01; + } + BSon=0; + } + } + for(x=0;x<8;x++) BSon|=NSFHeader.BankSwitch[x]; @@ -226,6 +254,8 @@ int NSFLoad(FCEUFILE *fp) GameInterface=NSFGI; + strcpy(LoadedRomFName,name); + FCEU_printf("NSF Loaded. File information:\n\n"); FCEU_printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright); if(NSFHeader.SoundChip) @@ -331,7 +361,7 @@ void NSF_init(void) SetReadHandler(0x3ff0,0x3fff,NSF_read); - if(NSFHeader.SoundChip&1) { + if(NSFHeader.SoundChip&1) { NSFVRC6_Init(); } else if(NSFHeader.SoundChip&2) { NSFVRC7_Init(); @@ -370,7 +400,7 @@ static DECLFW(NSF_write) A&=0xF; BANKSET((A*4096),V); break; - } + } } static DECLFR(NSF_read) @@ -393,13 +423,13 @@ static DECLFR(NSF_read) BWrite[0x4000+x](0x4000+x,0); BWrite[0x4015](0x4015,0xF); - if(NSFHeader.SoundChip&4) + if(NSFHeader.SoundChip&4) { BWrite[0x4017](0x4017,0xC0); /* FDS BIOS writes $C0 */ BWrite[0x4089](0x4089,0x80); BWrite[0x408A](0x408A,0xE8); } - else + else { memset(ExWRAM,0x00,8192); BWrite[0x4017](0x4017,0xC0); @@ -453,8 +483,8 @@ void DrawNSF(uint8 *XBuf) y=142+((Bufpl[(x*l)>>8]*mul)>>14); if(y<240) XBuf[x+y*256]=3; - } - } + } + } else if(special==1) { if(FSettings.SoundVolume) @@ -493,7 +523,7 @@ void DrawNSF(uint8 *XBuf) n=120+r*sin(t); if(m<256 && n<240) - XBuf[m+n*256]=3; + XBuf[m+n*256]=3; } for(x=128;x<256;x++) @@ -533,9 +563,9 @@ void DrawNSF(uint8 *XBuf) tmp=FCEU_GetJoyJoy(); if((tmp&JOY_RIGHT) && !(last&JOY_RIGHT)) { - if(CurrentSong @@ -102,45 +102,81 @@ BITREVLUT bitrevlut; struct PPUSTATUS { - int sl; - int cycle, end_cycle; + int32 sl; + int32 cycle, end_cycle; }; struct SPRITE_READ { - int num; - int count; - int fetch; - int found; - int found_pos[8]; - int ret; - int last; - int mode; + int32 num; + int32 count; + int32 fetch; + int32 found; + int32 found_pos[8]; + int32 ret; + int32 last; + int32 mode; + + void reset() { + num = count = fetch = found = ret = last = mode = 0; + found_pos[0] = found_pos[1] = found_pos[2] = found_pos[3] = 0; + found_pos[4] = found_pos[5] = found_pos[6] = found_pos[7] = 0; + } + + void start_scanline() + { + num = 1; + found = 0; + fetch = 1; + count = 0; + last = 64; + mode = 0; + found_pos[0] = found_pos[1] = found_pos[2] = found_pos[3] = 0; + found_pos[4] = found_pos[5] = found_pos[6] = found_pos[7] = 0; + } }; -struct SPRITE_READ spr_read = { 0 }; + +//doesn't need to be savestated as it is just a reflection of the current position in the ppu loop +PPUPHASE ppuphase; + +//this needs to be savestated since a game may be trying to read from this across vblanks +SPRITE_READ spr_read; + +//definitely needs to be savestated +uint8 idleSynch = 1; //uses the internal counters concept at http://nesdev.icequake.net/PPU%20addressing.txt struct PPUREGS { + //normal clocked regs. as the game can interfere with these at any time, they need to be savestated uint32 fv;//3 uint32 v;//1 uint32 h;//1 uint32 vt;//5 uint32 ht;//5 - uint32 fh;//3 - uint32 s;//1 - uint32 par;//8 - uint32 ar;//2 - - uint32 _fv, _v, _h, _vt, _ht; - - struct PPUSTATUS status; - - PPUREGS() - : fv(0), v(0), h(0), vt(0), ht(0), fh(0), s(0), par(0), ar(0) - , _fv(0), _v(0), _h(0), _vt(0), _ht(0) - { status.cycle = 0; status.end_cycle = 341; - status.sl = 241; - } + //temp unlatched regs (need savestating, can be written to at any time) + uint32 _fv, _v, _h, _vt, _ht; + + //other regs that need savestating + uint32 fh;//3 (horz scroll) + uint32 s;//1 ($2000 bit 4: "Background pattern table address (0: $0000; 1: $1000)") + + //other regs that don't need saving + uint32 par;//8 (sort of a hack, just stored in here, but not managed by this system) + + //cached state data. these are always reset at the beginning of a frame and don't need saving + //but just to be safe, we're gonna save it + PPUSTATUS status; + + void reset() + { + fv = v = h = vt = ht = 0; + fh = par = s = 0; + _fv = _v = _h = _vt = _ht = 0; + status.cycle = 0; + status.end_cycle = 341; + status.sl = 241; + } + void install_latches() { fv = _fv; v = _v; @@ -365,6 +401,8 @@ int FCEUPPU_GetAttr(int ntnum, int xt, int yt) { inline void FFCEUX_PPUWrite_Default(uint32 A, uint8 V) { uint32 tmp = A; + if(PPU_hook) PPU_hook(A); + if(tmp<0x2000) { if(PPUCHRRAM&(1<<(tmp>>10))) @@ -390,9 +428,11 @@ inline void FFCEUX_PPUWrite_Default(uint32 A, uint8 V) { } } -uint8 FFCEUX_PPURead_Default(uint32 A) { +uint8 FASTCALL FFCEUX_PPURead_Default(uint32 A) { uint32 tmp = A; + if(PPU_hook) PPU_hook(A); + if(tmp<0x2000) { return VPage[tmp>>10][tmp]; @@ -421,15 +461,10 @@ uint8 FFCEUX_PPURead_Default(uint32 A) { } -uint8 (*FFCEUX_PPURead)(uint32 A) = 0; +uint8 (FASTCALL *FFCEUX_PPURead)(uint32 A) = 0; void (*FFCEUX_PPUWrite)(uint32 A, uint8 V) = 0; -#define CALL_PPUREAD(A) (FFCEUX_PPURead?FFCEUX_PPURead(A):(\ - ((A)<0x2000)? \ - VPage[(A)>>10][(A)] \ - : vnapage[((A)>>10)&0x3][(A)&0x3FF] \ - )) - +#define CALL_PPUREAD(A) (FFCEUX_PPURead(A)) #define CALL_PPUWRITE(A,V) (FFCEUX_PPUWrite?FFCEUX_PPUWrite(A,V):FFCEUX_PPUWrite_Default(A,V)) @@ -1777,6 +1812,11 @@ void FCEUPPU_Init(void) makeppulut(); } +void PPU_ResetHooks() +{ + FFCEUX_PPURead = FFCEUX_PPURead_Default; +} + void FCEUPPU_Reset(void) { VRAMBuffer=PPU[0]=PPU[1]=PPU_status=PPU[3]=0; @@ -1786,7 +1826,11 @@ void FCEUPPU_Reset(void) vtoggle = 0; ppudead = 2; kook = 0; + idleSynch = 1; // XOffset=0; + + ppur.reset(); + spr_read.reset(); } void FCEUPPU_Power(void) @@ -1823,7 +1867,7 @@ void FCEUPPU_Power(void) int FCEUPPU_Loop(int skip) { - if(newppu) { + if((newppu) && (GameInfo->type!=GIT_NSF)) { int FCEUX_PPU_Loop(int skip); return FCEUX_PPU_Loop(skip); } @@ -1988,6 +2032,41 @@ SFORMAT FCEUPPU_STATEINFO[]={ { 0 } }; +SFORMAT FCEU_NEWPPU_STATEINFO[] = { + { &idleSynch, 1, "IDLS" }, + { &spr_read.num, 4|FCEUSTATE_RLSB, "SR_0" }, + { &spr_read.count, 4|FCEUSTATE_RLSB, "SR_1" }, + { &spr_read.fetch, 4|FCEUSTATE_RLSB, "SR_2" }, + { &spr_read.found, 4|FCEUSTATE_RLSB, "SR_3" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx0" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx1" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx2" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx3" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx4" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx5" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx6" }, + { &spr_read.found_pos[0], 4|FCEUSTATE_RLSB, "SRx7" }, + { &spr_read.ret, 4|FCEUSTATE_RLSB, "SR_4" }, + { &spr_read.last, 4|FCEUSTATE_RLSB, "SR_5" }, + { &spr_read.mode, 4|FCEUSTATE_RLSB, "SR_6" }, + { &ppur.fv, 4|FCEUSTATE_RLSB, "PFVx" }, + { &ppur.v, 4|FCEUSTATE_RLSB, "PVxx" }, + { &ppur.h, 4|FCEUSTATE_RLSB, "PHxx" }, + { &ppur.vt, 4|FCEUSTATE_RLSB, "PVTx" }, + { &ppur.ht, 4|FCEUSTATE_RLSB, "PHTx" }, + { &ppur._fv, 4|FCEUSTATE_RLSB, "P_FV" }, + { &ppur._v, 4|FCEUSTATE_RLSB, "P_Vx" }, + { &ppur._h, 4|FCEUSTATE_RLSB, "P_Hx" }, + { &ppur._vt, 4|FCEUSTATE_RLSB, "P_VT" }, + { &ppur._ht, 4|FCEUSTATE_RLSB, "P_HT" }, + { &ppur.fh, 4|FCEUSTATE_RLSB, "PFHx" }, + { &ppur.s, 4|FCEUSTATE_RLSB, "PSxx" }, + { &ppur.status.sl, 4|FCEUSTATE_RLSB, "PST0" }, + { &ppur.status.cycle, 4|FCEUSTATE_RLSB, "PST1" }, + { &ppur.status.end_cycle, 4|FCEUSTATE_RLSB, "PST2" }, + { 0 } +}; + void FCEUPPU_SaveState(void) { TempAddrT=TempAddr; @@ -2000,13 +2079,14 @@ int pputime=0; int totpputime=0; const int kLineTime=341; const int kFetchTime=2; -int idleSynch = 1; void runppu(int x) { //pputime+=x; //if(cputodo<200) return; + ppur.status.cycle = (ppur.status.cycle + x) % ppur.status.end_cycle; + X6502_Run(x); //pputime -= cputodo<<2; } @@ -2016,7 +2096,7 @@ struct BGData { struct Record { uint8 nt, at, pt[2]; - void Read() { + INLINE void Read() { RefreshAddr = ppur.get_ntread(); nt = CALL_PPUREAD(RefreshAddr); runppu(kFetchTime); @@ -2056,6 +2136,7 @@ struct BGData { int framectr=0; int FCEUX_PPU_Loop(int skip) { + //262 scanlines if (ppudead) { @@ -2071,7 +2152,7 @@ int FCEUX_PPU_Loop(int skip) { runppu(20*kLineTime); ppur.status.sl = 0; runppu(242*kLineTime); - ppudead = 0; + --ppudead; goto finish; } @@ -2108,26 +2189,20 @@ int FCEUX_PPU_Loop(int skip) { //if(PPUON) // ppur.install_latches(); - uint8 oams[2][64][7]; - int oamcounts[2]={0,0}; - int oamslot=0; - int oamcount; + static uint8 oams[2][64][8]; //[7] turned to [8] for faster indexing + static int oamcounts[2]={0,0}; + static int oamslot=0; + static int oamcount; //capture the initial xscroll //int xscroll = ppur.fh; //render 241 scanlines (including 1 dummy at beginning) for(int sl=0;sl<241;sl++) { - spr_read.num = 1; - spr_read.found = 0; - spr_read.fetch = 1; - spr_read.count = 0; - spr_read.last = 64; - spr_read.mode = 0; - memset(spr_read.found_pos, 0, sizeof(spr_read.found_pos)); + spr_read.start_scanline(); ppur.status.sl = sl; - int yp = sl-1; + const int yp = sl-1; ppuphase = PPUPHASE_BG; if(sl != 0) { @@ -2139,8 +2214,8 @@ int FCEUX_PPU_Loop(int skip) { //twiddle the oam buffers - int scanslot = oamslot^1; - int renderslot = oamslot; + const int scanslot = oamslot^1; + const int renderslot = oamslot; oamslot ^= 1; oamcount = oamcounts[renderslot]; @@ -2151,25 +2226,26 @@ int FCEUX_PPU_Loop(int skip) { for(int xt=0;xt<32;xt++) { bgdata.main[xt+2].Read(); + //ok, we're also going to draw here. //unless we're on the first dummy scanline if(sl != 0) { int xstart = xt<<3; oamcount = oamcounts[renderslot]; - uint8 *target=XBuf+(yp<<8)+xstart; + uint8 * const target=XBuf+(yp<<8)+xstart; uint8 *ptr = target; int rasterpos = xstart; //check all the conditions that can cause things to render in these 8px - bool renderspritenow = SpriteON && rendersprites && (xt>0 || SpriteLeft8); - bool renderbgnow = ScreenON && renderbg && (xt>0 || BGLeft8); + const bool renderspritenow = SpriteON && rendersprites && (xt>0 || SpriteLeft8); + const bool renderbgnow = ScreenON && renderbg && (xt>0 || BGLeft8); for(int xp=0;xp<8;xp++,rasterpos++) { //bg pos is different from raster pos due to its offsetability. //so adjust for that here - int bgpos = rasterpos + ppur.fh; - int bgpx = bgpos&7; - int bgtile = bgpos>>3; + const int bgpos = rasterpos + ppur.fh; + const int bgpx = bgpos&7; + const int bgtile = bgpos>>3; uint8 pixel=0, pixelcolor; @@ -2236,7 +2312,7 @@ int FCEUX_PPU_Loop(int skip) { //look for sprites (was supposed to run concurrent with bg rendering) oamcounts[scanslot] = 0; oamcount=0; - int spriteHeight = Sprite16?16:8; + const int spriteHeight = Sprite16?16:8; for(int i=0;i<64;i++) { uint8* spr = SPRAM+i*4; if(yp >= spr[0] && yp < spr[0]+spriteHeight) { @@ -2289,9 +2365,9 @@ int FCEUX_PPU_Loop(int skip) { //this is how we support the no 8 sprite limit feature. //not that at some point we may need a virtual CALL_PPUREAD which just peeks and doesnt increment any counters //this could be handy for the debugging tools also - bool realSprite = (s<8); + const bool realSprite = (s<8); - uint8* oam = oams[scanslot][s]; + uint8* const oam = oams[scanslot][s]; uint32 line = yp - oam[0]; if(oam[2]&0x80) //vflip line = spriteHeight-line-1; diff --git a/source/fceultra/ppu.h b/source/fceultra/ppu.h index 8e73bcc..c74a31d 100644 --- a/source/fceultra/ppu.h +++ b/source/fceultra/ppu.h @@ -18,8 +18,17 @@ void FCEUPPU_SaveState(void); void FCEUPPU_LoadState(int version); uint8* FCEUPPU_GetCHR(uint32 vadr, uint32 refreshaddr); -extern uint8 (*FFCEUX_PPURead)(uint32 A); +#ifdef _MSC_VER +#define FASTCALL __fastcall +#else +#define FASTCALL +#endif + +void PPU_ResetHooks(); +extern uint8 (FASTCALL *FFCEUX_PPURead)(uint32 A); extern void (*FFCEUX_PPUWrite)(uint32 A, uint8 V); +extern uint8 FASTCALL FFCEUX_PPURead_Default(uint32 A); +void FFCEUX_PPUWrite_Default(uint32 A, uint8 V); extern int scanline; extern uint8 PPU[4]; diff --git a/source/fceultra/state.cpp b/source/fceultra/state.cpp index 9cc3705..d450aa3 100644 --- a/source/fceultra/state.cpp +++ b/source/fceultra/state.cpp @@ -75,6 +75,7 @@ static int SFEXINDEX; extern SFORMAT FCEUPPU_STATEINFO[]; +extern SFORMAT FCEU_NEWPPU_STATEINFO[]; extern SFORMAT FCEUSND_STATEINFO[]; extern SFORMAT FCEUCTRL_STATEINFO[]; extern SFORMAT FCEUMOV_STATEINFO[]; @@ -248,6 +249,7 @@ static bool ReadStateChunks(std::istream* is, int32 totalsize) { case 1:if(!ReadStateChunk(is,SFCPU,size)) ret=false;break; case 3:if(!ReadStateChunk(is,FCEUPPU_STATEINFO,size)) ret=false;break; + case 31:if(!ReadStateChunk(is,FCEU_NEWPPU_STATEINFO,size)) ret=false;break; case 4:if(!ReadStateChunk(is,FCEUCTRL_STATEINFO,size)) ret=false;break; case 7: if(!FCEUMOV_ReadState(is,size)) { @@ -355,6 +357,7 @@ bool FCEUSS_SaveMS(std::ostream* outstream, int compressionLevel) totalsize=WriteStateChunk(os,1,SFCPU); totalsize+=WriteStateChunk(os,2,SFCPUC); totalsize+=WriteStateChunk(os,3,FCEUPPU_STATEINFO); + totalsize+=WriteStateChunk(os,31,FCEU_NEWPPU_STATEINFO); totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO); totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO); if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD)) @@ -451,7 +454,7 @@ void FCEUSS_Save(const char *fname) fn = strdup(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0).c_str()); //backup existing savestate first - if (CheckFileExists(fn)) + if (CheckFileExists(fn)) { CreateBackupSaveState(fn); //Make a backup of previous savestate before overwriting it strcpy(lastSavestateMade,fn); //Remember what the last savestate filename was (for undoing later) @@ -459,7 +462,7 @@ void FCEUSS_Save(const char *fname) } else undoSS = false; //so backup made so lastSavestateMade does have a backup file, so no undo - + st = FCEUD_UTF8_fstream(fn,"wb"); free(fn); } @@ -500,7 +503,7 @@ int FCEUSS_LoadFP_old(std::istream* is, ENUM_SSLOADPARAMS params) //{ // fn=FCEU_MakeFName(FCEUMKF_NPTEMP,0,0); // FILE *fp; - // + // // if((fp=fopen(fn,"wb"))) // { // if(FCEUSS_SaveFP(fp)) @@ -555,7 +558,7 @@ int FCEUSS_LoadFP_old(std::istream* is, ENUM_SSLOADPARAMS params) if(x) { FCEUPPU_LoadState(stateversion); - FCEUSND_LoadState(stateversion); + FCEUSND_LoadState(stateversion); x=FCEUMOV_PostLoad(); } if(fn) @@ -564,7 +567,7 @@ int FCEUSS_LoadFP_old(std::istream* is, ENUM_SSLOADPARAMS params) //{ // * Oops! Load the temporary savestate */ // FILE *fp; - // + // // if((fp=fopen(fn,"rb"))) // { // FCEUSS_LoadFP(fp,SSLOADPARAM_NOBACKUP); @@ -600,7 +603,7 @@ bool FCEUSS_LoadFP(std::istream* is, ENUM_SSLOADPARAMS params) if(!ret && backup) FCEUSS_LoadFP(&msBackupSavestate,SSLOADPARAM_NOBACKUP); return ret; } - + int totalsize = FCEU_de32lsb(header + 4); int stateversion = FCEU_de32lsb(header + 8); int comprlen = FCEU_de32lsb(header + 12); @@ -752,8 +755,8 @@ void ResetExState(void (*PreSave)(void), void (*PostSave)(void)) free(SFMDATA[x].desc); } // adelikat, 3/14/09: had to add this to clear out the size parameter. NROM(mapper 0) games were having savestate crashes if loaded after a non NROM game because the size variable was carrying over and causing savestates to save too much data - SFMDATA[0].s = 0; - + SFMDATA[0].s = 0; + SPreSave = PreSave; SPostSave = PostSave; SFEXINDEX=0; @@ -814,7 +817,7 @@ void FCEUI_SaveState(const char *fname) if(!FCEU_IsValidUI(FCEUI_SAVESTATE)) return; StateShow=0; - + FCEUSS_Save(fname); } @@ -833,10 +836,10 @@ void FCEUI_LoadState(const char *fname) from this ;)). */ BackupLoadState(); //Backup the current state before loading a new one - + if (!movie_readonly && autoMovieBackup && freshMovie) //If auto-backup is on, movie has not been altered this session and the movie is in read+write mode { - FCEUI_MakeBackupMovie(false); //Backup the movie before the contents get altered, but do not display messages + FCEUI_MakeBackupMovie(false); //Backup the movie before the contents get altered, but do not display messages } if(FCEUSS_Load(fname)) { @@ -890,8 +893,8 @@ string GenerateBackupSaveStateFn(const char *fname) string filename; filename = fname; //Convert fname to a string object int x = filename.find_last_of("."); //Find file extension - filename.insert(x,"-bak"); //add "-bak" before the dot. - + filename.insert(x,"-bak"); //add "-bak" before the dot. + return filename; } @@ -910,15 +913,15 @@ void SwapSaveState() //-------------------------------------------------------------------------------------------- //Both files must exist //-------------------------------------------------------------------------------------------- - - if (!lastSavestateMade) + + if (!lastSavestateMade) { FCEUI_DispMessage("Can't Undo"); FCEUI_printf("Undo savestate was attempted but unsuccessful because there was not a recently used savestate.\n"); return; //If there is no last savestate, can't undo } string backup = GenerateBackupSaveStateFn(lastSavestateMade); //Get filename of backup state - if (!CheckFileExists(backup.c_str())) + if (!CheckFileExists(backup.c_str())) { FCEUI_DispMessage("Can't Undo"); FCEUI_printf("Undo savestate was attempted but unsuccessful because there was not a backup of the last used savestate.\n"); @@ -930,11 +933,11 @@ void SwapSaveState() //-------------------------------------------------------------------------------------------- string temp = backup; //Put backup filename in temp temp.append("x"); //Add x - + rename(backup.c_str(),temp.c_str()); //rename backup file to temp file rename(lastSavestateMade,backup.c_str()); //rename current as backup rename(temp.c_str(),lastSavestateMade); //rename backup as current - + undoSS = true; //Just in case, if this was run, then there is definately a last savestate and backup if (redoSS) //This was a redo function, so if run again it will be an undo again redoSS = false; @@ -943,8 +946,8 @@ void SwapSaveState() FCEUI_DispMessage("%s restored",backup.c_str()); FCEUI_printf("%s restored\n",backup.c_str()); -} - +} + //------------------------------------------------------------------------------------------------------------------------------------------------------ //************************************************************************* //Loadstate backup functions @@ -957,7 +960,7 @@ string GetBackupFileName() //particularly from unintentional loadstating string filename; int x; - + filename = strdup(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0).c_str()); //Generate normal savestate filename x = filename.find_last_of("."); //Find last dot filename = filename.substr(0,x); //Chop off file extension @@ -971,11 +974,11 @@ bool CheckBackupSaveStateExist() //This function simply checks to see if the backup loadstate exists, the backup loadstate is a special savestate //That is made before loading any state, so that the user never loses his data string filename = GetBackupFileName(); //Get backup savestate filename - + //Check if this filename exists fstream test; test.open(filename.c_str(),fstream::in); - + if (test.fail()) { test.close(); @@ -1019,4 +1022,4 @@ void RedoLoadState() } redoLS = false; //Flag that RedoLoadState can not be run again undoLS = true; //Flag that LoadBackup can be run again -} +} \ No newline at end of file diff --git a/source/fceultra/types.h b/source/fceultra/types.h index 56b23c9..88d7a76 100644 --- a/source/fceultra/types.h +++ b/source/fceultra/types.h @@ -23,8 +23,8 @@ #define __FCEU_TYPES #define FCEU_NAME "FCEUX" -#define FCEU_VERSION_STRING "2.1.1-interim" -#define FCEU_VERSION_NUMERIC 20100 +#define FCEU_VERSION_STRING "2.1.2-interim" +#define FCEU_VERSION_NUMERIC 21020 #define FCEU_NAME_AND_VERSION FCEU_NAME " " FCEU_VERSION_STRING ///causes the code fragment argument to be compiled in if the build includes debugging diff --git a/source/ngc/fceuload.cpp b/source/ngc/fceuload.cpp index 7e95d35..7ab91b0 100644 --- a/source/ngc/fceuload.cpp +++ b/source/ngc/fceuload.cpp @@ -81,7 +81,7 @@ int GCMemROM(int method, int size) delete fceumem; fceumem = new memorystream((char *) nesrom, size); fceufp->stream = fceumem; - romLoaded = NSFLoad(fceufp); + romLoaded = NSFLoad(romFilename, fceufp); } if(!romLoaded) diff --git a/source/ngc/fceusupport.h b/source/ngc/fceusupport.h index c5db766..9eb06d7 100644 --- a/source/ngc/fceusupport.h +++ b/source/ngc/fceusupport.h @@ -40,7 +40,7 @@ int AddCheatEntry(char *name, uint32 addr, uint8 val, int compare, int status, i extern int FDSLoad(const char *name, FCEUFILE *fp); extern int iNESLoad(const char *name, FCEUFILE *fp, int o); extern int UNIFLoad(const char *name, FCEUFILE *fp); -extern int NSFLoad(FCEUFILE *fp); +extern int NSFLoad(const char *name, FCEUFILE *fp); extern uint8 FDSBIOS[8192]; extern uint8 *GENIEROM; extern FCEUGI *GameInfo;