fceux/src/fceu.cpp

1468 lines
35 KiB
C++
Raw Normal View History

/* FCE Ultra - NES/Famicom Emulator
2012-11-30 09:49:38 +00:00
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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 __WIN_DRIVER__
#include "drivers/win/pref.h"
#include "utils/xstring.h"
extern void CDLoggerROMClosed();
extern void CDLoggerROMChanged();
extern void ResetDebugStatisticsCounters();
extern void SetMainWindowText();
extern bool isTaseditorRecording();
extern int32 fps_scale;
extern int32 fps_scale_unpaused;
extern int32 fps_scale_frameadvance;
#endif
2015-06-18 06:29:46 +00:00
extern void RefreshThrottleFPS();
2008-09-24 06:06:31 +00:00
#ifdef _S9XLUA_H
2008-07-23 06:54:14 +00:00
#include "fceulua.h"
2008-09-24 06:06:31 +00:00
#endif
//TODO - we really need some kind of global platform-specific options api
#ifdef __WIN_DRIVER__
#include "drivers/win/main.h"
#include "drivers/win/memview.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"
2008-06-20 00:50:01 +00:00
#else
#ifdef __QT_DRIVER__
#include "drivers/Qt/sdl.h"
#else
2008-06-20 00:50:01 +00:00
#include "drivers/sdl/sdl.h"
#endif
#endif
#include <fstream>
#include <sstream>
#include <string>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <ctime>
using namespace std;
2015-06-18 06:29:46 +00:00
//-----------
//overclocking-related
2016-06-26 06:50:08 +00:00
// overclock the console by adding dummy scanlines to PPU loop or to vblank
// disables DMC DMA, WaveHi filling and image rendering for these dummies
2015-06-18 06:29:46 +00:00
// doesn't work with new PPU
bool overclock_enabled = 0;
2016-06-26 06:50:08 +00:00
bool overclocking = 0;
bool skip_7bit_overclocking = 1; // 7-bit samples have priority over overclocking
2015-06-18 06:29:46 +00:00
int normalscanlines;
int totalscanlines;
2016-06-26 06:50:08 +00:00
int postrenderscanlines = 0;
int vblankscanlines = 0;
2015-06-18 06:29:46 +00:00
//------------
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)
2012-12-12 20:28:34 +00:00
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
bool DebuggerWasUpdated = false; //To prevent the debugger from updating things without being updated.
bool AutoResumePlay = false;
char romNameWhenClosingEmulator[2048] = {0};
FCEUGI::FCEUGI()
: filename(0),
archiveFilename(0)
{
//printf("%08x",opsize); // WTF?!
}
FCEUGI::~FCEUGI()
{
if (filename)
{
free(filename);
filename = NULL;
}
if (archiveFilename)
{
free(archiveFilename);
archiveFilename = NULL;
}
}
2012-12-12 20:28:34 +00:00
bool CheckFileExists(const char* filename) {
//This function simply checks to see if the given filename exists
if (!filename) return false;
fstream test;
2012-12-12 20:28:34 +00:00
test.open(filename, fstream::in);
2012-12-12 20:28:34 +00:00
if (test.fail()) {
test.close();
return false;
2012-12-12 20:28:34 +00:00
} else {
test.close();
return true;
}
}
2012-12-12 20:28:34 +00:00
void FCEU_TogglePPU(void) {
newppu ^= 1;
2012-12-12 20:28:34 +00:00
if (newppu) {
FCEU_DispMessage("New PPU loaded", 0);
FCEUI_printf("New PPU loaded");
overclock_enabled = 0;
2012-12-12 20:28:34 +00:00
} else {
FCEU_DispMessage("Old PPU loaded", 0);
FCEUI_printf("Old PPU loaded");
}
normalscanlines = (dendy ? 290 : 240)+newppu; // use flag as number!
#ifdef __WIN_DRIVER__
SetMainWindowText();
#endif
}
static void FCEU_CloseGame(void)
{
if (GameInfo)
{
if (AutoResumePlay)
{
// save "-resume" savestate
FCEUSS_Save(FCEU_MakeFName(FCEUMKF_RESUMESTATE, 0, 0).c_str(), false);
}
#ifdef __WIN_DRIVER__
2012-12-12 20:28:34 +00:00
extern char LoadedRomFName[2048];
if (storePreferences(mass_replace(LoadedRomFName, "|", ".").c_str()))
2012-12-12 20:28:34 +00:00
FCEUD_PrintError("Couldn't store debugging data");
CDLoggerROMClosed();
#endif
2012-12-12 20:28:34 +00:00
if (FCEUnetplay) {
2008-06-17 16:59:16 +00:00
FCEUD_NetworkClose();
}
2012-12-12 20:28:34 +00:00
if (GameInfo->name) {
2008-06-17 16:59:16 +00:00
free(GameInfo->name);
GameInfo->name = NULL;
2008-06-17 16:59:16 +00:00
}
2012-12-12 20:28:34 +00:00
if (GameInfo->type != GIT_NSF) {
#ifdef __WIN_DRIVER__
if (disableAutoLSCheats == 2)
FCEU_FlushGameCheats(0, 1);
else if (disableAutoLSCheats == 1)
AskSaveCheat();
else if (disableAutoLSCheats == 0)
#endif
FCEU_FlushGameCheats(0, 0);
2008-06-17 16:59:16 +00:00
}
GameInterface(GI_CLOSE);
FCEUI_StopMovie();
2012-12-12 20:28:34 +00:00
ResetExState(0, 0);
2008-06-17 16:59:16 +00:00
//clear screen when game is closed
2008-06-17 16:59:16 +00:00
extern uint8 *XBuf;
2012-12-12 20:28:34 +00:00
if (XBuf)
memset(XBuf, 0, 256 * 256);
2008-06-17 16:59:16 +00:00
2012-12-12 20:28:34 +00:00
FCEU_CloseGenie();
2008-06-17 16:59:16 +00:00
delete GameInfo;
GameInfo = NULL;
currFrameCounter = 0;
//Reset flags for Undo/Redo/Auto Savestating //adelikat: TODO: maybe this stuff would be cleaner as a struct or class
lastSavestateMade[0] = 0;
undoSS = false;
redoSS = false;
lastLoadstateMade[0] = 0;
undoLS = false;
redoLS = false;
AutoSS = false;
2008-06-17 16:59:16 +00:00
}
}
2008-06-20 00:50:01 +00:00
uint64 timestampbase;
FCEUGI *GameInfo = NULL;
void (*GameInterface)(GI h);
void (*GameStateRestore)(int version);
readfunc ARead[0x10000];
writefunc BWrite[0x10000];
static readfunc *AReadG;
static writefunc *BWriteG;
2012-12-12 20:28:34 +00:00
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
2012-12-12 20:28:34 +00:00
int EmulationPaused = 0;
bool frameAdvanceRequested=false;
int frameAdvance_Delay_count = 0;
int frameAdvance_Delay = FRAMEADVANCE_DELAY_DEFAULT;
//indicates that the emulation core just frame advanced (consumed the frame advance state and paused)
bool JustFrameAdvanced = false;
static int *AutosaveStatus; //is it safe to load Auto-savestate
2008-06-25 00:43:02 +00:00
static int AutosaveIndex = 0; //which Auto-savestate we're on
int AutosaveQty = 4; // Number of Autosaves to store
int AutosaveFrequency = 256; // Number of frames between autosaves
2007-02-07 18:28:50 +00:00
2008-06-25 00:43:02 +00:00
// Flag that indicates whether the Auto-save option is enabled or not
int EnableAutosave = 0;
///a wrapper for unzip.c
2012-12-12 20:28:34 +00:00
extern "C" FILE *FCEUI_UTF8fopen_C(const char *n, const char *m) {
return ::FCEUD_UTF8fopen(n, m);
}
2012-12-12 20:28:34 +00:00
static DECLFW(BNull) {
}
2012-12-12 20:28:34 +00:00
static DECLFR(ANull) {
2008-07-23 06:54:14 +00:00
return(X.DB);
}
2012-12-12 20:28:34 +00:00
int AllocGenieRW(void) {
if (!(AReadG = (readfunc*)FCEU_malloc(0x8000 * sizeof(readfunc))))
2008-07-23 06:54:14 +00:00
return 0;
2012-12-12 20:28:34 +00:00
if (!(BWriteG = (writefunc*)FCEU_malloc(0x8000 * sizeof(writefunc))))
2008-07-23 06:54:14 +00:00
return 0;
2012-12-12 20:28:34 +00:00
RWWrap = 1;
2008-07-23 06:54:14 +00:00
return 1;
}
2012-12-12 20:28:34 +00:00
void FlushGenieRW(void) {
2008-07-23 06:54:14 +00:00
int32 x;
2012-12-12 20:28:34 +00:00
if (RWWrap) {
for (x = 0; x < 0x8000; x++) {
ARead[x + 0x8000] = AReadG[x];
BWrite[x + 0x8000] = BWriteG[x];
2008-07-23 06:54:14 +00:00
}
free(AReadG);
free(BWriteG);
AReadG = NULL;
BWriteG = NULL;
2012-12-12 20:28:34 +00:00
RWWrap = 0;
2008-07-23 06:54:14 +00:00
}
}
2012-12-12 20:28:34 +00:00
readfunc GetReadHandler(int32 a) {
if (a >= 0x8000 && RWWrap)
return AReadG[a - 0x8000];
2008-07-23 06:54:14 +00:00
else
return ARead[a];
}
2013-03-26 15:54:56 +00:00
2012-12-12 20:28:34 +00:00
void SetReadHandler(int32 start, int32 end, readfunc func) {
2008-07-23 06:54:14 +00:00
int32 x;
2013-03-26 15:54:56 +00:00
2012-12-12 20:28:34 +00:00
if (!func)
func = ANull;
if (RWWrap)
for (x = end; x >= start; x--) {
if (x >= 0x8000)
AReadG[x - 0x8000] = func;
2008-07-23 06:54:14 +00:00
else
2012-12-12 20:28:34 +00:00
ARead[x] = func;
2008-07-23 06:54:14 +00:00
}
else
2012-12-12 20:28:34 +00:00
for (x = end; x >= start; x--)
ARead[x] = func;
}
2012-12-12 20:28:34 +00:00
writefunc GetWriteHandler(int32 a) {
if (RWWrap && a >= 0x8000)
return BWriteG[a - 0x8000];
2008-07-23 06:54:14 +00:00
else
return BWrite[a];
}
2012-12-12 20:28:34 +00:00
void SetWriteHandler(int32 start, int32 end, writefunc func) {
2008-07-23 06:54:14 +00:00
int32 x;
2012-12-12 20:28:34 +00:00
if (!func)
func = BNull;
2012-12-12 20:28:34 +00:00
if (RWWrap)
for (x = end; x >= start; x--) {
if (x >= 0x8000)
BWriteG[x - 0x8000] = func;
2008-07-23 06:54:14 +00:00
else
2012-12-12 20:28:34 +00:00
BWrite[x] = func;
2008-07-23 06:54:14 +00:00
}
else
2012-12-12 20:28:34 +00:00
for (x = end; x >= start; x--)
BWrite[x] = func;
}
uint8 *RAM;
//---------
//windows might need to allocate these differently, so we have some special code
2012-12-12 20:28:34 +00:00
static void AllocBuffers() {
RAM = (uint8*)FCEU_gmalloc(0x800);
}
2012-12-12 20:28:34 +00:00
static void FreeBuffers() {
FCEU_free(RAM);
RAM = NULL;
}
//------
2012-12-12 20:28:34 +00:00
uint8 PAL = 0;
2012-12-12 20:28:34 +00:00
static DECLFW(BRAML) {
RAM[A] = V;
}
2012-12-12 20:28:34 +00:00
static DECLFW(BRAMH) {
RAM[A & 0x7FF] = V;
}
2012-12-12 20:28:34 +00:00
static DECLFR(ARAML) {
2008-07-23 06:54:14 +00:00
return RAM[A];
}
2012-12-12 20:28:34 +00:00
static DECLFR(ARAMH) {
return RAM[A & 0x7FF];
}
2012-12-12 20:28:34 +00:00
void ResetGameLoaded(void) {
if (GameInfo) FCEU_CloseGame();
EmulationPaused = 0; //mbg 5/8/08 - loading games while paused was bad news. maybe this fixes it
2012-12-12 20:28:34 +00:00
GameStateRestore = 0;
2013-03-26 15:54:56 +00:00
PPU_hook = NULL;
GameHBIRQHook = NULL;
FFCEUX_PPURead = NULL;
FFCEUX_PPUWrite = NULL;
2012-12-12 20:28:34 +00:00
if (GameExpSound.Kill)
GameExpSound.Kill();
2012-12-12 20:28:34 +00:00
memset(&GameExpSound, 0, sizeof(GameExpSound));
2013-03-26 15:54:56 +00:00
MapIRQHook = NULL;
2012-12-12 20:28:34 +00:00
MMC5Hack = 0;
PEC586Hack = 0;
QTAIHack = 0;
2012-12-12 20:28:34 +00:00
PAL &= 1;
default_palette_selection = 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);
//name should be UTF-8, hopefully, or else there may be trouble
FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silent)
{
//----------
//attempt to open the files
FCEUFILE *fp;
char fullname[2048]; // this name contains both archive name and ROM file name
int lastpal = PAL;
int lastdendy = dendy;
2021-06-17 23:50:15 +00:00
const char* romextensions[] = { "nes", "fds", "nsf", 0 };
// indicator for if the operaton was canceled by user
// currently there's only one situation:
// the user clicked cancel form the open from archive dialog
2019-05-23 10:59:30 +00:00
int userCancel = 0;
fp = FCEU_fopen(name, 0, "rb", 0, -1, romextensions, &userCancel);
if (!fp)
{
// Although !fp, if the operation was canceled from archive select dialog box, don't show the error message;
if (!silent && !userCancel)
FCEU_PrintError("Error opening \"%s\"!", name);
return 0;
}
else if (fp->archiveFilename != "")
{
strcpy(fullname, fp->archiveFilename.c_str());
strcat(fullname, "|");
strcat(fullname, fp->filename.c_str());
} else
strcpy(fullname, name);
2011-06-09 12:17:47 +00:00
2019-05-10 16:06:43 +00:00
// reset loaded game BEFORE it's loading.
ResetGameLoaded();
//file opened ok. start loading.
FCEU_printf("Loading %s...\n\n", fullname);
GetFileBase(fp->filename.c_str());
//reset parameters so they're cleared just in case a format's loader doesn't know to do the clearing
MasterRomInfoParams = TMasterRomInfoParams();
if (!AutosaveStatus)
2012-12-12 20:28:34 +00:00
AutosaveStatus = (int*)FCEU_dmalloc(sizeof(int) * AutosaveQty);
for (AutosaveIndex = 0; AutosaveIndex < AutosaveQty; ++AutosaveIndex)
AutosaveStatus[AutosaveIndex] = 0;
FCEU_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;
2012-12-12 20:28:34 +00:00
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
2012-12-12 20:28:34 +00:00
bool FCEUXLoad(const char *name, FCEUFILE * fp);
int load_result;
load_result = iNESLoad(fullname, fp, OverwriteVidMode);
if (load_result == LOADER_INVALID_FORMAT)
{
load_result = NSFLoad(fullname, fp);
if (load_result == LOADER_INVALID_FORMAT)
{
load_result = UNIFLoad(fullname, fp);
if (load_result == LOADER_INVALID_FORMAT)
{
load_result = FDSLoad(fullname, fp);
}
}
}
if (load_result == LOADER_OK)
2019-05-29 17:13:59 +00:00
{
#ifdef __WIN_DRIVER__
2019-05-29 17:13:59 +00:00
// ################################## Start of SP CODE ###########################
extern char LoadedRomFName[2048];
extern int loadDebugDataFailed;
2019-05-29 17:13:59 +00:00
if ((loadDebugDataFailed = loadPreferences(mass_replace(LoadedRomFName, "|", ".").c_str())))
if (!silent)
FCEU_printf("Couldn't load debugging data.\n");
2019-05-29 17:13:59 +00:00
// ################################## End of SP CODE ###########################
#endif
2019-05-29 17:13:59 +00:00
if (OverwriteVidMode)
FCEU_ResetVidSys();
2019-05-29 17:13:59 +00:00
if (GameInfo->type != GIT_NSF &&
FSettings.GameGenie &&
FCEU_OpenGenie())
{
2019-05-29 17:13:59 +00:00
FCEUI_SetGameGenie(false);
#ifdef __WIN_DRIVER__
2019-05-29 17:13:59 +00:00
genie = 0;
#endif
}
2019-05-29 17:13:59 +00:00
PowerNES();
2008-07-23 06:54:14 +00:00
2019-05-29 17:13:59 +00:00
if (GameInfo->type != GIT_NSF)
FCEU_LoadGamePalette();
FCEU_ResetPalette();
FCEU_ResetMessages(); // Save state, status messages, etc.
if (!lastpal && PAL) {
FCEU_DispMessage("PAL mode set", 0);
FCEUI_printf("PAL mode set");
}
else if (!lastdendy && dendy) {
// this won't happen, since we don't autodetect dendy, but maybe someday we will?
FCEU_DispMessage("Dendy mode set", 0);
FCEUI_printf("Dendy mode set");
}
else if ((lastpal || lastdendy) && !(PAL || dendy)) {
FCEU_DispMessage("NTSC mode set", 0);
FCEUI_printf("NTSC mode set");
}
if (GameInfo->type != GIT_NSF && !disableAutoLSCheats)
2019-05-29 17:13:59 +00:00
FCEU_LoadGameCheats(0);
if (AutoResumePlay)
{
// load "-resume" savestate
if (FCEUSS_Load(FCEU_MakeFName(FCEUMKF_RESUMESTATE, 0, 0).c_str(), false))
FCEU_DispMessage("Old play session resumed.", 0);
}
2019-05-29 17:13:59 +00:00
ResetScreenshotsCounter();
#ifdef __WIN_DRIVER__
2019-05-29 17:13:59 +00:00
DoDebuggerDataReload(); // Reloads data without reopening window
CDLoggerROMChanged();
if (hMemView) UpdateColorTable();
if (hCheat)
{
UpdateCheatsAdded();
UpdateCheatRelatedWindow();
}
if (FrozenAddressCount)
FCEU_DispMessage("%d cheats active", 0, FrozenAddressCount);
#endif
2019-05-29 17:13:59 +00:00
}
else {
if (!silent)
{
switch (load_result)
{
case LOADER_UNHANDLED_ERROR:
FCEU_PrintError("An error occurred while loading the file.");
break;
case LOADER_INVALID_FORMAT:
FCEU_PrintError("Unknown ROM file format.");
break;
}
}
2019-05-29 17:13:59 +00:00
delete GameInfo;
GameInfo = 0;
}
FCEU_fclose(fp);
return GameInfo;
}
FCEUGI *FCEUI_LoadGame(const char *name, int OverwriteVidMode, bool silent)
{
return FCEUI_LoadGameVirtual(name, OverwriteVidMode, silent);
}
//Return: Flag that indicates whether the function was succesful or not.
2012-12-12 20:28:34 +00:00
bool FCEUI_Initialize() {
srand(time(0));
2012-12-12 20:28:34 +00:00
if (!FCEU_InitVirtualVideo()) {
return false;
2007-02-05 20:41:23 +00:00
}
AllocBuffers();
2007-02-05 20:41:23 +00:00
// 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
2012-12-12 20:28:34 +00:00
memset(&FSettings, 0, sizeof(FSettings));
//FSettings.UsrFirstSLine[0]=8;
2012-12-12 20:28:34 +00:00
FSettings.UsrFirstSLine[0] = 0;
FSettings.UsrFirstSLine[1] = 0;
//FSettings.UsrLastSLine[0]=231;
2012-12-12 20:28:34 +00:00
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)
2008-07-23 06:54:14 +00:00
FCEUPPU_Init();
2008-07-23 06:54:14 +00:00
X6502_Init();
2008-07-23 06:54:14 +00:00
return true;
}
2012-12-12 20:28:34 +00:00
void FCEUI_Kill(void) {
2008-09-24 06:06:31 +00:00
#ifdef _S9XLUA_H
2008-07-23 06:54:14 +00:00
FCEU_LuaStop();
2008-09-24 06:06:31 +00:00
#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;
//}
AutoFirePatternLength = onframes + offframes;
AFon = onframes; AFoff = offframes;
}
void GetAutoFirePattern( int *onframes, int *offframes)
{
if ( onframes )
{
*onframes = AFon;
}
if ( offframes )
{
*offframes = AFoff;
}
}
void SetAutoFireOffset(int offset)
{
2012-12-12 20:28:34 +00:00
if (offset < 0 || offset > 8) return;
AutoFireOffset = offset;
}
bool GetAutoFireState(int btnIdx)
{
return rapidAlternator;
}
void AutoFire(void)
{
static int counter = 0;
if (justLagged == false)
{
//counter = (counter + 1) % (8 * 7 * 5 * 3);
counter = (counter + 1) % AutoFirePatternLength;
}
//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]; //adelikat: TODO: Think through this, MOVIEMODE_FINISHED should not use movie data for auto-fire?
//adelikat: TODO: Think through this, MOVIEMODE_FINISHED should not use movie data for auto-fire?
rapidAlternator = ( (AutoFireOffset + FCEUMOV_GetFrame()) % AutoFirePatternLength ) < AFon;
}
else
{
//rapidAlternator = AutoFirePattern[(AutoFireOffset + counter) % AutoFirePatternLength];
rapidAlternator = ( (AutoFireOffset + counter) % AutoFirePatternLength ) < AFon;
}
}
2008-06-25 00:43:02 +00:00
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
2012-12-12 20:28:34 +00:00
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
2012-12-12 20:28:34 +00:00
int r, ssize;
JustFrameAdvanced = false;
if (frameAdvanceRequested)
{
if (frameAdvance_Delay_count == 0 || frameAdvance_Delay_count >= frameAdvance_Delay)
EmulationPaused = EMULATIONPAUSED_FA;
if (frameAdvance_Delay_count < frameAdvance_Delay)
frameAdvance_Delay_count++;
}
if (EmulationPaused & EMULATIONPAUSED_FA)
{
// the user is holding Frame Advance key
// clear paused flag temporarily
EmulationPaused &= ~EMULATIONPAUSED_PAUSED;
#ifdef __WIN_DRIVER__
// different emulation speed when holding Frame Advance
if (fps_scale_frameadvance > 0)
{
fps_scale = fps_scale_frameadvance;
RefreshThrottleFPS();
}
#endif
} else
{
#ifdef __WIN_DRIVER__
if (fps_scale_frameadvance > 0)
{
// restore emulation speed when Frame Advance is not held
fps_scale = fps_scale_unpaused;
RefreshThrottleFPS();
}
#endif
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
{
// emulator is paused
memcpy(XBuf, XBackBuf, 256*256);
FCEU_PutImage();
*pXBuf = XBuf;
*SoundBuf = WaveFinal;
*SoundBufSize = 0;
return;
}
}
AutoFire();
2008-12-13 03:31:44 +00:00
UpdateAutosave();
2008-12-05 04:23:48 +00:00
#ifdef _S9XLUA_H
2008-07-23 06:54:14 +00:00
FCEU_LuaFrameBoundary();
2008-12-05 04:23:48 +00:00
#endif
FCEU_UpdateInput();
2008-08-07 04:56:40 +00:00
lagFlag = 1;
#ifdef _S9XLUA_H
CallRegisteredLuaFunctions(LUACALL_BEFOREEMULATION);
#endif
2012-12-12 20:28:34 +00:00
if (geniestage != 1) FCEU_ApplyPeriodicCheats();
r = FCEUPPU_Loop(skip);
2012-12-12 20:28:34 +00:00
if (skip != 2) ssize = FlushEmulateSound(); //If skip = 2 we are skipping sound processing
#ifdef _S9XLUA_H
CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION);
#endif
FCEU_PutImage();
#ifdef __WIN_DRIVER__
//These Windows only dialogs need to be updated only once per frame so they are included here
// CaH4e3: can't see why, this is only cause problems with selection
// adelikat: selection is only a problem when not paused, it should be paused to select, we want to see the values update
// owomomo: use an OWNERDATA CListCtrl to partially solve the problem
UpdateCheatList();
UpdateTextHooker();
Update_RAM_Search(); // Update_RAM_Watch() is also called.
RamChange();
2008-12-05 04:23:48 +00:00
//FCEUI_AviVideoUpdate(XBuf);
extern int KillFCEUXonFrame;
if (KillFCEUXonFrame && (FCEUMOV_GetFrame() >= KillFCEUXonFrame))
DoFCEUExit();
#else
extern int KillFCEUXonFrame;
if (KillFCEUXonFrame && (FCEUMOV_GetFrame() >= KillFCEUXonFrame))
exit(0);
2008-07-23 06:54:14 +00:00
#endif
timestampbase += timestamp;
timestamp = 0;
soundtimestamp = 0;
2012-12-12 20:28:34 +00:00
*pXBuf = skip ? 0 : XBuf;
if (skip == 2) { //If skip = 2, then bypass sound
*SoundBuf = 0;
*SoundBufSize = 0;
} else {
*SoundBuf = WaveFinal;
*SoundBufSize = ssize;
}
if ((EmulationPaused & EMULATIONPAUSED_FA) && (!frameAdvanceLagSkip || !lagFlag))
//Lots of conditions here. EmulationPaused & EMULATIONPAUSED_FA must be true. In addition frameAdvanceLagSkip or lagFlag must be false
// When Frame Advance is held, emulator is automatically paused after emulating one frame (or several lag frames)
{
EmulationPaused = EMULATIONPAUSED_PAUSED; // restore EMULATIONPAUSED_PAUSED flag and clear EMULATIONPAUSED_FA flag
JustFrameAdvanced = true;
#ifdef __WIN_DRIVER__
2012-12-12 20:28:34 +00:00
if (soundoptions & SO_MUTEFA) //mute the frame advance if the user requested it
*SoundBufSize = 0; //keep sound muted
#endif
}
2012-12-12 20:28:34 +00:00
if (lagFlag) {
lagCounter++;
justLagged = true;
2012-12-12 20:28:34 +00:00
} else justLagged = false;
2008-07-23 06:54:14 +00:00
if (movieSubtitles)
ProcessSubtitles();
}
2012-12-12 20:28:34 +00:00
void FCEUI_CloseGame(void) {
if (!FCEU_IsValidUI(FCEUI_CLOSEGAME))
return;
FCEU_CloseGame();
}
2012-12-12 20:28:34 +00:00
void ResetNES(void) {
2008-07-23 06:54:14 +00:00
FCEUMOV_AddCommand(FCEUNPCMD_RESET);
2012-12-12 20:28:34 +00:00
if (!GameInfo) return;
2008-07-23 06:54:14 +00:00
GameInterface(GI_RESETM2);
FCEUSND_Reset();
FCEUPPU_Reset();
X6502_Reset();
// clear back baffer
extern uint8 *XBackBuf;
2012-12-12 20:28:34 +00:00
memset(XBackBuf, 0, 256 * 256);
FCEU_DispMessage("Reset", 0);
}
int RAMInitSeed = 0;
int RAMInitOption = 0;
u64 splitmix64(u32 input) {
u64 z = (input + 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
static inline u64 xoroshiro128plus_rotl(const u64 x, int k) {
return (x << k) | (x >> (64 - k));
}
u64 xoroshiro128plus_s[2];
void xoroshiro128plus_seed(u32 input)
{
//http://xoroshiro.di.unimi.it/splitmix64.c
u64 x = input;
u64 z = (x += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
xoroshiro128plus_s[0] = z ^ (z >> 31);
z = (x += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
xoroshiro128plus_s[1] = z ^ (z >> 31);
}
//http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c
u64 xoroshiro128plus_next() {
const u64 s0 = xoroshiro128plus_s[0];
u64 s1 = xoroshiro128plus_s[1];
const u64 result = s0 + s1;
s1 ^= s0;
xoroshiro128plus_s[0] = xoroshiro128plus_rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b
xoroshiro128plus_s[1] = xoroshiro128plus_rotl(s1, 36); // c
return result;
}
void FCEU_MemoryRand(uint8 *ptr, uint32 size, bool default_zero) {
2012-12-12 20:28:34 +00:00
int x = 0;
2012-12-12 20:28:34 +00:00
while (size) {
uint8 v = 0;
switch (RAMInitOption)
{
default:
case 0:
if (!default_zero) v = (x & 4) ? 0xFF : 0x00;
else v = 0x00;
break;
case 1: v = 0xFF; break;
case 2: v = 0x00; break;
case 3: v = (u8)(xoroshiro128plus_next()); break;
// the default is this 8 byte pattern: 00 00 00 00 FF FF FF FF
// it has been used in FCEUX since time immemorial
// Some games to examine uninitialied RAM problems with:
// * Cybernoid - music option starts turned off with default pattern
// * Huang Di - debug mode is enabled with default pattern
// * Minna no Taabou no Nakayoshi Daisakusen - fails to boot with some patterns
// * F-15 City War - high score table
// * 1942 - high score table
// * Cheetahmen II - may start in different levels with different RAM startup
}
*ptr = v;
2008-07-23 06:54:14 +00:00
x++;
size--;
ptr++;
}
}
2012-12-12 20:28:34 +00:00
void hand(X6502 *X, int type, uint32 A) {
}
2012-12-12 20:28:34 +00:00
void PowerNES(void) {
FCEUMOV_AddCommand(FCEUNPCMD_POWER);
if (!GameInfo) return;
//reseed random, unless we're in a movie
extern int disableBatteryLoading;
if(FCEUMOV_Mode(MOVIEMODE_INACTIVE) && !disableBatteryLoading)
{
RAMInitSeed = rand() ^ (u32)xoroshiro128plus_next();
}
//always reseed the PRNG with the current seed, for deterministic results (for that seed)
xoroshiro128plus_seed(RAMInitSeed);
FCEU_CheatResetRAM();
2012-12-12 20:28:34 +00:00
FCEU_CheatAddRAM(2, 0, RAM);
2012-12-12 20:28:34 +00:00
FCEU_GeniePower();
2012-12-12 20:28:34 +00:00
FCEU_MemoryRand(RAM, 0x800);
2012-12-12 20:28:34 +00:00
SetReadHandler(0x0000, 0xFFFF, ANull);
SetWriteHandler(0x0000, 0xFFFF, BNull);
2012-12-12 20:28:34 +00:00
SetReadHandler(0, 0x7FF, ARAML);
SetWriteHandler(0, 0x7FF, BRAML);
2013-03-26 15:54:56 +00:00
SetReadHandler(0x800, 0x1FFF, ARAMH); // Part of a little
SetWriteHandler(0x800, 0x1FFF, BRAMH); //hack for a small speed boost.
2008-07-23 06:54:14 +00:00
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);
2012-12-12 20:28:34 +00:00
if (GameInfo->type == GIT_VSUNI)
FCEU_VSUniPower();
//if we are in a movie, then reset the saveram
extern int disableBatteryLoading;
2012-12-12 20:28:34 +00:00
if (disableBatteryLoading)
GameInterface(GI_RESETSAVE);
timestampbase = 0;
X6502_Power();
#ifdef __WIN_DRIVER__
ResetDebugStatisticsCounters();
2012-08-06 16:23:47 +00:00
#endif
FCEU_PowerCheats();
LagCounterReset();
// clear back buffer
extern uint8 *XBackBuf;
2012-12-12 20:28:34 +00:00
memset(XBackBuf, 0, 256 * 256);
#ifdef __WIN_DRIVER__
Update_RAM_Search(); // Update_RAM_Watch() is also called.
#endif
FCEU_DispMessage("Power on", 0);
}
2012-12-12 20:28:34 +00:00
void FCEU_ResetVidSys(void) {
2008-07-23 06:54:14 +00:00
int w;
2012-12-12 20:28:34 +00:00
if (GameInfo->vidsys == GIV_NTSC)
w = 0;
else if (GameInfo->vidsys == GIV_PAL) {
2012-12-12 20:28:34 +00:00
w = 1;
dendy = 0;
} else
2012-12-12 20:28:34 +00:00
w = FSettings.PAL;
2008-07-23 06:54:14 +00:00
2012-12-12 20:28:34 +00:00
PAL = w ? 1 : 0;
2013-03-26 15:54:56 +00:00
if (PAL)
dendy = 0;
if (newppu)
overclock_enabled = 0;
normalscanlines = (dendy ? 290 : 240)+newppu; // use flag as number!
2016-06-26 06:50:08 +00:00
totalscanlines = normalscanlines + (overclock_enabled ? postrenderscanlines : 0);
FCEUPPU_SetVideoSystem(w || dendy);
2008-07-23 06:54:14 +00:00
SetSoundVariables();
}
FCEUS FSettings;
void FCEU_printf(const char *format, ...)
{
2008-07-23 06:54:14 +00:00
char temp[2048];
2008-07-23 06:54:14 +00:00
va_list ap;
2012-12-12 20:28:34 +00:00
va_start(ap, format);
vsnprintf(temp, sizeof(temp), format, ap);
2008-07-23 06:54:14 +00:00
FCEUD_Message(temp);
#if 0
FILE *ofile;
ofile = fopen("stdout.txt", "ab");
fwrite(temp, 1, strlen(temp), ofile);
fclose(ofile);
#endif
2008-07-23 06:54:14 +00:00
va_end(ap);
}
void FCEU_PrintError(const char *format, ...)
{
2008-07-23 06:54:14 +00:00
char temp[2048];
2008-07-23 06:54:14 +00:00
va_list ap;
2012-12-12 20:28:34 +00:00
va_start(ap, format);
vsnprintf(temp, sizeof(temp), format, ap);
2008-07-23 06:54:14 +00:00
FCEUD_PrintError(temp);
2008-07-23 06:54:14 +00:00
va_end(ap);
}
2012-12-12 20:28:34 +00:00
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 || dendy) {
2012-12-12 20:28:34 +00:00
FSettings.FirstSLine = FSettings.UsrFirstSLine[1];
FSettings.LastSLine = FSettings.UsrLastSLine[1];
} else {
FSettings.FirstSLine = FSettings.UsrFirstSLine[0];
FSettings.LastSLine = FSettings.UsrLastSLine[0];
2008-07-23 06:54:14 +00:00
}
}
2012-12-12 20:28:34 +00:00
void FCEUI_SetVidSystem(int a) {
FSettings.PAL = a ? 1 : 0;
if (GameInfo) {
2008-07-23 06:54:14 +00:00
FCEU_ResetVidSys();
FCEU_ResetPalette();
FCEUD_VideoChanged();
2008-07-23 06:54:14 +00:00
}
}
2012-12-12 20:28:34 +00:00
int FCEUI_GetCurrentVidSystem(int *slstart, int *slend) {
if (slstart)
*slstart = FSettings.FirstSLine;
if (slend)
*slend = FSettings.LastSLine;
2008-07-23 06:54:14 +00:00
return(PAL);
}
int FCEUI_GetRegion(void)
{
int region;
if ( pal_emulation )
{
region = 1;
}
else if ( dendy )
{
region = 2;
}
else
{
region = 0;
}
return region;
}
void FCEUI_SetRegion(int region, int notify)
{
switch (region) {
case 0: // NTSC
normalscanlines = 240;
pal_emulation = 0;
dendy = 0;
2016-07-31 17:11:04 +00:00
if (notify)
{
FCEU_DispMessage("NTSC mode set", 0);
FCEUI_printf("NTSC mode set");
}
break;
case 1: // PAL
normalscanlines = 240;
pal_emulation = 1;
dendy = 0;
2016-07-31 17:11:04 +00:00
if (notify)
{
FCEU_DispMessage("PAL mode set", 0);
FCEUI_printf("PAL mode set");
}
break;
case 2: // Dendy
normalscanlines = 290;
pal_emulation = 0;
dendy = 1;
2016-07-31 17:11:04 +00:00
if (notify)
{
FCEU_DispMessage("Dendy mode set", 0);
FCEUI_printf("Dendy mode set");
}
break;
}
normalscanlines += newppu;
2016-06-26 06:50:08 +00:00
totalscanlines = normalscanlines + (overclock_enabled ? postrenderscanlines : 0);
FCEUI_SetVidSystem(pal_emulation);
RefreshThrottleFPS();
#ifdef __WIN_DRIVER__
UpdateCheckedMenuItems();
PushCurrentVideoSettings();
#endif
}
//Enable or disable Game Genie option.
2012-12-12 20:28:34 +00:00
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;
//}
2012-12-12 20:28:34 +00:00
int32 FCEUI_GetDesiredFPS(void) {
if (PAL || dendy)
2012-12-12 20:28:34 +00:00
return(838977920); // ~50.007
2008-07-23 06:54:14 +00:00
else
2012-12-12 20:28:34 +00:00
return(1008307711); // ~60.1
}
int FCEUI_EmulationPaused(void)
{
return (EmulationPaused & EMULATIONPAUSED_PAUSED);
}
int FCEUI_EmulationFrameStepped()
{
return (EmulationPaused & EMULATIONPAUSED_FA);
}
void FCEUI_ClearEmulationFrameStepped()
{
EmulationPaused &= ~EMULATIONPAUSED_FA;
}
//mbg merge 7/18/06 added
//ideally maybe we shouldnt be using this, but i need it for quick merging
2012-12-12 20:28:34 +00:00
void FCEUI_SetEmulationPaused(int val) {
EmulationPaused = val;
}
void FCEUI_ToggleEmulationPause(void)
{
EmulationPaused = (EmulationPaused & EMULATIONPAUSED_PAUSED) ^ EMULATIONPAUSED_PAUSED;
DebuggerWasUpdated = false;
}
2012-12-12 20:28:34 +00:00
void FCEUI_FrameAdvanceEnd(void) {
frameAdvanceRequested = false;
}
2012-12-12 20:28:34 +00:00
void FCEUI_FrameAdvance(void) {
frameAdvanceRequested = true;
frameAdvance_Delay_count = 0;
}
2008-06-25 00:43:02 +00:00
static int AutosaveCounter = 0;
2012-12-12 20:28:34 +00:00
void UpdateAutosave(void) {
if (!EnableAutosave || turbo)
return;
char * f;
2012-12-12 20:28:34 +00:00
if (++AutosaveCounter >= AutosaveFrequency) {
AutosaveCounter = 0;
AutosaveIndex = (AutosaveIndex + 1) % AutosaveQty;
2012-12-12 20:28:34 +00:00
f = strdup(FCEU_MakeFName(FCEUMKF_AUTOSTATE, AutosaveIndex, 0).c_str());
FCEUSS_Save(f, false);
2012-12-12 20:28:34 +00:00
AutoSS = true; //Flag that an auto-savestate was made
free(f);
f = NULL;
2008-06-25 00:43:02 +00:00
AutosaveStatus[AutosaveIndex] = 1;
}
}
void FCEUI_RewindToLastAutosave(void) {
2012-12-12 20:28:34 +00:00
if (!EnableAutosave || !AutoSS)
return;
2012-12-12 20:28:34 +00:00
if (AutosaveStatus[AutosaveIndex] == 1) {
char * f;
2012-12-12 20:28:34 +00:00
f = strdup(FCEU_MakeFName(FCEUMKF_AUTOSTATE, AutosaveIndex, 0).c_str());
FCEUSS_Load(f);
free(f);
f = NULL;
2008-07-23 06:54:14 +00:00
//Set pointer to previous available slot
2012-12-12 20:28:34 +00:00
if (AutosaveStatus[(AutosaveIndex + AutosaveQty - 1) % AutosaveQty] == 1) {
AutosaveIndex = (AutosaveIndex + AutosaveQty - 1) % AutosaveQty;
}
2008-06-25 00:43:02 +00:00
//Reset time to next Auto-save
AutosaveCounter = 0;
}
}
2012-12-12 20:28:34 +00:00
int FCEU_TextScanlineOffset(int y) {
return FSettings.FirstSLine * 256;
2008-05-26 01:55:45 +00:00
}
2012-12-12 20:28:34 +00:00
int FCEU_TextScanlineOffsetFromBottom(int y) {
return (FSettings.LastSLine - y) * 256;
2008-05-26 01:55:45 +00:00
}
2012-12-12 20:28:34 +00:00
bool FCEU_IsValidUI(EFCEUI ui) {
switch (ui) {
case FCEUI_OPENGAME:
case FCEUI_CLOSEGAME:
2012-12-12 20:28:34 +00:00
if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) 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:
2012-12-12 20:28:34 +00:00
if (!GameInfo) return false;
if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) return false;
break;
case FCEUI_STOPMOVIE:
case FCEUI_TOGGLERECORDINGMOVIE:
return FCEUMOV_Mode(MOVIEMODE_PLAY | MOVIEMODE_RECORD | MOVIEMODE_FINISHED);
case FCEUI_PLAYFROMBEGINNING:
return FCEUMOV_Mode(MOVIEMODE_PLAY | MOVIEMODE_RECORD | MOVIEMODE_TASEDITOR | MOVIEMODE_FINISHED);
case FCEUI_TRUNCATEMOVIE:
return FCEUMOV_Mode(MOVIEMODE_PLAY | MOVIEMODE_RECORD);
case FCEUI_STOPAVI:
return FCEUI_AviIsRecording();
case FCEUI_TASEDITOR:
2012-12-12 20:28:34 +00:00
if (!GameInfo) return false;
break;
case FCEUI_RESET:
case FCEUI_POWER:
case FCEUI_EJECT_DISK:
case FCEUI_SWITCH_DISK:
case FCEUI_INSERT_COIN:
2012-12-12 20:28:34 +00:00
if (!GameInfo) return false;
if (FCEUMOV_Mode(MOVIEMODE_RECORD)) return true;
#ifdef __WIN_DRIVER__
if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR) && isTaseditorRecording()) return true;
#endif
2012-12-12 20:28:34 +00:00
if (!FCEUMOV_Mode(MOVIEMODE_INACTIVE)) return false;
break;
case FCEUI_INPUT_BARCODE:
if (!GameInfo) return false;
if (!FCEUMOV_Mode(MOVIEMODE_INACTIVE)) return false;
}
return true;
}
//---------------------
//experimental new mapper and ppu system follows
class FCEUXCart {
public:
2012-12-12 20:28:34 +00:00
int mirroring;
int chrPages, prgPages;
uint32 chrSize, prgSize;
char* CHR, *PRG;
FCEUXCart()
: CHR(0)
, PRG(0) {
}
2012-12-12 20:28:34 +00:00
~FCEUXCart() {
if (CHR) delete[] CHR;
if (PRG) delete[] PRG;
}
virtual void Power() {
}
protected:
2012-12-12 20:28:34 +00:00
//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:
2012-12-12 20:28:34 +00:00
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) {
2012-12-12 20:28:34 +00:00
switch (command) {
case GI_POWER:
cart->Power();
}
}
2012-12-12 20:28:34 +00:00
bool FCEUXLoad(const char *name, FCEUFILE *fp) {
//read ines header
iNES_HEADER head;
2012-12-12 20:28:34 +00:00
if (FCEU_fread(&head, 1, 16, fp) != 16)
return false;
//validate header
2012-12-12 20:28:34 +00:00
if (memcmp(&head, "NES\x1a", 4))
return 0;
2012-12-12 20:28:34 +00:00
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;
2012-12-12 20:28:34 +00:00
if (cart->prgPages == 0) {
printf("FCEUX: received zero prgpages\n");
cart->prgPages = 256;
}
cart->chrPages = head.VROM_size;
2012-12-12 20:28:34 +00:00
cart->mirroring = (head.ROM_type & 1);
if (head.ROM_type & 8) cart->mirroring = 2;
//skip trainer
2012-12-12 20:28:34 +00:00
bool hasTrainer = (head.ROM_type & 4) != 0;
if (hasTrainer) {
FCEU_fseek(fp, 512, SEEK_CUR);
}
//load data
2012-12-12 20:28:34 +00:00
cart->prgSize = cart->prgPages * 16 * 1024;
cart->chrSize = cart->chrPages * 8 * 1024;
cart->PRG = new char[cart->prgSize];
cart->CHR = new char[cart->chrSize];
2012-12-12 20:28:34 +00:00
FCEU_fread(cart->PRG, 1, cart->prgSize, fp);
FCEU_fread(cart->CHR, 1, cart->chrSize, fp);
//setup the emulator
2012-12-12 20:28:34 +00:00
GameInterface = FCEUXGameInterface;
ResetCartMapping();
2012-12-12 20:28:34 +00:00
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;
2015-06-23 21:04:31 +00:00
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;
2009-10-10 23:12:21 +00:00
}
2015-06-23 21:04:31 +00:00
void FCEU_WriteRomByte(uint32 i, uint8 value) {
if (i < 16)
#ifdef __WIN_DRIVER__
MessageBox(hMemView, "Sorry", "You can't edit the ROM header.", MB_OK | MB_ICONERROR);
#else
printf("Sorry, you can't edit the ROM header.\n");
#endif
2015-06-23 21:04:31 +00:00
if (i < 16 + PRGsize[0])
PRGptr[0][i - 16] = value;
2017-01-16 20:59:14 +00:00
else if (i < 16 + PRGsize[0] + CHRsize[0])
2015-06-23 21:04:31 +00:00
CHRptr[0][i - 16 - PRGsize[0]] = value;
}