mirror of
https://github.com/dborth/fceugx.git
synced 2024-11-01 23:15:07 +01:00
1111 lines
22 KiB
C++
1111 lines
22 KiB
C++
|
/* 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"
|
||
|
|
||
|
#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/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)
|
||
|
{
|
||
|
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
|
||
|
FCEU_LuaWriteInform();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static DECLFW(BRAMH)
|
||
|
{
|
||
|
RAM[A&0x7FF]=V;
|
||
|
#ifdef _S9XLUA_H
|
||
|
FCEU_LuaWriteInform();
|
||
|
#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(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(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);
|
||
|
|
||
|
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);
|
||
|
|
||
|
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;
|
||
|
if(geniestage!=1) FCEU_ApplyPeriodicCheats();
|
||
|
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();
|
||
|
UpdateTextHooker();
|
||
|
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;
|
||
|
}
|