Logo Search packages:      
Sourcecode: fceux version File versions  Download package

fceu.cpp

/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
*  Copyright (C) 2003 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 <string>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include "types.h"
#include "x6502.h"
#include "fceu.h"
#include "ppu.h"
#include "sound.h"
#include "netplay.h"
#include "file.h"
#include "utils/endian.h"
#include "utils/memory.h"
#include "utils/crc32.h"

#include "cart.h"
#include "nsf.h"
#include "fds.h"
#include "ines.h"
#include "unif.h"
#include "cheat.h"
#include "palette.h"
#include "state.h"
#include "movie.h"
#include "video.h"
#include "input.h"
#include "file.h"
#include "vsuni.h"
#include "ines.h"

#ifdef WIN32
#include "drivers/win/pref.h"
#endif

#include <fstream>
#include <sstream>

#ifdef _S9XLUA_H
#include "fceulua.h"
#endif

//TODO - we really need some kind of global platform-specific options api
#ifdef WIN32
#include "drivers/win/main.h"
#include "drivers/win/cheat.h"
#include "drivers/win/texthook.h"
#include "drivers/win/ram_search.h"
#include "drivers/win/ramwatch.h"
#include "drivers/win/memwatch.h"
#include "drivers/win/tracer.h"
#else
#include "drivers/sdl/sdl.h"
#endif

using namespace std;

int AFon = 1, AFoff = 1, AutoFireOffset = 0; //For keeping track of autofire settings
bool justLagged = false;
bool frameAdvanceLagSkip = false; //If this is true, frame advance will skip over lag frame (i.e. it will emulate 2 frames instead of 1)
bool AutoSS = false;          //Flagged true when the first auto-savestate is made while a game is loaded, flagged false on game close
bool movieSubtitles = true; //Toggle for displaying movie subtitles

FCEUGI::FCEUGI()
: filename(0)
, archiveFilename(0)
{
      printf("%08x",opsize);
}

FCEUGI::~FCEUGI()
{
      if(filename) delete filename;
      if(archiveFilename) delete archiveFilename;
}

static void CloseGame(void)
{
      if(GameInfo)
      {

#ifdef WIN32
// ################################## Start of SP CODE ###########################
      extern char LoadedRomFName[2048];

      if (storePreferences(LoadedRomFName))
      {
            FCEUD_PrintError("Couldn't store debugging data");
      }
// ################################## End of SP CODE ###########################
#endif

            if(FCEUnetplay)
            {
                  FCEUD_NetworkClose();
            }

            if(GameInfo->name)
            {
                  free(GameInfo->name);
                  GameInfo->name=0;
            }

            if(GameInfo->type!=GIT_NSF)
            {
                  FCEU_FlushGameCheats(0,0);
            }

            GameInterface(GI_CLOSE);

            FCEUI_StopMovie();

            ResetExState(0,0);

            //mbg 5/9/08 - clear screen when game is closed
            //http://sourceforge.net/tracker/index.php?func=detail&aid=1787298&group_id=13536&atid=113536
            extern uint8 *XBuf;
            if(XBuf)
                  memset(XBuf,0,256*256);

            CloseGenie();

            delete GameInfo;
            GameInfo = 0;

            //Reset frame counter
            currFrameCounter = 0;

            //Reset flags for Undo/Redo/Auto Savestating
            lastSavestateMade[0] = 0;
            undoSS = false;
            redoSS = false;
            lastLoadstateMade[0] = 0;
            undoLS = false;
            redoLS = false;
            AutoSS = false;
      }
}


uint64 timestampbase;


FCEUGI *GameInfo = 0;

void (*GameInterface)(GI h);
void (*GameStateRestore)(int version);

readfunc ARead[0x10000];
writefunc BWrite[0x10000];
static readfunc *AReadG;
static writefunc *BWriteG;
static int RWWrap=0;

//mbg merge 7/18/06 docs
//bit0 indicates whether emulation is paused
//bit1 indicates whether emulation is in frame step mode
int EmulationPaused=0;
bool frameAdvanceRequested=false;
int frameAdvanceDelay;

//indicates that the emulation core just frame advanced (consumed the frame advance state and paused)
bool JustFrameAdvanced=false;

static int AutosaveStatus[4] = {0, 0, 0, 0}; //is it safe to load Auto-savestate
static int AutosaveIndex = 0; //which Auto-savestate we're on

// Flag that indicates whether the Auto-save option is enabled or not
int EnableAutosave = 0;

///a wrapper for unzip.c
extern "C" FILE *FCEUI_UTF8fopen_C(const char *n, const char *m) { return ::FCEUD_UTF8fopen(n,m); }

static DECLFW(BNull)
{

}

static DECLFR(ANull)
{
      return(X.DB);
}

int AllocGenieRW(void)
{
      if(!(AReadG=(readfunc *)FCEU_malloc(0x8000*sizeof(readfunc))))
            return 0;
      if(!(BWriteG=(writefunc *)FCEU_malloc(0x8000*sizeof(writefunc))))
            return 0;
      RWWrap=1;
      return 1;
}

void FlushGenieRW(void)
{
      int32 x;

      if(RWWrap)
      {
            for(x=0;x<0x8000;x++)
            {
                  ARead[x+0x8000]=AReadG[x];
                  BWrite[x+0x8000]=BWriteG[x];
            }
            free(AReadG);
            free(BWriteG);
            AReadG=0;
            BWriteG=0;
            RWWrap=0;
      }
}

readfunc GetReadHandler(int32 a)
{
      if(a>=0x8000 && RWWrap)
            return AReadG[a-0x8000];
      else
            return ARead[a];
}
void SetReadHandler(int32 start, int32 end, readfunc func)
{
      int32 x;
      if(!func)
            func=ANull;

      if(RWWrap)
            for(x=end;x>=start;x--)
            {
                  if(x>=0x8000)
                        AReadG[x-0x8000]=func;
                  else
                        ARead[x]=func;
            }
      else

            for(x=end;x>=start;x--)
                  ARead[x]=func;
}

writefunc GetWriteHandler(int32 a)
{
      if(RWWrap && a>=0x8000)
            return BWriteG[a-0x8000];
      else
            return BWrite[a];
}

void SetWriteHandler(int32 start, int32 end, writefunc func)
{
      int32 x;

      if(!func)
            func=BNull;

      if(RWWrap)
            for(x=end;x>=start;x--)
            {
                  if(x>=0x8000)
                        BWriteG[x-0x8000]=func;
                  else
                        BWrite[x]=func;
            }
      else
            for(x=end;x>=start;x--)
                  BWrite[x]=func;
}

uint8 *GameMemBlock;
uint8 *RAM;

//---------
//windows might need to allocate these differently, so we have some special code

static void AllocBuffers()
{

#ifdef _USE_SHARED_MEMORY_

      void win_AllocBuffers(uint8 **GameMemBlock, uint8 **RAM);
      win_AllocBuffers(&GameMemBlock, &RAM);

#else 

      GameMemBlock = (uint8*)FCEU_gmalloc(131072);
      RAM = (uint8*)FCEU_gmalloc(0x800);

#endif
}

static void FreeBuffers() {
#ifdef _USE_SHARED_MEMORY_
      void win_FreeBuffers(uint8 *GameMemBlock, uint8 *RAM);
      win_FreeBuffers(GameMemBlock, RAM);
#else 
      FCEU_free(GameMemBlock);
      FCEU_free(RAM);
#endif
}
//------

uint8 PAL=0;

static DECLFW(BRAML)
{  
      RAM[A]=V;
      #ifdef _S9XLUA_H
      CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE);
      #endif
}

static DECLFW(BRAMH)
{
      RAM[A&0x7FF]=V;
      #ifdef _S9XLUA_H
      CallRegisteredLuaMemHook(A&0x7FF, 1, V, LUAMEMHOOK_WRITE);
      #endif
}

static DECLFR(ARAML)
{
      return RAM[A];
}

static DECLFR(ARAMH)
{
      return RAM[A&0x7FF];
}


void ResetGameLoaded(void)
{
      if(GameInfo) CloseGame();
      EmulationPaused = 0; //mbg 5/8/08 - loading games while paused was bad news. maybe this fixes it
      GameStateRestore=0;
      PPU_hook=0;
      GameHBIRQHook=0;
      FFCEUX_PPURead = 0;
      FFCEUX_PPUWrite = 0;
      if(GameExpSound.Kill)
            GameExpSound.Kill();
      memset(&GameExpSound,0,sizeof(GameExpSound));
      MapIRQHook=0;
      MMC5Hack=0;
      PAL&=1;
      pale=0;
}

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(const char *name, FCEUFILE *fp);

//char lastLoadedGameName [2048] = {0,}; // hack for movie WRAM clearing on record from poweron


FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode)
{
      //mbg merge 7/17/07 - why is this here
      //#ifdef WIN32
      //    StopSound();
      //#endif

      //----------
      //attempt to open the files
      FCEUFILE *fp;

      FCEU_printf("Loading %s...\n\n",name);

      const char* romextensions[] = {"nes","fds",0};
      fp=FCEU_fopen(name,0,"rb",0,-1,romextensions);
      if(!fp)
      {
            return 0;
      }

      GetFileBase(fp->filename.c_str());

      if(!fp) {
            FCEU_PrintError("Error opening \"%s\"!",name);
            return 0;
      }
      //---------

      //file opened ok. start loading.

      ResetGameLoaded();

      AutosaveStatus[0] = AutosaveStatus[1] = 0;
      AutosaveStatus[2] = AutosaveStatus[3] = 0;

      CloseGame();
      GameInfo = new FCEUGI();
      memset(GameInfo, 0, sizeof(FCEUGI));

      GameInfo->filename = strdup(fp->filename.c_str());
      if(fp->archiveFilename != "") GameInfo->archiveFilename = strdup(fp->archiveFilename.c_str());
      GameInfo->archiveCount = fp->archiveCount;

      GameInfo->soundchan = 0;
      GameInfo->soundrate = 0;
      GameInfo->name=0;
      GameInfo->type=GIT_CART;
      GameInfo->vidsys=GIV_USER;
      GameInfo->input[0]=GameInfo->input[1]=SI_UNSET;
      GameInfo->inputfc=SIFC_UNSET;
      GameInfo->cspecial=SIS_NONE;

      //try to load each different format
      bool FCEUXLoad(const char *name, FCEUFILE *fp);
      /*if(FCEUXLoad(name,fp))
            goto endlseq;*/
      if(iNESLoad(name,fp,OverwriteVidMode))
            goto endlseq;
      if(NSFLoad(name,fp))
            goto endlseq;
      if(UNIFLoad(name,fp))
            goto endlseq;
      if(FDSLoad(name,fp))
            goto endlseq;

      FCEU_PrintError("An error occurred while loading the file.");
      FCEU_fclose(fp);

      delete GameInfo;
      GameInfo = 0;

      return 0;

endlseq:

      FCEU_fclose(fp);

#ifdef WIN32
// ################################## Start of SP CODE ###########################
            extern char LoadedRomFName[2048];
            extern int loadDebugDataFailed;

            if ((loadDebugDataFailed = loadPreferences(LoadedRomFName)))
            {
                  FCEUD_PrintError("Couldn't load debugging data");
            }

// ################################## End of SP CODE ###########################
#endif

      FCEU_ResetVidSys();

      if(GameInfo->type!=GIT_NSF)
            if(FSettings.GameGenie)
                  OpenGenie();
      PowerNES();

      if(GameInfo->type!=GIT_NSF)
            FCEU_LoadGamePalette();

      FCEU_ResetPalette();
      FCEU_ResetMessages();   // Save state, status messages, etc.

      if(GameInfo->type!=GIT_NSF)
            FCEU_LoadGameCheats(0);

#if defined (WIN32) || defined (WIN64)
      DoDebuggerRunCheck(); //Can't safely do it in loadPreferences
#endif

      return GameInfo;
}

FCEUGI *FCEUI_LoadGame(const char *name, int OverwriteVidMode)
{
      return FCEUI_LoadGameVirtual(name,OverwriteVidMode);
}


//Return: Flag that indicates whether the function was succesful or not.
bool FCEUI_Initialize()
{
      srand(time(0));

      if(!FCEU_InitVirtualVideo())
      {
            return false;
      }

      AllocBuffers();

      // Initialize some parts of the settings structure
      //mbg 5/7/08 - I changed the ntsc settings to match pal.
      //this is more for precision emulation, instead of entertainment, which is what fceux is all about nowadays
      memset(&FSettings,0,sizeof(FSettings));
      //FSettings.UsrFirstSLine[0]=8;
      FSettings.UsrFirstSLine[0]=0;
      FSettings.UsrFirstSLine[1]=0;
      //FSettings.UsrLastSLine[0]=231;
      FSettings.UsrLastSLine[0]=239;
      FSettings.UsrLastSLine[1]=239;
      FSettings.SoundVolume=150;          //0-150 scale
      FSettings.TriangleVolume=256; //0-256 scale (256 is max volume)
      FSettings.Square1Volume=256;  //0-256 scale (256 is max volume)
      FSettings.Square2Volume=256;  //0-256 scale (256 is max volume)
      FSettings.NoiseVolume=256;          //0-256 scale (256 is max volume)
      FSettings.PCMVolume=256;            //0-256 scale (256 is max volume)

      FCEUPPU_Init();

      X6502_Init();

      return true;
}

void FCEUI_Kill(void)
{
      #ifdef _S9XLUA_H
      FCEU_LuaStop();
      #endif
      FCEU_KillVirtualVideo();
      FCEU_KillGenie();
      FreeBuffers();
}

int rapidAlternator = 0;
int AutoFirePattern[8] = {1,0,0,0,0,0,0,0};
int AutoFirePatternLength = 2;

void SetAutoFirePattern(int onframes, int offframes)
{
      int i;
      for(i = 0; i < onframes && i < 8; i++)
      {
            AutoFirePattern[i] = 1;
      }
      for(;i < 8; i++)
      {
            AutoFirePattern[i] = 0;
      }
      if(onframes + offframes < 2)
      {
            AutoFirePatternLength = 2;
      }
      else if(onframes + offframes > 8)
      {
            AutoFirePatternLength = 8;
      }
      else
      {
            AutoFirePatternLength = onframes + offframes;
      }
      AFon = onframes; AFoff = offframes;
}

void SetAutoFireOffset(int offset)
{
      if(offset < 0 || offset > 8) return;
      AutoFireOffset = offset;
}

void AutoFire(void)
{
      static int counter = 0;
      if (justLagged == false) counter = (counter + 1) % (8*7*5*3);
      //If recording a movie, use the frame # for the autofire so the offset
      //doesn't get screwed up when loading.
      if(FCEUMOV_Mode(MOVIEMODE_RECORD | MOVIEMODE_PLAY))
      {
            rapidAlternator= AutoFirePattern[(AutoFireOffset + FCEUMOV_GetFrame())%AutoFirePatternLength];
      }
      else
      {
            rapidAlternator= AutoFirePattern[(AutoFireOffset + counter)%AutoFirePatternLength];
      }
}

void UpdateAutosave(void);

///Emulates a single frame.

///Skip may be passed in, if FRAMESKIP is #defined, to cause this to emulate more than one frame
void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int skip)
{
      //skip initiates frame skip if 1, or frame skip and sound skip if 2
      int r,ssize;

      JustFrameAdvanced = false;

      if (frameAdvanceRequested)
      {
            if (frameAdvanceDelay==0 || frameAdvanceDelay>=10)
                  EmulationPaused = 3;
            if (frameAdvanceDelay==0 || frameAdvanceDelay < 10)
                  frameAdvanceDelay++;
      }

      if(EmulationPaused&2)
            EmulationPaused &= ~1;        // clear paused flag temporarily (frame advance)
      else if((EmulationPaused&1))
      {
            memcpy(XBuf, XBackBuf, 256*256);
            FCEU_PutImage();
            *pXBuf=XBuf;
            *SoundBuf=WaveFinal;
            *SoundBufSize=0;

            return;
      }

      AutoFire();
      UpdateAutosave();

#ifdef _S9XLUA_H
      FCEU_LuaFrameBoundary();
#endif

      FCEU_UpdateInput();
      lagFlag = 1;

#ifdef _S9XLUA_H
      CallRegisteredLuaFunctions(LUACALL_BEFOREEMULATION);
#endif

      if(geniestage!=1) FCEU_ApplyPeriodicCheats();
      r = FCEUPPU_Loop(skip);

      if (skip != 2) ssize=FlushEmulateSound(); //If skip = 2 we are skipping sound processing

#ifdef _S9XLUA_H
      CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION);
#endif

#ifdef WIN32
      //These Windows only dialogs need to be updated only once per frame so they are included here
      UpdateCheatList();
      UpdateTextHooker();
      Update_RAM_Search(); // Update_RAM_Watch() is also called.
      RamChange();
      UpdateLogWindow();
      //FCEUI_AviVideoUpdate(XBuf);
#endif

      timestampbase += timestamp;
      timestamp = 0;

      *pXBuf=skip?0:XBuf;
      if (skip == 2) //If skip = 2, then bypass sound
      {
            *SoundBuf=0;
            *SoundBufSize=0;
      }
      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
      {
            EmulationPaused = 1;             // restore paused flag
            JustFrameAdvanced = true;
            #ifdef WIN32
                  if(soundoptions&SO_MUTEFA) //mute the frame advance if the user requested it
                        *SoundBufSize=0;       //keep sound muted
            #endif
            
      }
      
      currMovieData.TryDumpIncremental();
      
      if (lagFlag) 
      {
            lagCounter++;
            justLagged = true;
      }
      else justLagged = false;

      if (movieSubtitles)
            ProcessSubtitles();
}

void FCEUI_CloseGame(void)
{     
      if(!FCEU_IsValidUI(FCEUI_CLOSEGAME)) 
            return;

      CloseGame();
}

void ResetNES(void)
{
      FCEUMOV_AddCommand(FCEUNPCMD_RESET);
      if(!GameInfo) return;
      GameInterface(GI_RESETM2);
      FCEUSND_Reset();
      FCEUPPU_Reset();
      X6502_Reset();

      // clear back baffer
      extern uint8 *XBackBuf;
      memset(XBackBuf,0,256*256);
}

void FCEU_MemoryRand(uint8 *ptr, uint32 size)
{
      int x=0;
      while(size)
      {
            *ptr=(x&4)?0xFF:0x00;
            x++;
            size--;
            ptr++;
      }
}

void hand(X6502 *X, int type, unsigned int A)
{

}

int suppressAddPowerCommand=0; // hack... yeah, I know...
void PowerNES(void) 
{
      //void MapperInit();
      //MapperInit();

      if(!suppressAddPowerCommand)
            FCEUMOV_AddCommand(FCEUNPCMD_POWER);
      if(!GameInfo) return;

      FCEU_CheatResetRAM();
      FCEU_CheatAddRAM(2,0,RAM);

      GeniePower();

      FCEU_MemoryRand(RAM,0x800);
      //memset(RAM,0xFF,0x800);

      SetReadHandler(0x0000,0xFFFF,ANull);
      SetWriteHandler(0x0000,0xFFFF,BNull);

      SetReadHandler(0,0x7FF,ARAML);
      SetWriteHandler(0,0x7FF,BRAML);

      SetReadHandler(0x800,0x1FFF,ARAMH); // Part of a little
      SetWriteHandler(0x800,0x1FFF,BRAMH); //hack for a small speed boost.

      InitializeInput();
      FCEUSND_Power();
      FCEUPPU_Power();

      //Have the external game hardware "powered" after the internal NES stuff.  Needed for the NSF code and VS System code.
      GameInterface(GI_POWER);
      if(GameInfo->type==GIT_VSUNI)
            FCEU_VSUniPower();

      //if we are in a movie, then reset the saveram
      extern int disableBatteryLoading;
      if(disableBatteryLoading)
            GameInterface(GI_RESETSAVE);
            

      timestampbase=0;
      LagCounterReset();

      X6502_Power();
      FCEU_PowerCheats();
      // clear back baffer
      extern uint8 *XBackBuf;
      memset(XBackBuf,0,256*256);
}

void FCEU_ResetVidSys(void)
{
      int w;

      if(GameInfo->vidsys==GIV_NTSC)
            w=0; 
      else if(GameInfo->vidsys==GIV_PAL)
            w=1;  
      else
            w=FSettings.PAL;

      PAL=w?1:0;
      FCEUPPU_SetVideoSystem(w);
      SetSoundVariables();
}

FCEUS FSettings;

void FCEU_printf(char *format, ...)
{
      char temp[2048];

      va_list ap;

      va_start(ap,format);
      vsnprintf(temp,sizeof(temp),format,ap);
      FCEUD_Message(temp);

      va_end(ap);
}

void FCEU_PrintError(char *format, ...)
{
      char temp[2048];

      va_list ap;

      va_start(ap,format);
      vsnprintf(temp,sizeof(temp),format,ap);
      FCEUD_PrintError(temp);

      va_end(ap);
}

void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall)
{
      FSettings.UsrFirstSLine[0]=ntscf;
      FSettings.UsrLastSLine[0]=ntscl;
      FSettings.UsrFirstSLine[1]=palf;
      FSettings.UsrLastSLine[1]=pall;
      if(PAL)
      {
            FSettings.FirstSLine=FSettings.UsrFirstSLine[1];
            FSettings.LastSLine=FSettings.UsrLastSLine[1];
      }
      else
      {
            FSettings.FirstSLine=FSettings.UsrFirstSLine[0];
            FSettings.LastSLine=FSettings.UsrLastSLine[0];
      }

}

void FCEUI_SetVidSystem(int a)
{
      FSettings.PAL=a?1:0;
      if(GameInfo)
      {
            FCEU_ResetVidSys();
            FCEU_ResetPalette();
            FCEUD_VideoChanged();
      }
}

int FCEUI_GetCurrentVidSystem(int *slstart, int *slend)
{
      if(slstart)
            *slstart=FSettings.FirstSLine;
      if(slend)
            *slend=FSettings.LastSLine;
      return(PAL);
}

//Enable or disable Game Genie option.
void FCEUI_SetGameGenie(bool a)
{
      FSettings.GameGenie = a;
}

//this variable isn't used at all, snap is always name-based
//void FCEUI_SetSnapName(bool a)
//{
//    FSettings.SnapName = a;
//}

int32 FCEUI_GetDesiredFPS(void)
{
      if(PAL)
            return(838977920); // ~50.007
      else
            return(1008307711);     // ~60.1
}

int FCEUI_EmulationPaused(void)
{
      return (EmulationPaused&1);
}

int FCEUI_EmulationFrameStepped()
{
      return (EmulationPaused&2);
}

void FCEUI_ClearEmulationFrameStepped()
{
      EmulationPaused &=~2;
}

//mbg merge 7/18/06 added
//ideally maybe we shouldnt be using this, but i need it for quick merging
void FCEUI_SetEmulationPaused(int val)
{
      EmulationPaused = val;
}

void FCEUI_ToggleEmulationPause(void)
{
      EmulationPaused = (EmulationPaused&1)^1;
}

void FCEUI_FrameAdvanceEnd(void)
{
      frameAdvanceRequested = false;
}

void FCEUI_FrameAdvance(void)
{
      frameAdvanceRequested = true;
      frameAdvanceDelay = 0;
}

static int AutosaveCounter = 0;

void UpdateAutosave(void)
{
      if(!EnableAutosave)
            return;
      
      char * f;
      AutosaveCounter = (AutosaveCounter + 1) % 256;
      if(AutosaveCounter == 0)
      {
            AutosaveIndex = (AutosaveIndex + 1) % 4;
            f = strdup(FCEU_MakeFName(FCEUMKF_AUTOSTATE,AutosaveIndex,0).c_str());
            FCEUSS_Save(f);
            AutoSS = true;    //Flag that an auto-savestate was made
            free(f);
            AutosaveStatus[AutosaveIndex] = 1;
      }
}

void FCEUI_Autosave(void)
{
      if(!EnableAutosave || !AutoSS)
            return;

      if(AutosaveStatus[AutosaveIndex] == 1)
      {
            char * f;
            f = strdup(FCEU_MakeFName(FCEUMKF_AUTOSTATE,AutosaveIndex,0).c_str());
            FCEUSS_Load(f);
            free(f);

            //Set pointer to previous available slot
            if(AutosaveStatus[(AutosaveIndex + 3)%4] == 1)
            {
                  AutosaveIndex = (AutosaveIndex + 3)%4;
            }

            //Reset time to next Auto-save
            AutosaveCounter = 0;
      }
}

int FCEU_TextScanlineOffset(int y)
{
      return FSettings.FirstSLine*256;
}
int FCEU_TextScanlineOffsetFromBottom(int y)
{
      return (FSettings.LastSLine-y)*256;
}

bool FCEU_IsValidUI(EFCEUI ui)
{
      switch(ui)
      {
      case FCEUI_OPENGAME:
      case FCEUI_CLOSEGAME:
            if(FCEUMOV_Mode(MOVIEMODE_TASEDIT)) return false;
            break;
      case FCEUI_RECORDMOVIE:
      case FCEUI_PLAYMOVIE:
      case FCEUI_QUICKSAVE:
      case FCEUI_QUICKLOAD:
      case FCEUI_SAVESTATE:
      case FCEUI_LOADSTATE:
      case FCEUI_NEXTSAVESTATE:
      case FCEUI_PREVIOUSSAVESTATE:
      case FCEUI_VIEWSLOTS:
            if(!GameInfo) return false;
            if(FCEUMOV_Mode(MOVIEMODE_TASEDIT)) return false;
            break;

      case FCEUI_STOPMOVIE:
      case FCEUI_PLAYFROMBEGINNING:
            return FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD);

      case FCEUI_STOPAVI:
            return FCEUI_AviIsRecording();

      case FCEUI_TASEDIT:
            if(!GameInfo) return false;
            break;

      case FCEUI_RESET:
            if(!GameInfo) return false;
            if(FCEUMOV_Mode(MOVIEMODE_TASEDIT|MOVIEMODE_PLAY)) return false;
            break;

      case FCEUI_POWER:
            if(!GameInfo) return false;
            if(FCEUMOV_Mode(MOVIEMODE_RECORD)) return true;
            if(!FCEUMOV_Mode(MOVIEMODE_INACTIVE)) return false;
            break;

      }
      return true;
}

//---------------------
//experimental new mapper and ppu system follows

class FCEUXCart {
public:
      int mirroring;
      int chrPages, prgPages;
      uint32 chrSize, prgSize;
      char* CHR, *PRG;

      FCEUXCart()
            : CHR(0)
            , PRG(0)
      {}

      ~FCEUXCart() {
            if(CHR) delete[] CHR;
            if(PRG) delete[] PRG;
      }

      virtual void Power() {
      }

protected:
      //void SetReadHandler(int32 start, int32 end, readfunc func) {
};

FCEUXCart* cart = 0;

//uint8 Read_ByteFromRom(uint32 A) {
//    if(A>=cart->prgSize) return 0xFF;
//    return cart->PRG[A];
//}
//
//uint8 Read_Unmapped(uint32 A) {
//    return 0xFF;
//}



class NROM : FCEUXCart {
public:
      virtual void Power() {
            SetReadHandler(0x8000,0xFFFF,CartBR);
            setprg16(0x8000,0);
            setprg16(0xC000,~0);
            setchr8(0);

            vnapage[0] = NTARAM;
            vnapage[2] = NTARAM;
            vnapage[1] = NTARAM+0x400;
            vnapage[3] = NTARAM+0x400;
            PPUNTARAM=0xF;
      }
};

void FCEUXGameInterface(GI command) {
      switch(command) {
            case GI_POWER:
                  cart->Power();
      }
}



bool FCEUXLoad(const char *name, FCEUFILE *fp)
{
      //read ines header
      iNES_HEADER head;
      if(FCEU_fread(&head,1,16,fp)!=16)
            return false;

      //validate header
      if(memcmp(&head,"NES\x1a",4))
            return 0;

      int mapper = (head.ROM_type>>4);
      mapper |= (head.ROM_type2&0xF0);

      //choose what kind of cart to use.
      cart = (FCEUXCart*)new NROM();

      //fceu ines loading code uses 256 here when the romsize is 0.
      cart->prgPages = head.ROM_size;
      if(cart->prgPages == 0) {
            printf("FCEUX: received zero prgpages\n");
            cart->prgPages = 256;
      }

      cart->chrPages = head.VROM_size;

      cart->mirroring = (head.ROM_type&1); 
      if(head.ROM_type&8) cart->mirroring=2;

      //skip trainer
      bool hasTrainer = (head.ROM_type&4)!=0;
      if(hasTrainer) {
            FCEU_fseek(fp,512,SEEK_CUR);
      }

      //load data
      cart->prgSize = cart->prgPages*16*1024;
      cart->chrSize = cart->chrPages*8*1024;
      cart->PRG = new char[cart->prgSize];
      cart->CHR = new char[cart->chrSize];
      FCEU_fread(cart->PRG,1,cart->prgSize,fp);
      FCEU_fread(cart->CHR,1,cart->chrSize,fp);

      //setup the emulator
      GameInterface=FCEUXGameInterface;
      ResetCartMapping();
      SetupCartPRGMapping(0,(uint8*)cart->PRG,cart->prgSize,0);
      SetupCartCHRMapping(0,(uint8*)cart->CHR,cart->chrSize,0);
      
      return true;
}


uint8 FCEU_ReadRomByte(uint32 i) {
      extern iNES_HEADER head;
      if(i < 16) return *((unsigned char *)&head+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;
}

Generated by  Doxygen 1.6.0   Back to index