From d7c23fa92ef3b5829a2bd6ad137367b4e94b1678 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 4 Mar 2023 16:34:21 -0500 Subject: [PATCH 01/69] FCEU state recorder in work. --- src/emufile.h | 4 +- src/fceu.cpp | 9 +++ src/state.cpp | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/state.h | 6 ++ 4 files changed, 178 insertions(+), 2 deletions(-) diff --git a/src/emufile.h b/src/emufile.h index 0bd65c3d..623f6c21 100644 --- a/src/emufile.h +++ b/src/emufile.h @@ -179,10 +179,10 @@ public: va_start(argptr, format); vsprintf(tempbuf,format,argptr); - fwrite(tempbuf,amt); + fwrite(tempbuf,amt); delete[] tempbuf; - va_end(argptr); + va_end(argptr); return amt; }; diff --git a/src/fceu.cpp b/src/fceu.cpp index 49f5c750..ce870582 100644 --- a/src/fceu.cpp +++ b/src/fceu.cpp @@ -208,6 +208,8 @@ static void FCEU_CloseGame(void) GameInterface(GI_CLOSE); + FCEU_StateRecorderStop(); + FCEUI_StopMovie(); ResetExState(0, 0); @@ -592,6 +594,12 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen } FCEU_fclose(fp); + + if ( FCEU_StateRecorderIsEnabled() ) + { + FCEU_StateRecorderStart(); + } + return GameInfo; } @@ -793,6 +801,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski AutoFire(); UpdateAutosave(); + FCEU_StateRecorderUpdate(); #ifdef _S9XLUA_H FCEU_LuaFrameBoundary(); diff --git a/src/state.cpp b/src/state.cpp index 0ef771e8..cdce4ad8 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1179,3 +1179,164 @@ void RedoLoadState() redoLS = false; //Flag that RedoLoadState can not be run again undoLS = true; //Flag that LoadBackup can be run again } + +//----------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------- +//----------- Save State History ---------------- +//----------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------- +class StateRecorder +{ + public: + StateRecorder(void) + { + size_t ringBufSize = 60; + + for (size_t i=0; i(currFrameCounter); + + if (curFrame != frameCounter) + { + frameCounter = curFrame; + + if ( (frameCounter % framesPerSnap) == 0 ) + { + int ringBufSize = static_cast( ringBuf.size() ); + + EMUFILE_MEMORY *em = ringBuf[ ringHead ]; + + em->set_len(0); + + FCEUSS_SaveMS( em, compressionLevel ); + + //printf("Frame:%u Save:%i Size:%zu Total:%zukB \n", frameCounter, ringHead, em->size(), dataSize() / 1024 ); + + ringHead = (ringHead + 1) % ringBufSize; + + if (ringStart == ringHead) + { // Buffer Overrun + ringStart = (ringHead + 1) % ringBufSize; + } + } + } + } + + int loadState( int numSnapsFromLatest ) + { + int ringBufSize = static_cast( ringBuf.size() ); + + if (numSnapsFromLatest < 0) + { + numSnapsFromLatest = 0; + } + numSnapsFromLatest = numSnapsFromLatest % ringBufSize; + + int snapIdx = ringHead - numSnapsFromLatest - 1; + + if (snapIdx < 0) + { + snapIdx = snapIdx + ringBufSize; + } + + EMUFILE_MEMORY *em = ringBuf[ snapIdx ]; + + FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP ); + + return 0; + } + + int numSnapsSaved(void) + { + int numSnaps = ringHead - ringStart; + + if (numSnaps < 0) + { + numSnaps = numSnaps + static_cast( ringBuf.size() ); + } + return numSnaps; + } + + size_t dataSize(void) + { + return ringBuf.size() * ringBuf[0]->size(); + } + + static bool enabled; + private: + + void doSnap(void) + { + + } + + std::vector ringBuf; + int ringHead; + int ringTail; + int ringStart; + int compressionLevel; + unsigned int frameCounter; + unsigned int framesPerSnap; + +}; + +static StateRecorder *stateRecorder = nullptr; +bool StateRecorder::enabled = false; + +int FCEU_StateRecorderStart(void) +{ + if (stateRecorder == nullptr) + { + stateRecorder = new StateRecorder(); + } + return stateRecorder == nullptr; +} + +int FCEU_StateRecorderStop(void) +{ + if (stateRecorder != nullptr) + { + delete stateRecorder; stateRecorder = nullptr; + } + return stateRecorder != nullptr; +} + +int FCEU_StateRecorderUpdate(void) +{ + if (stateRecorder != nullptr) + { + stateRecorder->update(); + } + return 0; +} + +bool FCEU_StateRecorderIsEnabled(void) +{ + return StateRecorder::enabled; +} + +bool FCEU_StateRecorderRunning(void) +{ + return stateRecorder != nullptr; +} diff --git a/src/state.h b/src/state.h index 4157dc9e..3899a7aa 100644 --- a/src/state.h +++ b/src/state.h @@ -78,3 +78,9 @@ extern bool backupSavestates; //Whether or not to make backups, true by defaul bool CheckBackupSaveStateExist(); //Checks if backupsavestate exists extern bool compressSavestates; //Whether or not to compress non-movie savestates (by default, yes) + +int FCEU_StateRecorderStart(void); +int FCEU_StateRecorderStop(void); +int FCEU_StateRecorderUpdate(void); +bool FCEU_StateRecorderRunning(void); +bool FCEU_StateRecorderIsEnabled(void); From 6babc7fdaf76d9d667f302fe050f511470768b68 Mon Sep 17 00:00:00 2001 From: negative Date: Mon, 6 Mar 2023 13:13:06 +0800 Subject: [PATCH 02/69] Mapper 78: Add submapper support --- src/boards/datalatch.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/boards/datalatch.cpp b/src/boards/datalatch.cpp index a5bbf544..624b5795 100644 --- a/src/boards/datalatch.cpp +++ b/src/boards/datalatch.cpp @@ -26,6 +26,7 @@ static uint16 addrreg0=0, addrreg1=0; static uint8 *WRAM = NULL; static uint32 WRAMSIZE=0; static void (*WSync)(void) = nullptr; +static uint8 submapper; static DECLFW(LatchWrite) { // FCEU_printf("bs %04x %02x\n",A,V); @@ -68,6 +69,7 @@ static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 ad info->Power = LatchPower; info->Close = LatchClose; GameStateRestore = StateRestore; + submapper = info->submapper; if(info->ines2) if(info->battery_wram_size + info->wram_size > 0) wram = 1; @@ -295,7 +297,11 @@ static void M78Sync() { setprg16(0x8000, (latche & 7)); setprg16(0xc000, ~0); setchr8(latche >> 4); - setmirror(MI_0 + ((latche >> 3) & 1)); + if (submapper == 3) { + setmirror((latche >> 3) & 1); + } else { + setmirror(MI_0 + ((latche >> 3) & 1)); + } } void Mapper78_Init(CartInfo *info) { From 43bdd96d7e3400d4d417970a13a74b537fa272eb Mon Sep 17 00:00:00 2001 From: negative Date: Mon, 6 Mar 2023 13:24:20 +0800 Subject: [PATCH 03/69] Add mapper 174 --- src/boards/addrlatch.cpp | 17 +++++++++++++++++ src/ines.cpp | 2 +- src/ines.h | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/boards/addrlatch.cpp b/src/boards/addrlatch.cpp index b3f44c88..041cf885 100644 --- a/src/boards/addrlatch.cpp +++ b/src/boards/addrlatch.cpp @@ -220,6 +220,23 @@ void Mapper92_Init(CartInfo *info) { Latch_Init(info, M92Sync, NULL, 0x80B0, 0x8000, 0xFFFF, 0); } +//------------------ Map 174 --------------------------- + +static void M174Sync(void) { + if (latche & 0x80) { + setprg32(0x8000, (latche >> 5) & 3); + } else { + setprg16(0x8000, (latche >> 4) & 7); + setprg16(0xC000, (latche >> 4) & 7); + } + setchr8((latche >> 1) & 7); + setmirror((latche & 1) ^ 1); +} + +void Mapper174_Init(CartInfo *info) { + Latch_Init(info, M174Sync, NULL, 0, 0x8000, 0xFFFF, 0); +} + //------------------ Map 200 --------------------------- static void M200Sync(void) { diff --git a/src/ines.cpp b/src/ines.cpp index be7543a6..6873bdc5 100644 --- a/src/ines.cpp +++ b/src/ines.cpp @@ -689,7 +689,7 @@ BMAPPINGLocal bmap[] = { {"", 171, Mapper171_Init}, {"", 172, Mapper172_Init}, {"", 173, Mapper173_Init}, -// {"", 174, Mapper174_Init}, + {"NTDec 5-in-1", 174, Mapper174_Init}, {"", 175, Mapper175_Init}, {"BMCFK23C", 176, BMCFK23C_Init}, // zero 26-may-2012 - well, i have some WXN junk games that use 176 for instance ????. i dont know what game uses this BMCFK23C as mapper 176. we'll have to make a note when we find it. {"", 177, Mapper177_Init}, diff --git a/src/ines.h b/src/ines.h index f1b0d231..04e723d7 100644 --- a/src/ines.h +++ b/src/ines.h @@ -208,6 +208,7 @@ void Mapper170_Init(CartInfo *); void Mapper171_Init(CartInfo *); void Mapper172_Init(CartInfo *); void Mapper173_Init(CartInfo *); +void Mapper174_Init(CartInfo *); void Mapper175_Init(CartInfo *); void Mapper177_Init(CartInfo *); void Mapper178_Init(CartInfo *); From 012fbca2d80952421b4ef683be0c453071022716 Mon Sep 17 00:00:00 2001 From: negative Date: Mon, 6 Mar 2023 14:03:09 +0800 Subject: [PATCH 04/69] Move mapper 205 to mapper 361/366, add proper mapper 205 - Current mapper 205 implementation moved to describe mappers 361 and 366 instead. - Then add mapper new mapper 205. These mapper changes should now reflect what is described in mapper's wiki article --- src/boards/mmc3.cpp | 77 +++++++++++++++++++++++++++++++++++---------- src/ines.cpp | 4 ++- src/ines.h | 1 + src/unif.cpp | 1 + 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/boards/mmc3.cpp b/src/boards/mmc3.cpp index 17d9509b..49cc7ecb 100644 --- a/src/boards/mmc3.cpp +++ b/src/boards/mmc3.cpp @@ -1136,20 +1136,65 @@ void Mapper198_Init(CartInfo *info) { info->Power = M195Power; } -// ---------------------------- Mapper 205 ------------------------------ -// GN-45 BOARD +/* ---------------------------- Mapper 205 ------------------------------ */ +/* UNIF boardname BMC-JC-016-2 +https://wiki.nesdev.com/w/index.php/INES_Mapper_205 */ + +/* 2023-02 : Update reg write logic and add solder pad */ static void M205PW(uint32 A, uint8 V) { -// GN-30A - начальная маска должна быть 1F + аппаратный переключатель на шине адреса - setprg8(A, (V & 0x0f) | EXPREGS[0]); + uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x0F : 0x1F); + setprg8(A, EXPREGS[0] << 4 | bank); } static void M205CW(uint32 A, uint8 V) { -// GN-30A - начальная маска должна быть FF + uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x7F : 0xFF); + setchr1(A, (EXPREGS[0] << 7) | bank); +} + +static DECLFW(M205Write) { + EXPREGS[0] = V & 3; + if (V & 1) { + EXPREGS[0] |= EXPREGS[1]; + } + CartBW(A, V); + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M205Reset(void) { + EXPREGS[0] = 0; + EXPREGS[1] ^= 2; /* solder pad */ + MMC3RegReset(); +} + +static void M205Power(void) { + EXPREGS[0] = EXPREGS[1] = 0; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, M205Write); +} + +void Mapper205_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 128, 8, 0); + pwrap = M205PW; + cwrap = M205CW; + info->Power = M205Power; + info->Reset = M205Reset; + AddExState(EXPREGS, 2, 0, "EXPR"); +} + +/* --------------------------- GN-45 BOARD ------------------------------ */ + +/* Mapper 361 and 366, previously assigned as Mapper 205 */ +static void GN45PW(uint32 A, uint8 V) { + setprg8(A, (V & 0x0f) | EXPREGS[0]); +} + +static void GN45CW(uint32 A, uint8 V) { setchr1(A, (V & 0x7F) | (EXPREGS[0] << 3)); } -static DECLFW(M205Write0) { +static DECLFW(GN45Write0) { if (EXPREGS[2] == 0) { EXPREGS[0] = A & 0x30; EXPREGS[2] = A & 0x80; @@ -1159,7 +1204,7 @@ static DECLFW(M205Write0) { CartBW(A, V); } -static DECLFW(M205Write1) { +static DECLFW(GN45Write1) { if (EXPREGS[2] == 0) { EXPREGS[0] = V & 0x30; FixMMC3PRG(MMC3_cmd); @@ -1168,23 +1213,23 @@ static DECLFW(M205Write1) { CartBW(A, V); } -static void M205Reset(void) { +static void GN45Reset(void) { EXPREGS[0] = EXPREGS[2] = 0; MMC3RegReset(); } -static void M205Power(void) { +static void GN45Power(void) { GenMMC3Power(); - SetWriteHandler(0x6000, 0x6fff, M205Write0); - SetWriteHandler(0x7000, 0x7fff, M205Write1); // OK-411 boards, the same logic, but data latched, 2-in-1 frankenstein + SetWriteHandler(0x6000, 0x6fff, GN45Write0); + SetWriteHandler(0x7000, 0x7fff, GN45Write1); /* OK-411 boards, the same logic, but data latched, 2-in-1 frankenstein */ } -void Mapper205_Init(CartInfo *info) { +void GN45_Init(CartInfo *info) { GenMMC3_Init(info, 128, 128, 8, 0); - pwrap = M205PW; - cwrap = M205CW; - info->Power = M205Power; - info->Reset = M205Reset; + pwrap = GN45PW; + cwrap = GN45CW; + info->Power = GN45Power; + info->Reset = GN45Reset; AddExState(EXPREGS, 1, 0, "EXPR"); } diff --git a/src/ines.cpp b/src/ines.cpp index 6873bdc5..9bf183f5 100644 --- a/src/ines.cpp +++ b/src/ines.cpp @@ -720,7 +720,7 @@ BMAPPINGLocal bmap[] = { {"", 202, Mapper202_Init}, {"", 203, Mapper203_Init}, {"", 204, Mapper204_Init}, - {"", 205, Mapper205_Init}, + {"JC-016-2", 205, Mapper205_Init}, {"NAMCOT 108 Rev. C", 206, Mapper206_Init}, // Deprecated, Used to be "DEIROM" whatever it means, but actually simple version of MMC3 {"TAITO X1-005 Rev. B", 207, Mapper207_Init}, {"", 208, Mapper208_Init}, @@ -788,6 +788,8 @@ BMAPPINGLocal bmap[] = { {"HP10xx/H20xx Boards", 260, BMCHPxx_Init}, {"810544-CA-1", 261, BMC810544CA1_Init}, {"AA6023/AA6023B", 268, AA6023_Init}, + {"OK-411", 361, GN45_Init}, + {"GN-45", 366, GN45_Init}, {"COOLGIRL", 342, COOLGIRL_Init }, {"FAM250/81-01-39-C/SCHI-24", 354, Mapper354_Init }, diff --git a/src/ines.h b/src/ines.h index 04e723d7..6c95863a 100644 --- a/src/ines.h +++ b/src/ines.h @@ -280,6 +280,7 @@ void Mapper354_Init(CartInfo *); void Mapper406_Init(CartInfo *); void INX_007T_Init(CartInfo* info); +void GN45_Init(CartInfo *info); /* previously mapper 205 */ typedef struct { const char *name; diff --git a/src/unif.cpp b/src/unif.cpp index 9bcc44f7..7b6c73a8 100644 --- a/src/unif.cpp +++ b/src/unif.cpp @@ -476,6 +476,7 @@ static BMAPPING bmap[] = { { "BS-400R", BS400R_Init, 0 }, { "BS-4040R", BS4040R_Init, 0 }, { "COOLGIRL", COOLGIRL_Init, 0 }, + { "JC-016-2", Mapper205_Init, 0 }, { 0, 0, 0 } }; From 0aa49a56276f9b6c83b09a0763faa91047b04685 Mon Sep 17 00:00:00 2001 From: negative Date: Mon, 6 Mar 2023 14:42:25 +0800 Subject: [PATCH 05/69] Mapper 205: Fix for split-rom variant (UNIF) --- src/boards/mmc3.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/boards/mmc3.cpp b/src/boards/mmc3.cpp index 49cc7ecb..e7e229cf 100644 --- a/src/boards/mmc3.cpp +++ b/src/boards/mmc3.cpp @@ -1144,12 +1144,20 @@ https://wiki.nesdev.com/w/index.php/INES_Mapper_205 */ static void M205PW(uint32 A, uint8 V) { uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x0F : 0x1F); - setprg8(A, EXPREGS[0] << 4 | bank); + if (PRGsize[1]) { // split-rom variant + setprg8r((EXPREGS[0] & 3) ? (EXPREGS[0] - 1) : 0, A, bank); + } else { + setprg8(A, EXPREGS[0] << 4 | bank); + } } static void M205CW(uint32 A, uint8 V) { uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x7F : 0xFF); - setchr1(A, (EXPREGS[0] << 7) | bank); + if (CHRsize[1]) { // split-rom variant + setchr1r((EXPREGS[0] & 3) ? (EXPREGS[0] - 1) : 0, A, bank); + } else { + setchr1(A, (EXPREGS[0] << 7) | bank); + } } static DECLFW(M205Write) { @@ -1175,7 +1183,7 @@ static void M205Power(void) { } void Mapper205_Init(CartInfo *info) { - GenMMC3_Init(info, 256, 128, 8, 0); + GenMMC3_Init(info, 256, 128, 0, 0); pwrap = M205PW; cwrap = M205CW; info->Power = M205Power; From bc6164260dbfc274881de19cc43d9366df5d0b63 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 11 Mar 2023 07:47:58 -0500 Subject: [PATCH 06/69] State recorder in work. --- src/drivers/Qt/ConsoleWindow.cpp | 18 +++++++++ src/drivers/Qt/ConsoleWindow.h | 2 + src/drivers/Qt/config.cpp | 6 +++ src/drivers/Qt/config.h | 3 ++ src/state.cpp | 67 ++++++++++++++++++++++++++++++-- src/state.h | 2 + 6 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 0613edc9..b35f37aa 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -53,6 +53,7 @@ #include "../../input.h" #include "../../movie.h" #include "../../wave.h" +#include "../../state.h" #include "../../version.h" #include "common/os_utils.h" @@ -920,6 +921,9 @@ void consoleWin_t::initHotKeys(void) connect( Hotkeys[ HK_LOAD_STATE_7 ].getShortcut(), SIGNAL(activated()), this, SLOT(loadState7(void)) ); connect( Hotkeys[ HK_LOAD_STATE_8 ].getShortcut(), SIGNAL(activated()), this, SLOT(loadState8(void)) ); connect( Hotkeys[ HK_LOAD_STATE_9 ].getShortcut(), SIGNAL(activated()), this, SLOT(loadState9(void)) ); + + connect( Hotkeys[ HK_LOAD_PREV_STATE ].getShortcut(), SIGNAL(activated()), this, SLOT(loadPrevState(void)) ); + connect( Hotkeys[ HK_LOAD_NEXT_STATE ].getShortcut(), SIGNAL(activated()), this, SLOT(loadNextState(void)) ); } //--------------------------------------------------------------------------- void consoleWin_t::createMainMenu(void) @@ -2729,6 +2733,20 @@ void consoleWin_t::loadState7(void){ loadState(7); } void consoleWin_t::loadState8(void){ loadState(8); } void consoleWin_t::loadState9(void){ loadState(9); } +void consoleWin_t::loadPrevState(void) +{ + FCEU_WRAPPER_LOCK(); + FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 ); + FCEU_WRAPPER_UNLOCK(); +} + +void consoleWin_t::loadNextState(void) +{ + FCEU_WRAPPER_LOCK(); + FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 ); + FCEU_WRAPPER_UNLOCK(); +} + void consoleWin_t::quickSave(void) { FCEU_WRAPPER_LOCK(); diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index eda9fd34..59ccf23d 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -439,6 +439,8 @@ class consoleWin_t : public QMainWindow void loadState7(void); void loadState8(void); void loadState9(void); + void loadPrevState(void); + void loadNextState(void); void mainMenuOpen(void); void mainMenuClose(void); void warnAmbiguousShortcut( QShortcut*); diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index fc314283..7d7c1d87 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -295,6 +295,12 @@ int getHotKeyConfig( int i, const char **nameOut, const char **keySeqOut, const case HK_SELECT_STATE_PREV: name = "SelectStatePrev"; keySeq = ""; title = "Select Previous State Slot"; group = "State"; break; + case HK_LOAD_PREV_STATE: + name = "LoadPrevState"; keySeq = ""; title = "Load Previous Recorded State"; group = "State"; + break; + case HK_LOAD_NEXT_STATE: + name = "LoadNextState"; keySeq = ""; title = "Load Next Recorded State"; group = "State"; + break; case HK_VOLUME_MUTE: name = "VolumeMute"; keySeq = ""; title = "Sound Volume Mute"; group = "Sound"; break; diff --git a/src/drivers/Qt/config.h b/src/drivers/Qt/config.h index 54a6e5d8..60bbac7e 100644 --- a/src/drivers/Qt/config.h +++ b/src/drivers/Qt/config.h @@ -34,6 +34,9 @@ enum HOTKEY { HK_SELECT_STATE_5, HK_SELECT_STATE_6, HK_SELECT_STATE_7, HK_SELECT_STATE_8, HK_SELECT_STATE_9, HK_SELECT_STATE_NEXT, HK_SELECT_STATE_PREV, + // State Recorder + HK_LOAD_PREV_STATE, HK_LOAD_NEXT_STATE, + // GUI HK_FULLSCREEN, HK_MAIN_MENU_HIDE, diff --git a/src/state.cpp b/src/state.cpp index cdce4ad8..fdfbec4c 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1202,6 +1202,9 @@ class StateRecorder frameCounter = 0; framesPerSnap = 3 * 60; compressionLevel = Z_NO_COMPRESSION; + lastState = ringHead; + loadIndexReset = false; + lastLoadFrame = 0; } ~StateRecorder(void) @@ -1215,9 +1218,22 @@ class StateRecorder void update(void) { + bool isPaused = FCEUI_EmulationPaused() ? true : false; + unsigned int curFrame = static_cast(currFrameCounter); - if (curFrame != frameCounter) + if (!isPaused && loadIndexReset) + { + int ringBufSize = static_cast( ringBuf.size() ); + + ringHead = (lastState + 1) % ringBufSize; + + frameCounter = curFrame; + + loadIndexReset = false; + } + + if (!isPaused && (curFrame > frameCounter) ) { frameCounter = curFrame; @@ -1233,17 +1249,19 @@ class StateRecorder //printf("Frame:%u Save:%i Size:%zu Total:%zukB \n", frameCounter, ringHead, em->size(), dataSize() / 1024 ); + lastState = ringHead; + ringHead = (ringHead + 1) % ringBufSize; if (ringStart == ringHead) - { // Buffer Overrun + { ringStart = (ringHead + 1) % ringBufSize; } } } } - int loadState( int numSnapsFromLatest ) + int loadStateRelativeToEnd( int numSnapsFromLatest ) { int ringBufSize = static_cast( ringBuf.size() ); @@ -1255,18 +1273,43 @@ class StateRecorder int snapIdx = ringHead - numSnapsFromLatest - 1; + loadStateByIndex(snapIdx); + + return 0; + } + + int loadStateByIndex( int snapIdx ) + { + int ringBufSize = static_cast( ringBuf.size() ); + if (snapIdx < 0) { snapIdx = snapIdx + ringBufSize; } + snapIdx = snapIdx % ringBufSize; EMUFILE_MEMORY *em = ringBuf[ snapIdx ]; FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP ); + frameCounter = lastLoadFrame = static_cast(currFrameCounter); + + lastState = snapIdx; + loadIndexReset = true; + return 0; } + int getHeadIndex(void) + { + return ringHead; + } + + int getStartIndex(void) + { + return ringStart; + } + int numSnapsSaved(void) { int numSnaps = ringHead - ringStart; @@ -1284,6 +1327,7 @@ class StateRecorder } static bool enabled; + static int lastState; private: void doSnap(void) @@ -1298,11 +1342,14 @@ class StateRecorder int compressionLevel; unsigned int frameCounter; unsigned int framesPerSnap; + unsigned int lastLoadFrame; + bool loadIndexReset; }; static StateRecorder *stateRecorder = nullptr; bool StateRecorder::enabled = false; +int StateRecorder::lastState = 0; int FCEU_StateRecorderStart(void) { @@ -1340,3 +1387,17 @@ bool FCEU_StateRecorderRunning(void) { return stateRecorder != nullptr; } + +int FCEU_StateRecorderLoadState(int snapIndex) +{ + if (stateRecorder != nullptr) + { + stateRecorder->loadStateByIndex(snapIndex); + } + return 0; +} + +int FCEU_StateRecorderGetStateIndex(void) +{ + return StateRecorder::lastState; +} diff --git a/src/state.h b/src/state.h index 3899a7aa..097f1977 100644 --- a/src/state.h +++ b/src/state.h @@ -84,3 +84,5 @@ int FCEU_StateRecorderStop(void); int FCEU_StateRecorderUpdate(void); bool FCEU_StateRecorderRunning(void); bool FCEU_StateRecorderIsEnabled(void); +int FCEU_StateRecorderGetStateIndex(void); +int FCEU_StateRecorderLoadState(int snapIndex); From f87d746350a38a1c6bf02f1383d361407f266316 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 11 Mar 2023 15:54:44 -0500 Subject: [PATCH 07/69] State recorder config dialog in work for Qt GUI. --- src/CMakeLists.txt | 1 + src/drivers/Qt/ConsoleWindow.cpp | 19 +++ src/drivers/Qt/ConsoleWindow.h | 2 + src/drivers/Qt/StateRecorderConf.cpp | 225 +++++++++++++++++++++++++++ src/drivers/Qt/StateRecorderConf.h | 47 ++++++ 5 files changed, 294 insertions(+) create mode 100644 src/drivers/Qt/StateRecorderConf.cpp create mode 100644 src/drivers/Qt/StateRecorderConf.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d2f94ad..3dde1ae0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -551,6 +551,7 @@ set(SRC_DRIVERS_SDL ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleUtilities.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleVideoConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleSoundConf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/StateRecorderConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/iNesHeaderEditor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/SplashScreen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/TraceLogger.cpp diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index b35f37aa..4deb3a79 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -79,6 +79,7 @@ #include "Qt/MoviePlay.h" #include "Qt/MovieRecord.h" #include "Qt/MovieOptions.h" +#include "Qt/StateRecorderConf.h" #include "Qt/TimingConf.h" #include "Qt/FrameTimingStats.h" #include "Qt/LuaControl.h" @@ -1218,6 +1219,15 @@ void consoleWin_t::createMainMenu(void) optMenu->addAction(timingConfig); + // Options -> State Recorder Config + stateRecordConfig = new QAction(tr("&State Recorder Config"), this); + //stateRecordConfig->setShortcut( QKeySequence(tr("Ctrl+C"))); + stateRecordConfig->setStatusTip(tr("State Recorder Configure")); + stateRecordConfig->setIcon( QIcon(":icons/media-record.png") ); + connect(stateRecordConfig, SIGNAL(triggered()), this, SLOT(openStateRecorderConfWin(void)) ); + + optMenu->addAction(stateRecordConfig); + // Options -> Movie Options movieConfig = new QAction(tr("&Movie Options"), this); //movieConfig->setShortcut( QKeySequence(tr("Ctrl+C"))); @@ -3864,6 +3874,15 @@ void consoleWin_t::toggleTurboMode(void) NoWaiting ^= 1; } +void consoleWin_t::openStateRecorderConfWin(void) +{ + StateRecorderDialog_t *win; + + win = new StateRecorderDialog_t(this); + + win->show(); +} + void consoleWin_t::openMovie(void) { MoviePlayDialog_t *win; diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index 59ccf23d..065eaaa7 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -211,6 +211,7 @@ class consoleWin_t : public QMainWindow QAction *hotkeyConfig; QAction *paletteConfig; QAction *guiConfig; + QAction *stateRecordConfig; QAction *timingConfig; QAction *movieConfig; QAction *autoResume; @@ -344,6 +345,7 @@ class consoleWin_t : public QMainWindow void openPaletteConfWin(void); void openGuiConfWin(void); void openTimingConfWin(void); + void openStateRecorderConfWin(void); void openPaletteEditorWin(void); void openAviRiffViewer(void); void openTimingStatWin(void); diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp new file mode 100644 index 00000000..9c627957 --- /dev/null +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -0,0 +1,225 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2022 thor2016 + * + * 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 + */ +// StateRecorderConf.cpp +// +#include +#include +#include +#include + +#include +#include +#include + +#include "Qt/StateRecorderConf.h" + +#include "../../fceu.h" +#include "../../state.h" +#include "../../emufile.h" + +//---------------------------------------------------------------------------- +StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) + : QDialog(parent) +{ + QVBoxLayout *mainLayout; + QHBoxLayout *hbox, *hbox1; + QGroupBox *frame, *frame1; + QGridLayout *grid, *memStatsGrid; + QSettings settings; + + setWindowTitle("State Recorder Config"); + + mainLayout = new QVBoxLayout(); + grid = new QGridLayout(); + + recorderEnable = new QCheckBox(tr("Auto Start Recorder at ROM Load")); + snapMinutes = new QSpinBox(); + snapSeconds = new QSpinBox(); + historyDuration = new QSpinBox(); + + snapSeconds->setMinimum(0); + snapSeconds->setMaximum(60); + snapSeconds->setValue(3); + snapMinutes->setMinimum(0); + snapMinutes->setMaximum(60); + snapMinutes->setValue(0); + historyDuration->setMinimum(1); + historyDuration->setMaximum(180); + historyDuration->setValue(15); + + frame = new QGroupBox(tr("Retain History For:")); + hbox = new QHBoxLayout(); + + hbox->addWidget( historyDuration); + hbox->addWidget( new QLabel( tr("Minutes") ) ); + + frame->setLayout(hbox); + + grid->addWidget( recorderEnable, 0, 0 ); + grid->addWidget( frame , 1, 0 ); + + frame1 = new QGroupBox(tr("Time Between Snapshots:")); + hbox1 = new QHBoxLayout(); + frame1->setLayout(hbox1); + grid->addWidget( frame1, 2, 0 ); + + frame = new QGroupBox(); + hbox = new QHBoxLayout(); + + hbox1->addWidget(frame); + hbox->addWidget( snapMinutes); + hbox->addWidget( new QLabel( tr("Minutes") ) ); + + frame->setLayout(hbox); + + frame = new QGroupBox(); + hbox = new QHBoxLayout(); + + hbox1->addWidget(frame); + hbox->addWidget( snapSeconds); + hbox->addWidget( new QLabel( tr("Seconds") ) ); + + frame->setLayout(hbox); + + frame = new QGroupBox( tr("Memory Usage:") ); + memStatsGrid = new QGridLayout(); + + numSnapsLbl = new QLineEdit(); + snapMemSizeLbl = new QLineEdit(); + totalMemUsageLbl = new QLineEdit(); + + numSnapsLbl->setReadOnly(true); + snapMemSizeLbl->setReadOnly(true); + totalMemUsageLbl->setReadOnly(true); + + grid->addWidget(frame, 1, 2, 2, 2); + frame->setLayout(memStatsGrid); + memStatsGrid->addWidget( new QLabel( tr("Number of\nSnapshots:") ), 0, 0 ); + memStatsGrid->addWidget( numSnapsLbl, 0, 1 ); + + memStatsGrid->addWidget( new QLabel( tr("Snapshot Size:") ), 1, 0 ); + memStatsGrid->addWidget( snapMemSizeLbl, 1, 1 ); + + memStatsGrid->addWidget( new QLabel( tr("Total Size:") ), 2, 0 ); + memStatsGrid->addWidget( totalMemUsageLbl, 2, 1 ); + + mainLayout->addLayout(grid); + + hbox = new QHBoxLayout(); + mainLayout->addLayout(hbox); + + closeButton = new QPushButton( tr("Close") ); + applyButton = new QPushButton( tr("Apply") ); + + closeButton->setIcon( style()->standardIcon( QStyle::SP_DialogCloseButton ) ); + applyButton->setIcon( style()->standardIcon( QStyle::SP_DialogApplyButton ) ); + + hbox->addWidget(applyButton, 1); + hbox->addStretch(8); + hbox->addWidget(closeButton, 1); + + setLayout(mainLayout); + + restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray()); + + recalcMemoryUsage(); +} +//---------------------------------------------------------------------------- +StateRecorderDialog_t::~StateRecorderDialog_t(void) +{ + //printf("Destroy State Recorder Config Window\n"); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::closeEvent(QCloseEvent *event) +{ + QSettings settings; + settings.setValue("stateRecorderWindow/geometry", saveGeometry()); + done(0); + deleteLater(); + event->accept(); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::closeWindow(void) +{ + QSettings settings; + settings.setValue("stateRecorderWindow/geometry", saveGeometry()); + done(0); + deleteLater(); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::recalcMemoryUsage(void) +{ + char stmp[64]; + + float fhistMin = static_cast( historyDuration->value() ); + float fsnapMin = static_cast( snapMinutes->value() ) + + ( static_cast( snapSeconds->value() ) / 60.0f ); + + float fnumSnaps = fhistMin / fsnapMin; + float fsnapSize = 10.0f * 1024.0f; + float ftotalSize; + constexpr float oneKiloByte = 1024.0f; + constexpr float oneMegaByte = 1024.0f * 1024.0f; + + int inumSnaps = static_cast( fnumSnaps + 0.5f ); + + sprintf( stmp, "%i", inumSnaps ); + + numSnapsLbl->setText( tr(stmp) ); + + if (GameInfo) + { + EMUFILE_MEMORY em; + int compressionLevel = 0; + + FCEUSS_SaveMS( &em, compressionLevel ); + + fsnapSize = static_cast( em.size() ) / 1024.0f; + } + + if (fsnapSize >= oneKiloByte) + { + sprintf( stmp, "%.02f kB", fsnapSize / oneKiloByte ); + } + else + { + sprintf( stmp, "%.0f B", fsnapSize ); + } + + snapMemSizeLbl->setText( tr(stmp) ); + + ftotalSize = fsnapSize * static_cast(inumSnaps); + + if (ftotalSize >= oneMegaByte) + { + sprintf( stmp, "%.02f MB", ftotalSize / oneMegaByte ); + } + else if (ftotalSize >= oneKiloByte) + { + sprintf( stmp, "%.02f kB", ftotalSize / oneKiloByte ); + } + else + { + sprintf( stmp, "%.0f B", ftotalSize ); + } + + totalMemUsageLbl->setText( tr(stmp) ); +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h new file mode 100644 index 00000000..75c28d08 --- /dev/null +++ b/src/drivers/Qt/StateRecorderConf.h @@ -0,0 +1,47 @@ +// StateRecorderConf.h +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class StateRecorderDialog_t : public QDialog +{ + Q_OBJECT + +public: + StateRecorderDialog_t(QWidget *parent = 0); + ~StateRecorderDialog_t(void); + +protected: + void closeEvent(QCloseEvent *event); + + QSpinBox *snapMinutes; + QSpinBox *snapSeconds; + QSpinBox *historyDuration; + QCheckBox *recorderEnable; + QLineEdit *numSnapsLbl; + QLineEdit *snapMemSizeLbl; + QLineEdit *totalMemUsageLbl; + QPushButton *applyButton; + QPushButton *closeButton; + + void recalcMemoryUsage(void); + +public slots: + void closeWindow(void); +private slots: + +}; From bb76573112df80626b3906204e30b396448e446f Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 11 Mar 2023 20:45:13 -0500 Subject: [PATCH 08/69] State recorder config setup in work. --- src/drivers/Qt/StateRecorderConf.cpp | 32 ++++++++++++++ src/drivers/Qt/StateRecorderConf.h | 4 +- src/drivers/Qt/config.cpp | 5 +++ src/drivers/Qt/fceuWrapper.cpp | 7 ++++ src/state.cpp | 63 ++++++++++++++++++++++------ src/state.h | 17 ++++++++ 6 files changed, 114 insertions(+), 14 deletions(-) diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp index 9c627957..a39de387 100644 --- a/src/drivers/Qt/StateRecorderConf.cpp +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -28,6 +28,7 @@ #include #include +#include "Qt/fceuWrapper.h" #include "Qt/StateRecorderConf.h" #include "../../fceu.h" @@ -54,6 +55,10 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) snapSeconds = new QSpinBox(); historyDuration = new QSpinBox(); + recorderEnable->setChecked( FCEU_StateRecorderIsEnabled() ); + + connect( recorderEnable, SIGNAL(stateChanged(int)), this, SLOT(enableChanged(int)) ); + snapSeconds->setMinimum(0); snapSeconds->setMaximum(60); snapSeconds->setValue(3); @@ -64,6 +69,10 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) historyDuration->setMaximum(180); historyDuration->setValue(15); + connect( snapSeconds, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); + connect( snapMinutes, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); + connect( historyDuration, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); + frame = new QGroupBox(tr("Retain History For:")); hbox = new QHBoxLayout(); @@ -131,6 +140,9 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) closeButton->setIcon( style()->standardIcon( QStyle::SP_DialogCloseButton ) ); applyButton->setIcon( style()->standardIcon( QStyle::SP_DialogApplyButton ) ); + connect(closeButton, SIGNAL(clicked(void)), this, SLOT(closeWindow(void))); + connect(applyButton, SIGNAL(clicked(void)), this, SLOT(applyChanges(void))); + hbox->addWidget(applyButton, 1); hbox->addStretch(8); hbox->addWidget(closeButton, 1); @@ -164,6 +176,26 @@ void StateRecorderDialog_t::closeWindow(void) deleteLater(); } //---------------------------------------------------------------------------- +void StateRecorderDialog_t::applyChanges(void) +{ + +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::enableChanged(int val) +{ + bool ena = val ? true : false; + + FCEU_StateRecorderSetEnabled( ena ); + + g_config->setOption("SDL.StateRecorderEnable", ena); + g_config->save(); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::spinBoxValueChanged(int newValue) +{ + recalcMemoryUsage(); +} +//---------------------------------------------------------------------------- void StateRecorderDialog_t::recalcMemoryUsage(void) { char stmp[64]; diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h index 75c28d08..aee06f79 100644 --- a/src/drivers/Qt/StateRecorderConf.h +++ b/src/drivers/Qt/StateRecorderConf.h @@ -43,5 +43,7 @@ protected: public slots: void closeWindow(void); private slots: - + void applyChanges(void); + void spinBoxValueChanged(int newValue); + void enableChanged(int); }; diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index 7d7c1d87..e2997b3a 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -754,6 +754,11 @@ InitConfig() config->addOption("loadstate", "SDL.AutoLoadState", INVALID_STATE); config->addOption("savestate", "SDL.AutoSaveState", INVALID_STATE); + config->addOption("SDL.StateRecorderEnable", false); + config->addOption("SDL.StateRecorderHistoryDurationMin", 15); + config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0); + config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3); + //TODO implement this config->addOption("periodicsaves", "SDL.PeriodicSaves", 0); diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index ef80bd2a..fe1cf6fd 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -52,6 +52,7 @@ #include "../../fceu.h" #include "../../cheat.h" #include "../../movie.h" +#include "../../state.h" #include "../../version.h" #ifdef _S9XLUA_H @@ -972,6 +973,12 @@ int fceuWrapperInit( int argc, char *argv[] ) // load the hotkeys from the config life setHotKeys(); + // Initialize the State Recorder + bool srEnable = false; + g_config->getOption("SDL.StateRecorderEnable", &srEnable); + + FCEU_StateRecorderSetEnabled( srEnable ); + if (romIndex >= 0) { QFileInfo fi( argv[romIndex] ); diff --git a/src/state.cpp b/src/state.cpp index fdfbec4c..16ee2289 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1185,23 +1185,23 @@ void RedoLoadState() //----------- Save State History ---------------- //----------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------- +static StateRecorderConfigData stateRecorderConfig; + class StateRecorder { public: StateRecorder(void) { - size_t ringBufSize = 60; + loadConfig( stateRecorderConfig ); - for (size_t i=0; i config.historyDurationMinutes) + { + config.historyDurationMinutes = config.timeBetweenSnapsMinutes; + } + const double fhistMin = config.historyDurationMinutes; + const double fsnapMin = config.timeBetweenSnapsMinutes; + const double fnumSnaps = fhistMin / fsnapMin; + + ringBufSize = static_cast( fnumSnaps + 0.5f ); + + int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz + + double hz = ( ((double)fps) / 16777216.0 ); + + double framesPerSnapf = hz * fsnapMin * 60.0; + + framesPerSnap = static_cast( framesPerSnapf + 0.50 ); + + printf("ringBufSize:%i framesPerSnap:%i\n", ringBufSize, framesPerSnap ); + + compressionLevel = stateRecorderConfig.compressionLevel; + } + void update(void) { bool isPaused = FCEUI_EmulationPaused() ? true : false; @@ -1224,8 +1253,6 @@ class StateRecorder if (!isPaused && loadIndexReset) { - int ringBufSize = static_cast( ringBuf.size() ); - ringHead = (lastState + 1) % ringBufSize; frameCounter = curFrame; @@ -1239,8 +1266,6 @@ class StateRecorder if ( (frameCounter % framesPerSnap) == 0 ) { - int ringBufSize = static_cast( ringBuf.size() ); - EMUFILE_MEMORY *em = ringBuf[ ringHead ]; em->set_len(0); @@ -1263,8 +1288,6 @@ class StateRecorder int loadStateRelativeToEnd( int numSnapsFromLatest ) { - int ringBufSize = static_cast( ringBuf.size() ); - if (numSnapsFromLatest < 0) { numSnapsFromLatest = 0; @@ -1280,8 +1303,6 @@ class StateRecorder int loadStateByIndex( int snapIdx ) { - int ringBufSize = static_cast( ringBuf.size() ); - if (snapIdx < 0) { snapIdx = snapIdx + ringBufSize; @@ -1339,6 +1360,7 @@ class StateRecorder int ringHead; int ringTail; int ringStart; + int ringBufSize; int compressionLevel; unsigned int frameCounter; unsigned int framesPerSnap; @@ -1383,6 +1405,11 @@ bool FCEU_StateRecorderIsEnabled(void) return StateRecorder::enabled; } +void FCEU_StateRecorderSetEnabled(bool enabled) +{ + StateRecorder::enabled = enabled; +} + bool FCEU_StateRecorderRunning(void) { return stateRecorder != nullptr; @@ -1401,3 +1428,13 @@ int FCEU_StateRecorderGetStateIndex(void) { return StateRecorder::lastState; } + +const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void) +{ + return stateRecorderConfig; +} +int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig) +{ + stateRecorderConfig = newConfig; + return 0; +} diff --git a/src/state.h b/src/state.h index 097f1977..3a58fad9 100644 --- a/src/state.h +++ b/src/state.h @@ -79,10 +79,27 @@ bool CheckBackupSaveStateExist(); //Checks if backupsavestate exists extern bool compressSavestates; //Whether or not to compress non-movie savestates (by default, yes) +struct StateRecorderConfigData +{ + float historyDurationMinutes; + float timeBetweenSnapsMinutes; + int compressionLevel; + + StateRecorderConfigData(void) + { + historyDurationMinutes = 15.0f; + timeBetweenSnapsMinutes = 3.0f / 60.0f; + compressionLevel = 0; + } +}; + int FCEU_StateRecorderStart(void); int FCEU_StateRecorderStop(void); int FCEU_StateRecorderUpdate(void); bool FCEU_StateRecorderRunning(void); bool FCEU_StateRecorderIsEnabled(void); +void FCEU_StateRecorderSetEnabled(bool enabled); int FCEU_StateRecorderGetStateIndex(void); int FCEU_StateRecorderLoadState(int snapIndex); +int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig); +const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void); From 4b8528588de4a6ccbae1dfaecf4bfbef9ab1a29a Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 12 Mar 2023 20:46:45 -0400 Subject: [PATCH 09/69] Added code to save/load state recorder config parameters for Qt gui. --- src/drivers/Qt/StateRecorderConf.cpp | 72 +++++++++++++++++++++++++--- src/drivers/Qt/StateRecorderConf.h | 1 + src/drivers/Qt/config.cpp | 1 + src/drivers/Qt/fceuWrapper.cpp | 25 ++++++++-- 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp index a39de387..d2fa5718 100644 --- a/src/drivers/Qt/StateRecorderConf.cpp +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -44,6 +44,7 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) QGroupBox *frame, *frame1; QGridLayout *grid, *memStatsGrid; QSettings settings; + int opt; setWindowTitle("State Recorder Config"); @@ -61,13 +62,22 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) snapSeconds->setMinimum(0); snapSeconds->setMaximum(60); - snapSeconds->setValue(3); snapMinutes->setMinimum(0); snapMinutes->setMaximum(60); - snapMinutes->setValue(0); historyDuration->setMinimum(1); historyDuration->setMaximum(180); - historyDuration->setValue(15); + + opt = 15; + g_config->getOption("SDL.StateRecorderHistoryDurationMin", &opt ); + historyDuration->setValue(opt); + + opt = 0; + g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &opt); + snapMinutes->setValue(opt); + + opt = 3; + g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &opt); + snapSeconds->setValue(opt); connect( snapSeconds, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); connect( snapMinutes, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); @@ -84,10 +94,34 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) grid->addWidget( recorderEnable, 0, 0 ); grid->addWidget( frame , 1, 0 ); + frame = new QGroupBox(tr("Compression Level:")); + hbox = new QHBoxLayout(); + + cmprLvlCbox = new QComboBox(); + cmprLvlCbox->addItem( tr("0 - None"), 0 ); + cmprLvlCbox->addItem( tr("1"), 1 ); + cmprLvlCbox->addItem( tr("2"), 2 ); + cmprLvlCbox->addItem( tr("3"), 3 ); + cmprLvlCbox->addItem( tr("4"), 4 ); + cmprLvlCbox->addItem( tr("5"), 5 ); + cmprLvlCbox->addItem( tr("6"), 6 ); + cmprLvlCbox->addItem( tr("7"), 7 ); + cmprLvlCbox->addItem( tr("8"), 8 ); + cmprLvlCbox->addItem( tr("9 - Max"), 9 ); + + opt = 0; + g_config->getOption("SDL.StateRecorderCompressionLevel", &opt); + cmprLvlCbox->setCurrentIndex(opt); + + hbox->addWidget(cmprLvlCbox); + + frame->setLayout(hbox); + grid->addWidget( frame, 1, 1 ); + frame1 = new QGroupBox(tr("Time Between Snapshots:")); hbox1 = new QHBoxLayout(); frame1->setLayout(hbox1); - grid->addWidget( frame1, 2, 0 ); + grid->addWidget( frame1, 2, 0, 1, 2 ); frame = new QGroupBox(); hbox = new QHBoxLayout(); @@ -118,7 +152,7 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) snapMemSizeLbl->setReadOnly(true); totalMemUsageLbl->setReadOnly(true); - grid->addWidget(frame, 1, 2, 2, 2); + grid->addWidget(frame, 1, 3, 2, 2); frame->setLayout(memStatsGrid); memStatsGrid->addWidget( new QLabel( tr("Number of\nSnapshots:") ), 0, 0 ); memStatsGrid->addWidget( numSnapsLbl, 0, 1 ); @@ -178,16 +212,38 @@ void StateRecorderDialog_t::closeWindow(void) //---------------------------------------------------------------------------- void StateRecorderDialog_t::applyChanges(void) { + StateRecorderConfigData config; + config.historyDurationMinutes = static_cast( historyDuration->value() ); + config.timeBetweenSnapsMinutes = static_cast( snapMinutes->value() ) + + ( static_cast( snapSeconds->value() ) / 60.0f ); + config.compressionLevel = cmprLvlCbox->currentData().toInt(); + + FCEU_WRAPPER_LOCK(); + FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() ); + FCEU_StateRecorderSetConfigData( config ); + if (FCEU_StateRecorderRunning()) + { + // TODO restart with new settings + } + FCEU_WRAPPER_UNLOCK(); + + g_config->setOption("SDL.StateRecorderHistoryDurationMin", historyDuration->value() ); + g_config->setOption("SDL.StateRecorderTimeBetweenSnapsMin", snapMinutes->value() ); + g_config->setOption("SDL.StateRecorderTimeBetweenSnapsSec", snapSeconds->value() ); + g_config->setOption("SDL.StateRecorderEnable", recorderEnable->isChecked() ); + g_config->save(); } //---------------------------------------------------------------------------- void StateRecorderDialog_t::enableChanged(int val) { bool ena = val ? true : false; + FCEU_WRAPPER_LOCK(); FCEU_StateRecorderSetEnabled( ena ); + FCEU_WRAPPER_UNLOCK(); - g_config->setOption("SDL.StateRecorderEnable", ena); + g_config->setOption("SDL.StateRecorderEnable", ena ); g_config->save(); } //---------------------------------------------------------------------------- @@ -218,12 +274,16 @@ void StateRecorderDialog_t::recalcMemoryUsage(void) if (GameInfo) { + FCEU_WRAPPER_LOCK(); + EMUFILE_MEMORY em; int compressionLevel = 0; FCEUSS_SaveMS( &em, compressionLevel ); fsnapSize = static_cast( em.size() ) / 1024.0f; + + FCEU_WRAPPER_UNLOCK(); } if (fsnapSize >= oneKiloByte) diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h index aee06f79..68c2a741 100644 --- a/src/drivers/Qt/StateRecorderConf.h +++ b/src/drivers/Qt/StateRecorderConf.h @@ -37,6 +37,7 @@ protected: QLineEdit *totalMemUsageLbl; QPushButton *applyButton; QPushButton *closeButton; + QComboBox *cmprLvlCbox; void recalcMemoryUsage(void); diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index e2997b3a..28111b00 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -758,6 +758,7 @@ InitConfig() config->addOption("SDL.StateRecorderHistoryDurationMin", 15); config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0); config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3); + config->addOption("SDL.StateRecorderCompressionLevel", 0); //TODO implement this config->addOption("periodicsaves", "SDL.PeriodicSaves", 0); diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index fe1cf6fd..dee54dd9 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -974,11 +974,30 @@ int fceuWrapperInit( int argc, char *argv[] ) setHotKeys(); // Initialize the State Recorder - bool srEnable = false; - g_config->getOption("SDL.StateRecorderEnable", &srEnable); + { + bool srEnable = false; + int srHistDurMin = 15; + int srTimeBtwSnapsMin = 0; + int srTimeBtwSnapsSec = 3; + int srCompressionLevel = 0; + g_config->getOption("SDL.StateRecorderEnable", &srEnable); + g_config->getOption("SDL.StateRecorderHistoryDurationMin", &srHistDurMin); + g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &srTimeBtwSnapsMin); + g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &srTimeBtwSnapsSec); + g_config->getOption("SDL.StateRecorderCompressionLevel", &srCompressionLevel); - FCEU_StateRecorderSetEnabled( srEnable ); + StateRecorderConfigData srConfig; + srConfig.historyDurationMinutes = srHistDurMin; + srConfig.timeBetweenSnapsMinutes = static_cast( srTimeBtwSnapsMin ) + + ( static_cast( srTimeBtwSnapsSec ) / 60.0f ); + srConfig.compressionLevel = srCompressionLevel; + + FCEU_StateRecorderSetEnabled( srEnable ); + FCEU_StateRecorderSetConfigData( srConfig ); + } + + // Rom Load if (romIndex >= 0) { QFileInfo fi( argv[romIndex] ); From 4774a773a829e29bf31b7527952d135499103f01 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sun, 26 Mar 2023 15:40:30 -0400 Subject: [PATCH 10/69] Apply memory initialization settings to nametable, palettes, and sprites. --- src/ppu.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/ppu.cpp b/src/ppu.cpp index aa7502eb..ce84a9df 100644 --- a/src/ppu.cpp +++ b/src/ppu.cpp @@ -1734,10 +1734,17 @@ void FCEUPPU_Reset(void) { void FCEUPPU_Power(void) { int x; - memset(NTARAM, 0x00, 0x800); - memset(PALRAM, 0x00, 0x20); - memset(UPALRAM, 0x00, 0x03); - memset(SPRAM, 0x00, 0x100); + // initialize PPU memory regions according to settings + FCEU_MemoryRand(NTARAM, 0x800, true); + FCEU_MemoryRand(PALRAM, 0x20, true); + FCEU_MemoryRand(SPRAM, 0x100, true); + // palettes can only store values up to $3F, and PALRAM X4/X8/XC are mirrors of X0 for rendering purposes (UPALRAM is used for $2007 readback) + for (x = 0; x < 0x20; ++x) PALRAM[x] &= 0x3F; + UPALRAM[0] = PALRAM[0x04]; + UPALRAM[1] = PALRAM[0x08]; + UPALRAM[2] = PALRAM[0x0C]; + PALRAM[0x0C] = PALRAM[0x08] = PALRAM[0x04] = PALRAM[0x00]; + PALRAM[0x1C] = PALRAM[0x18] = PALRAM[0x14] = PALRAM[0x10]; FCEUPPU_Reset(); for (x = 0x2000; x < 0x4000; x += 8) { From 46654ed585503693b6ca533ee76d3b91314b4691 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Sun, 26 Mar 2023 15:48:26 -0400 Subject: [PATCH 11/69] ignore 7z_64.dll in output folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bacbe1cd..aa8ed88e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ /output/auxlib.lua /output/fceux.pdb /output/7z.dll +/output/7z_64.dll /output/fceux.exe /output/fceux.exp /output/fceux.lib From b6a8b46de06a9d5d722f7a4837727f99fed2ce78 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 26 Mar 2023 17:05:19 -0400 Subject: [PATCH 12/69] For Qt GUI, added feature that allows save state files to be loaded via window drag n drop from a file dialog. --- src/drivers/Qt/ConsoleWindow.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 4deb3a79..3b94cfb9 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -786,9 +786,21 @@ void consoleWin_t::dropEvent(QDropEvent *event) QFileInfo fi( filename ); QString suffix = fi.suffix(); + bool isStateSaveFile = (suffix.size() == 3) && + (suffix[0] == 'f') && (suffix[1] == 'c') && + ( (suffix[2] == 's') || suffix[2].isDigit() ); + //printf("DragNDrop Suffix: %s\n", suffix.toStdString().c_str() ); - if ( suffix.compare("lua", Qt::CaseInsensitive) == 0 ) + if (isStateSaveFile) + { + FCEU_WRAPPER_LOCK(); + FCEUI_LoadState( filename.toStdString().c_str() ); + FCEU_WRAPPER_UNLOCK(); + + event->accept(); + } + else if ( suffix.compare("lua", Qt::CaseInsensitive) == 0 ) { int luaLoadSuccess; From 3e9398f97316d19d1e752c85399e174255bf88ad Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 31 Mar 2023 05:34:32 -0400 Subject: [PATCH 13/69] Added a timed pause state for use by the state history loader. When the user backs up to a previous state, give the option of temporarily pausing to give the user time to visually process the state before resuming game play. When in this state, an unpause count down can also be optionally shown on the upper left of the video display. Also, setting pause during this state down will cancel the count down and put the emulation into full pause. TODO add GUI config hooks to control video overlay and duration config options. --- src/drawing.cpp | 2 +- src/driver.h | 2 ++ src/fceu.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++- src/fceu.h | 5 +++-- src/state.cpp | 8 ++++++++ src/state.h | 2 ++ src/video.cpp | 35 +++++++++++++++++++++++++++++++++++ 7 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/drawing.cpp b/src/drawing.cpp index 7597c068..1129b3c5 100644 --- a/src/drawing.cpp +++ b/src/drawing.cpp @@ -346,7 +346,7 @@ void FCEU_DrawRecordingStatus(uint8* XBuf) hasPlayRecIcon = true; } - if(FCEUI_EmulationPaused()) + if( EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) ) drawstatus(XBuf-ClipSidesOffset,3,28,hasPlayRecIcon?-16:0); } } diff --git a/src/driver.h b/src/driver.h index 9dde9c6d..efc1cd77 100644 --- a/src/driver.h +++ b/src/driver.h @@ -273,6 +273,8 @@ void FCEUI_ClearEmulationFrameStepped(); void FCEUI_SetEmulationPaused(int val); ///toggles the paused bit (bit0) for EmulationPaused. caused FCEUD_DebugUpdate() to fire if the emulation pauses void FCEUI_ToggleEmulationPause(); +void FCEUI_PauseForDuration(int secs); +int FCEUI_PauseFramesRemaining(); //indicates whether input aids should be drawn (such as crosshairs, etc; usually in fullscreen mode) bool FCEUD_ShouldDrawInputAids(); diff --git a/src/fceu.cpp b/src/fceu.cpp index ce870582..90ba9985 100644 --- a/src/fceu.cpp +++ b/src/fceu.cpp @@ -116,6 +116,7 @@ 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}; +static unsigned int pauseTimer = 0; FCEUGI::FCEUGI() @@ -764,6 +765,22 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski #endif } + if (EmulationPaused & EMULATIONPAUSED_TIMER) + { + if (pauseTimer > 0) + { + pauseTimer--; + } + else + { + EmulationPaused &= ~EMULATIONPAUSED_TIMER; + } + if (EmulationPaused & EMULATIONPAUSED_PAUSED) + { + EmulationPaused &= ~EMULATIONPAUSED_TIMER; + } + } + if (EmulationPaused & EMULATIONPAUSED_FA) { // the user is holding Frame Advance key @@ -787,7 +804,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski RefreshThrottleFPS(); } #endif - if (EmulationPaused & EMULATIONPAUSED_PAUSED) + if (EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) ) { // emulator is paused memcpy(XBuf, XBackBuf, 256*256); @@ -1263,6 +1280,33 @@ void FCEUI_FrameAdvance(void) { frameAdvanceRequested = true; } +void FCEUI_PauseForDuration(int secs) +{ + int framesPerSec; + + // If already paused, do nothing + if (EmulationPaused & EMULATIONPAUSED_PAUSED) + { + return; + } + + if (PAL || dendy) + { + framesPerSec = 50; + } + else + { + framesPerSec = 60; + } + pauseTimer = framesPerSec * secs; + EmulationPaused |= EMULATIONPAUSED_TIMER; +} + +int FCEUI_PauseFramesRemaining(void) +{ + return (EmulationPaused & EMULATIONPAUSED_TIMER) ? pauseTimer : 0; +} + static int AutosaveCounter = 0; void UpdateAutosave(void) { diff --git a/src/fceu.h b/src/fceu.h index 439c7dc9..b35bf4ab 100644 --- a/src/fceu.h +++ b/src/fceu.h @@ -181,8 +181,9 @@ extern uint8 vsdip; #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) -#define EMULATIONPAUSED_PAUSED 1 -#define EMULATIONPAUSED_FA 2 +#define EMULATIONPAUSED_PAUSED 0x01 +#define EMULATIONPAUSED_TIMER 0x02 +#define EMULATIONPAUSED_FA 0x04 #define FRAMEADVANCE_DELAY_DEFAULT 10 #define NES_HEADER_SIZE 16 diff --git a/src/state.cpp b/src/state.cpp index 16ee2289..e99a85ac 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1205,6 +1205,7 @@ class StateRecorder lastState = ringHead; loadIndexReset = false; lastLoadFrame = 0; + loadPauseTime = 3; } ~StateRecorder(void) @@ -1243,6 +1244,7 @@ class StateRecorder printf("ringBufSize:%i framesPerSnap:%i\n", ringBufSize, framesPerSnap ); compressionLevel = stateRecorderConfig.compressionLevel; + loadPauseTime = stateRecorderConfig.loadPauseTimeSeconds; } void update(void) @@ -1318,6 +1320,11 @@ class StateRecorder lastState = snapIdx; loadIndexReset = true; + if (loadPauseTime > 0) + { // Temporary pause after loading new state for user to have time to process + FCEUI_PauseForDuration(loadPauseTime); + } + return 0; } @@ -1362,6 +1369,7 @@ class StateRecorder int ringStart; int ringBufSize; int compressionLevel; + int loadPauseTime; unsigned int frameCounter; unsigned int framesPerSnap; unsigned int lastLoadFrame; diff --git a/src/state.h b/src/state.h index 3a58fad9..ed8193f6 100644 --- a/src/state.h +++ b/src/state.h @@ -84,12 +84,14 @@ struct StateRecorderConfigData float historyDurationMinutes; float timeBetweenSnapsMinutes; int compressionLevel; + int loadPauseTimeSeconds; StateRecorderConfigData(void) { historyDurationMinutes = 15.0f; timeBetweenSnapsMinutes = 3.0f / 60.0f; compressionLevel = 0; + loadPauseTimeSeconds = 3; } }; diff --git a/src/video.cpp b/src/video.cpp index 7d7744d4..b0f7dd0d 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -87,6 +87,8 @@ std::string AsSnapshotName =""; //adelikat:this will set the snapshot name whe void FCEUI_SetSnapshotAsName(std::string name) { AsSnapshotName = name; } std::string FCEUI_GetSnapshotAsName() { return AsSnapshotName; } +static void FCEU_DrawPauseCountDown(uint8 *XBuf); + void FCEU_KillVirtualVideo(void) { if ( XBuf ) @@ -254,6 +256,7 @@ void FCEU_PutImage(void) FCEU_DrawLagCounter(XBuf); FCEU_DrawNTSCControlBars(XBuf); FCEU_DrawRecordingStatus(XBuf); + FCEU_DrawPauseCountDown(XBuf); ShowFPS(); } @@ -771,3 +774,35 @@ void ShowFPS(void) DrawTextTrans(XBuf + ((256 - ClipSidesOffset) - 40) + (FSettings.FirstSLine + 4) * 256, 256, (uint8*)fpsmsg, 0xA0); } + +bool showPauseCountDown = true; + +static void FCEU_DrawPauseCountDown(uint8 *XBuf) +{ + if (EmulationPaused & EMULATIONPAUSED_TIMER) + { + int pauseFramesLeft = FCEUI_PauseFramesRemaining(); + + if (showPauseCountDown && (pauseFramesLeft > 0) ) + { + char text[32]; + int framesPerSec; + + if (PAL || dendy) + { + framesPerSec = 50; + } + else + { + framesPerSec = 60; + } + + sprintf(text, "Unpausing in %d...", (pauseFramesLeft / framesPerSec) + 1); + + if (text[0]) + { + DrawTextTrans(XBuf + ClipSidesOffset + (FSettings.FirstSLine) * 256, 256, (uint8*)text, 0xA0); + } + } + } +} From 86c6d3e56cb3a182b189a2750a58632b03703e7b Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 31 Mar 2023 06:55:59 -0400 Subject: [PATCH 14/69] Qt GUI state recorder config window updates. Added estimated state save CPU time display. Pause on load time widgets in work. --- src/drivers/Qt/StateRecorderConf.cpp | 75 ++++++++++++++++++++++++++-- src/drivers/Qt/StateRecorderConf.h | 6 +++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp index d2fa5718..e845a3af 100644 --- a/src/drivers/Qt/StateRecorderConf.cpp +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -28,6 +28,7 @@ #include #include +#include "Qt/throttle.h" #include "Qt/fceuWrapper.h" #include "Qt/StateRecorderConf.h" @@ -39,7 +40,7 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) : QDialog(parent) { - QVBoxLayout *mainLayout; + QVBoxLayout *mainLayout, *vbox1; QHBoxLayout *hbox, *hbox1; QGroupBox *frame, *frame1; QGridLayout *grid, *memStatsGrid; @@ -113,6 +114,8 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) g_config->getOption("SDL.StateRecorderCompressionLevel", &opt); cmprLvlCbox->setCurrentIndex(opt); + connect( cmprLvlCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(compressionLevelChanged(int)) ); + hbox->addWidget(cmprLvlCbox); frame->setLayout(hbox); @@ -141,16 +144,45 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) frame->setLayout(hbox); + frame1 = new QGroupBox(tr("Pause on Load:")); + vbox1 = new QVBoxLayout(); + frame1->setLayout(vbox1); + + pauseOnLoadCbox = new QComboBox(); + pauseOnLoadCbox->addItem( tr("No"), 0 ); + pauseOnLoadCbox->addItem( tr("Temporary"), 1 ); + pauseOnLoadCbox->addItem( tr("Full"), 2 ); + + pauseDuration = new QSpinBox(); + pauseDuration->setMinimum(0); + pauseDuration->setMaximum(60); + pauseDuration->setValue(3); // TODO + + vbox1->addWidget(pauseOnLoadCbox); + + frame = new QGroupBox( tr("Duration:") ); + hbox = new QHBoxLayout(); + + vbox1->addWidget(frame); + hbox->addWidget( pauseDuration); + hbox->addWidget( new QLabel( tr("Seconds") ) ); + + frame->setLayout(hbox); + + grid->addWidget(frame1, 3, 0, 2, 1); + frame = new QGroupBox( tr("Memory Usage:") ); memStatsGrid = new QGridLayout(); numSnapsLbl = new QLineEdit(); snapMemSizeLbl = new QLineEdit(); totalMemUsageLbl = new QLineEdit(); + saveTimeLbl = new QLineEdit(); numSnapsLbl->setReadOnly(true); snapMemSizeLbl->setReadOnly(true); totalMemUsageLbl->setReadOnly(true); + saveTimeLbl->setReadOnly(true); grid->addWidget(frame, 1, 3, 2, 2); frame->setLayout(memStatsGrid); @@ -163,6 +195,15 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) memStatsGrid->addWidget( new QLabel( tr("Total Size:") ), 2, 0 ); memStatsGrid->addWidget( totalMemUsageLbl, 2, 1 ); + frame = new QGroupBox( tr("CPU Usage:") ); + hbox = new QHBoxLayout(); + frame->setLayout(hbox); + + hbox->addWidget(new QLabel(tr("Save Time:"))); + hbox->addWidget(saveTimeLbl); + + grid->addWidget(frame, 3, 3, 1, 1); + mainLayout->addLayout(grid); hbox = new QHBoxLayout(); @@ -218,6 +259,7 @@ void StateRecorderDialog_t::applyChanges(void) config.timeBetweenSnapsMinutes = static_cast( snapMinutes->value() ) + ( static_cast( snapSeconds->value() ) / 60.0f ); config.compressionLevel = cmprLvlCbox->currentData().toInt(); + config.loadPauseTimeSeconds = pauseDuration->value(); FCEU_WRAPPER_LOCK(); FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() ); @@ -231,6 +273,7 @@ void StateRecorderDialog_t::applyChanges(void) g_config->setOption("SDL.StateRecorderHistoryDurationMin", historyDuration->value() ); g_config->setOption("SDL.StateRecorderTimeBetweenSnapsMin", snapMinutes->value() ); g_config->setOption("SDL.StateRecorderTimeBetweenSnapsSec", snapSeconds->value() ); + g_config->setOption("SDL.StateRecorderCompressionLevel", config.compressionLevel); g_config->setOption("SDL.StateRecorderEnable", recorderEnable->isChecked() ); g_config->save(); } @@ -252,6 +295,11 @@ void StateRecorderDialog_t::spinBoxValueChanged(int newValue) recalcMemoryUsage(); } //---------------------------------------------------------------------------- +void StateRecorderDialog_t::compressionLevelChanged(int newValue) +{ + recalcMemoryUsage(); +} +//---------------------------------------------------------------------------- void StateRecorderDialog_t::recalcMemoryUsage(void) { char stmp[64]; @@ -272,16 +320,32 @@ void StateRecorderDialog_t::recalcMemoryUsage(void) numSnapsLbl->setText( tr(stmp) ); + saveTimeMs = 0.0; + if (GameInfo) { + constexpr int numIterations = 10; + double ts_start, ts_end; + FCEU_WRAPPER_LOCK(); EMUFILE_MEMORY em; - int compressionLevel = 0; + int compressionLevel = cmprLvlCbox->currentData().toInt(); - FCEUSS_SaveMS( &em, compressionLevel ); + ts_start = getHighPrecTimeStamp(); - fsnapSize = static_cast( em.size() ) / 1024.0f; + // Perform State Save multiple times to get a good average + // on what the compression delays will be. + for (int i=0; i(numIterations); + + fsnapSize = static_cast( em.size() ); FCEU_WRAPPER_UNLOCK(); } @@ -313,5 +377,8 @@ void StateRecorderDialog_t::recalcMemoryUsage(void) } totalMemUsageLbl->setText( tr(stmp) ); + + sprintf( stmp, "%.02f ms", saveTimeMs); + saveTimeLbl->setText( tr(stmp) ); } //---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h index 68c2a741..b75c0ff9 100644 --- a/src/drivers/Qt/StateRecorderConf.h +++ b/src/drivers/Qt/StateRecorderConf.h @@ -31,13 +31,18 @@ protected: QSpinBox *snapMinutes; QSpinBox *snapSeconds; QSpinBox *historyDuration; + QSpinBox *pauseDuration; QCheckBox *recorderEnable; QLineEdit *numSnapsLbl; QLineEdit *snapMemSizeLbl; QLineEdit *totalMemUsageLbl; + QLineEdit *saveTimeLbl; QPushButton *applyButton; QPushButton *closeButton; QComboBox *cmprLvlCbox; + QComboBox *pauseOnLoadCbox; + + double saveTimeMs; void recalcMemoryUsage(void); @@ -47,4 +52,5 @@ private slots: void applyChanges(void); void spinBoxValueChanged(int newValue); void enableChanged(int); + void compressionLevelChanged(int newValue); }; From e95c0fe86bc1706d9346d293355ece9690ebac54 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 1 Apr 2023 18:22:54 -0400 Subject: [PATCH 15/69] For Qt GUI, hooked up state recorder pause on load options. --- src/drivers/Qt/StateRecorderConf.cpp | 31 +++++++++++++++++++++++----- src/drivers/Qt/StateRecorderConf.h | 1 + src/drivers/Qt/config.cpp | 2 ++ src/drivers/Qt/fceuWrapper.cpp | 7 +++++++ src/state.cpp | 22 ++++++++++++++++---- src/state.h | 8 +++++++ 6 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp index e845a3af..979d7aa5 100644 --- a/src/drivers/Qt/StateRecorderConf.cpp +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -144,19 +144,27 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) frame->setLayout(hbox); - frame1 = new QGroupBox(tr("Pause on Load:")); + frame1 = new QGroupBox(tr("Pause on State Load:")); vbox1 = new QVBoxLayout(); frame1->setLayout(vbox1); + g_config->getOption("SDL.StateRecorderPauseOnLoad", &opt); + pauseOnLoadCbox = new QComboBox(); - pauseOnLoadCbox->addItem( tr("No"), 0 ); - pauseOnLoadCbox->addItem( tr("Temporary"), 1 ); - pauseOnLoadCbox->addItem( tr("Full"), 2 ); + pauseOnLoadCbox->addItem( tr("No"), StateRecorderConfigData::NO_PAUSE ); + pauseOnLoadCbox->addItem( tr("Temporary"), StateRecorderConfigData::TEMPORARY_PAUSE ); + pauseOnLoadCbox->addItem( tr("Full"), StateRecorderConfigData::FULL_PAUSE ); + + pauseOnLoadCbox->setCurrentIndex( opt ); + + connect( pauseOnLoadCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(pauseOnLoadChanged(int)) ); + + g_config->getOption("SDL.StateRecorderPauseDuration", &opt); pauseDuration = new QSpinBox(); pauseDuration->setMinimum(0); pauseDuration->setMaximum(60); - pauseDuration->setValue(3); // TODO + pauseDuration->setValue(opt); vbox1->addWidget(pauseOnLoadCbox); @@ -227,6 +235,7 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray()); recalcMemoryUsage(); + pauseOnLoadChanged( pauseOnLoadCbox->currentIndex() ); } //---------------------------------------------------------------------------- StateRecorderDialog_t::~StateRecorderDialog_t(void) @@ -260,6 +269,7 @@ void StateRecorderDialog_t::applyChanges(void) ( static_cast( snapSeconds->value() ) / 60.0f ); config.compressionLevel = cmprLvlCbox->currentData().toInt(); config.loadPauseTimeSeconds = pauseDuration->value(); + config.pauseOnLoad = static_cast( pauseOnLoadCbox->currentData().toInt() ); FCEU_WRAPPER_LOCK(); FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() ); @@ -274,6 +284,8 @@ void StateRecorderDialog_t::applyChanges(void) g_config->setOption("SDL.StateRecorderTimeBetweenSnapsMin", snapMinutes->value() ); g_config->setOption("SDL.StateRecorderTimeBetweenSnapsSec", snapSeconds->value() ); g_config->setOption("SDL.StateRecorderCompressionLevel", config.compressionLevel); + g_config->setOption("SDL.StateRecorderPauseOnLoad", config.pauseOnLoad); + g_config->setOption("SDL.StateRecorderPauseDuration", config.loadPauseTimeSeconds); g_config->setOption("SDL.StateRecorderEnable", recorderEnable->isChecked() ); g_config->save(); } @@ -300,6 +312,15 @@ void StateRecorderDialog_t::compressionLevelChanged(int newValue) recalcMemoryUsage(); } //---------------------------------------------------------------------------- +void StateRecorderDialog_t::pauseOnLoadChanged(int index) +{ + StateRecorderConfigData::PauseType pauseOnLoad; + + pauseOnLoad = static_cast( pauseOnLoadCbox->currentData().toInt() ); + + pauseDuration->setEnabled( pauseOnLoad == StateRecorderConfigData::TEMPORARY_PAUSE ); +} +//---------------------------------------------------------------------------- void StateRecorderDialog_t::recalcMemoryUsage(void) { char stmp[64]; diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h index b75c0ff9..cb248998 100644 --- a/src/drivers/Qt/StateRecorderConf.h +++ b/src/drivers/Qt/StateRecorderConf.h @@ -53,4 +53,5 @@ private slots: void spinBoxValueChanged(int newValue); void enableChanged(int); void compressionLevelChanged(int newValue); + void pauseOnLoadChanged(int index); }; diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index 28111b00..57bce0f1 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -759,6 +759,8 @@ InitConfig() config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0); config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3); config->addOption("SDL.StateRecorderCompressionLevel", 0); + config->addOption("SDL.StateRecorderPauseOnLoad", 1); + config->addOption("SDL.StateRecorderPauseDuration", 3); //TODO implement this config->addOption("periodicsaves", "SDL.PeriodicSaves", 0); diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index dee54dd9..e5bd4ce7 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -980,11 +980,16 @@ int fceuWrapperInit( int argc, char *argv[] ) int srTimeBtwSnapsMin = 0; int srTimeBtwSnapsSec = 3; int srCompressionLevel = 0; + int pauseOnLoadTime = 3; + int pauseOnLoad = StateRecorderConfigData::TEMPORARY_PAUSE; + g_config->getOption("SDL.StateRecorderEnable", &srEnable); g_config->getOption("SDL.StateRecorderHistoryDurationMin", &srHistDurMin); g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &srTimeBtwSnapsMin); g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &srTimeBtwSnapsSec); g_config->getOption("SDL.StateRecorderCompressionLevel", &srCompressionLevel); + g_config->getOption("SDL.StateRecorderPauseOnLoad", &pauseOnLoad); + g_config->getOption("SDL.StateRecorderPauseDuration", &pauseOnLoadTime); StateRecorderConfigData srConfig; @@ -992,6 +997,8 @@ int fceuWrapperInit( int argc, char *argv[] ) srConfig.timeBetweenSnapsMinutes = static_cast( srTimeBtwSnapsMin ) + ( static_cast( srTimeBtwSnapsSec ) / 60.0f ); srConfig.compressionLevel = srCompressionLevel; + srConfig.loadPauseTimeSeconds = pauseOnLoadTime; + srConfig.pauseOnLoad = static_cast(pauseOnLoad); FCEU_StateRecorderSetEnabled( srEnable ); FCEU_StateRecorderSetConfigData( srConfig ); diff --git a/src/state.cpp b/src/state.cpp index e99a85ac..9faa189d 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1206,6 +1206,7 @@ class StateRecorder loadIndexReset = false; lastLoadFrame = 0; loadPauseTime = 3; + pauseOnLoad = StateRecorderConfigData::TEMPORARY_PAUSE; } ~StateRecorder(void) @@ -1245,6 +1246,7 @@ class StateRecorder compressionLevel = stateRecorderConfig.compressionLevel; loadPauseTime = stateRecorderConfig.loadPauseTimeSeconds; + pauseOnLoad = stateRecorderConfig.pauseOnLoad; } void update(void) @@ -1320,11 +1322,17 @@ class StateRecorder lastState = snapIdx; loadIndexReset = true; - if (loadPauseTime > 0) - { // Temporary pause after loading new state for user to have time to process - FCEUI_PauseForDuration(loadPauseTime); + if (pauseOnLoad == StateRecorderConfigData::TEMPORARY_PAUSE) + { + if (loadPauseTime > 0) + { // Temporary pause after loading new state for user to have time to process + FCEUI_PauseForDuration(loadPauseTime); + } + } + else if (pauseOnLoad == StateRecorderConfigData::FULL_PAUSE) + { + FCEUI_SetEmulationPaused( EMULATIONPAUSED_PAUSED ); } - return 0; } @@ -1370,6 +1378,7 @@ class StateRecorder int ringBufSize; int compressionLevel; int loadPauseTime; + StateRecorderConfigData::PauseType pauseOnLoad; unsigned int frameCounter; unsigned int framesPerSnap; unsigned int lastLoadFrame; @@ -1444,5 +1453,10 @@ const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void) int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig) { stateRecorderConfig = newConfig; + + if (stateRecorder != nullptr) + { + stateRecorder->loadConfig( stateRecorderConfig ); + } return 0; } diff --git a/src/state.h b/src/state.h index ed8193f6..133f206a 100644 --- a/src/state.h +++ b/src/state.h @@ -86,12 +86,20 @@ struct StateRecorderConfigData int compressionLevel; int loadPauseTimeSeconds; + enum PauseType + { + NO_PAUSE = 0, + TEMPORARY_PAUSE, + FULL_PAUSE, + } pauseOnLoad; + StateRecorderConfigData(void) { historyDurationMinutes = 15.0f; timeBetweenSnapsMinutes = 3.0f / 60.0f; compressionLevel = 0; loadPauseTimeSeconds = 3; + pauseOnLoad = TEMPORARY_PAUSE; } }; From 99aefa563a32078e9fefe549b2cc0d096b1a558b Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 1 Apr 2023 19:39:37 -0400 Subject: [PATCH 16/69] Fix for state recorder load state function. Set EMUFILE_MEMORY read position back to beginning of memory block before calling FCEUSS_LoadFP. --- src/state.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/state.cpp b/src/state.cpp index 9faa189d..d2bd658d 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1315,6 +1315,8 @@ class StateRecorder EMUFILE_MEMORY *em = ringBuf[ snapIdx ]; + em->fseek(SEEK_SET, 0); + FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP ); frameCounter = lastLoadFrame = static_cast(currFrameCounter); From 55bcb3d41a8afd8fd80cafc7ebb741694361d5e0 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 1 Apr 2023 21:17:34 -0400 Subject: [PATCH 17/69] Added a load prev and next state functions for state recorder. --- src/drivers/Qt/ConsoleWindow.cpp | 4 +- src/state.cpp | 63 +++++++++++++++++++++++++++++++- src/state.h | 2 + 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 3b94cfb9..7e2e3da4 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -2758,14 +2758,14 @@ void consoleWin_t::loadState9(void){ loadState(9); } void consoleWin_t::loadPrevState(void) { FCEU_WRAPPER_LOCK(); - FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 ); + FCEU_StateRecorderLoadPrevState(); FCEU_WRAPPER_UNLOCK(); } void consoleWin_t::loadNextState(void) { FCEU_WRAPPER_LOCK(); - FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 ); + FCEU_StateRecorderLoadNextState(); FCEU_WRAPPER_UNLOCK(); } diff --git a/src/state.cpp b/src/state.cpp index d2bd658d..db77ad77 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1338,6 +1338,41 @@ class StateRecorder return 0; } + int loadPrevState(void) + { + int snapIdx = lastState; + + if ( lastState == ringHead ) + { // No States to Load + return -1; + } + if ( lastState != ringStart ) + { + if ( (lastLoadFrame+30) > frameCounter) + { + snapIdx--; + + if (snapIdx < 0) + { + snapIdx += ringBufSize; + } + } + } + return loadStateByIndex( snapIdx ); + } + + int loadNextState(void) + { + int snapIdx = lastState; + int nextIdx = (lastState + 1) % ringBufSize; + + if ( nextIdx != ringHead ) + { + snapIdx = nextIdx; + } + return loadStateByIndex( snapIdx ); + } + int getHeadIndex(void) { return ringHead; @@ -1436,11 +1471,13 @@ bool FCEU_StateRecorderRunning(void) int FCEU_StateRecorderLoadState(int snapIndex) { + int ret = -1; + if (stateRecorder != nullptr) { - stateRecorder->loadStateByIndex(snapIndex); + ret = stateRecorder->loadStateByIndex(snapIndex); } - return 0; + return ret; } int FCEU_StateRecorderGetStateIndex(void) @@ -1448,6 +1485,28 @@ int FCEU_StateRecorderGetStateIndex(void) return StateRecorder::lastState; } +int FCEU_StateRecorderLoadPrevState(void) +{ + int ret = -1; + + if (stateRecorder != nullptr) + { + ret = stateRecorder->loadPrevState(); + } + return ret; +} + +int FCEU_StateRecorderLoadNextState(void) +{ + int ret = -1; + + if (stateRecorder != nullptr) + { + ret = stateRecorder->loadNextState(); + } + return ret; +} + const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void) { return stateRecorderConfig; diff --git a/src/state.h b/src/state.h index 133f206a..1c1155a8 100644 --- a/src/state.h +++ b/src/state.h @@ -111,5 +111,7 @@ bool FCEU_StateRecorderIsEnabled(void); void FCEU_StateRecorderSetEnabled(bool enabled); int FCEU_StateRecorderGetStateIndex(void); int FCEU_StateRecorderLoadState(int snapIndex); +int FCEU_StateRecorderLoadPrevState(void); +int FCEU_StateRecorderLoadNextState(void); int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig); const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void); From b4efaa91d8b689234cc4510a8f13a3aa36b1a5c0 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 2 Apr 2023 06:01:37 -0400 Subject: [PATCH 18/69] Changed memory_savestate and compressed_buf buffers in state.cpp to be static file scope. No reason to export these to the linker with global scope. --- src/state.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state.cpp b/src/state.cpp index db77ad77..1772fb05 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -84,9 +84,9 @@ bool backupSavestates = true; bool compressSavestates = true; //By default FCEUX compresses savestates when a movie is inactive. // a temp memory stream. We'll be dumping some data here and then compress -EMUFILE_MEMORY memory_savestate; +static EMUFILE_MEMORY memory_savestate; // temporary buffer for compressed data of a savestate -std::vector compressed_buf; +static std::vector compressed_buf; #define SFMDATA_SIZE (128) static SFORMAT SFMDATA[SFMDATA_SIZE]; From 47f795b04b793cc7137636db9e29bd6707347969 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 2 Apr 2023 07:39:00 -0400 Subject: [PATCH 19/69] For Qt GUI, added state recorder status display to config window. --- src/drivers/Qt/StateRecorderConf.cpp | 144 ++++++++++++++++++++++++++- src/drivers/Qt/StateRecorderConf.h | 39 +++++--- src/state.cpp | 28 +++++- src/state.h | 2 + 4 files changed, 196 insertions(+), 17 deletions(-) diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp index 979d7aa5..34909c5a 100644 --- a/src/drivers/Qt/StateRecorderConf.cpp +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -43,7 +43,7 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) QVBoxLayout *mainLayout, *vbox1; QHBoxLayout *hbox, *hbox1; QGroupBox *frame, *frame1; - QGridLayout *grid, *memStatsGrid; + QGridLayout *grid, *memStatsGrid, *sysStatusGrid; QSettings settings; int opt; @@ -207,13 +207,65 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) hbox = new QHBoxLayout(); frame->setLayout(hbox); - hbox->addWidget(new QLabel(tr("Save Time:"))); - hbox->addWidget(saveTimeLbl); + hbox->addWidget(new QLabel(tr("Snapshot\nSave Time:")), 2); + hbox->addWidget(saveTimeLbl, 2); grid->addWidget(frame, 3, 3, 1, 1); mainLayout->addLayout(grid); + frame1 = new QGroupBox(tr("Recorder Status")); + sysStatusGrid = new QGridLayout(); + frame1->setLayout(sysStatusGrid); + + frame = new QGroupBox(); + hbox = new QHBoxLayout(); + + sysStatusGrid->addWidget( frame, 0, 0, 1, 2); + frame->setLayout(hbox); + + recStatusLbl = new QLineEdit(); + recStatusLbl->setReadOnly(true); + startStopButton = new QPushButton( tr("Start") ); + hbox->addWidget( new QLabel(tr("State:")), 1 ); + hbox->addWidget( recStatusLbl, 2 ); + hbox->addWidget( startStopButton, 1 ); + + updateStartStopBuffon(); + updateRecorderStatusLabel(); + + connect(startStopButton, SIGNAL(clicked(void)), this, SLOT(startStopClicked(void))); + + frame = new QGroupBox(); + hbox = new QHBoxLayout(); + + sysStatusGrid->addWidget( frame, 0, 2, 1, 1); + frame->setLayout(hbox); + + recBufSizeLbl = new QLineEdit(); + recBufSizeLbl->setReadOnly(true); + hbox->addWidget( new QLabel(tr("Buffer Size:")), 1 ); + hbox->addWidget( recBufSizeLbl, 1 ); + + frame = new QGroupBox( tr("Buffer Use:") ); + hbox = new QHBoxLayout(); + + sysStatusGrid->addWidget( frame, 1, 0, 1, 3); + frame->setLayout(hbox); + + bufUsage = new QProgressBar(); + bufUsage->setToolTip( tr("% use of history record buffer.") ); + bufUsage->setOrientation( Qt::Horizontal ); + bufUsage->setMinimum( 0 ); + bufUsage->setMaximum( 100 ); + bufUsage->setValue( 0 ); + + hbox->addWidget(bufUsage); + + updateBufferSizeStatus(); + + mainLayout->addWidget(frame1); + hbox = new QHBoxLayout(); mainLayout->addLayout(hbox); @@ -236,6 +288,12 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) recalcMemoryUsage(); pauseOnLoadChanged( pauseOnLoadCbox->currentIndex() ); + + updateTimer = new QTimer(this); + + connect(updateTimer, &QTimer::timeout, this, &StateRecorderDialog_t::updatePeriodic); + + updateTimer->start(1000); // 1hz } //---------------------------------------------------------------------------- StateRecorderDialog_t::~StateRecorderDialog_t(void) @@ -290,6 +348,86 @@ void StateRecorderDialog_t::applyChanges(void) g_config->save(); } //---------------------------------------------------------------------------- +void StateRecorderDialog_t::startStopClicked(void) +{ + FCEU_WRAPPER_LOCK(); + bool isRunning = FCEU_StateRecorderRunning(); + + if (isRunning) + { + FCEU_StateRecorderStop(); + } + else + { + FCEU_StateRecorderStart(); + } + updateStatusDisplay(); + + FCEU_WRAPPER_UNLOCK(); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::updateStartStopBuffon(void) +{ + bool isRunning = FCEU_StateRecorderRunning(); + + if (isRunning) + { + startStopButton->setText( tr("Stop") ); + startStopButton->setIcon( style()->standardIcon( QStyle::SP_MediaStop ) ); + } + else + { + startStopButton->setText( tr("Start") ); + startStopButton->setIcon( QIcon(":icons/media-record.png") ); + } +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::updateBufferSizeStatus(void) +{ + char stmp[64]; + + int numSnapsSaved = FCEU_StateRecorderGetNumSnapsSaved(); + int maxSnaps = FCEU_StateRecorderGetMaxSnaps(); + + snprintf( stmp, sizeof(stmp), "%i", maxSnaps ); + + recBufSizeLbl->setText( tr(stmp) ); + + if (maxSnaps > 0) + { + bufUsage->setMaximum( maxSnaps ); + } + bufUsage->setValue( numSnapsSaved ); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::updateRecorderStatusLabel(void) +{ + bool isRunning = FCEU_StateRecorderRunning(); + + if (isRunning) + { + recStatusLbl->setText( tr("Recording") ); + } + else + { + recStatusLbl->setText( tr("Off") ); + } +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::updateStatusDisplay(void) +{ + updateStartStopBuffon(); + updateRecorderStatusLabel(); + updateBufferSizeStatus(); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::updatePeriodic(void) +{ + FCEU_WRAPPER_LOCK(); + updateStatusDisplay(); + FCEU_WRAPPER_UNLOCK(); +} +//---------------------------------------------------------------------------- void StateRecorderDialog_t::enableChanged(int val) { bool ena = val ? true : false; diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h index cb248998..de1919cf 100644 --- a/src/drivers/Qt/StateRecorderConf.h +++ b/src/drivers/Qt/StateRecorderConf.h @@ -12,10 +12,12 @@ #include #include #include +#include #include #include #include #include +#include class StateRecorderDialog_t : public QDialog { @@ -28,28 +30,39 @@ public: protected: void closeEvent(QCloseEvent *event); - QSpinBox *snapMinutes; - QSpinBox *snapSeconds; - QSpinBox *historyDuration; - QSpinBox *pauseDuration; - QCheckBox *recorderEnable; - QLineEdit *numSnapsLbl; - QLineEdit *snapMemSizeLbl; - QLineEdit *totalMemUsageLbl; - QLineEdit *saveTimeLbl; - QPushButton *applyButton; - QPushButton *closeButton; - QComboBox *cmprLvlCbox; - QComboBox *pauseOnLoadCbox; + QSpinBox *snapMinutes; + QSpinBox *snapSeconds; + QSpinBox *historyDuration; + QSpinBox *pauseDuration; + QCheckBox *recorderEnable; + QLineEdit *numSnapsLbl; + QLineEdit *snapMemSizeLbl; + QLineEdit *totalMemUsageLbl; + QLineEdit *saveTimeLbl; + QPushButton *applyButton; + QPushButton *closeButton; + QComboBox *cmprLvlCbox; + QComboBox *pauseOnLoadCbox; + QLineEdit *recStatusLbl; + QLineEdit *recBufSizeLbl; + QPushButton *startStopButton; + QProgressBar *bufUsage; + QTimer *updateTimer; double saveTimeMs; void recalcMemoryUsage(void); + void updateStartStopBuffon(void); + void updateRecorderStatusLabel(void); + void updateBufferSizeStatus(void); + void updateStatusDisplay(void); public slots: void closeWindow(void); private slots: void applyChanges(void); + void updatePeriodic(void); + void startStopClicked(void); void spinBoxValueChanged(int newValue); void enableChanged(int); void compressionLevelChanged(int newValue); diff --git a/src/state.cpp b/src/state.cpp index 1772fb05..65c2dbae 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1251,7 +1251,7 @@ class StateRecorder void update(void) { - bool isPaused = FCEUI_EmulationPaused() ? true : false; + bool isPaused = EmulationPaused ? true : false; unsigned int curFrame = static_cast(currFrameCounter); @@ -1399,6 +1399,10 @@ class StateRecorder return ringBuf.size() * ringBuf[0]->size(); } + size_t ringBufferSize(void) + { + return ringBuf.size(); + } static bool enabled; static int lastState; private: @@ -1469,6 +1473,28 @@ bool FCEU_StateRecorderRunning(void) return stateRecorder != nullptr; } +int FCEU_StateRecorderGetMaxSnaps(void) +{ + int size = 0; + + if (stateRecorder != nullptr) + { + size = stateRecorder->ringBufferSize(); + } + return size; +} + +int FCEU_StateRecorderGetNumSnapsSaved(void) +{ + int n = 0; + + if (stateRecorder != nullptr) + { + n = stateRecorder->numSnapsSaved(); + } + return n; +} + int FCEU_StateRecorderLoadState(int snapIndex) { int ret = -1; diff --git a/src/state.h b/src/state.h index 1c1155a8..dbc5066a 100644 --- a/src/state.h +++ b/src/state.h @@ -109,6 +109,8 @@ int FCEU_StateRecorderUpdate(void); bool FCEU_StateRecorderRunning(void); bool FCEU_StateRecorderIsEnabled(void); void FCEU_StateRecorderSetEnabled(bool enabled); +int FCEU_StateRecorderGetMaxSnaps(void); +int FCEU_StateRecorderGetNumSnapsSaved(void); int FCEU_StateRecorderGetStateIndex(void); int FCEU_StateRecorderLoadState(int snapIndex); int FCEU_StateRecorderLoadPrevState(void); From 4cb6d97183f0736c624bd05665bab7a81e36d368 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 3 Apr 2023 21:42:24 -0400 Subject: [PATCH 20/69] Added state recorder config dialogs to Qt GUI that prompt user to apply selected settings and to restart recorder for changes to take effect. --- src/drivers/Qt/StateRecorderConf.cpp | 72 ++++++++++++++++++++++++---- src/drivers/Qt/StateRecorderConf.h | 4 ++ src/state.h | 6 +++ 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp index 34909c5a..3aeaf144 100644 --- a/src/drivers/Qt/StateRecorderConf.cpp +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "Qt/throttle.h" #include "Qt/fceuWrapper.h" @@ -305,36 +306,89 @@ void StateRecorderDialog_t::closeEvent(QCloseEvent *event) { QSettings settings; settings.setValue("stateRecorderWindow/geometry", saveGeometry()); - done(0); - deleteLater(); - event->accept(); + + if (dataSavedCheck()) + { + done(0); + deleteLater(); + event->accept(); + } } //---------------------------------------------------------------------------- void StateRecorderDialog_t::closeWindow(void) { QSettings settings; settings.setValue("stateRecorderWindow/geometry", saveGeometry()); - done(0); - deleteLater(); + + if (dataSavedCheck()) + { + done(0); + deleteLater(); + } } //---------------------------------------------------------------------------- -void StateRecorderDialog_t::applyChanges(void) +void StateRecorderDialog_t::packConfig( StateRecorderConfigData &config ) { - StateRecorderConfigData config; - config.historyDurationMinutes = static_cast( historyDuration->value() ); config.timeBetweenSnapsMinutes = static_cast( snapMinutes->value() ) + ( static_cast( snapSeconds->value() ) / 60.0f ); config.compressionLevel = cmprLvlCbox->currentData().toInt(); config.loadPauseTimeSeconds = pauseDuration->value(); config.pauseOnLoad = static_cast( pauseOnLoadCbox->currentData().toInt() ); +} +//---------------------------------------------------------------------------- +bool StateRecorderDialog_t::dataSavedCheck(void) +{ + bool okToClose = true; + const StateRecorderConfigData &curConfig = FCEU_StateRecorderGetConfigData(); + + StateRecorderConfigData selConfig; + + packConfig( selConfig ); + + if ( selConfig.compare( curConfig ) == false ) + { + QMessageBox msgBox(QMessageBox::Question, tr("State Recorder"), + tr("Setting selections have not yet been saved.\nDo you wish to save/apply the new settings?"), + QMessageBox::No | QMessageBox::Yes, this); + + msgBox.setDefaultButton( QMessageBox::Yes ); + + int ret = msgBox.exec(); + + if ( ret == QMessageBox::Yes ) + { + applyChanges(); + } + } + return okToClose; +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::applyChanges(void) +{ + StateRecorderConfigData config; + + packConfig( config ); FCEU_WRAPPER_LOCK(); FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() ); FCEU_StateRecorderSetConfigData( config ); if (FCEU_StateRecorderRunning()) { - // TODO restart with new settings + QMessageBox msgBox(QMessageBox::Question, tr("State Recorder"), + tr("New settings will not take effect until state recorder is restarted. Do you wish to restart?"), + QMessageBox::No | QMessageBox::Yes, this); + + msgBox.setDefaultButton( QMessageBox::Yes ); + + int ret = msgBox.exec(); + + if ( ret == QMessageBox::Yes ) + { + FCEU_StateRecorderStop(); + FCEU_StateRecorderStart(); + updateStatusDisplay(); + } } FCEU_WRAPPER_UNLOCK(); diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h index de1919cf..d87e0c45 100644 --- a/src/drivers/Qt/StateRecorderConf.h +++ b/src/drivers/Qt/StateRecorderConf.h @@ -19,6 +19,8 @@ #include #include +struct StateRecorderConfigData; + class StateRecorderDialog_t : public QDialog { Q_OBJECT @@ -51,11 +53,13 @@ protected: double saveTimeMs; + bool dataSavedCheck(void); void recalcMemoryUsage(void); void updateStartStopBuffon(void); void updateRecorderStatusLabel(void); void updateBufferSizeStatus(void); void updateStatusDisplay(void); + void packConfig( StateRecorderConfigData &config ); public slots: void closeWindow(void); diff --git a/src/state.h b/src/state.h index dbc5066a..119c5f92 100644 --- a/src/state.h +++ b/src/state.h @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#pragma once #include enum ENUM_SSLOADPARAMS @@ -101,6 +102,11 @@ struct StateRecorderConfigData loadPauseTimeSeconds = 3; pauseOnLoad = TEMPORARY_PAUSE; } + + bool compare( const StateRecorderConfigData &other ) + { + return memcmp( this, &other, sizeof(StateRecorderConfigData) ) == 0; + } }; int FCEU_StateRecorderStart(void); From 6ad3837eebdab597da693a39068b2c05716282be Mon Sep 17 00:00:00 2001 From: Sam James Date: Mon, 10 Apr 2023 05:18:41 +0100 Subject: [PATCH 21/69] Fix build with GCC 13 GCC 13 (as usual for new compiler releases) shuffles around some internal includes and so etc is no longer transitively included: ``` In file included from /var/tmp/portage/games-emulation/fceux-2.6.5/work/fceux-2.6.5/src/drivers/Qt/AboutWindow.cpp:33: /usr/include/x264.h:40:4: warning: #warning You must include stdint.h or inttypes.h before x264.h [-Wcpp] 40 | # warning You must include stdint.h or inttypes.h before x264.h | ^~~~~~~ /usr/include/x264.h:127:5: error: uint8_t does not name a type 127 | uint8_t *p_payload; | ^~~~~~~ ``` See https://gnu.org/software/gcc/gcc-13/porting_to.html. Bug: https://bugs.gentoo.org/900611 --- src/drivers/Qt/AboutWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/Qt/AboutWindow.cpp b/src/drivers/Qt/AboutWindow.cpp index 81871444..eb2d0e7b 100644 --- a/src/drivers/Qt/AboutWindow.cpp +++ b/src/drivers/Qt/AboutWindow.cpp @@ -30,6 +30,7 @@ #endif #ifdef _USE_X264 +#include #include "x264.h" #endif From 5adbc1fcf28c404e773de40f5419a656be313aaf Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 15 Apr 2023 15:53:17 -0400 Subject: [PATCH 22/69] Minor initialization fix for state recorder. --- src/state.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/state.cpp b/src/state.cpp index 65c2dbae..f9c05c54 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1205,8 +1205,6 @@ class StateRecorder lastState = ringHead; loadIndexReset = false; lastLoadFrame = 0; - loadPauseTime = 3; - pauseOnLoad = StateRecorderConfigData::TEMPORARY_PAUSE; } ~StateRecorder(void) @@ -1244,9 +1242,9 @@ class StateRecorder printf("ringBufSize:%i framesPerSnap:%i\n", ringBufSize, framesPerSnap ); - compressionLevel = stateRecorderConfig.compressionLevel; - loadPauseTime = stateRecorderConfig.loadPauseTimeSeconds; - pauseOnLoad = stateRecorderConfig.pauseOnLoad; + compressionLevel = config.compressionLevel; + loadPauseTime = config.loadPauseTimeSeconds; + pauseOnLoad = config.pauseOnLoad; } void update(void) From 259c5754ee2a431dd98e2fb36f057da092b2b0c1 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 16 Apr 2023 21:38:50 -0400 Subject: [PATCH 23/69] Code cleanup of conddebug.cpp. Make functions that are not externally used static. Changed condition to have a constructor/destructor and allocate via std new/delete. Fixed a small memory leak. For Qt GUI, refactored debugger breakpoint editor window so that it has its own class to allow for more detailed error checking methods to be added. --- src/conddebug.cpp | 112 +++++---- src/conddebug.h | 21 +- src/debug.cpp | 8 +- src/drivers/Qt/ConsoleDebugger.cpp | 392 ++++++++++++++++++++++++++++- src/drivers/Qt/ConsoleDebugger.h | 43 ++++ src/drivers/win/debugger.cpp | 2 +- 6 files changed, 514 insertions(+), 64 deletions(-) diff --git a/src/conddebug.cpp b/src/conddebug.cpp index 3775d2b0..c8efa39b 100644 --- a/src/conddebug.cpp +++ b/src/conddebug.cpp @@ -52,17 +52,17 @@ #include uint16 debugLastAddress = 0; // used by 'T' and 'R' conditions -uint8 debugLastOpcode; // used to evaluate 'W' condition +uint8 debugLastOpcode = 0; // used to evaluate 'W' condition // Next non-whitespace character in string -char next; +static char next = 0; -int ishex(char c) +static int ishex(char c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } -void scan(const char** str) +static void scan(const char** str) { do { @@ -71,40 +71,37 @@ void scan(const char** str) } while (isspace(next)); } -// Frees a condition and all of it's sub conditions -void freeTree(Condition* c) -{ - if (c->lhs) freeTree(c->lhs); - if (c->rhs) freeTree(c->rhs); - - free(c); -} - // Generic function to handle all infix operators but the last one in the precedence hierarchy. : '(' E ')' -Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**)) +static Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**)) { Condition* t = nextPart(str); Condition* t1; Condition* mid; int op; + if (t == nullptr) + { + return nullptr; + } while ((op = operators(str))) { scan(str); t1 = nextPart(str); - if (t1 == 0) + if (t1 == nullptr) { - if(t) - freeTree(t); + delete t; return 0; } - mid = (Condition*)FCEU_dmalloc(sizeof(Condition)); - if (!mid) - return NULL; - memset(mid, 0, sizeof(Condition)); + mid = new Condition(); + if (mid == nullptr) + { + delete t; + delete t1; + return nullptr; + } mid->lhs = t; mid->rhs = t1; @@ -117,7 +114,7 @@ Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), i } // Generic handler for two-character operators -int TwoCharOperator(const char** str, char c1, char c2, int op) +static int TwoCharOperator(const char** str, char c1, char c2, int op) { if (next == c1 && **str == c2) { @@ -131,43 +128,43 @@ int TwoCharOperator(const char** str, char c1, char c2, int op) } // Determines if a character is a flag -int isFlag(char c) +static int isFlag(char c) { return c == 'N' || c == 'I' || c == 'C' || c == 'V' || c == 'Z' || c == 'B' || c == 'U' || c == 'D'; } // Determines if a character is a register -int isRegister(char c) +static int isRegister(char c) { return c == 'A' || c == 'X' || c == 'Y' || c == 'P' || c == 'S'; } // Determines if a character is for PC bank -int isPCBank(char c) +static int isPCBank(char c) { return c == 'K'; } // Determines if a character is for Data bank -int isDataBank(char c) +static int isDataBank(char c) { return c == 'T'; } // Determines if a character is for value read -int isValueRead(char c) +static int isValueRead(char c) { return c == 'R'; } // Determines if a character is for value write -int isValueWrite(char c) +static int isValueWrite(char c) { return c == 'W'; } // Reads a hexadecimal number from str -int getNumber(unsigned int* number, const char** str) +static int getNumber(unsigned int* number, const char** str) { // char buffer[5]; @@ -185,10 +182,10 @@ int getNumber(unsigned int* number, const char** str) return 1; } -Condition* Connect(const char** str); +static Condition* Connect(const char** str); // Handles the following part of the grammar: '(' E ')' -Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar) +static Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar) { if (next == openPar) { @@ -216,7 +213,7 @@ Condition* Parentheses(const char** str, Condition* c, char openPar, char closeP * Check for primitives * Flags, Registers, Numbers, Addresses and parentheses */ -Condition* Primitive(const char** str, Condition* c) +static Condition* Primitive(const char** str, Condition* c) { if (isFlag(next)) /* Flags */ { @@ -394,24 +391,22 @@ Condition* Primitive(const char** str, Condition* c) } /* Handle * and / operators */ -Condition* Term(const char** str) +static Condition* Term(const char** str) { Condition* t; Condition* t1; Condition* mid; - t = (Condition*)FCEU_dmalloc(sizeof(Condition)); + t = new Condition(); - if (!t) + if (t == nullptr) { return NULL; } - memset(t, 0, sizeof(Condition)); - if (!Primitive(str, t)) { - freeTree(t); + delete t; return 0; } @@ -421,22 +416,25 @@ Condition* Term(const char** str) scan(str); - if (!(t1 = (Condition*)FCEU_dmalloc(sizeof(Condition)))) - return NULL; - - memset(t1, 0, sizeof(Condition)); + if ((t1 = new Condition()) == nullptr) + { + delete t; + return nullptr; + } if (!Primitive(str, t1)) { - freeTree(t); - freeTree(t1); + delete t; + delete t1; return 0; } - if (!(mid = (Condition*)FCEU_dmalloc(sizeof(Condition)))) - return NULL; - - memset(mid, 0, sizeof(Condition)); + if ((mid = new Condition()) == nullptr) + { + delete t; + delete t1; + return nullptr; + } mid->lhs = t; mid->rhs = t1; @@ -449,7 +447,7 @@ Condition* Term(const char** str) } /* Check for + and - operators */ -int SumOperators(const char** str) +static int SumOperators(const char** str) { switch (next) { @@ -460,13 +458,13 @@ int SumOperators(const char** str) } /* Handle + and - operators */ -Condition* Sum(const char** str) +static Condition* Sum(const char** str) { return InfixOperator(str, Term, SumOperators); } /* Check for <=, =>, ==, !=, > and < operators */ -int CompareOperators(const char** str) +static int CompareOperators(const char** str) { int val = TwoCharOperator(str, '=', '=', OP_EQ); if (val) return val; @@ -490,13 +488,13 @@ int CompareOperators(const char** str) } /* Handle <=, =>, ==, !=, > and < operators */ -Condition* Compare(const char** str) +static Condition* Compare(const char** str) { return InfixOperator(str, Sum, CompareOperators); } /* Check for || or && operators */ -int ConnectOperators(const char** str) +static int ConnectOperators(const char** str) { int val = TwoCharOperator(str, '|', '|', OP_OR); if(val) return val; @@ -508,7 +506,7 @@ int ConnectOperators(const char** str) } /* Handle || and && operators */ -Condition* Connect(const char** str) +static Condition* Connect(const char** str) { return InfixOperator(str, Compare, ConnectOperators); } @@ -521,6 +519,10 @@ Condition* generateCondition(const char* str) scan(&str); c = Connect(&str); - if (!c || next != 0) return 0; + if (!c || next != 0) + { + if (c) delete c; + return 0; + } else return c; } diff --git a/src/conddebug.h b/src/conddebug.h index 24af528f..168eb4a7 100644 --- a/src/conddebug.h +++ b/src/conddebug.h @@ -61,9 +61,28 @@ struct Condition unsigned int type2; unsigned int value2; + + Condition(void) + { + op = 0; + lhs = rhs = nullptr; + type1 = value1 = 0; + type2 = value2 = 0; + }; + + ~Condition(void) + { + if (lhs) + { + delete lhs; + } + if (rhs) + { + delete rhs; + } + } }; -void freeTree(Condition* c); Condition* generateCondition(const char* str); #endif diff --git a/src/debug.cpp b/src/debug.cpp index 62e1f798..aa06f0f8 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -139,7 +139,7 @@ int checkCondition(const char* condition, int num) // Remove the old breakpoint condition before adding a new condition. if (watchpoint[num].cond) { - freeTree(watchpoint[num].cond); + delete watchpoint[num].cond; free(watchpoint[num].condText); watchpoint[num].cond = 0; watchpoint[num].condText = 0; @@ -153,8 +153,8 @@ int checkCondition(const char* condition, int num) { watchpoint[num].cond = c; watchpoint[num].condText = (char*)malloc(strlen(condition) + 1); - if (!watchpoint[num].condText) - return 0; + if (!watchpoint[num].condText) + return 0; strcpy(watchpoint[num].condText, condition); } else @@ -169,7 +169,7 @@ int checkCondition(const char* condition, int num) // Remove the old breakpoint condition if (watchpoint[num].cond) { - freeTree(watchpoint[num].cond); + delete watchpoint[num].cond; free(watchpoint[num].condText); watchpoint[num].cond = 0; watchpoint[num].condText = 0; diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp index 6f64cb4f..b39bdc97 100644 --- a/src/drivers/Qt/ConsoleDebugger.cpp +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -1834,6 +1834,391 @@ void ConsoleDebugger::selBmAddrChanged(const QString &txt) //printf("selBmAddrVal = %04X\n", selBmAddrVal ); } //---------------------------------------------------------------------------- +DebuggerBreakpointEditor::DebuggerBreakpointEditor(int editIndex, watchpointinfo *wpIn, QWidget *parent) + : QDialog(parent) +{ + editIdx = editIndex; + wp = wpIn; + + QHBoxLayout *hbox; + QVBoxLayout *mainLayout, *vbox; + QLabel *lbl; + QGridLayout *grid; + QFrame *frame; + QGroupBox *gbox; + + if ( editIdx >= 0 ) + { + setWindowTitle( tr("Edit Breakpoint") ); + } + else + { + setWindowTitle( tr("Add Breakpoint") ); + } + + hbox = new QHBoxLayout(); + mainLayout = new QVBoxLayout(); + + mainLayout->addLayout( hbox ); + + lbl = new QLabel( tr("Address") ); + addr1 = new QLineEdit(); + + hbox->addWidget( lbl ); + hbox->addWidget( addr1 ); + + lbl = new QLabel( tr("-") ); + addr2 = new QLineEdit(); + hbox->addWidget( lbl ); + hbox->addWidget( addr2 ); + + forbidChkBox = new QCheckBox( tr("Forbid") ); + hbox->addWidget( forbidChkBox ); + + frame = new QFrame(); + vbox = new QVBoxLayout(); + hbox = new QHBoxLayout(); + gbox = new QGroupBox(); + + rbp = new QCheckBox( tr("Read") ); + wbp = new QCheckBox( tr("Write") ); + xbp = new QCheckBox( tr("Execute") ); + ebp = new QCheckBox( tr("Enable") ); + + gbox->setTitle( tr("Memory") ); + mainLayout->addWidget( frame ); + frame->setLayout( vbox ); + frame->setFrameShape( QFrame::Box ); + vbox->addLayout( hbox ); + vbox->addWidget( gbox ); + + hbox->addWidget( rbp ); + hbox->addWidget( wbp ); + hbox->addWidget( xbp ); + hbox->addWidget( ebp ); + + hbox = new QHBoxLayout(); + cpu_radio = new QRadioButton( tr("CPU") ); + ppu_radio = new QRadioButton( tr("PPU") ); + oam_radio = new QRadioButton( tr("OAM") ); + rom_radio = new QRadioButton( tr("ROM") ); + cpu_radio->setChecked(true); + + gbox->setLayout( hbox ); + hbox->addWidget( cpu_radio ); + hbox->addWidget( ppu_radio ); + hbox->addWidget( oam_radio ); + hbox->addWidget( rom_radio ); + + grid = new QGridLayout(); + + mainLayout->addLayout( grid ); + lbl = new QLabel( tr("Condition") ); + cond = new QLineEdit(); + condValid = true; + + connect( cond, SIGNAL(textChanged(const QString &)), this, SLOT(conditionTextChanged(const QString &))); + + grid->addWidget( lbl, 0, 0 ); + grid->addWidget( cond, 0, 1 ); + + lbl = new QLabel( tr("Name") ); + name = new QLineEdit(); + + grid->addWidget( lbl, 1, 0 ); + grid->addWidget( name, 1, 1 ); + + hbox = new QHBoxLayout(); + msgLbl = new QLabel(); + okButton = new QPushButton( tr("OK") ); + cancelButton = new QPushButton( tr("Cancel") ); + + mainLayout->addLayout( hbox ); + hbox->addWidget( msgLbl, 5 ); + hbox->addWidget( cancelButton, 1 ); + hbox->addWidget( okButton, 1 ); + + connect( okButton, SIGNAL(clicked(void)), this, SLOT(accept(void)) ); + connect( cancelButton, SIGNAL(clicked(void)), this, SLOT(reject(void)) ); + + okButton->setIcon( style()->standardIcon( QStyle::SP_DialogOkButton ) ); + cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) ); + + okButton->setDefault(true); + + if ( wp != NULL ) + { + char stmp[256]; + + if ( wp->flags & BT_P ) + { + ppu_radio->setChecked(true); + } + else if ( wp->flags & BT_S ) + { + oam_radio->setChecked(true); + } + else if ( wp->flags & BT_R ) + { + rom_radio->setChecked(true); + } + + sprintf( stmp, "%04X", wp->address ); + + addr1->setText( tr(stmp) ); + + if ( wp->endaddress > 0 ) + { + sprintf( stmp, "%04X", wp->endaddress ); + + addr2->setText( tr(stmp) ); + } + + if ( wp->flags & WP_R ) + { + rbp->setChecked(true); + } + if ( wp->flags & WP_W ) + { + wbp->setChecked(true); + } + if ( wp->flags & WP_X ) + { + xbp->setChecked(true); + } + if ( wp->flags & WP_F ) + { + forbidChkBox->setChecked(true); + } + if ( wp->flags & WP_E ) + { + ebp->setChecked(true); + } + + if ( wp->condText ) + { + cond->setText( tr(wp->condText) ); + } + else + { + if ( editIdx < 0 ) + { + // If new breakpoint, default enable checkbox to true + ebp->setChecked(true); + + // If new breakpoint, suggest condition if in ROM Mapping area of memory. + if ( cpu_radio->isChecked() && (wp->address >= 0x8000) ) + { + int romAddr = GetNesFileAddress(wp->address); + + if ( romAddr >= 0 ) + { + wp->address = romAddr; + sprintf( stmp, "%X", wp->address ); + addr1->setText( tr(stmp) ); + rom_radio->setChecked(true); + } + else + { + char str[64]; + sprintf(str, "K==#%02X", getBank(wp->address)); + cond->setText( tr(str) ); + } + } + } + } + + if ( wp->desc ) + { + name->setText( tr(wp->desc) ); + } + } + else + { + // If new breakpoint, default enable checkbox to true + ebp->setChecked(true); + } + + setLayout( mainLayout ); + + connect( this , SIGNAL(finished(int)), this, SLOT(closeWindow(int)) ); +} +//---------------------------------------------------------------------------- +DebuggerBreakpointEditor::~DebuggerBreakpointEditor(void) +{ + +} +//---------------------------------------------------------------------------- +void DebuggerBreakpointEditor::closeEvent(QCloseEvent *event) +{ + //printf("Close Window Event\n"); + done(QDialog::Rejected); + deleteLater(); + event->accept(); +} +//---------------------------------------------------------------------------- +void DebuggerBreakpointEditor::closeWindow(int ret) +{ + //printf("Close Window %i\n", ret); + if ( ret == QDialog::Accepted ) + { + loadBreakpoint(); + } + deleteLater(); +} +//---------------------------------------------------------------------------- +void DebuggerBreakpointEditor::checkDataValid(void) +{ + bool allEntriesValid = condValid; + + okButton->setEnabled( allEntriesValid ); + + if (allEntriesValid) + { + msgLbl->clear(); + } + else if (!condValid) + { + msgLbl->setText(tr("Condition Invalid")); + } + else + { + msgLbl->clear(); + } +} +//---------------------------------------------------------------------------- +void DebuggerBreakpointEditor::conditionTextChanged(const QString &txt) +{ + if ( txt.size() > 0 ) + { + Condition *c = generateCondition( txt.toStdString().c_str() ); + + condValid = (c != nullptr); + + if (c) + { + delete c; c = nullptr; + } + } + else + { + condValid = true; + } + checkDataValid(); +} +//---------------------------------------------------------------------------- +void DebuggerBreakpointEditor::loadBreakpoint(void) +{ + int start_addr = -1, end_addr = -1, type = 0, enable = 1, slot; + std::string s; + + FCEU_WRAPPER_LOCK(); + + slot = (editIdx < 0) ? numWPs : editIdx; + + if ( cpu_radio->isChecked() ) + { + type |= BT_C; + } + else if ( ppu_radio->isChecked() ) + { + type |= BT_P; + } + else if ( oam_radio->isChecked() ) + { + type |= BT_S; + } + else if ( rom_radio->isChecked() ) + { + type |= BT_R; + } + + s = addr1->text().toStdString(); + + if ( s.size() > 0 ) + { + start_addr = offsetStringToInt( type, s.c_str() ); + } + + s = addr2->text().toStdString(); + + if ( s.size() > 0 ) + { + end_addr = offsetStringToInt( type, s.c_str() ); + } + + if ( rbp->isChecked() ) + { + type |= WP_R; + } + if ( wbp->isChecked() ) + { + type |= WP_W; + } + if ( xbp->isChecked() ) + { + type |= WP_X; + } + + if ( forbidChkBox->isChecked() ) + { + type |= WP_F; + } + + enable = ebp->isChecked(); + + if ( (start_addr >= 0) && (numWPs < 64) ) + { + unsigned int retval; + std::string nameString, condString; + + nameString = name->text().toStdString(); + condString = cond->text().toStdString(); + + retval = NewBreak( nameString.c_str(), start_addr, end_addr, type, condString.c_str(), slot, enable); + + if ( (retval == 1) || (retval == 2) ) + { + printf("Breakpoint Add Failed\n"); + } + else + { + if (editIdx < 0) + { + numWPs++; + } + + //bpListUpdate( false ); + } + } + FCEU_WRAPPER_UNLOCK(); +} +//---------------------------------------------------------------------------- + //int editIndex, watchpointinfo *wp, bool forceAccept, +//---------------------------------------------------------------------------- +void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool forceAccept ) +{ + int ret; + + DebuggerBreakpointEditor *dialog = new DebuggerBreakpointEditor( editIdx, wp, this ); + + if ( forceAccept ) + { + dialog->loadBreakpoint(); + dialog->deleteLater(); + ret = QDialog::Accepted; + } + else + { + ret = dialog->exec(); + } + + if (ret == QDialog::Accepted) + { + bpListUpdate( false ); + } +} +/* void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool forceAccept ) { int ret; @@ -2131,6 +2516,7 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo } } } +*/ //---------------------------------------------------------------------------- void ConsoleDebugger::openDebugSymbolEditWindow( int addr ) { @@ -2439,7 +2825,7 @@ static void DeleteBreak(int sel) if (watchpoint[sel].cond) { - freeTree(watchpoint[sel].cond); + delete watchpoint[sel].cond; } if (watchpoint[sel].condText) { @@ -2488,7 +2874,7 @@ void debuggerClearAllBreakpoints(void) { if (watchpoint[i].cond) { - freeTree(watchpoint[i].cond); + delete watchpoint[i].cond; } if (watchpoint[i].condText) { @@ -2499,7 +2885,7 @@ void debuggerClearAllBreakpoints(void) free(watchpoint[i].desc); } - watchpoint[i].address = 0; + watchpoint[i].address = 0; watchpoint[i].endaddress = 0; watchpoint[i].flags = 0; watchpoint[i].cond = 0; diff --git a/src/drivers/Qt/ConsoleDebugger.h b/src/drivers/Qt/ConsoleDebugger.h index be5b2e0b..5d55003f 100644 --- a/src/drivers/Qt/ConsoleDebugger.h +++ b/src/drivers/Qt/ConsoleDebugger.h @@ -420,6 +420,49 @@ class DebugBreakOnDialog : public QDialog void resetDeltas(void); }; +class DebuggerBreakpointEditor : public QDialog +{ + Q_OBJECT + + public: + DebuggerBreakpointEditor(int editIndex = -1, watchpointinfo *wpIn = nullptr, QWidget *parent = 0); + ~DebuggerBreakpointEditor(void); + + void loadBreakpoint(void); + + protected: + void closeEvent(QCloseEvent *event) override; + void checkDataValid(void); + + private: + int editIdx; + watchpointinfo *wp; + + QLineEdit *addr1; + QLineEdit *addr2; + QLineEdit *cond; + QLineEdit *name; + QCheckBox *forbidChkBox; + QCheckBox *rbp; + QCheckBox *wbp; + QCheckBox *xbp; + QCheckBox *ebp; + QLabel *msgLbl; + + QPushButton *okButton; + QPushButton *cancelButton; + QRadioButton *cpu_radio; + QRadioButton *ppu_radio; + QRadioButton *oam_radio; + QRadioButton *rom_radio; + + bool condValid; + + private slots: + void closeWindow(int ret); + void conditionTextChanged( const QString &text ); +}; + class ConsoleDebugger : public QDialog { Q_OBJECT diff --git a/src/drivers/win/debugger.cpp b/src/drivers/win/debugger.cpp index a688a636..d32ac839 100644 --- a/src/drivers/win/debugger.cpp +++ b/src/drivers/win/debugger.cpp @@ -1235,7 +1235,7 @@ void DeleteBreak(int sel) if(sel<0) return; if(sel>=numWPs) return; if (watchpoint[sel].cond) - freeTree(watchpoint[sel].cond); + delete watchpoint[sel].cond; if (watchpoint[sel].condText) free(watchpoint[sel].condText); if (watchpoint[sel].desc) From 8649a38ef9208f6ffb1cafd657be8d20879d966a Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 16 Apr 2023 21:41:49 -0400 Subject: [PATCH 24/69] Removed old commented out method in Qt debugger. --- src/drivers/Qt/ConsoleDebugger.cpp | 299 ----------------------------- 1 file changed, 299 deletions(-) diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp index b39bdc97..fe10d82a 100644 --- a/src/drivers/Qt/ConsoleDebugger.cpp +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -2218,305 +2218,6 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo bpListUpdate( false ); } } -/* -void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool forceAccept ) -{ - int ret; - QDialog dialog(this); - QHBoxLayout *hbox; - QVBoxLayout *mainLayout, *vbox; - QLabel *lbl; - QLineEdit *addr1, *addr2, *cond, *name; - QCheckBox *forbidChkBox, *rbp, *wbp, *xbp, *ebp; - QGridLayout *grid; - QFrame *frame; - QGroupBox *gbox; - QPushButton *okButton, *cancelButton; - QRadioButton *cpu_radio, *ppu_radio, *oam_radio, *rom_radio; - - if ( editIdx >= 0 ) - { - dialog.setWindowTitle( tr("Edit Breakpoint") ); - } - else - { - dialog.setWindowTitle( tr("Add Breakpoint") ); - } - - hbox = new QHBoxLayout(); - mainLayout = new QVBoxLayout(); - - mainLayout->addLayout( hbox ); - - lbl = new QLabel( tr("Address") ); - addr1 = new QLineEdit(); - - hbox->addWidget( lbl ); - hbox->addWidget( addr1 ); - - lbl = new QLabel( tr("-") ); - addr2 = new QLineEdit(); - hbox->addWidget( lbl ); - hbox->addWidget( addr2 ); - - forbidChkBox = new QCheckBox( tr("Forbid") ); - hbox->addWidget( forbidChkBox ); - - frame = new QFrame(); - vbox = new QVBoxLayout(); - hbox = new QHBoxLayout(); - gbox = new QGroupBox(); - - rbp = new QCheckBox( tr("Read") ); - wbp = new QCheckBox( tr("Write") ); - xbp = new QCheckBox( tr("Execute") ); - ebp = new QCheckBox( tr("Enable") ); - - gbox->setTitle( tr("Memory") ); - mainLayout->addWidget( frame ); - frame->setLayout( vbox ); - frame->setFrameShape( QFrame::Box ); - vbox->addLayout( hbox ); - vbox->addWidget( gbox ); - - hbox->addWidget( rbp ); - hbox->addWidget( wbp ); - hbox->addWidget( xbp ); - hbox->addWidget( ebp ); - - hbox = new QHBoxLayout(); - cpu_radio = new QRadioButton( tr("CPU") ); - ppu_radio = new QRadioButton( tr("PPU") ); - oam_radio = new QRadioButton( tr("OAM") ); - rom_radio = new QRadioButton( tr("ROM") ); - cpu_radio->setChecked(true); - - gbox->setLayout( hbox ); - hbox->addWidget( cpu_radio ); - hbox->addWidget( ppu_radio ); - hbox->addWidget( oam_radio ); - hbox->addWidget( rom_radio ); - - grid = new QGridLayout(); - - mainLayout->addLayout( grid ); - lbl = new QLabel( tr("Condition") ); - cond = new QLineEdit(); - - grid->addWidget( lbl, 0, 0 ); - grid->addWidget( cond, 0, 1 ); - - lbl = new QLabel( tr("Name") ); - name = new QLineEdit(); - - grid->addWidget( lbl, 1, 0 ); - grid->addWidget( name, 1, 1 ); - - hbox = new QHBoxLayout(); - okButton = new QPushButton( tr("OK") ); - cancelButton = new QPushButton( tr("Cancel") ); - - mainLayout->addLayout( hbox ); - hbox->addWidget( cancelButton ); - hbox->addWidget( okButton ); - - connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); - connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); - - okButton->setIcon( style()->standardIcon( QStyle::SP_DialogOkButton ) ); - cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) ); - - okButton->setDefault(true); - - if ( wp != NULL ) - { - char stmp[256]; - - if ( wp->flags & BT_P ) - { - ppu_radio->setChecked(true); - } - else if ( wp->flags & BT_S ) - { - oam_radio->setChecked(true); - } - else if ( wp->flags & BT_R ) - { - rom_radio->setChecked(true); - } - - sprintf( stmp, "%04X", wp->address ); - - addr1->setText( tr(stmp) ); - - if ( wp->endaddress > 0 ) - { - sprintf( stmp, "%04X", wp->endaddress ); - - addr2->setText( tr(stmp) ); - } - - if ( wp->flags & WP_R ) - { - rbp->setChecked(true); - } - if ( wp->flags & WP_W ) - { - wbp->setChecked(true); - } - if ( wp->flags & WP_X ) - { - xbp->setChecked(true); - } - if ( wp->flags & WP_F ) - { - forbidChkBox->setChecked(true); - } - if ( wp->flags & WP_E ) - { - ebp->setChecked(true); - } - - if ( wp->condText ) - { - cond->setText( tr(wp->condText) ); - } - else - { - if ( editIdx < 0 ) - { - // If new breakpoint, default enable checkbox to true - ebp->setChecked(true); - - // If new breakpoint, suggest condition if in ROM Mapping area of memory. - if ( cpu_radio->isChecked() && (wp->address >= 0x8000) ) - { - int romAddr = GetNesFileAddress(wp->address); - - if ( romAddr >= 0 ) - { - wp->address = romAddr; - sprintf( stmp, "%X", wp->address ); - addr1->setText( tr(stmp) ); - rom_radio->setChecked(true); - } - else - { - char str[64]; - sprintf(str, "K==#%02X", getBank(wp->address)); - cond->setText( tr(str) ); - } - } - } - } - - if ( wp->desc ) - { - name->setText( tr(wp->desc) ); - } - } - else - { - // If new breakpoint, default enable checkbox to true - ebp->setChecked(true); - } - - dialog.setLayout( mainLayout ); - - if ( forceAccept ) - { - ret = QDialog::Accepted; - } - else - { - ret = dialog.exec(); - } - - if ( ret == QDialog::Accepted ) - { - int start_addr = -1, end_addr = -1, type = 0, enable = 1, slot; - std::string s; - - slot = (editIdx < 0) ? numWPs : editIdx; - - if ( cpu_radio->isChecked() ) - { - type |= BT_C; - } - else if ( ppu_radio->isChecked() ) - { - type |= BT_P; - } - else if ( oam_radio->isChecked() ) - { - type |= BT_S; - } - else if ( rom_radio->isChecked() ) - { - type |= BT_R; - } - - s = addr1->text().toStdString(); - - if ( s.size() > 0 ) - { - start_addr = offsetStringToInt( type, s.c_str() ); - } - - s = addr2->text().toStdString(); - - if ( s.size() > 0 ) - { - end_addr = offsetStringToInt( type, s.c_str() ); - } - - if ( rbp->isChecked() ) - { - type |= WP_R; - } - if ( wbp->isChecked() ) - { - type |= WP_W; - } - if ( xbp->isChecked() ) - { - type |= WP_X; - } - - if ( forbidChkBox->isChecked() ) - { - type |= WP_F; - } - - enable = ebp->isChecked(); - - if ( (start_addr >= 0) && (numWPs < 64) ) - { - unsigned int retval; - std::string nameString, condString; - - nameString = name->text().toStdString(); - condString = cond->text().toStdString(); - - retval = NewBreak( nameString.c_str(), start_addr, end_addr, type, condString.c_str(), slot, enable); - - if ( (retval == 1) || (retval == 2) ) - { - printf("Breakpoint Add Failed\n"); - } - else - { - if (editIdx < 0) - { - numWPs++; - } - - bpListUpdate( false ); - } - } - } -} -*/ //---------------------------------------------------------------------------- void ConsoleDebugger::openDebugSymbolEditWindow( int addr ) { From c4ce3ac3fd6dd47723c978636bfd3389ebd81b99 Mon Sep 17 00:00:00 2001 From: negative Date: Mon, 17 Apr 2023 17:53:42 +0800 Subject: [PATCH 25/69] Mapper 227: Fix CHR-RAM protect check --- src/boards/addrlatch.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/boards/addrlatch.cpp b/src/boards/addrlatch.cpp index 041cf885..500231d6 100644 --- a/src/boards/addrlatch.cpp +++ b/src/boards/addrlatch.cpp @@ -27,6 +27,7 @@ static void (*WSync)(void); static readfunc defread; static uint8 *WRAM = NULL; static uint32 WRAMSIZE=0; +static uint8 hasBattery = 0; static DECLFW(LatchWrite) { latche = A; @@ -358,20 +359,17 @@ void Mapper217_Init(CartInfo *info) { } //------------------ Map 227 --------------------------- - static void M227Sync(void) { uint32 S = latche & 1; uint32 p = ((latche >> 2) & 0x1F) + ((latche & 0x100) >> 3); uint32 L = (latche >> 9) & 1; -// ok, according to nesdev wiki (refrenced to the nesdev dumping thread) there is a CHR write protection bit7. -// however, this bit clearly determined a specific PRG layout for some game but does not meant to have additional -// functionality. as I see from the menu code, it disables the chr writing before run an actual game. -// this fix here makes happy both waixing rpgs and multigame menus at once. can't veryfy it on a hardware -// but if I find some i'll definitly do this. - - if ((latche & 0xF000) == 0xF000) - SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0); +// Only Waixing appear to have battery flag enabled, while multicarts don't. +// Multicarts needs CHR-RAM protect in NROM modes, so only apply CHR-RAM protect +// on non battery-enabled carts. + if (!hasBattery && (latche & 0x80) == 0x80) + /* CHR-RAM write protect hack, needed for some multicarts */ + SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0); else SetupCartCHRMapping(0, CHRptr[0], 0x2000, 1); @@ -409,6 +407,7 @@ static void M227Sync(void) { void Mapper227_Init(CartInfo *info) { Latch_Init(info, M227Sync, NULL, 0x0000, 0x8000, 0xFFFF, 1); + hasBattery = info->battery; } //------------------ Map 229 --------------------------- From 46ad7bbd382efecfe41e26ba2d7ccca2abbe24c5 Mon Sep 17 00:00:00 2001 From: Fritz Mahnke Date: Mon, 17 Apr 2023 08:06:24 -0700 Subject: [PATCH 26/69] Remove redundant setFocus call, which actually removes focus. Under i3wm 4.21.1/Qt 6.4.1, the existing code actually removes keyboard focus from the newly-activated window. Removing the call to setFocus in these cases fixes the problem. The call to activateWindow is enough to put keyboard focus on it. The documentation for activateWindow implies the same. --- src/drivers/Qt/CheatsConf.cpp | 1 - src/drivers/Qt/CodeDataLogger.cpp | 1 - src/drivers/Qt/ConsoleDebugger.cpp | 1 - src/drivers/Qt/FamilyKeyboard.cpp | 1 - src/drivers/Qt/HexEditor.cpp | 1 - src/drivers/Qt/NameTableViewer.cpp | 1 - src/drivers/Qt/TasEditor/TasEditorWindow.cpp | 2 -- src/drivers/Qt/TraceLogger.cpp | 1 - src/drivers/Qt/ppuViewer.cpp | 2 -- 9 files changed, 11 deletions(-) diff --git a/src/drivers/Qt/CheatsConf.cpp b/src/drivers/Qt/CheatsConf.cpp index a67ef5af..bd918a3b 100644 --- a/src/drivers/Qt/CheatsConf.cpp +++ b/src/drivers/Qt/CheatsConf.cpp @@ -54,7 +54,6 @@ void openCheatDialog(QWidget *parent) { win->activateWindow(); win->raise(); - win->setFocus(); return; } win = new GuiCheatsDialog_t(parent); diff --git a/src/drivers/Qt/CodeDataLogger.cpp b/src/drivers/Qt/CodeDataLogger.cpp index 8d6b5908..50b2032a 100644 --- a/src/drivers/Qt/CodeDataLogger.cpp +++ b/src/drivers/Qt/CodeDataLogger.cpp @@ -63,7 +63,6 @@ int openCDLWindow( QWidget *parent ) { cdlWin->activateWindow(); cdlWin->raise(); - cdlWin->setFocus(); } else { diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp index fe10d82a..20c06510 100644 --- a/src/drivers/Qt/ConsoleDebugger.cpp +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -4548,7 +4548,6 @@ void debuggerWindowSetFocus(bool val) { dbgWin->activateWindow(); dbgWin->raise(); - dbgWin->setFocus(); } } //---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/FamilyKeyboard.cpp b/src/drivers/Qt/FamilyKeyboard.cpp index 3ee9c1fa..dcf4f229 100644 --- a/src/drivers/Qt/FamilyKeyboard.cpp +++ b/src/drivers/Qt/FamilyKeyboard.cpp @@ -126,7 +126,6 @@ int openFamilyKeyboardDialog(QWidget *parent) { fkbWin->activateWindow(); fkbWin->raise(); - fkbWin->setFocus(); } else { diff --git a/src/drivers/Qt/HexEditor.cpp b/src/drivers/Qt/HexEditor.cpp index 04f1365d..decf6ba2 100644 --- a/src/drivers/Qt/HexEditor.cpp +++ b/src/drivers/Qt/HexEditor.cpp @@ -4222,7 +4222,6 @@ int hexEditorOpenFromDebugger( int mode, int addr ) { win->activateWindow(); win->raise(); - win->setFocus(); } win->editor->setMode( mode ); diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 631da5d4..abdfaf35 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -108,7 +108,6 @@ int openNameTableViewWindow( QWidget *parent ) { nameTableViewWindow->activateWindow(); nameTableViewWindow->raise(); - nameTableViewWindow->setFocus(); return -1; } initNameTableViewer(); diff --git a/src/drivers/Qt/TasEditor/TasEditorWindow.cpp b/src/drivers/Qt/TasEditor/TasEditorWindow.cpp index c7314be9..c2167a4e 100644 --- a/src/drivers/Qt/TasEditor/TasEditorWindow.cpp +++ b/src/drivers/Qt/TasEditor/TasEditorWindow.cpp @@ -116,7 +116,6 @@ void tasWindowSetFocus(bool val) { tasWin->activateWindow(); tasWin->raise(); - tasWin->setFocus(); } } // this getter contains formula to decide whether to record or replay movie @@ -2917,7 +2916,6 @@ void TasEditorWindow::openFindNoteWindow(void) { findWin->activateWindow(); findWin->raise(); - findWin->setFocus(); } else { diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index b8fe13b4..55459615 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -1211,7 +1211,6 @@ void openTraceLoggerWindow(QWidget *parent) { traceLogWindow->activateWindow(); traceLogWindow->raise(); - traceLogWindow->setFocus(); return; } //printf("Open Trace Logger Window\n"); diff --git a/src/drivers/Qt/ppuViewer.cpp b/src/drivers/Qt/ppuViewer.cpp index d151da42..91b8a38d 100644 --- a/src/drivers/Qt/ppuViewer.cpp +++ b/src/drivers/Qt/ppuViewer.cpp @@ -92,7 +92,6 @@ int openPPUViewWindow( QWidget *parent ) { ppuViewWindow->activateWindow(); ppuViewWindow->raise(); - ppuViewWindow->setFocus(); return -1; } initPPUViewer(); @@ -110,7 +109,6 @@ int openOAMViewWindow( QWidget *parent ) { spriteViewWindow->activateWindow(); spriteViewWindow->raise(); - spriteViewWindow->setFocus(); return -1; } initPPUViewer(); From f885abb71bd30466b40a5c70203e1787082766b8 Mon Sep 17 00:00:00 2001 From: Fritz Mahnke Date: Mon, 17 Apr 2023 08:28:37 -0700 Subject: [PATCH 27/69] Don't index into empty QString in HexEditor. QKeyEvent::text may return an empty string in some environments for some keys, such as modifier keys. This can cause a cause a crash. Add a check for the hex editor to skip the editing code, which assumes the keyboard event is a printable character and depends on a non-empty keyboard event text. --- src/drivers/Qt/HexEditor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/drivers/Qt/HexEditor.cpp b/src/drivers/Qt/HexEditor.cpp index 04f1365d..eda90bdf 100644 --- a/src/drivers/Qt/HexEditor.cpp +++ b/src/drivers/Qt/HexEditor.cpp @@ -2777,7 +2777,13 @@ void QHexEdit::keyPressEvent(QKeyEvent *event) event->accept(); } else - { + { // Use the input text to modify the values in the editor area. + + if (event->text().isEmpty()) + { + return; + } + int key; if ( cursorPosX >= 32 ) { // Edit Area is ASCII From 39bb749f5dd51bdd3dc26183b4f5e1d81d222376 Mon Sep 17 00:00:00 2001 From: Fritz Mahnke Date: Mon, 17 Apr 2023 11:25:46 -0700 Subject: [PATCH 28/69] Don't always scroll to PC when updating debug window assembly view. Change ConsoleDebugger::updateWindowData and updateAllDebuggerWindows to support two different types of updates: UPDATE_ALL and UPDATE_NO_SCROLL. The new "no scroll" type lets callers request the assembly view be updated without scrolling the view to the PC line. Change the hex editor and trace logger calls to updateAllDebuggerWindows to use the no scroll update. The user may use these functions with the current disassembly position in mind and not want to lose it. --- src/drivers/Qt/ConsoleDebugger.cpp | 44 ++++++++++++++++-------------- src/drivers/Qt/ConsoleDebugger.h | 11 +++++--- src/drivers/Qt/HexEditor.cpp | 5 ++-- src/drivers/Qt/TraceLogger.cpp | 5 ++-- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp index 20c06510..a97c790b 100644 --- a/src/drivers/Qt/ConsoleDebugger.cpp +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -172,7 +172,7 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent) loadDisplayViews(); - windowUpdateReq = true; + windowUpdateReq = QAsmView::UPDATE_ALL; dbgWin = this; @@ -373,7 +373,7 @@ void ConsoleDebugger::ld65ImportDebug(void) debugSymbolTable.ld65LoadDebugFile( filename.toStdString().c_str() ); - queueUpdate(); + queueUpdate(QAsmView::UPDATE_ALL); return; } @@ -2985,7 +2985,7 @@ void ConsoleDebugger::debugStepBackCB(void) { FCEU_WRAPPER_LOCK(); FCEUD_TraceLoggerBackUpInstruction(); - updateWindowData(); + updateWindowData(QAsmView::UPDATE_ALL); hexEditorUpdateMemoryValues(true); hexEditorRequestUpdateAll(); lastBpIdx = BREAK_TYPE_STEP; @@ -3180,8 +3180,7 @@ void ConsoleDebugger::seekPCCB (void) setRegsFromEntry(); //updateAllDebugWindows(); } - windowUpdateReq = true; - //asmView->scrollToPC(); + windowUpdateReq = QAsmView::UPDATE_ALL; } //---------------------------------------------------------------------------- void ConsoleDebugger::openChangePcDialog(void) @@ -3235,7 +3234,7 @@ void ConsoleDebugger::openChangePcDialog(void) { X.PC = sbox->value(); - windowUpdateReq = true; + windowUpdateReq = QAsmView::UPDATE_ALL; } } //---------------------------------------------------------------------------- @@ -4229,20 +4228,25 @@ void ConsoleDebugger::updateRegisterView(void) ppuScrollY->setText( tr(stmp) ); } //---------------------------------------------------------------------------- -void ConsoleDebugger::updateWindowData(void) +void ConsoleDebugger::updateWindowData(enum QAsmView::UpdateType type) { - asmView->updateAssemblyView(); - - asmView->scrollToPC(); + if (type == QAsmView::UPDATE_ALL) + { + asmView->updateAssemblyView(); + asmView->scrollToPC(); + updateRegisterView(); + } else if (type == QAsmView::UPDATE_NO_SCROLL) + { + asmView->updateAssemblyView(); + updateRegisterView(); + } - updateRegisterView(); - - windowUpdateReq = false; + windowUpdateReq = QAsmView::UPDATE_NONE; } //---------------------------------------------------------------------------- -void ConsoleDebugger::queueUpdate(void) +void ConsoleDebugger::queueUpdate(enum QAsmView::UpdateType type) { - windowUpdateReq = true; + windowUpdateReq = type; } //---------------------------------------------------------------------------- void ConsoleDebugger::updatePeriodic(void) @@ -4260,10 +4264,10 @@ void ConsoleDebugger::updatePeriodic(void) bpNotifyReq = false; } - if ( windowUpdateReq ) + if ( windowUpdateReq != QAsmView::UPDATE_NONE ) { FCEU_WRAPPER_LOCK(); - updateWindowData(); + updateWindowData(windowUpdateReq); FCEU_WRAPPER_UNLOCK(); } asmView->update(); @@ -4433,7 +4437,7 @@ void ConsoleDebugger::breakPointNotify( int bpNum ) } } - windowUpdateReq = true; + windowUpdateReq = QAsmView::UPDATE_ALL; } //---------------------------------------------------------------------------- void ConsoleDebugger::hbarChanged(int value) @@ -4556,11 +4560,11 @@ bool debuggerWaitingAtBreakpoint(void) return waitingAtBp; } //---------------------------------------------------------------------------- -void updateAllDebuggerWindows( void ) +void updateAllDebuggerWindows( enum QAsmView::UpdateType type ) { if ( dbgWin ) { - dbgWin->queueUpdate(); + dbgWin->queueUpdate(type); } } //---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/ConsoleDebugger.h b/src/drivers/Qt/ConsoleDebugger.h index 5d55003f..c26c92e3 100644 --- a/src/drivers/Qt/ConsoleDebugger.h +++ b/src/drivers/Qt/ConsoleDebugger.h @@ -167,6 +167,8 @@ class QAsmView : public QWidget QFont getFont(void){ return font; }; + enum UpdateType { UPDATE_NONE, UPDATE_ALL, UPDATE_NO_SCROLL }; + protected: bool event(QEvent *event) override; void paintEvent(QPaintEvent *event) override; @@ -471,7 +473,7 @@ class ConsoleDebugger : public QDialog ConsoleDebugger(QWidget *parent = 0); ~ConsoleDebugger(void); - void updateWindowData(void); + void updateWindowData(enum QAsmView::UpdateType type); void updateRegisterView(void); void updateTabVisibility(void); void breakPointNotify(int bpNum); @@ -480,7 +482,7 @@ class ConsoleDebugger : public QDialog void setBookmarkSelectedAddress( int addr ); int getBookmarkSelectedAddress(void){ return selBmAddrVal; }; void edit_BM_name( int addr ); - void queueUpdate(void); + void queueUpdate(enum QAsmView::UpdateType type); QLabel *asmLineSelLbl; @@ -573,7 +575,8 @@ class ConsoleDebugger : public QDialog ColorMenuItem *pcColorAct; int selBmAddrVal; - bool windowUpdateReq; + enum QAsmView::UpdateType windowUpdateReq; + bool startedTraceLogger; private: @@ -668,6 +671,6 @@ void saveGameDebugBreakpoints( bool force = false ); void loadGameDebugBreakpoints(void); void debuggerClearAllBreakpoints(void); void debuggerClearAllBookmarks(void); -void updateAllDebuggerWindows(void); +void updateAllDebuggerWindows(enum QAsmView::UpdateType type); extern debuggerBookmarkManager_t dbgBmMgr; diff --git a/src/drivers/Qt/HexEditor.cpp b/src/drivers/Qt/HexEditor.cpp index decf6ba2..51a6139e 100644 --- a/src/drivers/Qt/HexEditor.cpp +++ b/src/drivers/Qt/HexEditor.cpp @@ -399,7 +399,7 @@ static int writeMem( int mode, unsigned int addr, int value ) { if (debuggerWindowIsOpen()) { - updateAllDebuggerWindows(); + updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL); } } @@ -1853,7 +1853,7 @@ void HexEditorDialog_t::openDebugSymbolEditWindow( int addr ) if ( ret == QDialog::Accepted ) { - updateAllDebuggerWindows(); + updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL); } } //---------------------------------------------------------------------------- @@ -2830,6 +2830,7 @@ void QHexEdit::keyPressEvent(QKeyEvent *event) } else { // Edit Area is Hex + key = int(event->text()[0].toUpper().toLatin1()); if ( ::isxdigit( key ) ) diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index 55459615..d23de1b8 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -57,6 +57,7 @@ #include "common/os_utils.h" +#include "Qt/ConsoleDebugger.h" #include "Qt/ConsoleWindow.h" #include "Qt/ConsoleUtilities.h" #include "Qt/TraceLogger.h" @@ -2187,7 +2188,7 @@ void QTraceLogView::openBpEditWindow(int editIdx, watchpointinfo *wp, traceRecor numWPs++; } - updateAllDebuggerWindows(); + updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL); } } } @@ -2232,7 +2233,7 @@ void QTraceLogView::openDebugSymbolEditWindow(int addr, int bank) if (ret == QDialog::Accepted) { - updateAllDebuggerWindows(); + updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL); } } //---------------------------------------------------------------------------- From da7c7761a2670bc48ae86a8630903dd481b3fe27 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 17 Apr 2023 21:21:59 -0400 Subject: [PATCH 29/69] Added breakpoint editor address validity checking to Qt GUI. --- src/debug.cpp | 50 +++++++++++---- src/debug.h | 2 +- src/drivers/Qt/ConsoleDebugger.cpp | 97 +++++++++++++++++++++++++++++- src/drivers/Qt/ConsoleDebugger.h | 2 + 4 files changed, 137 insertions(+), 14 deletions(-) diff --git a/src/debug.cpp b/src/debug.cpp index aa06f0f8..fad9c6f6 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -19,10 +19,15 @@ unsigned int debuggerPageSize = 14; int vblankScanLines = 0; //Used to calculate scanlines 240-261 (vblank) int vblankPixel = 0; //Used to calculate the pixels in vblank -int offsetStringToInt(unsigned int type, const char* offsetBuffer) +int offsetStringToInt(unsigned int type, const char* offsetBuffer, bool *conversionOk) { int offset = -1; + if (conversionOk) + { + *conversionOk = false; + } + if (sscanf(offsetBuffer,"%7X",(unsigned int *)&offset) == EOF) { return -1; @@ -30,14 +35,26 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer) if (type & BT_P) { + if (conversionOk) + { + *conversionOk = (offset >= 0) && (offset < 0x4000); + } return offset & 0x3FFF; } else if (type & BT_S) { + if (conversionOk) + { + *conversionOk = (offset >= 0) && (offset < 0x100); + } return offset & 0x00FF; } else if (type & BT_R) { + if (conversionOk) + { + *conversionOk = (offset >= 0); + } return offset; } else // BT_C @@ -46,6 +63,10 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer) if (sym) { + if (conversionOk) + { + *conversionOk = true; + } return sym->offset() & 0xFFFF; } @@ -56,21 +77,26 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer) type = GameInfo->type; } if (type == GIT_NSF) { //NSF Breakpoint keywords - if (strcmp(offsetBuffer,"LOAD") == 0) return (NSFHeader.LoadAddressLow | (NSFHeader.LoadAddressHigh<<8)); - if (strcmp(offsetBuffer,"INIT") == 0) return (NSFHeader.InitAddressLow | (NSFHeader.InitAddressHigh<<8)); - if (strcmp(offsetBuffer,"PLAY") == 0) return (NSFHeader.PlayAddressLow | (NSFHeader.PlayAddressHigh<<8)); + if (strcmp(offsetBuffer,"LOAD") == 0) offset = (NSFHeader.LoadAddressLow | (NSFHeader.LoadAddressHigh<<8)); + else if (strcmp(offsetBuffer,"INIT") == 0) offset = (NSFHeader.InitAddressLow | (NSFHeader.InitAddressHigh<<8)); + else if (strcmp(offsetBuffer,"PLAY") == 0) offset = (NSFHeader.PlayAddressLow | (NSFHeader.PlayAddressHigh<<8)); } else if (type == GIT_FDS) { //FDS Breakpoint keywords - if (strcmp(offsetBuffer,"NMI1") == 0) return (GetMem(0xDFF6) | (GetMem(0xDFF7)<<8)); - if (strcmp(offsetBuffer,"NMI2") == 0) return (GetMem(0xDFF8) | (GetMem(0xDFF9)<<8)); - if (strcmp(offsetBuffer,"NMI3") == 0) return (GetMem(0xDFFA) | (GetMem(0xDFFB)<<8)); - if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xDFFC) | (GetMem(0xDFFD)<<8)); - if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xDFFE) | (GetMem(0xDFFF)<<8)); + if (strcmp(offsetBuffer,"NMI1") == 0) offset = (GetMem(0xDFF6) | (GetMem(0xDFF7)<<8)); + else if (strcmp(offsetBuffer,"NMI2") == 0) offset = (GetMem(0xDFF8) | (GetMem(0xDFF9)<<8)); + else if (strcmp(offsetBuffer,"NMI3") == 0) offset = (GetMem(0xDFFA) | (GetMem(0xDFFB)<<8)); + else if (strcmp(offsetBuffer,"RST") == 0) offset = (GetMem(0xDFFC) | (GetMem(0xDFFD)<<8)); + else if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) offset = (GetMem(0xDFFE) | (GetMem(0xDFFF)<<8)); } else { //NES Breakpoint keywords - if ((strcmp(offsetBuffer,"NMI") == 0) || (strcmp(offsetBuffer,"VBL") == 0)) return (GetMem(0xFFFA) | (GetMem(0xFFFB)<<8)); - if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xFFFC) | (GetMem(0xFFFD)<<8)); - if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xFFFE) | (GetMem(0xFFFF)<<8)); + if ((strcmp(offsetBuffer,"NMI") == 0) || (strcmp(offsetBuffer,"VBL") == 0)) offset = (GetMem(0xFFFA) | (GetMem(0xFFFB)<<8)); + else if (strcmp(offsetBuffer,"RST") == 0) offset = (GetMem(0xFFFC) | (GetMem(0xFFFD)<<8)); + else if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) offset = (GetMem(0xFFFE) | (GetMem(0xFFFF)<<8)); + } + + if (conversionOk) + { + *conversionOk = (offset >= 0) && (offset < 0x10000); } } diff --git a/src/debug.h b/src/debug.h index e1d8b573..708c12ef 100644 --- a/src/debug.h +++ b/src/debug.h @@ -172,7 +172,7 @@ DebuggerState &FCEUI_Debugger(); //#define WRITE_BREAKPOINT 16 //#define EXECUTE_BREAKPOINT 32 -int offsetStringToInt(unsigned int type, const char* offsetBuffer); +int offsetStringToInt(unsigned int type, const char* offsetBuffer, bool *conversionOk = nullptr); unsigned int NewBreak(const char* name, int start, int end, unsigned int type, const char* condition, unsigned int num, bool enable); #endif diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp index 20c06510..33e9d096 100644 --- a/src/drivers/Qt/ConsoleDebugger.cpp +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -1872,6 +1872,9 @@ DebuggerBreakpointEditor::DebuggerBreakpointEditor(int editIndex, watchpointinfo hbox->addWidget( lbl ); hbox->addWidget( addr2 ); + connect( addr1, SIGNAL(textChanged(const QString &)), this, SLOT(addressTextChanged(const QString &))); + connect( addr2, SIGNAL(textChanged(const QString &)), this, SLOT(addressTextChanged(const QString &))); + forbidChkBox = new QCheckBox( tr("Forbid") ); hbox->addWidget( forbidChkBox ); @@ -1904,6 +1907,11 @@ DebuggerBreakpointEditor::DebuggerBreakpointEditor(int editIndex, watchpointinfo rom_radio = new QRadioButton( tr("ROM") ); cpu_radio->setChecked(true); + connect( cpu_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool))); + connect( ppu_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool))); + connect( oam_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool))); + connect( rom_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool))); + gbox->setLayout( hbox ); hbox->addWidget( cpu_radio ); hbox->addWidget( ppu_radio ); @@ -2069,7 +2077,73 @@ void DebuggerBreakpointEditor::closeWindow(int ret) //---------------------------------------------------------------------------- void DebuggerBreakpointEditor::checkDataValid(void) { - bool allEntriesValid = condValid; + int type = 0; + bool startAddrValid = false; + bool endAddrValid = false; + bool allEntriesValid = false; + int addrLowerBound = 0; + int addrUpperBound = 0; + int start_addr = 0, end_addr = 0; + + if ( cpu_radio->isChecked() ) + { + type |= BT_C; + addrLowerBound = 0; + addrUpperBound = 0x10000; + } + else if ( ppu_radio->isChecked() ) + { + type |= BT_P; + addrLowerBound = 0; + addrUpperBound = 0x4000; + } + else if ( oam_radio->isChecked() ) + { + type |= BT_S; + addrLowerBound = 0; + addrUpperBound = 0x100; + } + else if ( rom_radio->isChecked() ) + { + type |= BT_R; + addrLowerBound = 0; + addrUpperBound = 0x10000; + + if (GameInfo != nullptr) + { + addrUpperBound = 16+PRGsize[0]+CHRsize[0]; + } + } + + if ( addr1->text().size() > 0 ) + { + bool convOk = false; + + start_addr = offsetStringToInt( type, addr1->text().toStdString().c_str(), &convOk ); + + //printf("StartAddr:0x%04X Upper:0x%04X\n", start_addr, addrUpperBound); + startAddrValid = convOk && (start_addr >= addrLowerBound) && (start_addr < addrUpperBound); + } + else + { + startAddrValid = false; + } + + if ( addr2->text().size() > 0 ) + { + bool convOk = false; + + end_addr = offsetStringToInt( type, addr2->text().toStdString().c_str(), &convOk ); + + endAddrValid = convOk && (end_addr >= addrLowerBound) && + (end_addr < addrUpperBound) && (start_addr < end_addr); + } + else + { + endAddrValid = true; + } + + allEntriesValid = startAddrValid && endAddrValid && condValid; okButton->setEnabled( allEntriesValid ); @@ -2077,6 +2151,14 @@ void DebuggerBreakpointEditor::checkDataValid(void) { msgLbl->clear(); } + else if (!startAddrValid) + { + msgLbl->setText(tr("Start Address Invalid")); + } + else if (!endAddrValid) + { + msgLbl->setText(tr("End Address Invalid")); + } else if (!condValid) { msgLbl->setText(tr("Condition Invalid")); @@ -2087,6 +2169,19 @@ void DebuggerBreakpointEditor::checkDataValid(void) } } //---------------------------------------------------------------------------- +void DebuggerBreakpointEditor::typeChanged(bool checked) +{ + if (checked) + { + checkDataValid(); + } +} +//---------------------------------------------------------------------------- +void DebuggerBreakpointEditor::addressTextChanged(const QString &txt) +{ + checkDataValid(); +} +//---------------------------------------------------------------------------- void DebuggerBreakpointEditor::conditionTextChanged(const QString &txt) { if ( txt.size() > 0 ) diff --git a/src/drivers/Qt/ConsoleDebugger.h b/src/drivers/Qt/ConsoleDebugger.h index 5d55003f..fec0a8dd 100644 --- a/src/drivers/Qt/ConsoleDebugger.h +++ b/src/drivers/Qt/ConsoleDebugger.h @@ -460,6 +460,8 @@ class DebuggerBreakpointEditor : public QDialog private slots: void closeWindow(int ret); + void typeChanged(bool checked); + void addressTextChanged( const QString &text ); void conditionTextChanged( const QString &text ); }; From 783aa5f38c174f34b172b903eed31e62034ebe1d Mon Sep 17 00:00:00 2001 From: Fritz Mahnke Date: Tue, 18 Apr 2023 14:23:49 -0700 Subject: [PATCH 30/69] Don't divide by 0 when PPU/sprite viewer windows are small. Some of the views are proportional to window dimensions. This caused division by 0 where window dimensions round down to 0. This change avoids that, instead setting those views' sizes to 0 in these cases. --- src/drivers/Qt/ppuViewer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/drivers/Qt/ppuViewer.cpp b/src/drivers/Qt/ppuViewer.cpp index 91b8a38d..39071698 100644 --- a/src/drivers/Qt/ppuViewer.cpp +++ b/src/drivers/Qt/ppuViewer.cpp @@ -635,8 +635,8 @@ QPoint ppuPatternView_t::convPixToTile( QPoint p ) w = pattern->w; h = pattern->h; - i = x / (w*8); - j = y / (h*8); + i = w == 0 ? 0 : x / (w*8); + j = h == 0 ? 0 : y / (h*8); if ( PPUView_sprite16Mode[ patternIndex ] ) { @@ -3336,8 +3336,8 @@ QPoint oamPatternView_t::convPixToTile( QPoint p ) w = oamPattern.w; h = oamPattern.h; - i = x / (w*8); - j = y / (h*16); + i = w == 0 ? 0 : x / (w*8); + j = h == 0 ? 0 : y / (h*16); //printf("(x,y) = (%i,%i) w=%i h=%i $%X%X \n", x, y, w, h, jj, ii ); From dbb688ec413b99e3eff4f3a30304a40a0dc64d56 Mon Sep 17 00:00:00 2001 From: Fritz Mahnke Date: Thu, 20 Apr 2023 13:11:27 -0400 Subject: [PATCH 31/69] Fixes for updating the cheats list. Update the cheats list after adding a new cheat from the Game Genie window. With this change, the new cheat is shown immediately in the cheats window, if it's open. Fix an issue where the cheats list isn't always immediately updated after a change. For example, clicking Add while an item is already selected might not show the item until some other user action is taken. This change updates the cheats list QTreeWidget viewport explicitly after making changes to its items. This causes changes to be shown immediately. Remove unused GuiCheatsDialog_t::actvCheatRedraw member. --- src/drivers/Qt/CheatsConf.cpp | 10 +++------- src/drivers/Qt/CheatsConf.h | 1 - src/drivers/Qt/GameGenie.cpp | 2 ++ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/drivers/Qt/CheatsConf.cpp b/src/drivers/Qt/CheatsConf.cpp index bd918a3b..7c8b3ec9 100644 --- a/src/drivers/Qt/CheatsConf.cpp +++ b/src/drivers/Qt/CheatsConf.cpp @@ -704,14 +704,10 @@ int GuiCheatsDialog_t::activeCheatListCB(const char *name, uint32 a, uint8 v, in if (item == NULL) { - item = new QTreeWidgetItem(); - - actvCheatList->addTopLevelItem(item); + item = new QTreeWidgetItem(actvCheatList); } - //item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable ); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren); - item->setCheckState(0, s ? Qt::Checked : Qt::Unchecked); item->setText(0, tr(codeStr)); @@ -737,8 +733,6 @@ void GuiCheatsDialog_t::showActiveCheatList(bool redraw) enaCheats->setChecked(!globalCheatDisabled); - actvCheatRedraw = redraw; - if (redraw) { actvCheatList->clear(); @@ -746,6 +740,8 @@ void GuiCheatsDialog_t::showActiveCheatList(bool redraw) actvCheatIdx = 0; FCEUI_ListCheats(::activeCheatListCB, (void *)this); + + actvCheatList->viewport()->update(); } //---------------------------------------------------------------------------- void GuiCheatsDialog_t::openCheatFile(void) diff --git a/src/drivers/Qt/CheatsConf.h b/src/drivers/Qt/CheatsConf.h index 4eab63a9..44b44123 100644 --- a/src/drivers/Qt/CheatsConf.h +++ b/src/drivers/Qt/CheatsConf.h @@ -72,7 +72,6 @@ protected: int fontCharWidth; int actvCheatIdx; - bool actvCheatRedraw; bool pauseWhileActive; bool wasPausedByCheats; diff --git a/src/drivers/Qt/GameGenie.cpp b/src/drivers/Qt/GameGenie.cpp index 7d77b00c..56a3b02d 100644 --- a/src/drivers/Qt/GameGenie.cpp +++ b/src/drivers/Qt/GameGenie.cpp @@ -42,6 +42,7 @@ #include "Qt/config.h" #include "Qt/keyscan.h" #include "Qt/fceuWrapper.h" +#include "Qt/CheatsConf.h" #include "Qt/HexEditor.h" #include "Qt/GameGenie.h" @@ -278,6 +279,7 @@ void GameGenieDialog_t::addCheatClicked(void) FCEU_WRAPPER_LOCK(); FCEUI_AddCheat( name.c_str(), a, v, c, 1 ); + updateCheatDialog(); FCEU_WRAPPER_UNLOCK(); } From 084b2b9ccc67e31e630ae840f16f09a70c629415 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 22 Apr 2023 15:56:31 -0400 Subject: [PATCH 32/69] Added snap by number of frames (instead of by time) option for Qt state recorder. --- src/drivers/Qt/StateRecorderConf.cpp | 83 +++++++++++++++++++++++++++- src/drivers/Qt/StateRecorderConf.h | 9 +++ src/drivers/Qt/config.cpp | 2 + src/drivers/Qt/fceuWrapper.cpp | 7 +++ src/state.cpp | 36 +++++++++--- src/state.h | 9 +++ 6 files changed, 135 insertions(+), 11 deletions(-) diff --git a/src/drivers/Qt/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp index 3aeaf144..d0cfcba3 100644 --- a/src/drivers/Qt/StateRecorderConf.cpp +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -35,6 +35,7 @@ #include "../../fceu.h" #include "../../state.h" +#include "../../driver.h" #include "../../emufile.h" //---------------------------------------------------------------------------- @@ -54,14 +55,19 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) grid = new QGridLayout(); recorderEnable = new QCheckBox(tr("Auto Start Recorder at ROM Load")); + snapFrames = new QSpinBox(); snapMinutes = new QSpinBox(); snapSeconds = new QSpinBox(); historyDuration = new QSpinBox(); + snapFrameSelBtn = new QRadioButton( tr("By Frames") ); + snapTimeSelBtn = new QRadioButton( tr("By Time") ); recorderEnable->setChecked( FCEU_StateRecorderIsEnabled() ); connect( recorderEnable, SIGNAL(stateChanged(int)), this, SLOT(enableChanged(int)) ); + snapFrames->setMinimum(1); + snapFrames->setMaximum(10000); snapSeconds->setMinimum(0); snapSeconds->setMaximum(60); snapMinutes->setMinimum(0); @@ -69,6 +75,10 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) historyDuration->setMinimum(1); historyDuration->setMaximum(180); + opt = 10; + g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &opt); + snapFrames->setValue(opt); + opt = 15; g_config->getOption("SDL.StateRecorderHistoryDurationMin", &opt ); historyDuration->setValue(opt); @@ -81,6 +91,14 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &opt); snapSeconds->setValue(opt); + opt = 0; + g_config->getOption("SDL.StateRecorderTimingMode", &opt); + + snapFrameSelBtn->setChecked(opt == 0); + snapTimeSelBtn->setChecked(opt == 1); + snapUseTime = opt ? true : false; + + connect( snapFrames , SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); connect( snapSeconds, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); connect( snapMinutes, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); connect( historyDuration, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) ); @@ -122,11 +140,31 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) frame->setLayout(hbox); grid->addWidget( frame, 1, 1 ); - frame1 = new QGroupBox(tr("Time Between Snapshots:")); + frame1 = new QGroupBox(tr("Snapshot Timing Setting:")); hbox1 = new QHBoxLayout(); frame1->setLayout(hbox1); grid->addWidget( frame1, 2, 0, 1, 2 ); + g_config->getOption("SDL.StateRecorderTimingMode", &opt); + + hbox1->addWidget( snapFrameSelBtn ); + hbox1->addWidget( snapTimeSelBtn ); + + snapFramesGroup = new QGroupBox(tr("Frames Between Snapshots:")); + hbox1 = new QHBoxLayout(); + snapFramesGroup->setLayout(hbox1); + snapFramesGroup->setEnabled(snapFrameSelBtn->isChecked()); + grid->addWidget( snapFramesGroup, 3, 0, 1, 2 ); + + hbox1->addWidget( snapFrames); + hbox1->addWidget( new QLabel( tr("Range (1 - 10000)") ) ); + + snapTimeGroup = new QGroupBox(tr("Time Between Snapshots:")); + hbox1 = new QHBoxLayout(); + snapTimeGroup->setLayout(hbox1); + snapTimeGroup->setEnabled(snapTimeSelBtn->isChecked()); + grid->addWidget( snapTimeGroup, 4, 0, 1, 2 ); + frame = new QGroupBox(); hbox = new QHBoxLayout(); @@ -178,7 +216,7 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) frame->setLayout(hbox); - grid->addWidget(frame1, 3, 0, 2, 1); + grid->addWidget(frame1, 4, 3, 2, 1); frame = new QGroupBox( tr("Memory Usage:") ); memStatsGrid = new QGridLayout(); @@ -236,6 +274,8 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) updateRecorderStatusLabel(); connect(startStopButton, SIGNAL(clicked(void)), this, SLOT(startStopClicked(void))); + connect(snapFrameSelBtn, SIGNAL(clicked(void)), this, SLOT(snapFrameModeClicked(void))); + connect(snapTimeSelBtn , SIGNAL(clicked(void)), this, SLOT(snapTimeModeClicked(void))); frame = new QGroupBox(); hbox = new QHBoxLayout(); @@ -288,6 +328,7 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray()); recalcMemoryUsage(); + pauseOnLoadChanged( pauseOnLoadCbox->currentIndex() ); updateTimer = new QTimer(this); @@ -329,6 +370,10 @@ void StateRecorderDialog_t::closeWindow(void) //---------------------------------------------------------------------------- void StateRecorderDialog_t::packConfig( StateRecorderConfigData &config ) { + config.timingMode = snapTimeSelBtn->isChecked() ? + StateRecorderConfigData::TIME : StateRecorderConfigData::FRAMES; + + config.framesBetweenSnaps = snapFrames->value(); config.historyDurationMinutes = static_cast( historyDuration->value() ); config.timeBetweenSnapsMinutes = static_cast( snapMinutes->value() ) + ( static_cast( snapSeconds->value() ) / 60.0f ); @@ -393,6 +438,8 @@ void StateRecorderDialog_t::applyChanges(void) FCEU_WRAPPER_UNLOCK(); g_config->setOption("SDL.StateRecorderHistoryDurationMin", historyDuration->value() ); + g_config->setOption("SDL.StateRecorderTimingMode", snapTimeSelBtn->isChecked() ? 1 : 0); + g_config->setOption("SDL.StateRecorderFramesBetweenSnaps", snapFrames->value() ); g_config->setOption("SDL.StateRecorderTimeBetweenSnapsMin", snapMinutes->value() ); g_config->setOption("SDL.StateRecorderTimeBetweenSnapsSec", snapSeconds->value() ); g_config->setOption("SDL.StateRecorderCompressionLevel", config.compressionLevel); @@ -420,6 +467,24 @@ void StateRecorderDialog_t::startStopClicked(void) FCEU_WRAPPER_UNLOCK(); } //---------------------------------------------------------------------------- +void StateRecorderDialog_t::snapFrameModeClicked(void) +{ + snapUseTime = false; + snapTimeGroup->setEnabled(false); + snapFramesGroup->setEnabled(true); + + recalcMemoryUsage(); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::snapTimeModeClicked(void) +{ + snapUseTime = true; + snapFramesGroup->setEnabled(false); + snapTimeGroup->setEnabled(true); + + recalcMemoryUsage(); +} +//---------------------------------------------------------------------------- void StateRecorderDialog_t::updateStartStopBuffon(void) { bool isRunning = FCEU_StateRecorderRunning(); @@ -517,9 +582,21 @@ void StateRecorderDialog_t::recalcMemoryUsage(void) { char stmp[64]; + float fsnapMin = 1.0; float fhistMin = static_cast( historyDuration->value() ); - float fsnapMin = static_cast( snapMinutes->value() ) + + + if (snapUseTime) + { + fsnapMin = static_cast( snapMinutes->value() ) + ( static_cast( snapSeconds->value() ) / 60.0f ); + } + else + { + int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz + double hz = ( ((double)fps) / 16777216.0 ); + + fsnapMin = static_cast(snapFrames->value()) / (hz * 60.0); + } float fnumSnaps = fhistMin / fsnapMin; float fsnapSize = 10.0f * 1024.0f; diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h index d87e0c45..e57faf2f 100644 --- a/src/drivers/Qt/StateRecorderConf.h +++ b/src/drivers/Qt/StateRecorderConf.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ protected: QSpinBox *snapMinutes; QSpinBox *snapSeconds; + QSpinBox *snapFrames; QSpinBox *historyDuration; QSpinBox *pauseDuration; QCheckBox *recorderEnable; @@ -45,6 +47,10 @@ protected: QPushButton *closeButton; QComboBox *cmprLvlCbox; QComboBox *pauseOnLoadCbox; + QGroupBox *snapTimeGroup; + QGroupBox *snapFramesGroup; + QRadioButton *snapFrameSelBtn; + QRadioButton *snapTimeSelBtn; QLineEdit *recStatusLbl; QLineEdit *recBufSizeLbl; QPushButton *startStopButton; @@ -52,6 +58,7 @@ protected: QTimer *updateTimer; double saveTimeMs; + bool snapUseTime; bool dataSavedCheck(void); void recalcMemoryUsage(void); @@ -67,6 +74,8 @@ private slots: void applyChanges(void); void updatePeriodic(void); void startStopClicked(void); + void snapTimeModeClicked(void); + void snapFrameModeClicked(void); void spinBoxValueChanged(int newValue); void enableChanged(int); void compressionLevelChanged(int newValue); diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index 57bce0f1..949c591e 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -756,6 +756,8 @@ InitConfig() config->addOption("SDL.StateRecorderEnable", false); config->addOption("SDL.StateRecorderHistoryDurationMin", 15); + config->addOption("SDL.StateRecorderTimingMode", 0); + config->addOption("SDL.StateRecorderFramesBetweenSnaps", 60); config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0); config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3); config->addOption("SDL.StateRecorderCompressionLevel", 0); diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index e5bd4ce7..d44900e8 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -976,7 +976,9 @@ int fceuWrapperInit( int argc, char *argv[] ) // Initialize the State Recorder { bool srEnable = false; + bool srUseTimeMode = false; int srHistDurMin = 15; + int srFramesBtwSnaps = 60; int srTimeBtwSnapsMin = 0; int srTimeBtwSnapsSec = 3; int srCompressionLevel = 0; @@ -984,7 +986,9 @@ int fceuWrapperInit( int argc, char *argv[] ) int pauseOnLoad = StateRecorderConfigData::TEMPORARY_PAUSE; g_config->getOption("SDL.StateRecorderEnable", &srEnable); + g_config->getOption("SDL.StateRecorderTimingMode", &srUseTimeMode); g_config->getOption("SDL.StateRecorderHistoryDurationMin", &srHistDurMin); + g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &srFramesBtwSnaps); g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &srTimeBtwSnapsMin); g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &srTimeBtwSnapsSec); g_config->getOption("SDL.StateRecorderCompressionLevel", &srCompressionLevel); @@ -994,8 +998,11 @@ int fceuWrapperInit( int argc, char *argv[] ) StateRecorderConfigData srConfig; srConfig.historyDurationMinutes = srHistDurMin; + srConfig.timingMode = srUseTimeMode ? + StateRecorderConfigData::TIME : StateRecorderConfigData::FRAMES; srConfig.timeBetweenSnapsMinutes = static_cast( srTimeBtwSnapsMin ) + ( static_cast( srTimeBtwSnapsSec ) / 60.0f ); + srConfig.framesBetweenSnaps = srFramesBtwSnaps; srConfig.compressionLevel = srCompressionLevel; srConfig.loadPauseTimeSeconds = pauseOnLoadTime; srConfig.pauseOnLoad = static_cast(pauseOnLoad); diff --git a/src/state.cpp b/src/state.cpp index f9c05c54..622168a2 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1218,6 +1218,10 @@ class StateRecorder void loadConfig( StateRecorderConfigData &config ) { + if (config.framesBetweenSnaps < 1) + { + config.framesBetweenSnaps = 1; + } if (config.timeBetweenSnapsMinutes < 0.0) { config.timeBetweenSnapsMinutes = 3.0f / 60.0f; @@ -1226,19 +1230,35 @@ class StateRecorder { config.historyDurationMinutes = config.timeBetweenSnapsMinutes; } - const double fhistMin = config.historyDurationMinutes; - const double fsnapMin = config.timeBetweenSnapsMinutes; - const double fnumSnaps = fhistMin / fsnapMin; - ringBufSize = static_cast( fnumSnaps + 0.5f ); + if (config.timingMode) + { + const double fhistMin = config.historyDurationMinutes; + const double fsnapMin = config.timeBetweenSnapsMinutes; + const double fnumSnaps = fhistMin / fsnapMin; - int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz + ringBufSize = static_cast( fnumSnaps + 0.5f ); - double hz = ( ((double)fps) / 16777216.0 ); + int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz - double framesPerSnapf = hz * fsnapMin * 60.0; + double hz = ( ((double)fps) / 16777216.0 ); - framesPerSnap = static_cast( framesPerSnapf + 0.50 ); + double framesPerSnapf = hz * fsnapMin * 60.0; + + framesPerSnap = static_cast( framesPerSnapf + 0.50 ); + } + else + { + const double fhistMin = config.historyDurationMinutes; + int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz + double hz = ( ((double)fps) / 16777216.0 ); + + const double fsnapMin = static_cast(config.framesBetweenSnaps) / (hz * 60.0); + const double fnumSnaps = fhistMin / fsnapMin; + + ringBufSize = static_cast( fnumSnaps + 0.5f ); + framesPerSnap = config.framesBetweenSnaps; + } printf("ringBufSize:%i framesPerSnap:%i\n", ringBufSize, framesPerSnap ); diff --git a/src/state.h b/src/state.h index 119c5f92..705d7e86 100644 --- a/src/state.h +++ b/src/state.h @@ -84,9 +84,16 @@ struct StateRecorderConfigData { float historyDurationMinutes; float timeBetweenSnapsMinutes; + int framesBetweenSnaps; int compressionLevel; int loadPauseTimeSeconds; + enum TimingType + { + FRAMES = 0, + TIME, + } timingMode; + enum PauseType { NO_PAUSE = 0, @@ -96,11 +103,13 @@ struct StateRecorderConfigData StateRecorderConfigData(void) { + framesBetweenSnaps = 60; historyDurationMinutes = 15.0f; timeBetweenSnapsMinutes = 3.0f / 60.0f; compressionLevel = 0; loadPauseTimeSeconds = 3; pauseOnLoad = TEMPORARY_PAUSE; + timingMode = FRAMES; } bool compare( const StateRecorderConfigData &other ) From 45b0ea6f8daddd2d4940e3024940f97c6e79c372 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 24 Apr 2023 22:49:30 -0400 Subject: [PATCH 33/69] Added cross-platform libarchive interface to allow Qt GUI to use 7zip ROM archives. Code is setup to allow for libarchive to be an optional dependency and will fallback to regular minizip if unavailable. --- src/CMakeLists.txt | 9 +- src/drivers/Qt/ConsoleWindow.cpp | 2 +- src/drivers/Qt/fceuWrapper.cpp | 463 +++++++++++++++++++++---------- 3 files changed, 324 insertions(+), 150 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3dde1ae0..73608e9d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -110,6 +110,13 @@ else(WIN32) add_definitions( -D_SYSTEM_MINIZIP ${MINIZIP_CFLAGS} ) endif() + pkg_check_modules( LIBARCHIVE libarchive) + + if ( ${LIBARCHIVE_FOUND} ) + message( STATUS "Using System Libarchive Library ${LIBARCHIVE_VERSION}" ) + add_definitions( -D_USE_LIBARCHIVE ${LIBARCHIVE_CFLAGS} ) + endif() + pkg_check_modules( X264 x264) if ( ${X264_FOUND} ) @@ -643,7 +650,7 @@ target_link_libraries( ${APP_NAME} ${ASAN_LDFLAGS} ${${Qt}OpenGLWidgets_LIBRARIES} ${OPENGL_LDFLAGS} ${SDL2_LDFLAGS} - ${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES} + ${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES} ${LIBARCHIVE_LDFLAGS} ${LUA_LDFLAGS} ${X264_LDFLAGS} ${X265_LDFLAGS} ${LIBAV_LDFLAGS} ${SYS_LIBS} ) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 7e2e3da4..20a91243 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -2388,7 +2388,7 @@ void consoleWin_t::openROMFile(void) QDir d; const QStringList filters( - { "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP)", + { "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP, *.7z *.7zip)", "NES files (*.nes *.NES)", "NSF files (*.nsf *.NSF)", "UNF files (*.unf *.UNF *.unif *.UNIF)", diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index d44900e8..9954987a 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -1483,20 +1483,19 @@ int fceuWrapperUpdate( void ) return 0; } -ArchiveScanRecord FCEUD_ScanArchive(std::string fname) +static int minizip_ScanArchive( const char *filepath, ArchiveScanRecord &rec) { int idx=0, ret; unzFile zf; unz_file_info fi; char filename[512]; - ArchiveScanRecord rec; - - zf = unzOpen( fname.c_str() ); + + zf = unzOpen( filepath ); if ( zf == NULL ) { //printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() ); - return rec; + return -1; } rec.type = 0; @@ -1526,18 +1525,313 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname) unzClose( zf ); + return 0; +} + +#ifdef _USE_LIBARCHIVE +#include +#include + +static int libarchive_ScanArchive( const char *filepath, ArchiveScanRecord &rec) +{ + int r, idx=0; + struct archive *a; + struct archive_entry *entry; + + a = archive_read_new(); + + if (a == nullptr) + { + return -1; + } + + // Initialize decoders + r = archive_read_support_filter_all(a); + if (r) + { + archive_read_free(a); + return -1; + } + + // Initialize formats + r = archive_read_support_format_all(a); + if (r) + { + archive_read_free(a); + return -1; + } + + r = archive_read_open_filename(a, filepath, 10240); + + if (r) + { + archive_read_free(a); + return -1; + } + rec.type = 1; + + while (1) + { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + break; + } + else if (r != ARCHIVE_OK) + { + printf("archive_read_next_header() %s\n", archive_error_string(a)); + break; + } + const char *filename = archive_entry_pathname(entry); + + FCEUARCHIVEFILEINFO_ITEM item; + item.name.assign( filename ); + item.size = archive_entry_size(entry); + item.index = idx; idx++; + + rec.files.push_back( item ); + } + rec.numFilesInArchive = idx; + + archive_read_free(a); + + return 0; +} +#endif + +ArchiveScanRecord FCEUD_ScanArchive(std::string fname) +{ + int ret = -1; + ArchiveScanRecord rec; + +#ifdef _USE_LIBARCHIVE + ret = libarchive_ScanArchive( fname.c_str(), rec ); +#endif + + if (ret == -1) + { + minizip_ScanArchive( fname.c_str(), rec ); + } return rec; } -FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel) +static FCEUFILE* minizip_OpenArchive(ArchiveScanRecord& asr, std::string &fname, std::string *searchFile, int innerIndex ) { - int ret; - FCEUFILE* fp = 0; + int ret, idx=0; + FCEUFILE* fp = nullptr; + void *tmpMem = nullptr; unzFile zf; unz_file_info fi; char filename[512]; - char foundFile = 0; - void *tmpMem = NULL; + bool foundFile = false; + + zf = unzOpen( fname.c_str() ); + + if ( zf == NULL ) + { + //printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() ); + return fp; + } + + //printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() ); + + ret = unzGoToFirstFile( zf ); + + //printf("unzGoToFirstFile: %i \n", ret ); + + while ( ret == 0 ) + { + unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 ); + + //printf("Filename: %u '%s' \n", fi.uncompressed_size, filename ); + + if (searchFile) + { + if ( strcmp( searchFile->c_str(), filename ) == 0 ) + { + //printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename ); + foundFile = true; break; + } + } + else if ((innerIndex != -1) && (idx == innerIndex)) + { + foundFile = true; break; + } + + ret = unzGoToNextFile( zf ); + + //printf("unzGoToNextFile: %i \n", ret ); + idx++; + } + + if ( !foundFile ) + { + unzClose( zf ); + return fp; + } + + tmpMem = ::malloc( fi.uncompressed_size ); + + if ( tmpMem == NULL ) + { + unzClose( zf ); + return fp; + } + //printf("Loading via minizip\n"); + + EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size); + + unzOpenCurrentFile( zf ); + unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size ); + unzCloseCurrentFile( zf ); + + ms->fwrite( tmpMem, fi.uncompressed_size ); + + free( tmpMem ); + + //if we extracted the file correctly + fp = new FCEUFILE(); + fp->archiveFilename = fname; + fp->filename = filename; + fp->fullFilename = fp->archiveFilename + "|" + fp->filename; + fp->archiveIndex = idx; + fp->mode = FCEUFILE::READ; + fp->size = fi.uncompressed_size; + fp->stream = ms; + fp->archiveCount = (int)asr.numFilesInArchive; + ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file + + unzClose( zf ); + + return fp; +} + +#ifdef _USE_LIBARCHIVE +static FCEUFILE* libarchive_OpenArchive( ArchiveScanRecord& asr, std::string& fname, std::string *searchFile, int innerIndex) +{ + int r, idx=0; + struct archive *a; + struct archive_entry *entry; + const char *filename = nullptr; + bool foundFile = false; + int fileSize = 0; + FCEUFILE* fp = nullptr; + + a = archive_read_new(); + + if (a == nullptr) + { + archive_read_free(a); + return nullptr; + } + + // Initialize decoders + r = archive_read_support_filter_all(a); + if (r) + { + archive_read_free(a); + return nullptr; + } + + // Initialize formats + r = archive_read_support_format_all(a); + if (r) + { + archive_read_free(a); + return nullptr; + } + + r = archive_read_open_filename(a, fname.c_str(), 10240); + + if (r) + { + archive_read_free(a); + return nullptr; + } + + while (1) + { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + break; + } + else if (r != ARCHIVE_OK) + { + printf("archive_read_next_header() %s\n", archive_error_string(a)); + break; + } + filename = archive_entry_pathname(entry); + fileSize = archive_entry_size(entry); + + if (searchFile) + { + if (strcmp( filename, searchFile->c_str() ) == 0) + { + foundFile = true; break; + } + } + else if ((innerIndex != -1) && (idx == innerIndex)) + { + foundFile = true; break; + } + idx++; + } + + if (foundFile && (fileSize > 0)) + { + const void *buff; + size_t size, totalSize = 0; + #if ARCHIVE_VERSION_NUMBER >= 3000000 + int64_t offset; + #else + off_t offset; + #endif + + //printf("Loading via libarchive\n"); + + EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fileSize); + + while (1) + { + r = archive_read_data_block(a, &buff, &size, &offset); + + if (r == ARCHIVE_EOF) + { + break; + } + if (r != ARCHIVE_OK) + { + break; + } + //printf("Read: %p Size:%zu Offset:%llu\n", buff, size, (long long int)offset); + ms->fwrite( buff, size ); + totalSize += size; + } + + //if we extracted the file correctly + fp = new FCEUFILE(); + fp->archiveFilename = fname; + fp->filename = filename; + fp->fullFilename = fp->archiveFilename + "|" + fp->filename; + fp->archiveIndex = idx; + fp->mode = FCEUFILE::READ; + fp->size = totalSize; + fp->stream = ms; + fp->archiveCount = (int)asr.numFilesInArchive; + ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file + } + + archive_read_free(a); + + return fp; +} + +#endif + +FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel) +{ + FCEUFILE* fp = nullptr; std::string searchFile; if ( innerFilename != NULL ) @@ -1587,75 +1881,14 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str } } - zf = unzOpen( fname.c_str() ); +#ifdef _USE_LIBARCHIVE + fp = libarchive_OpenArchive(asr, fname, &searchFile, -1 ); +#endif - if ( zf == NULL ) + if (fp == nullptr) { - //printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() ); - return fp; + fp = minizip_OpenArchive(asr, fname, &searchFile, -1 ); } - - //printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() ); - - ret = unzGoToFirstFile( zf ); - - //printf("unzGoToFirstFile: %i \n", ret ); - - while ( ret == 0 ) - { - unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 ); - - //printf("Filename: %u '%s' \n", fi.uncompressed_size, filename ); - - if ( strcmp( searchFile.c_str(), filename ) == 0 ) - { - //printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename ); - foundFile = 1; break; - } - - ret = unzGoToNextFile( zf ); - - //printf("unzGoToNextFile: %i \n", ret ); - } - - if ( !foundFile ) - { - unzClose( zf ); - return fp; - } - - tmpMem = ::malloc( fi.uncompressed_size ); - - if ( tmpMem == NULL ) - { - unzClose( zf ); - return fp; - } - - EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size); - - unzOpenCurrentFile( zf ); - unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size ); - unzCloseCurrentFile( zf ); - - ms->fwrite( tmpMem, fi.uncompressed_size ); - - free( tmpMem ); - - //if we extracted the file correctly - fp = new FCEUFILE(); - fp->archiveFilename = fname; - fp->filename = searchFile; - fp->fullFilename = fp->archiveFilename + "|" + fp->filename; - fp->archiveIndex = ret; - fp->mode = FCEUFILE::READ; - fp->size = fi.uncompressed_size; - fp->stream = ms; - fp->archiveCount = (int)asr.numFilesInArchive; - ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file - - unzClose( zf ); - return fp; } @@ -1668,82 +1901,16 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string &fname, int innerIndex, int* userCancel) { - int ret, idx=0; - FCEUFILE* fp = 0; - unzFile zf; - unz_file_info fi; - char filename[512]; - char foundFile = 0; - void *tmpMem = NULL; + FCEUFILE* fp = nullptr; - zf = unzOpen( fname.c_str() ); - - if ( zf == NULL ) +#ifdef _USE_LIBARCHIVE + fp = libarchive_OpenArchive( asr, fname, nullptr, innerIndex ); +#endif + if (fp == nullptr) { - //printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() ); - return fp; + fp = minizip_OpenArchive(asr, fname, nullptr, innerIndex); } - ret = unzGoToFirstFile( zf ); - - //printf("unzGoToFirstFile: %i \n", ret ); - - while ( ret == 0 ) - { - unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 ); - - //printf("Filename: %u '%s' \n", fi.uncompressed_size, filename ); - - if ( idx == innerIndex ) - { - //printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename ); - foundFile = 1; break; - } - idx++; - - ret = unzGoToNextFile( zf ); - - //printf("unzGoToNextFile: %i \n", ret ); - } - - if ( !foundFile ) - { - unzClose( zf ); - return fp; - } - - tmpMem = ::malloc( fi.uncompressed_size ); - - if ( tmpMem == NULL ) - { - unzClose( zf ); - return fp; - } - - EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size); - - unzOpenCurrentFile( zf ); - unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size ); - unzCloseCurrentFile( zf ); - - ms->fwrite( tmpMem, fi.uncompressed_size ); - - free( tmpMem ); - - //if we extracted the file correctly - fp = new FCEUFILE(); - fp->archiveFilename = fname; - fp->filename = filename; - fp->fullFilename = fp->archiveFilename + "|" + fp->filename; - fp->archiveIndex = ret; - fp->mode = FCEUFILE::READ; - fp->size = fi.uncompressed_size; - fp->stream = ms; - fp->archiveCount = (int)asr.numFilesInArchive; - ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file - - unzClose( zf ); - return fp; } From b7bc8a29f050518fbad2e3c1f04eda2901b0fd5f Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 24 Apr 2023 23:08:05 -0400 Subject: [PATCH 34/69] Added libarchive version info to Qt GUI about window. --- src/drivers/Qt/AboutWindow.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/drivers/Qt/AboutWindow.cpp b/src/drivers/Qt/AboutWindow.cpp index eb2d0e7b..5c8fa539 100644 --- a/src/drivers/Qt/AboutWindow.cpp +++ b/src/drivers/Qt/AboutWindow.cpp @@ -34,6 +34,10 @@ #include "x264.h" #endif +#ifdef _USE_LIBARCHIVE +#include +#endif + #ifdef _USE_LIBAV #ifdef __cplusplus extern "C" @@ -202,6 +206,19 @@ AboutWindow::AboutWindow(QWidget *parent) sprintf( stmp, " Compiled with zlib %s\n", ZLIB_VERSION ); credits->insertPlainText( stmp ); #endif +#ifdef _USE_LIBARCHIVE + sprintf( stmp, " Compiled with libarchive %s\n", ARCHIVE_VERSION_ONLY_STRING ); + credits->insertPlainText( stmp ); + const char *libArcName[] = { "zlib", "liblzma", "bzlib", "liblz4", "libzstd", nullptr }; + const char *libArcVersion[] = { archive_zlib_version(), archive_liblzma_version(), + archive_bzlib_version(), archive_liblz4_version(), archive_libzstd_version(), nullptr }; + i=0; + while (libArcName[i]) + { + sprintf( stmp, " %s %s\n", libArcName[i], libArcVersion[i]); i++; + credits->insertPlainText( stmp ); + } +#endif #ifdef _S9XLUA_H sprintf( stmp, " Compiled with %s\n", LUA_RELEASE ); From 02baa1dfaa1a136f32b2e4e3789c0fbd741a81f0 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 24 Apr 2023 23:11:20 -0400 Subject: [PATCH 35/69] Added libarchive to linux build pipeline. --- pipelines/linux_build.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pipelines/linux_build.sh b/pipelines/linux_build.sh index cc6cf21c..517d115e 100755 --- a/pipelines/linux_build.sh +++ b/pipelines/linux_build.sh @@ -61,6 +61,13 @@ echo '****************************************' sudo apt-get --assume-yes install libminizip-dev pkg-config --cflags --libs minizip +# Install libarchive-dev +echo '****************************************' +echo 'Install Dependency libarchive-dev' +echo '****************************************' +sudo apt-get --assume-yes install libarchive-dev +pkg-config --cflags --libs libarchive + # GTK+-2 is no longer needed #sudo apt-get install libgtk2.0-dev From 99bc0fa17fdfd0a28cbaeee6b91069a12fd66d70 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 24 Apr 2023 23:14:43 -0400 Subject: [PATCH 36/69] Add nullptr check to ensure that libarchive version strings are valid before printing. They will be null when a particular format or filter is not supported. --- src/drivers/Qt/AboutWindow.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/drivers/Qt/AboutWindow.cpp b/src/drivers/Qt/AboutWindow.cpp index 5c8fa539..20719222 100644 --- a/src/drivers/Qt/AboutWindow.cpp +++ b/src/drivers/Qt/AboutWindow.cpp @@ -215,8 +215,12 @@ AboutWindow::AboutWindow(QWidget *parent) i=0; while (libArcName[i]) { - sprintf( stmp, " %s %s\n", libArcName[i], libArcVersion[i]); i++; - credits->insertPlainText( stmp ); + if (libArcVersion[i]) + { + sprintf( stmp, " %s %s\n", libArcName[i], libArcVersion[i]); + credits->insertPlainText( stmp ); + } + i++; } #endif From e1e88b7298e34b7ddf36a62ddc6813f64a4b410e Mon Sep 17 00:00:00 2001 From: harry Date: Tue, 25 Apr 2023 04:14:30 -0400 Subject: [PATCH 37/69] Added libarchive to Qt win64 build. --- pipelines/qwin64_build.bat | 12 +++++++++--- src/CMakeLists.txt | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pipelines/qwin64_build.bat b/pipelines/qwin64_build.bat index 7acf8ea8..2bbf5c0a 100644 --- a/pipelines/qwin64_build.bat +++ b/pipelines/qwin64_build.bat @@ -24,28 +24,33 @@ mkdir bin set SDL_VERSION=2.24.1 set FFMPEG_VERSION=5.1.2 +set LIBARCHIVE_VERSION=3.6.2 curl -s -LO https://github.com/libsdl-org/SDL/releases/download/release-%SDL_VERSION%/SDL2-devel-%SDL_VERSION%-VC.zip curl -s -LO https://github.com/GyanD/codexffmpeg/releases/download/%FFMPEG_VERSION%/ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip +curl -s -LO https://www.libarchive.org/downloads/libarchive-v%LIBARCHIVE_VERSION%-amd64.zip REM rmdir /q /s SDL2 powershell -command "Expand-Archive" SDL2-devel-%SDL_VERSION%-VC.zip . powershell -command "Expand-Archive" ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip +powershell -command "Expand-Archive" libarchive-v%LIBARCHIVE_VERSION%-amd64.zip rename SDL2-%SDL_VERSION% SDL2 move ffmpeg-%FFMPEG_VERSION%-full_build-shared\ffmpeg-%FFMPEG_VERSION%-full_build-shared ffmpeg rmdir ffmpeg-%FFMPEG_VERSION%-full_build-shared del ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip +move libarchive-v%LIBARCHIVE_VERSION%-amd64\libarchive libarchive set SDL_INSTALL_PREFIX=%CD% set FFMPEG_INSTALL_PREFIX=%CD% +set LIBARCHIVE_INSTALL_PREFIX=%CD% set PUBLIC_RELEASE=0 IF DEFINED FCEU_RELEASE_VERSION (set PUBLIC_RELEASE=1) REM cmake -h REM cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% .. -cmake -DQT6=0 -DPUBLIC_RELEASE=%PUBLIC_RELEASE% -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% -DUSE_LIBAV=1 -DFFMPEG_INSTALL_PREFIX=%FFMPEG_INSTALL_PREFIX% -G"Visual Studio 16" -T"v142" .. +cmake -DQT6=0 -DPUBLIC_RELEASE=%PUBLIC_RELEASE% -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% -DLIBARCHIVE_INSTALL_PREFIX=%LIBARCHIVE_INSTALL_PREFIX% -DUSE_LIBAV=1 -DFFMPEG_INSTALL_PREFIX=%FFMPEG_INSTALL_PREFIX% -G"Visual Studio 16" -T"v142" .. REM nmake msbuild /m fceux.sln /p:Configuration=Release @@ -53,9 +58,10 @@ if %ERRORLEVEL% NEQ 0 EXIT /B 1 copy src\Release\fceux.exe bin\qfceux.exe copy %PROJECT_ROOT%\src\auxlib.lua bin\. -REM copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua51.dll bin\. -REM copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua5.1.dll bin\. +copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua51.dll bin\. +copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua5.1.dll bin\. copy %SDL_INSTALL_PREFIX%\SDL2\lib\x64\SDL2.dll bin\. +copy %LIBARCHIVE_INSTALL_PREFIX%\libarchive\bin\archive.dll bin\. copy %FFMPEG_INSTALL_PREFIX%\ffmpeg\bin\*.dll bin\. windeployqt --no-compiler-runtime bin\qfceux.exe diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 73608e9d..3318ca5d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,12 +41,15 @@ if(WIN32) add_definitions( -DMSVC -D_CRT_SECURE_NO_WARNINGS ) add_definitions( -D__SDL__ -D__QT_DRIVER__ -DQT_DEPRECATED_WARNINGS ) add_definitions( -DFCEUDEF_DEBUGGER ) + add_definitions( -D_USE_LIBARCHIVE ) add_definitions( /wd4267 /wd4244 ) #add_definitions( /wd4018 ) # Integer comparison sign mismatch warnings include_directories( ${SDL_INSTALL_PREFIX}/SDL2/include ) + include_directories( ${LIBARCHIVE_INSTALL_PREFIX}/libarchive/include ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib ) set( OPENGL_LDFLAGS OpenGL::GL ) set( SDL2_LDFLAGS ${SDL_INSTALL_PREFIX}/SDL2/lib/x64/SDL2.lib ) + set( LIBARCHIVE_LDFLAGS ${LIBARCHIVE_INSTALL_PREFIX}/libarchive/lib/archive.lib ) set( SYS_LIBS wsock32 ws2_32 vfw32 Htmlhelp ) set(APP_ICON_RESOURCES_WINDOWS ${CMAKE_SOURCE_DIR}/icons/fceux.rc ) From f4c88411b0b111b52ed48a91d0640be252750371 Mon Sep 17 00:00:00 2001 From: harry Date: Tue, 25 Apr 2023 21:21:40 -0400 Subject: [PATCH 38/69] Added libarchive to Qt GUI macOS build. --- pipelines/macOS_build.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pipelines/macOS_build.sh b/pipelines/macOS_build.sh index 7b61754a..fd8cb7fc 100755 --- a/pipelines/macOS_build.sh +++ b/pipelines/macOS_build.sh @@ -70,6 +70,12 @@ echo 'Install Dependency minizip' echo '****************************************' brew install minizip +echo '****************************************' +echo 'Install Optional Dependency libarchive' +echo '****************************************' +brew install libarchive +LIBARCHIVE_PATH=`brew --prefix libarchive`; + echo '****************************************' echo 'Install Optional Dependency x264' echo '****************************************' @@ -87,7 +93,7 @@ brew install ffmpeg #brew install zlib # Already installed in appveyor macOS -export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig: +export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:$LIBARCHIVE_PATH: ls -ltr $HOME/Qt; From 8857f99dd07c499d5943a6311287ee36a8680103 Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Fri, 28 Apr 2023 15:02:04 +0400 Subject: [PATCH 39/69] Using 8KB of CHR RAM memory for UNROM-512 by default (required by some games), use NES 2.0 to override --- src/ines.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ines.cpp b/src/ines.cpp index 9bf183f5..3123ea72 100644 --- a/src/ines.cpp +++ b/src/ines.cpp @@ -1166,7 +1166,6 @@ static int iNES_Init(int num) { case 13: CHRRAMSize = 16 * 1024; break; case 6: case 29: - case 30: case 45: case 96: CHRRAMSize = 32 * 1024; break; case 176: CHRRAMSize = 128 * 1024; break; From 8b37ad91e5c41b199341aaeff753fc0764b53299 Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Fri, 28 Apr 2023 17:33:05 +0400 Subject: [PATCH 40/69] Commit 8857f99d rollback --- src/ines.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ines.cpp b/src/ines.cpp index 3123ea72..9bf183f5 100644 --- a/src/ines.cpp +++ b/src/ines.cpp @@ -1166,6 +1166,7 @@ static int iNES_Init(int num) { case 13: CHRRAMSize = 16 * 1024; break; case 6: case 29: + case 30: case 45: case 96: CHRRAMSize = 32 * 1024; break; case 176: CHRRAMSize = 128 * 1024; break; From 201cb50d8914e55223a1bf3ad19f0bc46bfdc407 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 30 Apr 2023 09:16:59 -0400 Subject: [PATCH 41/69] Updated README and macOSX build from source instructions to include optional dependency libarchive. --- README | 3 ++- web/osx.html | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README b/README index 18641da1..1d6a12b6 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ Updated By mjbudd77 https://fceux.com -Last Modified: March 24, 2022 +Last Modified: April 30, 2023 Table of Contents ----------------- @@ -32,6 +32,7 @@ Table of Contents * libx265 (optional) - H.265 video encoder for avi recording (recommended) * ffmpeg libraries (optional) - for avi recording (recommended) * - ffmpeg libraries used: libavcodec libavformat libavutil libswresample libswscale +* libarchive (optional) - for 7zip archive support (test with version 3.4.0) * minizip * zlib * openGL diff --git a/web/osx.html b/web/osx.html index e030a0ac..0c0c9df1 100644 --- a/web/osx.html +++ b/web/osx.html @@ -81,6 +81,7 @@ brew install qt5 brew install sdl2 brew install minizip + brew install libarchive (optional dependency for 7zip archive support) brew install ffmpeg (optional dependency but recommended for AVI recording) brew install x264 (optional dependency but recommended for AVI recording) From f8b8e93eaf076a500d22e31b86b25d2b5c85692b Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Tue, 2 May 2023 14:35:08 +0400 Subject: [PATCH 42/69] Mapper 342 memory optimization --- src/boards/coolgirl.cpp | 90 +++++++++++++++++++---------------------- src/unif.cpp | 2 +- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/boards/coolgirl.cpp b/src/boards/coolgirl.cpp index d9a22331..a3055f9d 100644 --- a/src/boards/coolgirl.cpp +++ b/src/boards/coolgirl.cpp @@ -103,13 +103,11 @@ const uint32 FLASH_SECTOR_SIZE = 128 * 1024; const int ROM_CHIP = 0x00; const int WRAM_CHIP = 0x10; const int FLASH_CHIP = 0x11; -const int CHR_RAM_CHIP = 0x12; const int CFI_CHIP = 0x13; +static int CHR_SIZE = 0; static uint32 WRAM_SIZE = 0; static uint8 *WRAM = NULL; -static uint32 CHR_RAM_SIZE = 0; -static uint8 *CHR_RAM; static uint8 *SAVE_FLASH = NULL; static uint8* CFI; @@ -388,75 +386,75 @@ static void COOLGIRL_Sync_CHR(void) { int chr_shift_left = 0; // enable or disable writes to CHR RAM, setup CHR mask - SetupCartCHRMapping(CHR_RAM_CHIP, CHR_RAM, ((((~(chr_mask >> 13) & 0x3F) + 1) * 0x2000 - 1) & (CHR_RAM_SIZE - 1)) + 1, can_write_chr); + SetupCartCHRMapping(0, UNIFchrrama, ((((~(chr_mask >> 13) & 0x3F) + 1) * 0x2000 - 1) & (CHR_SIZE - 1)) + 1, can_write_chr); switch (chr_mode & 7) { default: case 0: - setchr8r(0x12, chr_bank_a >> 3 >> chr_shift_right << chr_shift_left); + setchr8(chr_bank_a >> 3 >> chr_shift_right << chr_shift_left); break; case 1: - setchr4r(0x12, 0x0000, mapper_163_latch >> chr_shift_right << chr_shift_left); - setchr4r(0x12, 0x1000, mapper_163_latch >> chr_shift_right << chr_shift_left); + setchr4(0x0000, mapper_163_latch >> chr_shift_right << chr_shift_left); + setchr4(0x1000, mapper_163_latch >> chr_shift_right << chr_shift_left); break; case 2: - setchr2r(0x12, 0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); TKSMIR[0] = TKSMIR[1] = chr_bank_a; - setchr2r(0x12, 0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); TKSMIR[2] = TKSMIR[3] = chr_bank_c; - setchr1r(0x12, 0x1000, chr_bank_e >> chr_shift_right << chr_shift_left); + setchr1(0x1000, chr_bank_e >> chr_shift_right << chr_shift_left); TKSMIR[4] = chr_bank_e; - setchr1r(0x12, 0x1400, chr_bank_f >> chr_shift_right << chr_shift_left); + setchr1(0x1400, chr_bank_f >> chr_shift_right << chr_shift_left); TKSMIR[5] = chr_bank_f; - setchr1r(0x12, 0x1800, chr_bank_g >> chr_shift_right << chr_shift_left); + setchr1(0x1800, chr_bank_g >> chr_shift_right << chr_shift_left); TKSMIR[6] = chr_bank_g; - setchr1r(0x12, 0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left); + setchr1(0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left); TKSMIR[7] = chr_bank_h; break; case 3: - setchr1r(0x12, 0x0000, chr_bank_e >> chr_shift_right << chr_shift_left); + setchr1(0x0000, chr_bank_e >> chr_shift_right << chr_shift_left); TKSMIR[0] = chr_bank_e; - setchr1r(0x12, 0x0400, chr_bank_f >> chr_shift_right << chr_shift_left); + setchr1(0x0400, chr_bank_f >> chr_shift_right << chr_shift_left); TKSMIR[1] = chr_bank_f; - setchr1r(0x12, 0x0800, chr_bank_g >> chr_shift_right << chr_shift_left); + setchr1(0x0800, chr_bank_g >> chr_shift_right << chr_shift_left); TKSMIR[2] = chr_bank_g; - setchr1r(0x12, 0x0C00, chr_bank_h >> chr_shift_right << chr_shift_left); + setchr1(0x0C00, chr_bank_h >> chr_shift_right << chr_shift_left); TKSMIR[3] = chr_bank_h; - setchr2r(0x12, 0x1000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x1000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); TKSMIR[4] = TKSMIR[5] = chr_bank_a; - setchr2r(0x12, 0x1800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x1800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); TKSMIR[6] = TKSMIR[7] = chr_bank_c; break; case 4: - setchr4r(0x12, 0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left); - setchr4r(0x12, 0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left); + setchr4(0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left); + setchr4(0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left); break; case 5: if (!ppu_latch0) - setchr4r(0x12, 0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left); + setchr4(0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left); else - setchr4r(0x12, 0x0000, chr_bank_b >> 2 >> chr_shift_right << chr_shift_left); + setchr4(0x0000, chr_bank_b >> 2 >> chr_shift_right << chr_shift_left); if (!ppu_latch1) - setchr4r(0x12, 0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left); + setchr4(0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left); else - setchr4r(0x12, 0x1000, chr_bank_f >> 2 >> chr_shift_right << chr_shift_left); + setchr4(0x1000, chr_bank_f >> 2 >> chr_shift_right << chr_shift_left); break; case 6: - setchr2r(0x12, 0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); - setchr2r(0x12, 0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); - setchr2r(0x12, 0x1000, chr_bank_e >> 1 >> chr_shift_right << chr_shift_left); - setchr2r(0x12, 0x1800, chr_bank_g >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x1000, chr_bank_e >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x1800, chr_bank_g >> 1 >> chr_shift_right << chr_shift_left); break; case 7: - setchr1r(0x12, 0x0000, chr_bank_a >> chr_shift_right << chr_shift_left); - setchr1r(0x12, 0x0400, chr_bank_b >> chr_shift_right << chr_shift_left); - setchr1r(0x12, 0x0800, chr_bank_c >> chr_shift_right << chr_shift_left); - setchr1r(0x12, 0x0C00, chr_bank_d >> chr_shift_right << chr_shift_left); - setchr1r(0x12, 0x1000, chr_bank_e >> chr_shift_right << chr_shift_left); - setchr1r(0x12, 0x1400, chr_bank_f >> chr_shift_right << chr_shift_left); - setchr1r(0x12, 0x1800, chr_bank_g >> chr_shift_right << chr_shift_left); - setchr1r(0x12, 0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left); + setchr1(0x0000, chr_bank_a >> chr_shift_right << chr_shift_left); + setchr1(0x0400, chr_bank_b >> chr_shift_right << chr_shift_left); + setchr1(0x0800, chr_bank_c >> chr_shift_right << chr_shift_left); + setchr1(0x0C00, chr_bank_d >> chr_shift_right << chr_shift_left); + setchr1(0x1000, chr_bank_e >> chr_shift_right << chr_shift_left); + setchr1(0x1400, chr_bank_f >> chr_shift_right << chr_shift_left); + setchr1(0x1800, chr_bank_g >> chr_shift_right << chr_shift_left); + setchr1(0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left); break; } } @@ -468,10 +466,10 @@ static void COOLGIRL_Sync_Mirroring(void) { setmirror((mirroring < 2) ? (mirroring ^ 1) : mirroring); } else { // four screen mode - vnapage[0] = CHR_RAM + 0x3F000; - vnapage[1] = CHR_RAM + 0x3F400; - vnapage[2] = CHR_RAM + 0x3F800; - vnapage[3] = CHR_RAM + 0x3FC00; + vnapage[0] = UNIFchrrama + 0x3F000; + vnapage[1] = UNIFchrrama + 0x3F400; + vnapage[2] = UNIFchrrama + 0x3F800; + vnapage[3] = UNIFchrrama + 0x3FC00; } } @@ -2238,15 +2236,13 @@ static void COOLGIRL_Power(void) { } static void COOLGIRL_Close(void) { - if (CHR_RAM) - FCEU_gfree(CHR_RAM); if (WRAM) FCEU_gfree(WRAM); if (SAVE_FLASH) FCEU_gfree(SAVE_FLASH); if (CFI) FCEU_gfree(CFI); - CHR_RAM = WRAM = SAVE_FLASH = CFI = NULL; + WRAM = SAVE_FLASH = CFI = NULL; } static void COOLGIRL_Restore(int version) { @@ -2257,11 +2253,7 @@ static void COOLGIRL_Restore(int version) { #define ExState(var, varname) AddExState(&var, sizeof(var), 0, varname) void COOLGIRL_Init(CartInfo *info) { - CHR_RAM_SIZE = info->ines2 ? (info->vram_size + info->battery_vram_size) : (512 * 1024); - CHR_RAM = (uint8*)FCEU_gmalloc(CHR_RAM_SIZE); - memset(CHR_RAM, 0, CHR_RAM_SIZE); - SetupCartCHRMapping(CHR_RAM_CHIP, CHR_RAM, CHR_RAM_SIZE, 0); - AddExState(CHR_RAM, sizeof(CHR_RAM_SIZE), 0, "CHR_"); + CHR_SIZE = info->vram_size ? info->vram_size /* NES 2.0 */ : 256 * 1024 /* UNIF, fixed */; WRAM_SIZE = info->ines2 ? (info->wram_size + info->battery_wram_size) : (32 * 1024); if (WRAM_SIZE > 0) { diff --git a/src/unif.cpp b/src/unif.cpp index 7b6c73a8..e47a8c54 100644 --- a/src/unif.cpp +++ b/src/unif.cpp @@ -475,7 +475,7 @@ static BMAPPING bmap[] = { { "FNS", FNS_Init, BMCFLAG_16KCHRR }, { "BS-400R", BS400R_Init, 0 }, { "BS-4040R", BS4040R_Init, 0 }, - { "COOLGIRL", COOLGIRL_Init, 0 }, + { "COOLGIRL", COOLGIRL_Init, BMCFLAG_256KCHRR }, { "JC-016-2", Mapper205_Init, 0 }, { 0, 0, 0 } From 17e04a9b748cc14844b978682d5db1e576d61ca0 Mon Sep 17 00:00:00 2001 From: Fritz Mahnke Date: Sat, 22 Apr 2023 17:49:13 -0400 Subject: [PATCH 43/69] Implement LUA function memory.registerread. --- src/lua-engine.cpp | 3 +-- src/x6502.cpp | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/lua-engine.cpp b/src/lua-engine.cpp index 6aa16a52..23a64641 100644 --- a/src/lua-engine.cpp +++ b/src/lua-engine.cpp @@ -2470,7 +2470,6 @@ static int memory_registerwrite(lua_State *L) { return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_WRITE), 1); } -FCEU_MAYBE_UNUSED static int memory_registerread(lua_State *L) { return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_READ), 1); @@ -6137,7 +6136,7 @@ static const struct luaL_reg memorylib [] = { // memory hooks {"registerwrite", memory_registerwrite}, - //{"registerread", memory_registerread}, TODO + {"registerread", memory_registerread}, {"registerexec", memory_registerexec}, // alternate names {"register", memory_registerwrite}, diff --git a/src/x6502.cpp b/src/x6502.cpp index 1948cd46..eba55955 100644 --- a/src/x6502.cpp +++ b/src/x6502.cpp @@ -47,7 +47,11 @@ void (*MapIRQHook)(int a); //normal memory read static INLINE uint8 RdMem(unsigned int A) { - return(_DB=ARead[A](A)); + _DB=ARead[A](A); + #ifdef _S9XLUA_H + CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ); + #endif + return(_DB); } //normal memory write @@ -61,9 +65,13 @@ static INLINE void WrMem(unsigned int A, uint8 V) static INLINE uint8 RdRAM(unsigned int A) { + _DB=ARead[A](A); + #ifdef _S9XLUA_H + CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ); + #endif //bbit edited: this was changed so cheat substituion would work - return(_DB=ARead[A](A)); // return(_DB=RAM[A]); + return(_DB); } static INLINE void WrRAM(unsigned int A, uint8 V) @@ -77,7 +85,11 @@ static INLINE void WrRAM(unsigned int A, uint8 V) uint8 X6502_DMR(uint32 A) { ADDCYC(1); - return(X.DB=ARead[A](A)); + _DB=ARead[A](A); + #ifdef _S9XLUA_H + CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ); + #endif + return(_DB); } void X6502_DMW(uint32 A, uint8 V) From 150372eeca2ad315eb03d18a2175907f920d9352 Mon Sep 17 00:00:00 2001 From: Fritz Mahnke Date: Sat, 22 Apr 2023 22:16:06 -0700 Subject: [PATCH 44/69] Remove some code that was ported to FCEUX from gens, but isn't used. --- src/fceulua.h | 3 --- src/lua-engine.cpp | 46 +++------------------------------------------- 2 files changed, 3 insertions(+), 46 deletions(-) diff --git a/src/fceulua.h b/src/fceulua.h index 08b8a879..2a82d2df 100644 --- a/src/fceulua.h +++ b/src/fceulua.h @@ -21,9 +21,6 @@ enum LuaMemHookType LUAMEMHOOK_WRITE, LUAMEMHOOK_READ, LUAMEMHOOK_EXEC, - LUAMEMHOOK_WRITE_SUB, - LUAMEMHOOK_READ_SUB, - LUAMEMHOOK_EXEC_SUB, LUAMEMHOOK_COUNT }; diff --git a/src/lua-engine.cpp b/src/lua-engine.cpp index 23a64641..6ca7a4bb 100644 --- a/src/lua-engine.cpp +++ b/src/lua-engine.cpp @@ -26,7 +26,6 @@ #include "debug.h" #include "debugsymboltable.h" #include "sound.h" -#include "drawing.h" #include "state.h" #include "movie.h" #include "driver.h" @@ -319,10 +318,6 @@ static const char* luaMemHookTypeStrings [] = "MEMHOOK_WRITE", "MEMHOOK_READ", "MEMHOOK_EXEC", - - "MEMHOOK_WRITE_SUB", - "MEMHOOK_READ_SUB", - "MEMHOOK_EXEC_SUB", }; //make sure we have the right number of strings @@ -2431,52 +2426,17 @@ static int memory_registerHook(lua_State* L, LuaMemHookType hookType, int defaul return 0; } -LuaMemHookType MatchHookTypeToCPU(lua_State* L, LuaMemHookType hookType) -{ - int cpuID = 0; - - int cpunameIndex = 0; - if(lua_type(L,2) == LUA_TSTRING) - cpunameIndex = 2; - else if(lua_type(L,3) == LUA_TSTRING) - cpunameIndex = 3; - - if(cpunameIndex) - { - const char* cpuName = lua_tostring(L, cpunameIndex); - if(!stricmp(cpuName, "sub")) - cpuID = 1; - lua_remove(L, cpunameIndex); - } - - switch(cpuID) - { - case 0: - return hookType; - - case 1: - switch(hookType) - { - case LUAMEMHOOK_WRITE: return LUAMEMHOOK_WRITE_SUB; - case LUAMEMHOOK_READ: return LUAMEMHOOK_READ_SUB; - case LUAMEMHOOK_EXEC: return LUAMEMHOOK_EXEC_SUB; - default: return hookType; - } - } - return hookType; -} - static int memory_registerwrite(lua_State *L) { - return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_WRITE), 1); + return memory_registerHook(L, LUAMEMHOOK_WRITE, 1); } static int memory_registerread(lua_State *L) { - return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_READ), 1); + return memory_registerHook(L, LUAMEMHOOK_READ, 1); } static int memory_registerexec(lua_State *L) { - return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_EXEC), 1); + return memory_registerHook(L, LUAMEMHOOK_EXEC, 1); } //adelikat: table pulled from GENS. credz nitsuja! From f660f132af75ad773895e85330967e96d1138fe4 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 7 May 2023 07:37:48 -0400 Subject: [PATCH 45/69] Added GNU profiler instrumentation build option to Qt GUI. --- src/CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3318ca5d..f454e3e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,6 +96,12 @@ else(WIN32) #endif() add_definitions( -D__QT_DRIVER__ -DQT_DEPRECATED_WARNINGS ) + if ( ${GPROF_ENABLE} ) + add_definitions( -pg ) + set( GPROF_LDFLAGS -pg ) + message( STATUS "GNU Profiling Enabled" ) + endif() + if ( ${ASAN_ENABLE} ) add_definitions( -fsanitize=address -fsanitize=bounds-strict ) add_definitions( -fsanitize=undefined -fno-sanitize=vptr ) @@ -646,7 +652,8 @@ add_executable( ${APP_NAME} ${SOURCES} ../resources.qrc ${CMAKE_CURRENT_BINARY_DIR}/fceux_git_info.cpp) endif() -target_link_libraries( ${APP_NAME} ${ASAN_LDFLAGS} +target_link_libraries( ${APP_NAME} + ${ASAN_LDFLAGS} ${GPROF_LDFLAGS} ${${Qt}Widgets_LIBRARIES} ${${Qt}Help_LIBRARIES} ${${Qt}OpenGL_LIBRARIES} From e95e1d109508a1cb74fb6a54452595d774ca0576 Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Mon, 8 May 2023 13:46:43 +0400 Subject: [PATCH 46/69] Mapper 342 fixes --- src/boards/coolgirl.cpp | 60 +++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/boards/coolgirl.cpp b/src/boards/coolgirl.cpp index a3055f9d..bddc6f80 100644 --- a/src/boards/coolgirl.cpp +++ b/src/boards/coolgirl.cpp @@ -16,11 +16,11 @@ * 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 -* -* Very complicated homebrew multicart mapper with. -* The code is so obscured and weird because it's ported from Verilog CPLD source code: +* +* Very complicated homebrew multicart mapper with. +* The code is so obscured and weird because it's ported from Verilog CPLD source code: * https://github.com/ClusterM/coolgirl-famicom-multicart/blob/master/CoolGirl_mappers.vh -* +* * Range: $5000-$5FFF * * Mask: $5007 @@ -107,8 +107,8 @@ const int CFI_CHIP = 0x13; static int CHR_SIZE = 0; static uint32 WRAM_SIZE = 0; -static uint8 *WRAM = NULL; -static uint8 *SAVE_FLASH = NULL; +static uint8* WRAM = NULL; +static uint8* SAVE_FLASH = NULL; static uint8* CFI; static uint8 sram_enabled = 0; @@ -509,7 +509,7 @@ static DECLFW(COOLGIRL_Flash_Write) { SAVE_FLASH[i % SAVE_FLASH_SIZE] = 0xFF; FCEU_printf("Flash sector #%d is erased: 0x%08x - 0x%08x.\n", sector, sector_address, sector_address + FLASH_SECTOR_SIZE - 1); flash_state = 0; - } + } // write byte if ((flash_state == 4) && @@ -884,7 +884,7 @@ static DECLFW(COOLGIRL_WRITE) { if (mapper == 0b000100) { // prg_bank_a[5:1] = cpu_data_in[4:0]; - SET_BITS(chr_bank_a, "5:1", V, "4:0"); + SET_BITS(prg_bank_a, "5:1", V, "4:0"); // mirroring = { 1'b0, ~cpu_data_in[7]}; mirroring = get_bits(V, "7") ^ 1; } @@ -1462,6 +1462,7 @@ static DECLFW(COOLGIRL_WRITE) { case 0b1100: // 4'b1100: if (flags[0]) mirroring = {1'b0, cpu_data_in[6]}; // $E000, mirroring, for mapper #48 if (flags & 1) // 48 mirroring = get_bits(V, "6"); // mirroring = cpu_data_in[6]; + break; case 0b1000: // 4'b1000: irq_scanline_latch = ~cpu_data_in; // $C000, IRQ latch mmc3_irq_latch = set_bits(mmc3_irq_latch, "7:0", get_bits(V, "7:0") ^ 0b11111111); break; @@ -1811,7 +1812,7 @@ static DECLFW(COOLGIRL_WRITE) { SET_BITS(chr_bank_c, "8:1", V, "7:0"); break; // 3'b001: chr_bank_c[8:1] <= cpu_data_in[7:0]; case 0b110: SET_BITS(chr_bank_e, "8:1", V, "7:0"); break; // 3'b110: chr_bank_e[8:1] <= cpu_data_in[7:0]; - case 0b111: + case 0b111: SET_BITS(chr_bank_g, "8:1", V, "7:0"); break; // 3'b111: chr_bank_g[8:1] <= cpu_data_in[7:0]; } } @@ -1961,32 +1962,19 @@ static void COOLGIRL_CpuCounter(int a) { // Mapper #23 - VRC4 if (vrc4_irq_control & 2) // if (ENABLE_MAPPER_021_022_023_025 & ENABLE_VRC4_INTERRUPTS & (vrc4_irq_control[1])) { - // Cycle mode without prescaler is not used by any games? It's missed in fceux source code. - if (vrc4_irq_control & 4) // if (vrc4_irq_control[2]) // cycle mode + vrc4_irq_prescaler++; // vrc4_irq_prescaler = vrc4_irq_prescaler + 1'b1; // count prescaler + // if ((vrc4_irq_prescaler_counter[1] == 0 && vrc4_irq_prescaler == 114) + // || (vrc4_irq_prescaler_counter[1] == 1 && vrc4_irq_prescaler == 113)) // 114, 114, 113 + if ((!(vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 114) || ((vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 113)) { - FCEU_PrintError("Cycle IRQ mode is not supported, please report to Cluster"); - vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1; // just count IRQ value - if (vrc4_irq_value == 0) // if (carry) + vrc4_irq_prescaler = 0; // vrc4_irq_prescaler = 0; + vrc4_irq_prescaler_counter++; // vrc4_irq_prescaler_counter = vrc4_irq_prescaler_counter + 1'b1; + if (vrc4_irq_prescaler_counter == 0b11) vrc4_irq_prescaler_counter = 0; // if (vrc4_irq_prescaler_counter == 2'b11) vrc4_irq_prescaler_counter = 2'b00; + vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1; + if (vrc4_irq_value == 0) // f (carry) { - X6502_IRQBegin(FCEU_IQEXT); // vrc4_irq_out = 1; - vrc4_irq_value = vrc4_irq_latch; // vrc4_irq_value[7:0] = vrc4_irq_latch[7:0]; - } - } - else { - vrc4_irq_prescaler++; // vrc4_irq_prescaler = vrc4_irq_prescaler + 1'b1; // count prescaler - // if ((vrc4_irq_prescaler_counter[1] == 0 && vrc4_irq_prescaler == 114) - // || (vrc4_irq_prescaler_counter[1] == 1 && vrc4_irq_prescaler == 113)) // 114, 114, 113 - if ((!(vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 114) || ((vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 113)) - { - vrc4_irq_prescaler = 0; // vrc4_irq_prescaler = 0; - vrc4_irq_prescaler_counter++; // vrc4_irq_prescaler_counter = vrc4_irq_prescaler_counter + 1'b1; - if (vrc4_irq_prescaler_counter == 0b11) vrc4_irq_prescaler_counter = 0; // if (vrc4_irq_prescaler_counter == 2'b11) vrc4_irq_prescaler_counter = 2'b00; - vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1; - if (vrc4_irq_value == 0) // f (carry) - { - X6502_IRQBegin(FCEU_IQEXT); - vrc4_irq_value = vrc4_irq_latch; // irq_cpu_value[7:0] = vrc4_irq_latch[7:0]; - } + X6502_IRQBegin(FCEU_IQEXT); + vrc4_irq_value = vrc4_irq_latch; // irq_cpu_value[7:0] = vrc4_irq_latch[7:0]; } } } @@ -2252,7 +2240,7 @@ static void COOLGIRL_Restore(int version) { #define ExState(var, varname) AddExState(&var, sizeof(var), 0, varname) -void COOLGIRL_Init(CartInfo *info) { +void COOLGIRL_Init(CartInfo* info) { CHR_SIZE = info->vram_size ? info->vram_size /* NES 2.0 */ : 256 * 1024 /* UNIF, fixed */; WRAM_SIZE = info->ines2 ? (info->wram_size + info->battery_wram_size) : (32 * 1024); @@ -2263,7 +2251,7 @@ void COOLGIRL_Init(CartInfo *info) { AddExState(WRAM, 32 * 1024, 0, "SRAM"); if (info->battery) { - info->addSaveGameBuf( WRAM, 32 * 1024); + info->addSaveGameBuf(WRAM, 32 * 1024); } } @@ -2272,7 +2260,7 @@ void COOLGIRL_Init(CartInfo *info) { SAVE_FLASH = (uint8*)FCEU_gmalloc(SAVE_FLASH_SIZE); SetupCartPRGMapping(FLASH_CHIP, SAVE_FLASH, SAVE_FLASH_SIZE, 1); AddExState(SAVE_FLASH, SAVE_FLASH_SIZE, 0, "SAVF"); - info->addSaveGameBuf( SAVE_FLASH, SAVE_FLASH_SIZE ); + info->addSaveGameBuf(SAVE_FLASH, SAVE_FLASH_SIZE); } CFI = (uint8*)FCEU_gmalloc(sizeof(cfi_data) * 2); From 62b72b6141eb38d129378cfb74d10775c386c6bd Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Mon, 8 May 2023 15:17:53 +0400 Subject: [PATCH 47/69] Minor comments fixes --- src/boards/coolgirl.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/boards/coolgirl.cpp b/src/boards/coolgirl.cpp index bddc6f80..7c567af3 100644 --- a/src/boards/coolgirl.cpp +++ b/src/boards/coolgirl.cpp @@ -606,6 +606,8 @@ static DECLFW(COOLGIRL_WRITE) { if (mapper == 0b010001) prg_bank_b = ~2; // if (USE_MAPPER_009_010 && mapper == 6'b010001) prg_bank_b = 8'b11111101; if (mapper == 0b010111) map_rom_on_6000 = 1; // if (ENABLE_MAPPER_042 && (mapper == 6'b010111)) map_rom_on_6000 <= 1; if (mapper == 0b001110) prg_bank_b = 1; // if (USE_MAPPER_065 && mapper == 6'b001110) prg_bank_b = 1; + if (lockout) + FCEU_printf("Mapper: %02X/%02X\n", mapper, flags); break; } } @@ -1174,7 +1176,6 @@ static DECLFW(COOLGIRL_WRITE) { // Mapper #1 - MMC1 /* - r0 - load register flag0 - 16KB of SRAM (SOROM) */ if (mapper == 0b010000) @@ -1228,7 +1229,7 @@ static DECLFW(COOLGIRL_WRITE) { SET_BITS(chr_bank_e, "6:2", mmc1_load_register, "5:1"); break; case 0b11: // 2'b11 - // prg_bank_a[4:1] = r0[4:1]; + // prg_bank_a[4:1] = mmc1_load_register[4:1]; SET_BITS(prg_bank_a, "4:1", mmc1_load_register, "4:1"); // sram_enabled = ~mmc1_load_register[5]; sram_enabled = get_bits(mmc1_load_register, "5") ^ 1; @@ -1395,9 +1396,6 @@ static DECLFW(COOLGIRL_WRITE) { } // Mapper #112 - /* - r0[2:0] - internal register - */ if (mapper == 0b010101) { switch (get_bits(A, "14:13")) From 5847c0c364fd715968b5f79d1c465a2078c75aab Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 8 May 2023 07:50:26 -0400 Subject: [PATCH 48/69] Initial add of execution profiling debug code. This code is only included in the build if the __FCEU_PROFILER_ENABLE__ macro is set. --- src/CMakeLists.txt | 7 + src/drivers/Qt/ConsoleWindow.cpp | 3 + src/fceu.cpp | 2 + src/profiler.cpp | 257 +++++++++++++++++++++++++++++++ src/profiler.h | 220 ++++++++++++++++++++++++++ src/utils/mutex.cpp | 9 ++ src/utils/mutex.h | 1 + 7 files changed, 499 insertions(+) create mode 100644 src/profiler.cpp create mode 100644 src/profiler.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f454e3e0..1a451904 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,12 @@ if ( ${QHELP} ) add_definitions( -D_USE_QHELP ) endif() +if ( ${FCEU_PROFILER_ENABLE} ) + message( STATUS "FCEU Profiler Enabled") + add_definitions( -D__FCEU_PROFILER_ENABLE__ ) +endif() + + if ( ${QT6} ) find_package( Qt6 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets ${QtHelpModule}) add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} ) @@ -309,6 +315,7 @@ set(SRC_CORE ${CMAKE_CURRENT_SOURCE_DIR}/nsf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/oldmovie.cpp ${CMAKE_CURRENT_SOURCE_DIR}/palette.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/profiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ppu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sound.cpp ${CMAKE_CURRENT_SOURCE_DIR}/state.cpp diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 20a91243..79add172 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -54,6 +54,7 @@ #include "../../movie.h" #include "../../wave.h" #include "../../state.h" +#include "../../profiler.h" #include "../../version.h" #include "common/os_utils.h" @@ -4484,6 +4485,7 @@ int consoleWin_t::getPeriodicInterval(void) void consoleWin_t::transferVideoBuffer(void) { + FCEU_PROFILE_FUNC(prof, "VideoXfer"); if ( nes_shm->blitUpdated ) { nes_shm->blitUpdated = 0; @@ -4525,6 +4527,7 @@ void consoleWin_t::emuFrameFinish(void) void consoleWin_t::updatePeriodic(void) { + FCEU_PROFILE_FUNC(prof, "updatePeriodic"); static bool eventProcessingInProg = false; if ( eventProcessingInProg ) diff --git a/src/fceu.cpp b/src/fceu.cpp index 90ba9985..37d61a53 100644 --- a/src/fceu.cpp +++ b/src/fceu.cpp @@ -36,6 +36,7 @@ #include "unif.h" #include "cheat.h" #include "palette.h" +#include "profiler.h" #include "state.h" #include "movie.h" #include "video.h" @@ -734,6 +735,7 @@ extern unsigned int frameAdvHoldTimer; ///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) { + FCEU_PROFILE_FUNC(prof, "Emulate Single Frame"); //skip initiates frame skip if 1, or frame skip and sound skip if 2 FCEU_MAYBE_UNUSED int r; int ssize; diff --git a/src/profiler.cpp b/src/profiler.cpp new file mode 100644 index 00000000..c7582eae --- /dev/null +++ b/src/profiler.cpp @@ -0,0 +1,257 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +// profiler.cpp +// +#ifdef __FCEU_PROFILER_ENABLE__ + +#include + +#include "utils/mutex.h" +#include "profiler.h" + +namespace FCEU +{ +static thread_local profilerFuncMap threadProfileMap; + +class profilerManager +{ + public: + profilerManager(void) + { + printf("profilerManager Constructor\n"); + if (pLog == nullptr) + { + pLog = stdout; + } + } + + ~profilerManager(void) + { + printf("profilerManager Destructor\n"); + { + autoScopedLock aLock(threadListMtx); + threadList.clear(); + } + + if (pLog && (pLog != stdout)) + { + fclose(pLog); pLog = nullptr; + } + } + + int addThreadProfiler( profilerFuncMap *m ) + { + autoScopedLock aLock(threadListMtx); + threadList.push_back(m); + return 0; + } + + int removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy = false ) + { + int result = -1; + autoScopedLock aLock(threadListMtx); + + for (auto it = threadList.begin(); it != threadList.end(); it++) + { + if (*it == m ) + { + threadList.erase(it); + if (shouldDestroy) + { + delete m; + } + result = 0; + break; + } + } + return result; + } + + static FILE *pLog; + private: + + mutex threadListMtx; + std::list threadList; + +}; +FILE *profilerManager::pLog = nullptr; + +static profilerManager pMgr; + +//------------------------------------------------------------------------- +//---- Time Stamp Record +//------------------------------------------------------------------------- +void timeStampRecord::readNew(void) +{ +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + clock_gettime( CLOCK_REALTIME, &ts ); +#else + ts = 0; +#endif +} + +//------------------------------------------------------------------------- +//---- Function Profile Record +//------------------------------------------------------------------------- +funcProfileRecord::funcProfileRecord(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral) + + : fileLineNum(fileLineNumber), fileName(fileNameStringLiteral), + funcName(funcNameStringLiteral), comment(commentStringLiteral) +{ + min.zero(); + max.zero(); + sum.zero(); + numCalls = 0; + recursionCount = 0; +} +//------------------------------------------------------------------------- +void funcProfileRecord::reset(void) +{ + min.zero(); + max.zero(); + sum.zero(); + numCalls = 0; +} +//------------------------------------------------------------------------- +double funcProfileRecord::average(void) +{ + double avg = 0.0; + + if (numCalls) + { + avg = sum.toSeconds() / static_cast(numCalls); + } + return avg; +} +//------------------------------------------------------------------------- +//---- Profile Scoped Function Class +//------------------------------------------------------------------------- +profileFuncScoped::profileFuncScoped(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral) +{ + rec = nullptr; + + //if (threadProfileMap == nullptr) + //{ + // threadProfileMap = new profilerFuncMap(); + //} + + rec = threadProfileMap.findRecord( fileNameStringLiteral, fileLineNumber, + funcNameStringLiteral, commentStringLiteral, true); + + if (rec) + { + threadProfileMap.pushStack(rec); + start.readNew(); + rec->numCalls++; + rec->recursionCount++; + } +} +//------------------------------------------------------------------------- +profileFuncScoped::~profileFuncScoped(void) +{ + if (rec) + { + timeStampRecord ts, dt; + ts.readNew(); + dt = ts - start; + + rec->sum += dt; + if (dt < rec->min) rec->min = dt; + if (dt > rec->max) rec->max = dt; + + rec->recursionCount--; + + //printf("%s: %u %f %f %f %f\n", rec->funcName, rec->numCalls, dt.toSeconds(), rec->average(), rec->min.toSeconds(), rec->max.toSeconds()); + threadProfileMap.popStack(rec); + } +} +//------------------------------------------------------------------------- +//---- Profile Function Record Map +//------------------------------------------------------------------------- +profilerFuncMap::profilerFuncMap(void) +{ + printf("profilerFuncMap Constructor: %p\n", this); + pMgr.addThreadProfiler(this); +} +//------------------------------------------------------------------------- +profilerFuncMap::~profilerFuncMap(void) +{ + printf("profilerFuncMap Destructor: %p\n", this); + pMgr.removeThreadProfiler(this); + + for (auto it = _map.begin(); it != _map.end(); it++) + { + delete it->second; + } + _map.clear(); +} +//------------------------------------------------------------------------- +void profilerFuncMap::pushStack(funcProfileRecord *rec) +{ + stack.push_back(rec); +} +//------------------------------------------------------------------------- +void profilerFuncMap::popStack(funcProfileRecord *rec) +{ + stack.pop_back(); +} +//------------------------------------------------------------------------- +funcProfileRecord *profilerFuncMap::findRecord(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral, + bool create) +{ + char lineString[64]; + funcProfileRecord *rec = nullptr; + + sprintf( lineString, ":%i", fileLineNumber); + + std::string fname(fileNameStringLiteral); + + fname.append( lineString ); + + auto it = _map.find(fname); + + if (it != _map.end()) + { + rec = it->second; + } + else if (create) + { + fprintf( pMgr.pLog, "Creating Function Profile Record: %s %s\n", fname.c_str(), funcNameStringLiteral); + + rec = new funcProfileRecord( fileNameStringLiteral, fileLineNumber, + funcNameStringLiteral, commentStringLiteral); + + _map[fname] = rec; + } + return rec; +} +//------------------------------------------------------------------------- +// +} +#endif // __FCEU_PROFILER_ENABLE__ diff --git a/src/profiler.h b/src/profiler.h new file mode 100644 index 00000000..5b41e0db --- /dev/null +++ b/src/profiler.h @@ -0,0 +1,220 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +// profiler.h + +#pragma once + +/* + * This module is intended for debug use only. This allows for high precision timing of function + * execution. This functionality is not included in the build unless __FCEU_PROFILER_ENABLE__ + * is defined. To check timing on a particular function, add FCEU_PROFILE_FUNC macro to the top + * of the function body in the following manner. + * FCEU_PROFILE_FUNC(prof, "String Literal comment, whatever I want it to say") + * When __FCEU_PROFILER_ENABLE__ is not defined, the FCEU_PROFILE_FUNC macro evaluates to nothing + * so it won't break the regular build by having it used in code. + */ +#ifdef __FCEU_PROFILER_ENABLE__ + +#include +#include +#include +#include +#include + + +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) +#include +#endif + +namespace FCEU +{ + struct timeStampRecord + { +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + struct timespec ts; + + timeStampRecord& operator = (const timeStampRecord& in) + { + ts = in.ts; + return *this; + } + + timeStampRecord& operator += (const timeStampRecord& op) + { + ts.tv_sec += op.ts.tv_sec; + ts.tv_nsec += op.ts.tv_nsec; + + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + return *this; + } + + timeStampRecord operator + (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts.tv_sec = ts.tv_sec + op.ts.tv_sec; + res.ts.tv_nsec = ts.tv_nsec + op.ts.tv_nsec; + + if (res.ts.tv_nsec >= 1000000000) + { + res.ts.tv_nsec -= 1000000000; + res.ts.tv_sec++; + } + return res; + } + + timeStampRecord operator - (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts.tv_sec = ts.tv_sec - op.ts.tv_sec; + res.ts.tv_nsec = ts.tv_nsec - op.ts.tv_nsec; + + if (res.ts.tv_nsec < 0) + { + res.ts.tv_nsec += 1000000000; + res.ts.tv_sec--; + } + return res; + } + + bool operator > (const timeStampRecord& op) + { + bool res = false; + if (ts.tv_sec == op.ts.tv_sec) + { + res = (ts.tv_nsec > op.ts.tv_nsec); + } + else if (ts.tv_sec > op.ts.tv_sec) + { + res = (ts.tv_sec > op.ts.tv_sec); + } + return res; + } + + bool operator < (const timeStampRecord& op) + { + bool res = false; + if (ts.tv_sec == op.ts.tv_sec) + { + res = (ts.tv_nsec < op.ts.tv_nsec); + } + else if (ts.tv_sec > op.ts.tv_sec) + { + res = (ts.tv_sec < op.ts.tv_sec); + } + return res; + } + + void zero(void) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + + double toSeconds(void) + { + double sec = static_cast(ts.tv_sec) + ( static_cast(ts.tv_nsec) * 1.0e-9 ); + return sec; + } +#else // WIN32 + uint64_t ts; +#endif + + void readNew(void); + + //timeStampRecord& operator = (timeStampRecord&); + }; + + struct funcProfileRecord + { + const int fileLineNum; + const char *fileName; + const char *funcName; + const char *comment; + + timeStampRecord min; + timeStampRecord max; + timeStampRecord sum; + unsigned int numCalls; + unsigned int recursionCount; + + funcProfileRecord(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral); + + void reset(void); + + double average(void); + }; + + struct profileFuncScoped + { + funcProfileRecord *rec; + timeStampRecord start; + + profileFuncScoped(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral); + + ~profileFuncScoped(void); + }; + + class profilerFuncMap + { + public: + profilerFuncMap(); + ~profilerFuncMap(); + + funcProfileRecord *findRecord(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral, + bool create = false); + + void pushStack(funcProfileRecord *rec); + void popStack(funcProfileRecord *rec); + private: + std::map _map; + + std::vector stack; + }; +} + +#if defined(__PRETTY_FUNCTION__) +#define __FCEU_PROFILE_FUNC_NAME__ __PRETTY_FUNCTION__ +#else +#define __FCEU_PROFILE_FUNC_NAME__ __func__ +#endif + +#define FCEU_PROFILE_FUNC(id, comment) FCEU::profileFuncScoped id( __FILE__, __LINE__, __FCEU_PROFILE_FUNC_NAME__, comment ) + +#else // __FCEU_PROFILER_ENABLE__ not defined + +#define FCEU_PROFILE_FUNC(id, comment) + +#endif // __FCEU_PROFILER_ENABLE__ + diff --git a/src/utils/mutex.cpp b/src/utils/mutex.cpp index 0556ea07..60376a40 100644 --- a/src/utils/mutex.cpp +++ b/src/utils/mutex.cpp @@ -60,6 +60,15 @@ autoScopedLock::autoScopedLock( mutex *mtx ) } } +autoScopedLock::autoScopedLock( mutex &mtx ) +{ + m = &mtx; + if (m) + { + m->lock(); + } +} + autoScopedLock::~autoScopedLock(void) { if (m) diff --git a/src/utils/mutex.h b/src/utils/mutex.h index 721bf241..795e083c 100644 --- a/src/utils/mutex.h +++ b/src/utils/mutex.h @@ -32,6 +32,7 @@ namespace FCEU { public: autoScopedLock( mutex *mtx ); + autoScopedLock( mutex &mtx ); ~autoScopedLock(void); private: From 3d1062c9fe13b3864c33dc6348a2612817769b9d Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Mon, 8 May 2023 19:42:58 +0400 Subject: [PATCH 49/69] Removed debug code from mapper 342 --- src/boards/coolgirl.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/boards/coolgirl.cpp b/src/boards/coolgirl.cpp index 7c567af3..3e24994f 100644 --- a/src/boards/coolgirl.cpp +++ b/src/boards/coolgirl.cpp @@ -507,7 +507,6 @@ static DECLFW(COOLGIRL_Flash_Write) { uint32 sector_address = sector * FLASH_SECTOR_SIZE; for (uint32 i = sector_address; i < sector_address + FLASH_SECTOR_SIZE; i++) SAVE_FLASH[i % SAVE_FLASH_SIZE] = 0xFF; - FCEU_printf("Flash sector #%d is erased: 0x%08x - 0x%08x.\n", sector, sector_address, sector_address + FLASH_SECTOR_SIZE - 1); flash_state = 0; } @@ -555,7 +554,6 @@ static DECLFW(COOLGIRL_WRITE) { if (A >= 0x5000 && A < 0x6000 && !lockout) { - //FCEU_printf("Write: %02x => %04x\n", V, A); switch (A & 7) { case 0: @@ -571,7 +569,6 @@ static DECLFW(COOLGIRL_WRITE) { // {chr_mask[18], prg_mask[20:14]} = cpu_data_in[7:0]; SET_BITS(chr_mask, "18", V, "7"); SET_BITS(prg_mask, "20:14", V, "6:0"); - //FCEU_printf("REG_prg_mask: %02x\n", REG_prg_mask); break; case 3: // {prg_mode[2:0], chr_bank_a[7:3]} = cpu_data_in[7:0]; @@ -606,8 +603,6 @@ static DECLFW(COOLGIRL_WRITE) { if (mapper == 0b010001) prg_bank_b = ~2; // if (USE_MAPPER_009_010 && mapper == 6'b010001) prg_bank_b = 8'b11111101; if (mapper == 0b010111) map_rom_on_6000 = 1; // if (ENABLE_MAPPER_042 && (mapper == 6'b010111)) map_rom_on_6000 <= 1; if (mapper == 0b001110) prg_bank_b = 1; // if (USE_MAPPER_065 && mapper == 6'b001110) prg_bank_b = 1; - if (lockout) - FCEU_printf("Mapper: %02X/%02X\n", mapper, flags); break; } } From 157b8531a2e450f2359df27b0c2945ff944a42d9 Mon Sep 17 00:00:00 2001 From: harry Date: Tue, 9 May 2023 08:26:28 -0400 Subject: [PATCH 50/69] Added TSC timing to suppliment lower resolution time measurements. --- src/profiler.cpp | 179 ++++++++++++++++++++++++++++++---------------- src/profiler.h | 98 ++++++++++++++++++++++++- src/utils/mutex.h | 1 + 3 files changed, 215 insertions(+), 63 deletions(-) diff --git a/src/profiler.cpp b/src/profiler.cpp index c7582eae..53ccb97e 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -23,74 +23,21 @@ #include +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) +#include +#endif + #include "utils/mutex.h" #include "profiler.h" +#if defined(WIN32) +#include +#endif + namespace FCEU { static thread_local profilerFuncMap threadProfileMap; -class profilerManager -{ - public: - profilerManager(void) - { - printf("profilerManager Constructor\n"); - if (pLog == nullptr) - { - pLog = stdout; - } - } - - ~profilerManager(void) - { - printf("profilerManager Destructor\n"); - { - autoScopedLock aLock(threadListMtx); - threadList.clear(); - } - - if (pLog && (pLog != stdout)) - { - fclose(pLog); pLog = nullptr; - } - } - - int addThreadProfiler( profilerFuncMap *m ) - { - autoScopedLock aLock(threadListMtx); - threadList.push_back(m); - return 0; - } - - int removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy = false ) - { - int result = -1; - autoScopedLock aLock(threadListMtx); - - for (auto it = threadList.begin(); it != threadList.end(); it++) - { - if (*it == m ) - { - threadList.erase(it); - if (shouldDestroy) - { - delete m; - } - result = 0; - break; - } - } - return result; - } - - static FILE *pLog; - private: - - mutex threadListMtx; - std::list threadList; - -}; FILE *profilerManager::pLog = nullptr; static profilerManager pMgr; @@ -98,13 +45,68 @@ static profilerManager pMgr; //------------------------------------------------------------------------- //---- Time Stamp Record //------------------------------------------------------------------------- +#if defined(WIN32) +uint64_t timeStampRecord::qpcFreq = 0; +#include +#pragma intrinsic(__rdtsc) +#else +#include +#endif +uint64_t timeStampRecord::tscFreq = 0; + +static uint64_t rdtsc() +{ + return __rdtsc(); +} + void timeStampRecord::readNew(void) { #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) clock_gettime( CLOCK_REALTIME, &ts ); +#elif defined(WIN32) + QueryPerformanceCounter((LARGE_INTEGER*)&ts); #else ts = 0; #endif + tsc = rdtsc(); +} + +static void calibrateTSC(void) +{ + constexpr int numSamples = 1; + timeStampRecord t1, t2, td; + uint64_t td_sum = 0; + double td_avg; + +#if defined(WIN32) + if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0) + { + printf("QueryPerformanceFrequency FAILED!\n"); + } +#endif + printf("Running TSC Calibration: %i sec...\n", numSamples); + + for (int i=0; i(td_sum); + + timeStampRecord::tscFreq = static_cast( td_avg / td.toSeconds() ); + + printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(), + static_cast(td.tsc), static_cast(timeStampRecord::tscFreq) * 1.0e-6 ); + } } //------------------------------------------------------------------------- @@ -252,6 +254,61 @@ funcProfileRecord *profilerFuncMap::findRecord(const char *fileNameStringLiteral return rec; } //------------------------------------------------------------------------- +//----- profilerManager class +//------------------------------------------------------------------------- +profilerManager::profilerManager(void) +{ + calibrateTSC(); + + printf("profilerManager Constructor\n"); + if (pLog == nullptr) + { + pLog = stdout; + } +} + +profilerManager::~profilerManager(void) +{ + printf("profilerManager Destructor\n"); + { + autoScopedLock aLock(threadListMtx); + threadList.clear(); + } + + if (pLog && (pLog != stdout)) + { + fclose(pLog); pLog = nullptr; + } +} + +int profilerManager::addThreadProfiler( profilerFuncMap *m ) +{ + autoScopedLock aLock(threadListMtx); + threadList.push_back(m); + return 0; +} + +int profilerManager::removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy ) +{ + int result = -1; + autoScopedLock aLock(threadListMtx); + + for (auto it = threadList.begin(); it != threadList.end(); it++) + { + if (*it == m ) + { + threadList.erase(it); + if (shouldDestroy) + { + delete m; + } + result = 0; + break; + } + } + return result; +} +//------------------------------------------------------------------------- // } #endif // __FCEU_PROFILER_ENABLE__ diff --git a/src/profiler.h b/src/profiler.h index 5b41e0db..50dca879 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -43,16 +43,27 @@ #include #endif +#include "utils/mutex.h" + namespace FCEU { struct timeStampRecord { #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) struct timespec ts; + uint64_t tsc; + + timeStampRecord(void) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + tsc = 0; + } timeStampRecord& operator = (const timeStampRecord& in) { ts = in.ts; + tsc = in.tsc; return *this; } @@ -66,6 +77,7 @@ namespace FCEU ts.tv_nsec -= 1000000000; ts.tv_sec++; } + tsc += op.tsc; return *this; } @@ -81,6 +93,7 @@ namespace FCEU res.ts.tv_nsec -= 1000000000; res.ts.tv_sec++; } + res.tsc = tsc + op.tsc; return res; } @@ -96,6 +109,8 @@ namespace FCEU res.ts.tv_nsec += 1000000000; res.ts.tv_sec--; } + res.tsc = tsc - op.tsc; + return res; } @@ -131,6 +146,7 @@ namespace FCEU { ts.tv_sec = 0; ts.tv_nsec = 0; + tsc = 0; } double toSeconds(void) @@ -140,11 +156,73 @@ namespace FCEU } #else // WIN32 uint64_t ts; + uint64_t tsc; + + timeStampRecord(void) + { + ts = 0; + tsc = 0; + } + + timeStampRecord& operator = (const timeStampRecord& in) + { + ts = in.ts; + tsc = in.tsc; + return *this; + } + + timeStampRecord& operator += (const timeStampRecord& op) + { + ts += op.ts; + tsc += op.tsc; + return *this; + } + + timeStampRecord operator + (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts = ts + op.ts; + res.tsc = tsc + op.tsc; + return res; + } + + timeStampRecord operator - (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts = ts - op.ts; + res.tsc = tsc - op.tsc; + + return res; + } + + bool operator > (const timeStampRecord& op) + { + return ts > op.ts; + } + + bool operator < (const timeStampRecord& op) + { + return ts < op.ts; + } + + void zero(void) + { + ts = 0; + tsc = 0; + } + + double toSeconds(void) + { + double sec = static_cast(ts) / static_cast(qpcFreq); + return sec; + } + static uint64_t qpcFreq; #endif + static uint64_t tscFreq; void readNew(void); - - //timeStampRecord& operator = (timeStampRecord&); }; struct funcProfileRecord @@ -202,6 +280,22 @@ namespace FCEU std::vector stack; }; + + class profilerManager + { + public: + profilerManager(void); + ~profilerManager(void); + + int addThreadProfiler( profilerFuncMap *m ); + int removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy = false ); + + static FILE *pLog; + private: + + mutex threadListMtx; + std::list threadList; + }; } #if defined(__PRETTY_FUNCTION__) diff --git a/src/utils/mutex.h b/src/utils/mutex.h index 795e083c..e7a2968d 100644 --- a/src/utils/mutex.h +++ b/src/utils/mutex.h @@ -1,4 +1,5 @@ // mutex.h +#pragma once #ifdef __QT_DRIVER__ #include From d0d822447d3438c24db691af78d6c1e78352d626 Mon Sep 17 00:00:00 2001 From: harry Date: Tue, 9 May 2023 18:28:49 -0400 Subject: [PATCH 51/69] Preparing to merge debug profiler in to main dev line. --- src/drivers/Qt/MsgLogViewer.cpp | 4 ++++ src/profiler.cpp | 13 +++++++------ vc/vc14_fceux.vcxproj | 2 ++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/drivers/Qt/MsgLogViewer.cpp b/src/drivers/Qt/MsgLogViewer.cpp index a665d428..7c4c4db4 100644 --- a/src/drivers/Qt/MsgLogViewer.cpp +++ b/src/drivers/Qt/MsgLogViewer.cpp @@ -230,10 +230,14 @@ MsgLogViewDialog_t::MsgLogViewDialog_t(QWidget *parent) updateTimer->start(500); // 2hz + FCEU_WRAPPER_LOCK(); + msgLog.loadTextViewer(txtView); totalLines = msgLog.getTotalLineCount(); + FCEU_WRAPPER_UNLOCK(); + txtView->moveCursor(QTextCursor::End); restoreGeometry(settings.value("MsgLogWindow/geometry").toByteArray()); diff --git a/src/profiler.cpp b/src/profiler.cpp index 53ccb97e..fba9d559 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -28,6 +28,7 @@ #endif #include "utils/mutex.h" +#include "fceu.h" #include "profiler.h" #if defined(WIN32) @@ -84,7 +85,7 @@ static void calibrateTSC(void) printf("QueryPerformanceFrequency FAILED!\n"); } #endif - printf("Running TSC Calibration: %i sec...\n", numSamples); + FCEU_printf("Running TSC Calibration: %i sec...\n", numSamples); for (int i=0; i( td_avg / td.toSeconds() ); - printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(), + FCEU_printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(), static_cast(td.tsc), static_cast(timeStampRecord::tscFreq) * 1.0e-6 ); } } @@ -195,13 +196,13 @@ profileFuncScoped::~profileFuncScoped(void) //------------------------------------------------------------------------- profilerFuncMap::profilerFuncMap(void) { - printf("profilerFuncMap Constructor: %p\n", this); + //printf("profilerFuncMap Constructor: %p\n", this); pMgr.addThreadProfiler(this); } //------------------------------------------------------------------------- profilerFuncMap::~profilerFuncMap(void) { - printf("profilerFuncMap Destructor: %p\n", this); + //printf("profilerFuncMap Destructor: %p\n", this); pMgr.removeThreadProfiler(this); for (auto it = _map.begin(); it != _map.end(); it++) @@ -260,7 +261,7 @@ profilerManager::profilerManager(void) { calibrateTSC(); - printf("profilerManager Constructor\n"); + //printf("profilerManager Constructor\n"); if (pLog == nullptr) { pLog = stdout; @@ -269,7 +270,7 @@ profilerManager::profilerManager(void) profilerManager::~profilerManager(void) { - printf("profilerManager Destructor\n"); + //printf("profilerManager Destructor\n"); { autoScopedLock aLock(threadListMtx); threadList.clear(); diff --git a/vc/vc14_fceux.vcxproj b/vc/vc14_fceux.vcxproj index f5197ad8..d37cff0f 100644 --- a/vc/vc14_fceux.vcxproj +++ b/vc/vc14_fceux.vcxproj @@ -1007,6 +1007,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + @@ -1136,6 +1137,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + From a9f4176f2b1d2a0b2ea3e5256f68e8a14afddf79 Mon Sep 17 00:00:00 2001 From: harry Date: Tue, 9 May 2023 22:53:40 -0400 Subject: [PATCH 52/69] Minor profiler update --- src/profiler.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++--- src/profiler.h | 8 +++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/profiler.cpp b/src/profiler.cpp index fba9d559..263b07bd 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -198,6 +198,8 @@ profilerFuncMap::profilerFuncMap(void) { //printf("profilerFuncMap Constructor: %p\n", this); pMgr.addThreadProfiler(this); + + _map_it = _map.begin(); } //------------------------------------------------------------------------- profilerFuncMap::~profilerFuncMap(void) @@ -205,11 +207,15 @@ profilerFuncMap::~profilerFuncMap(void) //printf("profilerFuncMap Destructor: %p\n", this); pMgr.removeThreadProfiler(this); - for (auto it = _map.begin(); it != _map.end(); it++) { - delete it->second; + autoScopedLock aLock(_mapMtx); + + for (auto it = _map.begin(); it != _map.end(); it++) + { + delete it->second; + } + _map.clear(); } - _map.clear(); } //------------------------------------------------------------------------- void profilerFuncMap::pushStack(funcProfileRecord *rec) @@ -228,6 +234,7 @@ funcProfileRecord *profilerFuncMap::findRecord(const char *fileNameStringLiteral const char *commentStringLiteral, bool create) { + autoScopedLock aLock(_mapMtx); char lineString[64]; funcProfileRecord *rec = nullptr; @@ -255,8 +262,45 @@ funcProfileRecord *profilerFuncMap::findRecord(const char *fileNameStringLiteral return rec; } //------------------------------------------------------------------------- +funcProfileRecord *profilerFuncMap::iterateBegin(void) +{ + autoScopedLock aLock(_mapMtx); + funcProfileRecord *rec = nullptr; + + _map_it = _map.begin(); + + if (_map_it != _map.end()) + { + rec = _map_it->second; + } + return rec; +} +//------------------------------------------------------------------------- +funcProfileRecord *profilerFuncMap::iterateNext(void) +{ + autoScopedLock aLock(_mapMtx); + funcProfileRecord *rec = nullptr; + + if (_map_it != _map.end()) + { + _map_it++; + } + if (_map_it != _map.end()) + { + rec = _map_it->second; + } + return rec; +} +//------------------------------------------------------------------------- //----- profilerManager class //------------------------------------------------------------------------- +profilerManager* profilerManager::instance = nullptr; + +profilerManager* profilerManager::getInstance(void) +{ + return instance; +} +//------------------------------------------------------------------------- profilerManager::profilerManager(void) { calibrateTSC(); @@ -266,6 +310,11 @@ profilerManager::profilerManager(void) { pLog = stdout; } + + if (instance == nullptr) + { + instance = this; + } } profilerManager::~profilerManager(void) @@ -280,6 +329,10 @@ profilerManager::~profilerManager(void) { fclose(pLog); pLog = nullptr; } + if (instance == this) + { + instance = nullptr; + } } int profilerManager::addThreadProfiler( profilerFuncMap *m ) diff --git a/src/profiler.h b/src/profiler.h index 50dca879..ceef4b15 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -273,10 +273,15 @@ namespace FCEU const char *commentStringLiteral, bool create = false); + funcProfileRecord *iterateBegin(void); + funcProfileRecord *iterateNext(void); + void pushStack(funcProfileRecord *rec); void popStack(funcProfileRecord *rec); private: + mutex _mapMtx; std::map _map; + std::map::iterator _map_it; std::vector stack; }; @@ -291,10 +296,13 @@ namespace FCEU int removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy = false ); static FILE *pLog; + + static profilerManager *getInstance(); private: mutex threadListMtx; std::list threadList; + static profilerManager *instance; }; } From 3d6cf7a730e92fa87a408e606472bcef848a4a25 Mon Sep 17 00:00:00 2001 From: harry Date: Tue, 9 May 2023 22:57:05 -0400 Subject: [PATCH 53/69] Added a third video driver option for Qt GUI that uses a QPainter object to render QImages to the viewport. Consolidated video driver interface into a base class so generic object pointer can be used throughout the code to control the viewport. --- src/CMakeLists.txt | 1 + src/drivers/Qt/ConsoleVideoConf.cpp | 86 ++-- src/drivers/Qt/ConsoleViewerGL.h | 13 +- src/drivers/Qt/ConsoleViewerInterface.h | 51 +++ src/drivers/Qt/ConsoleViewerQWidget.cpp | 509 ++++++++++++++++++++++++ src/drivers/Qt/ConsoleViewerQWidget.h | 92 +++++ src/drivers/Qt/ConsoleViewerSDL.h | 12 +- src/drivers/Qt/ConsoleWindow.cpp | 457 ++++++++++++++------- src/drivers/Qt/ConsoleWindow.h | 7 +- 9 files changed, 1012 insertions(+), 216 deletions(-) create mode 100644 src/drivers/Qt/ConsoleViewerInterface.h create mode 100644 src/drivers/Qt/ConsoleViewerQWidget.cpp create mode 100644 src/drivers/Qt/ConsoleViewerQWidget.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a451904..387f3879 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -549,6 +549,7 @@ set(SRC_DRIVERS_SDL ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleWindow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerGL.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerSDL.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerQWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/InputConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/GamePadConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/FamilyKeyboard.cpp diff --git a/src/drivers/Qt/ConsoleVideoConf.cpp b/src/drivers/Qt/ConsoleVideoConf.cpp index 0c45f2f3..8b478cb8 100644 --- a/src/drivers/Qt/ConsoleVideoConf.cpp +++ b/src/drivers/Qt/ConsoleVideoConf.cpp @@ -85,8 +85,9 @@ ConsoleVideoConfDialog_t::ConsoleVideoConfDialog_t(QWidget *parent) driverSelect = new QComboBox(); - driverSelect->addItem( tr("OpenGL"), 0 ); - driverSelect->addItem( tr("SDL"), 1 ); + driverSelect->addItem( tr("OpenGL"), ConsoleViewerBase::VIDEO_DRIVER_OPENGL ); + driverSelect->addItem( tr("SDL"), ConsoleViewerBase::VIDEO_DRIVER_SDL ); + driverSelect->addItem( tr("QPainter"), ConsoleViewerBase::VIDEO_DRIVER_QPAINTER ); hbox1 = new QHBoxLayout(); @@ -232,15 +233,10 @@ ConsoleVideoConfDialog_t::ConsoleVideoConfDialog_t(QWidget *parent) if ( consoleWindow ) { - if ( consoleWindow->viewport_GL ) + if ( consoleWindow->viewport_Interface ) { - autoScaleCbx->setChecked( consoleWindow->viewport_GL->getAutoScaleOpt() ); - aspectCbx->setChecked( consoleWindow->viewport_GL->getForceAspectOpt() ); - } - else if ( consoleWindow->viewport_SDL ) - { - autoScaleCbx->setChecked( consoleWindow->viewport_SDL->getAutoScaleOpt() ); - aspectCbx->setChecked( consoleWindow->viewport_SDL->getForceAspectOpt() ); + autoScaleCbx->setChecked( consoleWindow->viewport_Interface->getAutoScaleOpt() ); + aspectCbx->setChecked( consoleWindow->viewport_Interface->getForceAspectOpt() ); } } @@ -305,15 +301,10 @@ ConsoleVideoConfDialog_t::ConsoleVideoConfDialog_t(QWidget *parent) if ( consoleWindow ) { - if ( consoleWindow->viewport_GL ) + if ( consoleWindow->viewport_Interface ) { - xScaleBox->setValue( consoleWindow->viewport_GL->getScaleX() ); - yScaleBox->setValue( consoleWindow->viewport_GL->getScaleY() ); - } - else if ( consoleWindow->viewport_SDL ) - { - xScaleBox->setValue( consoleWindow->viewport_SDL->getScaleX() ); - yScaleBox->setValue( consoleWindow->viewport_SDL->getScaleY() ); + xScaleBox->setValue( consoleWindow->viewport_Interface->getScaleX() ); + yScaleBox->setValue( consoleWindow->viewport_Interface->getScaleY() ); } } @@ -594,13 +585,9 @@ void ConsoleVideoConfDialog_t::updateReadouts(void) w = consoleWindow->size(); - if ( consoleWindow->viewport_GL ) + if ( consoleWindow->viewport_Interface ) { - v = consoleWindow->viewport_GL->size(); - } - else if ( consoleWindow->viewport_SDL ) - { - v = consoleWindow->viewport_SDL->size(); + v = consoleWindow->viewport_Interface->size(); } sprintf( stmp, "%i x %i ", w.width(), w.height() ); @@ -726,13 +713,9 @@ void ConsoleVideoConfDialog_t::openGL_linearFilterChanged( int value ) if ( consoleWindow != NULL ) { - if ( consoleWindow->viewport_GL ) + if ( consoleWindow->viewport_Interface ) { - consoleWindow->viewport_GL->setLinearFilterEnable( opt ); - } - if ( consoleWindow->viewport_SDL ) - { - consoleWindow->viewport_SDL->setLinearFilterEnable( opt ); + consoleWindow->viewport_Interface->setLinearFilterEnable( opt ); } } } @@ -745,13 +728,9 @@ void ConsoleVideoConfDialog_t::autoScaleChanged( int value ) if ( consoleWindow != NULL ) { - if ( consoleWindow->viewport_GL ) + if ( consoleWindow->viewport_Interface ) { - consoleWindow->viewport_GL->setAutoScaleOpt( opt ); - } - if ( consoleWindow->viewport_SDL ) - { - consoleWindow->viewport_SDL->setAutoScaleOpt( opt ); + consoleWindow->viewport_Interface->setAutoScaleOpt( opt ); } } } @@ -832,9 +811,9 @@ void ConsoleVideoConfDialog_t::vsync_changed( int value ) //consoleWindow->viewport_GL->setVsyncEnable( opt ); consoleWindow->loadVideoDriver( 0, true ); } - if ( consoleWindow->viewport_SDL ) + else if ( consoleWindow->viewport_Interface ) { - consoleWindow->viewport_SDL->setVsyncEnable( opt ); + consoleWindow->viewport_Interface->setVsyncEnable( opt ); } } } @@ -1094,15 +1073,10 @@ QSize ConsoleVideoConfDialog_t::calcNewScreenSize(void) w = consoleWindow->size(); - if ( consoleWindow->viewport_GL ) + if ( consoleWindow->viewport_Interface ) { - v = consoleWindow->viewport_GL->size(); - aspectRatio = consoleWindow->viewport_GL->getAspectRatio(); - } - else if ( consoleWindow->viewport_SDL ) - { - v = consoleWindow->viewport_SDL->size(); - aspectRatio = consoleWindow->viewport_SDL->getAspectRatio(); + v = consoleWindow->viewport_Interface->size(); + aspectRatio = consoleWindow->viewport_Interface->getAspectRatio(); } dw = w.width() - v.width(); @@ -1167,21 +1141,13 @@ void ConsoleVideoConfDialog_t::applyChanges( void ) g_config->setOption("SDL.WinSizeX", s.width() ); g_config->setOption("SDL.WinSizeY", s.height() ); - if ( consoleWindow->viewport_GL ) + if ( consoleWindow->viewport_Interface ) { - consoleWindow->viewport_GL->setLinearFilterEnable( gl_LF_chkBox->isChecked() ); - consoleWindow->viewport_GL->setForceAspectOpt( aspectCbx->isChecked() ); - consoleWindow->viewport_GL->setAutoScaleOpt( autoScaleCbx->isChecked() ); - consoleWindow->viewport_GL->setScaleXY( xscale, yscale ); - consoleWindow->viewport_GL->reset(); - } - if ( consoleWindow->viewport_SDL ) - { - consoleWindow->viewport_SDL->setLinearFilterEnable( gl_LF_chkBox->isChecked() ); - consoleWindow->viewport_SDL->setForceAspectOpt( aspectCbx->isChecked() ); - consoleWindow->viewport_SDL->setAutoScaleOpt( autoScaleCbx->isChecked() ); - consoleWindow->viewport_SDL->setScaleXY( xscale, yscale ); - consoleWindow->viewport_SDL->reset(); + consoleWindow->viewport_Interface->setLinearFilterEnable( gl_LF_chkBox->isChecked() ); + consoleWindow->viewport_Interface->setForceAspectOpt( aspectCbx->isChecked() ); + consoleWindow->viewport_Interface->setAutoScaleOpt( autoScaleCbx->isChecked() ); + consoleWindow->viewport_Interface->setScaleXY( xscale, yscale ); + consoleWindow->viewport_Interface->reset(); } if ( !consoleWindow->isFullScreen() && !consoleWindow->isMaximized() ) diff --git a/src/drivers/Qt/ConsoleViewerGL.h b/src/drivers/Qt/ConsoleViewerGL.h index dbefe817..12768ff7 100644 --- a/src/drivers/Qt/ConsoleViewerGL.h +++ b/src/drivers/Qt/ConsoleViewerGL.h @@ -10,7 +10,9 @@ #include #include -class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions +#include "Qt/ConsoleViewerInterface.h" + +class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions, public ConsoleViewerBase { Q_OBJECT @@ -20,6 +22,8 @@ class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions int init(void); void reset(void); + void queueRedraw(void){ update(); }; + int driver(void){ return VIDEO_DRIVER_OPENGL; }; void transfer2LocalBuffer(void); @@ -41,6 +45,13 @@ class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions void screenChanged(QScreen *scr); void setBgColor( QColor &c ); + void setCursor(const QCursor &c){ QOpenGLWidget::setCursor(c); }; + void setCursor( Qt::CursorShape s ){ QOpenGLWidget::setCursor(s); }; + + QSize size(void){ return QOpenGLWidget::size(); }; + QCursor cursor(void){ return QOpenGLWidget::cursor(); }; + void setMinimumSize(const QSize &s){ return QOpenGLWidget::setMinimumSize(s); }; + void setMaximumSize(const QSize &s){ return QOpenGLWidget::setMaximumSize(s); }; protected: void initializeGL(void); diff --git a/src/drivers/Qt/ConsoleViewerInterface.h b/src/drivers/Qt/ConsoleViewerInterface.h new file mode 100644 index 00000000..b267122d --- /dev/null +++ b/src/drivers/Qt/ConsoleViewerInterface.h @@ -0,0 +1,51 @@ +// ConsoleViewerInterface.h +// +#pragma once + +#include +#include +#include + +class ConsoleViewerBase +{ + public: + enum VideoDriver + { + VIDEO_DRIVER_OPENGL = 0, + VIDEO_DRIVER_SDL, + VIDEO_DRIVER_QPAINTER + }; + virtual int init(void) = 0; + virtual void reset(void) = 0; + virtual void queueRedraw(void) = 0; + virtual int driver(void) = 0; + + virtual void transfer2LocalBuffer(void) = 0; + + virtual void setVsyncEnable( bool ena ) = 0; + virtual void setLinearFilterEnable( bool ena ) = 0; + + virtual bool getForceAspectOpt(void) = 0; + virtual void setForceAspectOpt( bool val ) = 0; + virtual bool getAutoScaleOpt(void) = 0; + virtual void setAutoScaleOpt( bool val ) = 0; + virtual double getScaleX(void) = 0; + virtual double getScaleY(void) = 0; + virtual void setScaleXY( double xs, double ys ) = 0; + virtual void getNormalizedCursorPos( double &x, double &y ) = 0; + virtual bool getMouseButtonState( unsigned int btn ) = 0; + virtual void setAspectXY( double x, double y ) = 0; + virtual void getAspectXY( double &x, double &y ) = 0; + virtual double getAspectRatio(void) = 0; + + virtual void setCursor(const QCursor &c) = 0; + virtual void setCursor( Qt::CursorShape s ) = 0; + virtual void setBgColor( QColor &c ) = 0; + + virtual QSize size(void) = 0; + virtual QCursor cursor(void) = 0; + virtual void setMinimumSize(const QSize &) = 0; + virtual void setMaximumSize(const QSize &) = 0; + + protected: +}; diff --git a/src/drivers/Qt/ConsoleViewerQWidget.cpp b/src/drivers/Qt/ConsoleViewerQWidget.cpp new file mode 100644 index 00000000..0065124b --- /dev/null +++ b/src/drivers/Qt/ConsoleViewerQWidget.cpp @@ -0,0 +1,509 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2020 mjbudd77 + * + * 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 + */ +// GameViewer.cpp +// +#include +#include +#include +#include +//#include + +#include "Qt/nes_shm.h" +#include "Qt/throttle.h" +#include "Qt/fceuWrapper.h" +#include "Qt/ConsoleViewerQWidget.h" +#include "Qt/ConsoleUtilities.h" +#include "Qt/ConsoleWindow.h" + +extern unsigned int gui_draw_area_width; +extern unsigned int gui_draw_area_height; + +ConsoleViewQWidget_t::ConsoleViewQWidget_t(QWidget *parent) + : QWidget( parent ) +{ + consoleWin_t *win = qobject_cast (parent); + + printf("Initialing QPainter Video Driver\n"); + + QPalette pal = palette(); + + pal.setColor(QPalette::Window, Qt::black); + setAutoFillBackground(true); + setPalette(pal); + + bgColor = nullptr; + + if ( win ) + { + bgColor = win->getVideoBgColorPtr(); + bgColor->setRgb( 0, 0, 0 ); + } + + setMinimumWidth( 256 ); + setMinimumHeight( 224 ); + setFocusPolicy(Qt::StrongFocus); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + view_width = GL_NES_WIDTH; + view_height = GL_NES_HEIGHT; + + sx = sy = 0; + rw = view_width; + rh = view_height; + sdlRendW = 0; + sdlRendH = 0; + xscale = 2.0; + yscale = 2.0; + + devPixRatio = 1.0f; + aspectRatio = 1.0f; + aspectX = 1.0f; + aspectY = 1.0f; + + vsyncEnabled = false; + mouseButtonMask = 0; + + localBufSize = (4 * GL_NES_WIDTH) * (4 * GL_NES_HEIGHT) * sizeof(uint32_t); + + localBuf = (uint32_t*)malloc( localBufSize ); + + if ( localBuf ) + { + memset( localBuf, 0, localBufSize ); + } + + forceAspect = true; + autoScaleEna = true; + linearFilter = false; + + if ( g_config ) + { + int opt; + g_config->getOption("SDL.OpenGLip", &opt ); + + linearFilter = (opt) ? true : false; + + g_config->getOption ("SDL.AutoScale", &opt); + + autoScaleEna = (opt) ? true : false; + + g_config->getOption("SDL.XScale", &xscale); + g_config->getOption("SDL.YScale", &yscale); + + g_config->getOption ("SDL.ForceAspect", &forceAspect); + + if ( bgColor ) + { + fceuLoadConfigColor( "SDL.VideoBgColor", bgColor ); + } + g_config->getOption ("SDL.VideoVsync", &vsyncEnabled); + } +} + +ConsoleViewQWidget_t::~ConsoleViewQWidget_t(void) +{ + if ( localBuf ) + { + free( localBuf ); localBuf = nullptr; + } + cleanup(); +} + +void ConsoleViewQWidget_t::setBgColor( QColor &c ) +{ + if ( bgColor ) + { + *bgColor = c; + } +} + +void ConsoleViewQWidget_t::setVsyncEnable( bool ena ) +{ + if ( vsyncEnabled != ena ) + { + vsyncEnabled = ena; + + reset(); + } +} + +void ConsoleViewQWidget_t::setLinearFilterEnable( bool ena ) +{ + if ( ena != linearFilter ) + { + linearFilter = ena; + + reset(); + } +} + +void ConsoleViewQWidget_t::setScaleXY( double xs, double ys ) +{ + xscale = xs; + yscale = ys; + + if ( forceAspect ) + { + if ( xscale < yscale ) + { + yscale = xscale; + } + else + { + xscale = yscale; + } + } +} + +void ConsoleViewQWidget_t::setAspectXY( double x, double y ) +{ + aspectX = x; + aspectY = y; + + aspectRatio = aspectY / aspectX; +} + +void ConsoleViewQWidget_t::getAspectXY( double &x, double &y ) +{ + x = aspectX; + y = aspectY; +} + +double ConsoleViewQWidget_t::getAspectRatio(void) +{ + return aspectRatio; +} + +void ConsoleViewQWidget_t::transfer2LocalBuffer(void) +{ + int i=0, hq = 0, bufIdx; + int numPixels = nes_shm->video.ncol * nes_shm->video.nrow; + unsigned int cpSize = numPixels * 4; + uint8_t *src, *dest; + + bufIdx = nes_shm->pixBufIdx-1; + + if ( bufIdx < 0 ) + { + bufIdx = NES_VIDEO_BUFLEN-1; + } + if ( cpSize > localBufSize ) + { + cpSize = localBufSize; + } + src = (uint8_t*)nes_shm->pixbuf[bufIdx]; + dest = (uint8_t*)localBuf; + + hq = (nes_shm->video.preScaler == 1) || (nes_shm->video.preScaler == 4); // hq2x and hq3x + + if ( hq ) + { + for (i=0; isize(); + view_width = s.width(); + view_height = s.height(); + printf("QWidget Resize: %i x %i \n", view_width, view_height); + + gui_draw_area_width = view_width; + gui_draw_area_height = view_height; + + reset(); +} + +void ConsoleViewQWidget_t::mousePressEvent(QMouseEvent * event) +{ + //printf("Mouse Button Press: (%i,%i) %x %x\n", + // event->pos().x(), event->pos().y(), event->button(), event->buttons() ); + + mouseButtonMask = event->buttons(); +} + +void ConsoleViewQWidget_t::mouseReleaseEvent(QMouseEvent * event) +{ + //printf("Mouse Button Release: (%i,%i) %x %x\n", + // event->pos().x(), event->pos().y(), event->button(), event->buttons() ); + + mouseButtonMask = event->buttons(); +} + +bool ConsoleViewQWidget_t::getMouseButtonState( unsigned int btn ) +{ + bool isPressed = false; + + if ( mouseButtonMask & btn ) + { + isPressed = true; + } + else + { // Check SDL mouse state just in case SDL is intercepting + // mouse events from window system causing Qt not to see them. + int x, y; + uint32_t b; + b = SDL_GetMouseState( &x, &y); + + if ( btn & Qt::LeftButton ) + { + if ( b & SDL_BUTTON(SDL_BUTTON_LEFT) ) + { + isPressed = true; + } + } + + if ( btn & Qt::RightButton ) + { + if ( b & SDL_BUTTON(SDL_BUTTON_RIGHT) ) + { + isPressed = true; + } + } + + if ( btn & Qt::MiddleButton ) + { + if ( b & SDL_BUTTON(SDL_BUTTON_MIDDLE) ) + { + isPressed = true; + } + } + } + return isPressed; +} + +void ConsoleViewQWidget_t::getNormalizedCursorPos( double &x, double &y ) +{ + QPoint cursor; + + cursor = QCursor::pos(); + + //printf("Global Cursor (%i,%i) \n", cursor.x(), cursor.y() ); + + cursor = mapFromGlobal( cursor ); + + //printf("Window Cursor (%i,%i) \n", cursor.x(), cursor.y() ); + + x = (double)(cursor.x() - sx) / (double)rw; + y = (double)(cursor.y() - sy) / (double)rh; + + if ( x < 0.0 ) + { + x = 0.0; + } + else if ( x > 1.0 ) + { + x = 1.0; + } + if ( y < 0.0 ) + { + y = 0.0; + } + else if ( y > 1.0 ) + { + y = 1.0; + } + //printf("Normalized Cursor (%f,%f) \n", x, y ); +} + +void ConsoleViewQWidget_t::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + int nesWidth = GL_NES_WIDTH; + int nesHeight = GL_NES_HEIGHT; + float ixScale = 1.0; + float iyScale = 1.0; + + if ( nes_shm != nullptr ) + { + nesWidth = nes_shm->video.ncol; + nesHeight = nes_shm->video.nrow; + ixScale = (float)nes_shm->video.xscale; + iyScale = (float)nes_shm->video.yscale; + } + //printf(" %i x %i \n", nesWidth, nesHeight ); + float xscaleTmp = (float)view_width / (float)nesWidth; + float yscaleTmp = (float)view_height / (float)nesHeight; + + xscaleTmp *= ixScale; + yscaleTmp *= iyScale; + + if ( forceAspect ) + { + if ( xscaleTmp < yscaleTmp ) + { + yscaleTmp = xscaleTmp; + } + else + { + xscaleTmp = yscaleTmp; + } + } + + if ( autoScaleEna ) + { + xscale = xscaleTmp; + yscale = yscaleTmp; + } + else + { + if ( xscaleTmp > xscale ) + { + xscaleTmp = xscale; + } + if ( yscaleTmp > yscale ) + { + yscaleTmp = yscale; + } + } + + rw=(int)(nesWidth*xscaleTmp/ixScale); + rh=(int)(nesHeight*yscaleTmp/iyScale); + + if ( forceAspect ) + { + int iw, ih, ax, ay; + + ax = (int)(aspectX+0.50); + ay = (int)(aspectY+0.50); + + iw = rw * ay; + ih = rh * ax; + + if ( iw > ih ) + { + rh = (rw * ay) / ax; + } + else + { + rw = (rh * ax) / ay; + } + + if ( rw > view_width ) + { + rw = view_width; + rh = (rw * ay) / ax; + } + + if ( rh > view_height ) + { + rh = view_height; + rw = (rh * ax) / ay; + } + } + + if ( rw > view_width ) rw = view_width; + if ( rh > view_height) rh = view_height; + + sx=(view_width-rw)/2; + sy=(view_height-rh)/2; + + if ( bgColor ) + { + painter.fillRect( 0, 0, view_width, view_height, *bgColor ); + } + else + { + painter.fillRect( 0, 0, view_width, view_height, Qt::black ); + } + + int rowPitch = nesWidth * sizeof(uint32_t); + + QImage tmpImage( (const uchar*)localBuf, nesWidth, nesHeight, rowPitch, QImage::Format_RGB32); + + //SDL_Rect source = {0, 0, nesWidth, nesHeight }; + QRect dest( sx, sy, rw, rh ); + + painter.drawImage( dest, tmpImage ); + + videoBufferSwapMark(); + + nes_shm->render_count++; +} diff --git a/src/drivers/Qt/ConsoleViewerQWidget.h b/src/drivers/Qt/ConsoleViewerQWidget.h new file mode 100644 index 00000000..ca299ac1 --- /dev/null +++ b/src/drivers/Qt/ConsoleViewerQWidget.h @@ -0,0 +1,92 @@ +// ConsoleViewerQWidget.h +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Qt/ConsoleViewerInterface.h" + +class ConsoleViewQWidget_t : public QWidget, public ConsoleViewerBase +{ + Q_OBJECT + + public: + ConsoleViewQWidget_t(QWidget *parent = 0); + ~ConsoleViewQWidget_t(void); + + int init(void); + void reset(void); + void cleanup(void); + void queueRedraw(void){ update(); }; + int driver(void){ return VIDEO_DRIVER_QPAINTER; }; + + void transfer2LocalBuffer(void); + + void setVsyncEnable( bool ena ); + void setLinearFilterEnable( bool ena ); + + bool getForceAspectOpt(void){ return forceAspect; }; + void setForceAspectOpt( bool val ){ forceAspect = val; return; }; + bool getAutoScaleOpt(void){ return autoScaleEna; }; + void setAutoScaleOpt( bool val ){ autoScaleEna = val; return; }; + double getScaleX(void){ return xscale; }; + double getScaleY(void){ return yscale; }; + void setScaleXY( double xs, double ys ); + void getNormalizedCursorPos( double &x, double &y ); + bool getMouseButtonState( unsigned int btn ); + void setAspectXY( double x, double y ); + void getAspectXY( double &x, double &y ); + double getAspectRatio(void); + + void setCursor(const QCursor &c); + void setCursor( Qt::CursorShape s ); + void setBgColor( QColor &c ); + + QSize size(void){ return QWidget::size(); }; + QCursor cursor(void){ return QWidget::cursor(); }; + void setMinimumSize(const QSize &s){ return QWidget::setMinimumSize(s); }; + void setMaximumSize(const QSize &s){ return QWidget::setMaximumSize(s); }; + + protected: + + void paintEvent(QPaintEvent *event); + void showEvent(QShowEvent *event); + void resizeEvent(QResizeEvent *event); + void mousePressEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); + + int view_width; + int view_height; + + double devPixRatio; + double aspectRatio; + double aspectX; + double aspectY; + double xscale; + double yscale; + int rw; + int rh; + int sx; + int sy; + int sdlRendW; + int sdlRendH; + + bool vsyncEnabled; + bool linearFilter; + bool forceAspect; + bool autoScaleEna; + QColor *bgColor; + + uint32_t *localBuf; + uint32_t localBufSize; + unsigned int mouseButtonMask; + + private slots: +}; + diff --git a/src/drivers/Qt/ConsoleViewerSDL.h b/src/drivers/Qt/ConsoleViewerSDL.h index 834c4ac8..7aa3ffdf 100644 --- a/src/drivers/Qt/ConsoleViewerSDL.h +++ b/src/drivers/Qt/ConsoleViewerSDL.h @@ -10,7 +10,9 @@ #include #include -class ConsoleViewSDL_t : public QWidget +#include "Qt/ConsoleViewerInterface.h" + +class ConsoleViewSDL_t : public QWidget, public ConsoleViewerBase { Q_OBJECT @@ -22,6 +24,8 @@ class ConsoleViewSDL_t : public QWidget void reset(void); void cleanup(void); void render(void); + void queueRedraw(void){ render(); }; + int driver(void){ return VIDEO_DRIVER_SDL; }; void transfer2LocalBuffer(void); @@ -44,6 +48,12 @@ class ConsoleViewSDL_t : public QWidget void setCursor(const QCursor &c); void setCursor( Qt::CursorShape s ); void setBgColor( QColor &c ); + + QSize size(void){ return QWidget::size(); }; + QCursor cursor(void){ return QWidget::cursor(); }; + void setMinimumSize(const QSize &s){ return QWidget::setMinimumSize(s); }; + void setMaximumSize(const QSize &s){ return QWidget::setMaximumSize(s); }; + protected: //void paintEvent(QPaintEvent *event); diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 79add172..e46470e6 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -113,7 +113,7 @@ consoleWin_t::consoleWin_t(QWidget *parent) : QMainWindow( parent ) { int opt, xWinPos = -1, yWinPos = -1, xWinSize = 256, yWinSize = 240; - int use_SDL_video = false; + int videoDriver = 0; int setFullScreen = false; //QString libpath = QLibraryInfo::location(QLibraryInfo::PluginsPath); @@ -132,8 +132,10 @@ consoleWin_t::consoleWin_t(QWidget *parent) firstResize = true; closeRequested = false; errorMsgValid = false; - viewport_GL = NULL; - viewport_SDL = NULL; + viewport_GL = NULL; + viewport_SDL = NULL; + viewport_QWidget = NULL; + viewport_Interface = NULL; contextMenuEnable = false; soundUseGlobalFocus = false; @@ -148,19 +150,31 @@ consoleWin_t::consoleWin_t(QWidget *parent) g_config->getOption( "SDL.AutoHideMenuFullsreen", &autoHideMenuFullscreen ); g_config->getOption( "SDL.ContextMenuEnable", &contextMenuEnable ); g_config->getOption( "SDL.Sound.UseGlobalFocus", &soundUseGlobalFocus ); - g_config->getOption ("SDL.VideoDriver", &use_SDL_video); + g_config->getOption ("SDL.VideoDriver", &videoDriver); - if ( use_SDL_video ) + if ( videoDriver == 1) { viewport_SDL = new ConsoleViewSDL_t(this); setCentralWidget(viewport_SDL); + + viewport_Interface = static_cast(viewport_SDL); + } + else if ( videoDriver == 2) + { + viewport_QWidget = new ConsoleViewQWidget_t(this); + + setCentralWidget(viewport_QWidget); + + viewport_Interface = static_cast(viewport_QWidget); } else { viewport_GL = new ConsoleViewGL_t(this); setCentralWidget(viewport_GL); + + viewport_Interface = static_cast(viewport_GL); } setViewportAspect(); @@ -228,14 +242,18 @@ consoleWin_t::consoleWin_t(QWidget *parent) // the window is resized appropriately. On the first resize event, // we will set the minimum viewport size back to 1x values that the // window can be shrunk by dragging lower corner. - if ( viewport_GL != NULL ) + if ( viewport_Interface != NULL ) { - viewport_GL->setMinimumSize( reqSize ); - } - else if ( viewport_SDL != NULL ) - { - viewport_SDL->setMinimumSize( reqSize ); + viewport_Interface->setMinimumSize( reqSize ); } + //if ( viewport_GL != NULL ) + //{ + // viewport_GL->setMinimumSize( reqSize ); + //} + //else if ( viewport_SDL != NULL ) + //{ + // viewport_SDL->setMinimumSize( reqSize ); + //} //this->resize( reqSize ); } @@ -336,6 +354,10 @@ consoleWin_t::~consoleWin_t(void) { delete viewport_SDL; viewport_SDL = NULL; } + if ( viewport_QWidget != NULL ) + { + delete viewport_QWidget; viewport_QWidget = NULL; + } delete mutex; // LoadGame() checks for an IP and if it finds one begins a network session @@ -368,27 +390,44 @@ int consoleWin_t::videoInit(void) { int ret = 0; - if ( viewport_SDL ) + if (viewport_Interface) { - ret = viewport_SDL->init(); - } - else if ( viewport_GL ) - { - ret = viewport_GL->init(); + ret = viewport_Interface->init(); } + + //if ( viewport_SDL ) + //{ + // ret = viewport_SDL->init(); + //} + //else if ( viewport_GL ) + //{ + // ret = viewport_GL->init(); + //} + //else if ( viewport_QWidget ) + //{ + // ret = viewport_QWidget->init(); + //} return ret; } void consoleWin_t::videoReset(void) { - if ( viewport_SDL ) + if (viewport_Interface) { - viewport_SDL->reset(); - } - else if ( viewport_GL ) - { - viewport_GL->reset(); + viewport_Interface->reset(); } + //if ( viewport_SDL ) + //{ + // viewport_SDL->reset(); + //} + //else if ( viewport_GL ) + //{ + // viewport_GL->reset(); + //} + //else if ( viewport_QWidget ) + //{ + // viewport_QWidget->reset(); + //} return; } @@ -490,23 +529,40 @@ QSize consoleWin_t::calcRequiredSize(void) w = size(); - if ( viewport_GL ) + if ( viewport_Interface ) { - v = viewport_GL->size(); - forceAspect = viewport_GL->getForceAspectOpt(); - aspectRatio = viewport_GL->getAspectRatio(); - xscale = viewport_GL->getScaleX(); - yscale = viewport_GL->getScaleY(); - } - else if ( viewport_SDL ) - { - v = viewport_SDL->size(); - forceAspect = viewport_SDL->getForceAspectOpt(); - aspectRatio = viewport_SDL->getAspectRatio(); - xscale = viewport_SDL->getScaleX(); - yscale = viewport_SDL->getScaleY(); + v = viewport_Interface->size(); + forceAspect = viewport_Interface->getForceAspectOpt(); + aspectRatio = viewport_Interface->getAspectRatio(); + xscale = viewport_Interface->getScaleX(); + yscale = viewport_Interface->getScaleY(); } + //if ( viewport_GL ) + //{ + // v = viewport_GL->size(); + // forceAspect = viewport_GL->getForceAspectOpt(); + // aspectRatio = viewport_GL->getAspectRatio(); + // xscale = viewport_GL->getScaleX(); + // yscale = viewport_GL->getScaleY(); + //} + //else if ( viewport_SDL ) + //{ + // v = viewport_SDL->size(); + // forceAspect = viewport_SDL->getForceAspectOpt(); + // aspectRatio = viewport_SDL->getAspectRatio(); + // xscale = viewport_SDL->getScaleX(); + // yscale = viewport_SDL->getScaleY(); + //} + //else if ( viewport_QWidget ) + //{ + // v = viewport_QWidget->size(); + // forceAspect = viewport_QWidget->getForceAspectOpt(); + // aspectRatio = viewport_QWidget->getAspectRatio(); + // xscale = viewport_QWidget->getScaleX(); + // yscale = viewport_QWidget->getScaleY(); + //} + dw = 0; dh = 0; @@ -575,14 +631,22 @@ void consoleWin_t::setViewportAspect(void) break; } - if ( viewport_GL ) + if (viewport_Interface) { - viewport_GL->setAspectXY( x, y ); - } - else if ( viewport_SDL ) - { - viewport_SDL->setAspectXY( x, y ); + viewport_Interface->setAspectXY( x, y ); } + //if ( viewport_GL ) + //{ + // viewport_GL->setAspectXY( x, y ); + //} + //else if ( viewport_SDL ) + //{ + // viewport_SDL->setAspectXY( x, y ); + //} + //else if ( viewport_QWidget ) + //{ + // viewport_QWidget->setAspectXY( x, y ); + //} } void consoleWin_t::setMenuAccessPauseEnable( bool enable ) @@ -651,40 +715,65 @@ void consoleWin_t::loadCursor(void) void consoleWin_t::setViewerCursor( QCursor s ) { - if ( viewport_GL ) + if (viewport_Interface) { - viewport_GL->setCursor(s); - } - else if ( viewport_SDL ) - { - viewport_SDL->setCursor(s); + viewport_Interface->setCursor(s); } + //if ( viewport_GL ) + //{ + // viewport_GL->setCursor(s); + //} + //else if ( viewport_SDL ) + //{ + // viewport_SDL->setCursor(s); + //} + //else if ( viewport_QWidget ) + //{ + // viewport_QWidget->setCursor(s); + //} } void consoleWin_t::setViewerCursor( Qt::CursorShape s ) { - if ( viewport_GL ) + if (viewport_Interface) { - viewport_GL->setCursor(s); - } - else if ( viewport_SDL ) - { - viewport_SDL->setCursor(s); + viewport_Interface->setCursor(s); } + //if ( viewport_GL ) + //{ + // viewport_GL->setCursor(s); + //} + //else if ( viewport_SDL ) + //{ + // viewport_SDL->setCursor(s); + //} + //else if ( viewport_QWidget ) + //{ + // viewport_QWidget->setCursor(s); + //} } Qt::CursorShape consoleWin_t::getViewerCursor(void) { Qt::CursorShape s = Qt::ArrowCursor; - if ( viewport_GL ) + if (viewport_Interface) { - s = viewport_GL->cursor().shape(); - } - else if ( viewport_SDL ) - { - s = viewport_SDL->cursor().shape(); + s = viewport_Interface->cursor().shape(); } + + //if ( viewport_GL ) + //{ + // s = viewport_GL->cursor().shape(); + //} + //else if ( viewport_SDL ) + //{ + // s = viewport_SDL->cursor().shape(); + //} + //else if ( viewport_QWidget ) + //{ + // s = viewport_QWidget->cursor().shape(); + //} return s; } @@ -695,14 +784,23 @@ void consoleWin_t::resizeEvent(QResizeEvent *event) // We are assuming that window has been exposed and all sizing of menu is finished // Restore minimum sizes to 1x values after first resize event so that // window is still able to be shrunk by dragging lower corners. - if ( viewport_GL != NULL ) + if (viewport_Interface) { - viewport_GL->setMinimumSize( QSize( 256, 224 ) ); - } - else if ( viewport_SDL != NULL ) - { - viewport_SDL->setMinimumSize( QSize( 256, 224 ) ); + viewport_Interface->setMinimumSize( QSize( 256, 224 ) ); } + + //if ( viewport_GL != NULL ) + //{ + // viewport_GL->setMinimumSize( QSize( 256, 224 ) ); + //} + //else if ( viewport_SDL != NULL ) + //{ + // viewport_SDL->setMinimumSize( QSize( 256, 224 ) ); + //} + //else if ( viewport_QWidget != NULL ) + //{ + // viewport_QWidget->setMinimumSize( QSize( 256, 224 ) ); + //} firstResize = false; } //printf("%i x %i \n", event->size().width(), event->size().height() ); @@ -2011,83 +2109,101 @@ void consoleWin_t::createMainMenu(void) //--------------------------------------------------------------------------- int consoleWin_t::loadVideoDriver( int driverId, bool force ) { - if ( driverId ) - { // SDL Driver - if ( viewport_SDL != NULL ) + if (viewport_Interface) + { + if (viewport_Interface->driver() == driverId) { // Already Loaded - if ( force ) + if (force) { - if ( viewport_SDL == centralWidget() ) + switch (viewport_Interface->driver()) { - takeCentralWidget(); - } - delete viewport_SDL; + case ConsoleViewerBase::VIDEO_DRIVER_OPENGL: + { + if ( viewport_GL == centralWidget() ) + { + takeCentralWidget(); + } + delete viewport_GL; + + viewport_GL = NULL; + } + break; + case ConsoleViewerBase::VIDEO_DRIVER_SDL: + { + if ( viewport_SDL == centralWidget() ) + { + takeCentralWidget(); + } + delete viewport_SDL; + + viewport_SDL = NULL; + } + break; + case ConsoleViewerBase::VIDEO_DRIVER_QPAINTER: + { + if ( viewport_QWidget == centralWidget() ) + { + takeCentralWidget(); + } + delete viewport_QWidget; + + viewport_QWidget = NULL; + } + break; + default: + printf("Error: Invalid video driver\n"); + break; + } - viewport_SDL = NULL; } else { return 0; } } - - if ( viewport_GL != NULL ) - { - if ( viewport_GL == centralWidget() ) - { - takeCentralWidget(); - } - delete viewport_GL; - - viewport_GL = NULL; - } - - viewport_SDL = new ConsoleViewSDL_t(this); - - setCentralWidget(viewport_SDL); - - setViewportAspect(); - - viewport_SDL->init(); - } - else - { // OpenGL Driver - if ( viewport_GL != NULL ) - { // Already Loaded - if ( force ) - { - if ( viewport_GL == centralWidget() ) - { - takeCentralWidget(); - } - delete viewport_GL; - viewport_GL = NULL; - } - else - { - return 0; - } - } - - if ( viewport_SDL != NULL ) + switch ( driverId ) + { + case ConsoleViewerBase::VIDEO_DRIVER_SDL: { - if ( viewport_SDL == centralWidget() ) - { - takeCentralWidget(); - } - delete viewport_SDL; + viewport_SDL = new ConsoleViewSDL_t(this); - viewport_SDL = NULL; + viewport_Interface = static_cast(viewport_SDL); + + setCentralWidget(viewport_SDL); + + setViewportAspect(); + + viewport_SDL->init(); } - viewport_GL = new ConsoleViewGL_t(this); + break; + case ConsoleViewerBase::VIDEO_DRIVER_OPENGL: + { + viewport_GL = new ConsoleViewGL_t(this); - setCentralWidget(viewport_GL); + viewport_Interface = static_cast(viewport_GL); - setViewportAspect(); + setCentralWidget(viewport_GL); - viewport_GL->init(); + setViewportAspect(); + + viewport_GL->init(); + } + break; + case ConsoleViewerBase::VIDEO_DRIVER_QPAINTER: + { + viewport_QWidget = new ConsoleViewQWidget_t(this); + + viewport_Interface = static_cast(viewport_QWidget); + + setCentralWidget(viewport_QWidget); + + setViewportAspect(); + + viewport_QWidget->init(); + } + break; } // Reload Viewport Cursor Type and Visibility @@ -2283,16 +2399,27 @@ void consoleWin_t::videoBgColorChanged( QColor &c ) { //printf("Color Changed\n"); - if ( viewport_GL ) + if ( viewport_Interface ) { - viewport_GL->setBgColor(c); - viewport_GL->update(); - } - else if ( viewport_SDL ) - { - viewport_SDL->setBgColor(c); - viewport_SDL->render(); + viewport_Interface->setBgColor(c); + viewport_Interface->queueRedraw(); } + + //if ( viewport_GL ) + //{ + // viewport_GL->setBgColor(c); + // viewport_GL->update(); + //} + //else if ( viewport_SDL ) + //{ + // viewport_SDL->setBgColor(c); + // viewport_SDL->render(); + //} + //else if ( viewport_QWidget ) + //{ + // viewport_QWidget->setBgColor(c); + // viewport_QWidget->update(); + //} } //--------------------------------------------------------------------------- int consoleWin_t::showListSelectDialog( const char *title, std::vector &l ) @@ -2899,6 +3026,10 @@ void consoleWin_t::takeScreenShot(void) { image = screen->grabWindow( viewport_SDL->winId() ); } + else if ( viewport_QWidget ) + { + image = screen->grabWindow( viewport_QWidget->winId() ); + } for (u = 0; u < 99999; ++u) { @@ -3214,18 +3345,30 @@ void consoleWin_t::winResizeIx(int iscale) w = size(); - if ( viewport_GL ) + if ( viewport_Interface ) { - v = viewport_GL->size(); - aspectRatio = viewport_GL->getAspectRatio(); - forceAspect = viewport_GL->getForceAspectOpt(); - } - else if ( viewport_SDL ) - { - v = viewport_SDL->size(); - aspectRatio = viewport_SDL->getAspectRatio(); - forceAspect = viewport_SDL->getForceAspectOpt(); + v = viewport_Interface->size(); + aspectRatio = viewport_Interface->getAspectRatio(); + forceAspect = viewport_Interface->getForceAspectOpt(); } + //if ( viewport_GL ) + //{ + // v = viewport_GL->size(); + // aspectRatio = viewport_GL->getAspectRatio(); + // forceAspect = viewport_GL->getForceAspectOpt(); + //} + //else if ( viewport_SDL ) + //{ + // v = viewport_SDL->size(); + // aspectRatio = viewport_SDL->getAspectRatio(); + // forceAspect = viewport_SDL->getForceAspectOpt(); + //} + //else if ( viewport_QWidget ) + //{ + // v = viewport_QWidget->size(); + // aspectRatio = viewport_QWidget->getAspectRatio(); + // forceAspect = viewport_QWidget->getForceAspectOpt(); + //} dw = w.width() - v.width(); dh = w.height() - v.height(); @@ -4490,16 +4633,26 @@ void consoleWin_t::transferVideoBuffer(void) { nes_shm->blitUpdated = 0; - if ( viewport_SDL ) + if (viewport_Interface) { - viewport_SDL->transfer2LocalBuffer(); - viewport_SDL->render(); - } - else if ( viewport_GL ) - { - viewport_GL->transfer2LocalBuffer(); - viewport_GL->update(); + viewport_Interface->transfer2LocalBuffer(); + viewport_Interface->queueRedraw(); } + //if ( viewport_SDL ) + //{ + // viewport_SDL->transfer2LocalBuffer(); + // viewport_SDL->render(); + //} + //else if ( viewport_GL ) + //{ + // viewport_GL->transfer2LocalBuffer(); + // viewport_GL->update(); + //} + //else if ( viewport_QWidget ) + //{ + // viewport_QWidget->transfer2LocalBuffer(); + // viewport_QWidget->update(); + //} } } diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index 065eaaa7..67a2a786 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -30,6 +30,7 @@ #include "Qt/ColorMenu.h" #include "Qt/ConsoleViewerGL.h" #include "Qt/ConsoleViewerSDL.h" +#include "Qt/ConsoleViewerQWidget.h" #include "Qt/GamePadConf.h" #include "Qt/AviRecord.h" @@ -126,8 +127,10 @@ class consoleWin_t : public QMainWindow consoleWin_t(QWidget *parent = 0); ~consoleWin_t(void); - ConsoleViewGL_t *viewport_GL; - ConsoleViewSDL_t *viewport_SDL; + ConsoleViewGL_t *viewport_GL; + ConsoleViewSDL_t *viewport_SDL; + ConsoleViewQWidget_t *viewport_QWidget; + ConsoleViewerBase *viewport_Interface; void setCyclePeriodms( int ms ); From 13b52f5c8e87b15c74f45aab201377bf95644371 Mon Sep 17 00:00:00 2001 From: harry Date: Tue, 9 May 2023 23:01:53 -0400 Subject: [PATCH 54/69] A few corrections to the QPainter Video driver. --- src/drivers/Qt/ConsoleViewerQWidget.cpp | 2 +- src/drivers/Qt/ConsoleWindow.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/drivers/Qt/ConsoleViewerQWidget.cpp b/src/drivers/Qt/ConsoleViewerQWidget.cpp index 0065124b..78313a39 100644 --- a/src/drivers/Qt/ConsoleViewerQWidget.cpp +++ b/src/drivers/Qt/ConsoleViewerQWidget.cpp @@ -40,7 +40,7 @@ ConsoleViewQWidget_t::ConsoleViewQWidget_t(QWidget *parent) { consoleWin_t *win = qobject_cast (parent); - printf("Initialing QPainter Video Driver\n"); + printf("Initializing QPainter Video Driver\n"); QPalette pal = palette(); diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index e46470e6..e713ef0d 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -152,7 +152,7 @@ consoleWin_t::consoleWin_t(QWidget *parent) g_config->getOption( "SDL.Sound.UseGlobalFocus", &soundUseGlobalFocus ); g_config->getOption ("SDL.VideoDriver", &videoDriver); - if ( videoDriver == 1) + if ( videoDriver == ConsoleViewerBase::VIDEO_DRIVER_SDL) { viewport_SDL = new ConsoleViewSDL_t(this); @@ -160,7 +160,7 @@ consoleWin_t::consoleWin_t(QWidget *parent) viewport_Interface = static_cast(viewport_SDL); } - else if ( videoDriver == 2) + else if ( videoDriver == ConsoleViewerBase::VIDEO_DRIVER_QPAINTER) { viewport_QWidget = new ConsoleViewQWidget_t(this); From 3208c01b38f6e3c14e68bab50e8aa99806dc2b63 Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 10 May 2023 00:25:07 -0400 Subject: [PATCH 55/69] Hooked up bi-linear filter for new QPainter video driver. --- src/drivers/Qt/ConsoleViewerQWidget.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/drivers/Qt/ConsoleViewerQWidget.cpp b/src/drivers/Qt/ConsoleViewerQWidget.cpp index 78313a39..d0d2f55e 100644 --- a/src/drivers/Qt/ConsoleViewerQWidget.cpp +++ b/src/drivers/Qt/ConsoleViewerQWidget.cpp @@ -233,15 +233,6 @@ void ConsoleViewQWidget_t::transfer2LocalBuffer(void) int ConsoleViewQWidget_t::init(void) { - if ( linearFilter ) - { - //SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ); - } - else - { - //SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "0" ); - } - return 0; } @@ -493,6 +484,7 @@ void ConsoleViewQWidget_t::paintEvent(QPaintEvent *event) { painter.fillRect( 0, 0, view_width, view_height, Qt::black ); } + painter.setRenderHint( QPainter::SmoothPixmapTransform, linearFilter ); int rowPitch = nesWidth * sizeof(uint32_t); From b125d48db5f6d7cfee0a7b95f28b1380408ddf83 Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 10 May 2023 20:58:19 -0400 Subject: [PATCH 56/69] For Qt GUI add code that forces video alpha bits to 255 for all pixels. Some video drivers aren't ignoring the alpha bits as they should so make sure they as always set to be safe. --- src/CMakeLists.txt | 1 + src/drivers/Qt/ConsoleViewerGL.cpp | 4 ++-- src/drivers/Qt/ConsoleViewerInterface.cpp | 28 +++++++++++++++++++++++ src/drivers/Qt/ConsoleViewerInterface.h | 5 ++++ src/drivers/Qt/ConsoleViewerQWidget.cpp | 8 ++++--- src/drivers/Qt/ConsoleViewerSDL.cpp | 4 ++-- 6 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 src/drivers/Qt/ConsoleViewerInterface.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 387f3879..b0957b7b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -550,6 +550,7 @@ set(SRC_DRIVERS_SDL ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerGL.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerSDL.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerQWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/InputConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/GamePadConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/FamilyKeyboard.cpp diff --git a/src/drivers/Qt/ConsoleViewerGL.cpp b/src/drivers/Qt/ConsoleViewerGL.cpp index c877cefe..d7dbe7e3 100644 --- a/src/drivers/Qt/ConsoleViewerGL.cpp +++ b/src/drivers/Qt/ConsoleViewerGL.cpp @@ -92,7 +92,7 @@ ConsoleViewGL_t::ConsoleViewGL_t(QWidget *parent) if ( localBuf ) { - memset( localBuf, 0, localBufSize ); + memset32( localBuf, alphaMask, localBufSize ); } vsyncEnabled = true; @@ -502,7 +502,7 @@ void ConsoleViewGL_t::transfer2LocalBuffer(void) } else { - memcpy( localBuf, src, cpSize ); + copyPixels32( dest, src, cpSize, alphaMask); } } diff --git a/src/drivers/Qt/ConsoleViewerInterface.cpp b/src/drivers/Qt/ConsoleViewerInterface.cpp new file mode 100644 index 00000000..362a30f3 --- /dev/null +++ b/src/drivers/Qt/ConsoleViewerInterface.cpp @@ -0,0 +1,28 @@ +// ConsoleViewerInterface.cpp +// +#include "Qt/ConsoleViewerInterface.h" + +//---------------------------------------------------------- +void ConsoleViewerBase::memset32( void *buf, uint32_t val, size_t size) +{ + uint32_t *p = static_cast(buf); + size_t n = size / sizeof(uint32_t); + + for (size_t i=0; i(dest); + uint32_t *s = static_cast(src); + size_t n = size / sizeof(uint32_t); + + for (size_t i=0; i #include #include @@ -47,5 +48,9 @@ class ConsoleViewerBase virtual void setMinimumSize(const QSize &) = 0; virtual void setMaximumSize(const QSize &) = 0; + static void memset32( void *buf, uint32_t val, size_t size); + static void copyPixels32( void *dest, void *src, size_t size, uint32_t alphaMask); + + static constexpr uint32_t alphaMask = 0xff000000; protected: }; diff --git a/src/drivers/Qt/ConsoleViewerQWidget.cpp b/src/drivers/Qt/ConsoleViewerQWidget.cpp index d0d2f55e..427012cb 100644 --- a/src/drivers/Qt/ConsoleViewerQWidget.cpp +++ b/src/drivers/Qt/ConsoleViewerQWidget.cpp @@ -25,6 +25,7 @@ #include //#include +#include "../../profiler.h" #include "Qt/nes_shm.h" #include "Qt/throttle.h" #include "Qt/fceuWrapper.h" @@ -86,7 +87,7 @@ ConsoleViewQWidget_t::ConsoleViewQWidget_t(QWidget *parent) if ( localBuf ) { - memset( localBuf, 0, localBufSize ); + memset32( localBuf, alphaMask, localBufSize ); } forceAspect = true; @@ -227,7 +228,8 @@ void ConsoleViewQWidget_t::transfer2LocalBuffer(void) } else { - memcpy( localBuf, src, cpSize ); + //memcpy( localBuf, src, cpSize ); + copyPixels32( dest, src, cpSize, alphaMask); } } @@ -488,7 +490,7 @@ void ConsoleViewQWidget_t::paintEvent(QPaintEvent *event) int rowPitch = nesWidth * sizeof(uint32_t); - QImage tmpImage( (const uchar*)localBuf, nesWidth, nesHeight, rowPitch, QImage::Format_RGB32); + QImage tmpImage( (const uchar*)localBuf, nesWidth, nesHeight, rowPitch, QImage::Format_ARGB32); //SDL_Rect source = {0, 0, nesWidth, nesHeight }; QRect dest( sx, sy, rw, rh ); diff --git a/src/drivers/Qt/ConsoleViewerSDL.cpp b/src/drivers/Qt/ConsoleViewerSDL.cpp index 04216592..339ec1c6 100644 --- a/src/drivers/Qt/ConsoleViewerSDL.cpp +++ b/src/drivers/Qt/ConsoleViewerSDL.cpp @@ -89,7 +89,7 @@ ConsoleViewSDL_t::ConsoleViewSDL_t(QWidget *parent) if ( localBuf ) { - memset( localBuf, 0, localBufSize ); + memset32( localBuf, alphaMask, localBufSize ); } forceAspect = true; @@ -240,7 +240,7 @@ void ConsoleViewSDL_t::transfer2LocalBuffer(void) } else { - memcpy( localBuf, src, cpSize ); + copyPixels32( dest, src, cpSize, alphaMask); } } From 52c53dfe5b477b2c36f57e957cd908a1997857a3 Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 10 May 2023 21:30:27 -0400 Subject: [PATCH 57/69] Removed commented out code Qt GUI video interface code. --- src/drivers/Qt/ConsoleWindow.cpp | 179 ------------------------------- src/drivers/Qt/input.cpp | 21 +--- 2 files changed, 4 insertions(+), 196 deletions(-) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index e713ef0d..f343a62f 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -246,14 +246,6 @@ consoleWin_t::consoleWin_t(QWidget *parent) { viewport_Interface->setMinimumSize( reqSize ); } - //if ( viewport_GL != NULL ) - //{ - // viewport_GL->setMinimumSize( reqSize ); - //} - //else if ( viewport_SDL != NULL ) - //{ - // viewport_SDL->setMinimumSize( reqSize ); - //} //this->resize( reqSize ); } @@ -295,17 +287,6 @@ consoleWin_t::~consoleWin_t(void) if ( !isFullScreen() && !isMaximized() ) { // Scaling is only saved when applying video settings - //if ( viewport_GL != NULL ) - //{ - // g_config->setOption( "SDL.XScale", viewport_GL->getScaleX() ); - // g_config->setOption( "SDL.YScale", viewport_GL->getScaleY() ); - //} - //else if ( viewport_SDL != NULL ) - //{ - // g_config->setOption( "SDL.XScale", viewport_SDL->getScaleX() ); - // g_config->setOption( "SDL.YScale", viewport_SDL->getScaleY() ); - //} - g_config->setOption( "SDL.WinPosX" , pos().x() ); g_config->setOption( "SDL.WinPosY" , pos().y() ); g_config->setOption( "SDL.WinSizeX", w.width() ); @@ -394,19 +375,6 @@ int consoleWin_t::videoInit(void) { ret = viewport_Interface->init(); } - - //if ( viewport_SDL ) - //{ - // ret = viewport_SDL->init(); - //} - //else if ( viewport_GL ) - //{ - // ret = viewport_GL->init(); - //} - //else if ( viewport_QWidget ) - //{ - // ret = viewport_QWidget->init(); - //} return ret; } @@ -416,18 +384,6 @@ void consoleWin_t::videoReset(void) { viewport_Interface->reset(); } - //if ( viewport_SDL ) - //{ - // viewport_SDL->reset(); - //} - //else if ( viewport_GL ) - //{ - // viewport_GL->reset(); - //} - //else if ( viewport_QWidget ) - //{ - // viewport_QWidget->reset(); - //} return; } @@ -538,31 +494,6 @@ QSize consoleWin_t::calcRequiredSize(void) yscale = viewport_Interface->getScaleY(); } - //if ( viewport_GL ) - //{ - // v = viewport_GL->size(); - // forceAspect = viewport_GL->getForceAspectOpt(); - // aspectRatio = viewport_GL->getAspectRatio(); - // xscale = viewport_GL->getScaleX(); - // yscale = viewport_GL->getScaleY(); - //} - //else if ( viewport_SDL ) - //{ - // v = viewport_SDL->size(); - // forceAspect = viewport_SDL->getForceAspectOpt(); - // aspectRatio = viewport_SDL->getAspectRatio(); - // xscale = viewport_SDL->getScaleX(); - // yscale = viewport_SDL->getScaleY(); - //} - //else if ( viewport_QWidget ) - //{ - // v = viewport_QWidget->size(); - // forceAspect = viewport_QWidget->getForceAspectOpt(); - // aspectRatio = viewport_QWidget->getAspectRatio(); - // xscale = viewport_QWidget->getScaleX(); - // yscale = viewport_QWidget->getScaleY(); - //} - dw = 0; dh = 0; @@ -635,18 +566,6 @@ void consoleWin_t::setViewportAspect(void) { viewport_Interface->setAspectXY( x, y ); } - //if ( viewport_GL ) - //{ - // viewport_GL->setAspectXY( x, y ); - //} - //else if ( viewport_SDL ) - //{ - // viewport_SDL->setAspectXY( x, y ); - //} - //else if ( viewport_QWidget ) - //{ - // viewport_QWidget->setAspectXY( x, y ); - //} } void consoleWin_t::setMenuAccessPauseEnable( bool enable ) @@ -719,18 +638,6 @@ void consoleWin_t::setViewerCursor( QCursor s ) { viewport_Interface->setCursor(s); } - //if ( viewport_GL ) - //{ - // viewport_GL->setCursor(s); - //} - //else if ( viewport_SDL ) - //{ - // viewport_SDL->setCursor(s); - //} - //else if ( viewport_QWidget ) - //{ - // viewport_QWidget->setCursor(s); - //} } void consoleWin_t::setViewerCursor( Qt::CursorShape s ) @@ -739,18 +646,6 @@ void consoleWin_t::setViewerCursor( Qt::CursorShape s ) { viewport_Interface->setCursor(s); } - //if ( viewport_GL ) - //{ - // viewport_GL->setCursor(s); - //} - //else if ( viewport_SDL ) - //{ - // viewport_SDL->setCursor(s); - //} - //else if ( viewport_QWidget ) - //{ - // viewport_QWidget->setCursor(s); - //} } Qt::CursorShape consoleWin_t::getViewerCursor(void) @@ -761,19 +656,6 @@ Qt::CursorShape consoleWin_t::getViewerCursor(void) { s = viewport_Interface->cursor().shape(); } - - //if ( viewport_GL ) - //{ - // s = viewport_GL->cursor().shape(); - //} - //else if ( viewport_SDL ) - //{ - // s = viewport_SDL->cursor().shape(); - //} - //else if ( viewport_QWidget ) - //{ - // s = viewport_QWidget->cursor().shape(); - //} return s; } @@ -789,18 +671,6 @@ void consoleWin_t::resizeEvent(QResizeEvent *event) viewport_Interface->setMinimumSize( QSize( 256, 224 ) ); } - //if ( viewport_GL != NULL ) - //{ - // viewport_GL->setMinimumSize( QSize( 256, 224 ) ); - //} - //else if ( viewport_SDL != NULL ) - //{ - // viewport_SDL->setMinimumSize( QSize( 256, 224 ) ); - //} - //else if ( viewport_QWidget != NULL ) - //{ - // viewport_QWidget->setMinimumSize( QSize( 256, 224 ) ); - //} firstResize = false; } //printf("%i x %i \n", event->size().width(), event->size().height() ); @@ -2404,22 +2274,6 @@ void consoleWin_t::videoBgColorChanged( QColor &c ) viewport_Interface->setBgColor(c); viewport_Interface->queueRedraw(); } - - //if ( viewport_GL ) - //{ - // viewport_GL->setBgColor(c); - // viewport_GL->update(); - //} - //else if ( viewport_SDL ) - //{ - // viewport_SDL->setBgColor(c); - // viewport_SDL->render(); - //} - //else if ( viewport_QWidget ) - //{ - // viewport_QWidget->setBgColor(c); - // viewport_QWidget->update(); - //} } //--------------------------------------------------------------------------- int consoleWin_t::showListSelectDialog( const char *title, std::vector &l ) @@ -3351,24 +3205,6 @@ void consoleWin_t::winResizeIx(int iscale) aspectRatio = viewport_Interface->getAspectRatio(); forceAspect = viewport_Interface->getForceAspectOpt(); } - //if ( viewport_GL ) - //{ - // v = viewport_GL->size(); - // aspectRatio = viewport_GL->getAspectRatio(); - // forceAspect = viewport_GL->getForceAspectOpt(); - //} - //else if ( viewport_SDL ) - //{ - // v = viewport_SDL->size(); - // aspectRatio = viewport_SDL->getAspectRatio(); - // forceAspect = viewport_SDL->getForceAspectOpt(); - //} - //else if ( viewport_QWidget ) - //{ - // v = viewport_QWidget->size(); - // aspectRatio = viewport_QWidget->getAspectRatio(); - // forceAspect = viewport_QWidget->getForceAspectOpt(); - //} dw = w.width() - v.width(); dh = w.height() - v.height(); @@ -4638,21 +4474,6 @@ void consoleWin_t::transferVideoBuffer(void) viewport_Interface->transfer2LocalBuffer(); viewport_Interface->queueRedraw(); } - //if ( viewport_SDL ) - //{ - // viewport_SDL->transfer2LocalBuffer(); - // viewport_SDL->render(); - //} - //else if ( viewport_GL ) - //{ - // viewport_GL->transfer2LocalBuffer(); - // viewport_GL->update(); - //} - //else if ( viewport_QWidget ) - //{ - // viewport_QWidget->transfer2LocalBuffer(); - // viewport_QWidget->update(); - //} } } diff --git a/src/drivers/Qt/input.cpp b/src/drivers/Qt/input.cpp index 7308f2e6..5df0d426 100644 --- a/src/drivers/Qt/input.cpp +++ b/src/drivers/Qt/input.cpp @@ -1230,28 +1230,15 @@ void GetMouseData(uint32 (&d)[3]) b = 0; // map mouse buttons - if (consoleWindow->viewport_SDL) + if (consoleWindow->viewport_Interface) { - consoleWindow->viewport_SDL->getNormalizedCursorPos(nx, ny); + consoleWindow->viewport_Interface->getNormalizedCursorPos(nx, ny); - if (consoleWindow->viewport_SDL->getMouseButtonState(Qt::LeftButton)) + if (consoleWindow->viewport_Interface->getMouseButtonState(Qt::LeftButton)) { b |= 0x01; } - if (consoleWindow->viewport_SDL->getMouseButtonState(Qt::RightButton)) - { - b |= 0x02; - } - } - else if (consoleWindow->viewport_GL) - { - consoleWindow->viewport_GL->getNormalizedCursorPos(nx, ny); - - if (consoleWindow->viewport_GL->getMouseButtonState(Qt::LeftButton)) - { - b |= 0x01; - } - if (consoleWindow->viewport_GL->getMouseButtonState(Qt::RightButton)) + if (consoleWindow->viewport_Interface->getMouseButtonState(Qt::RightButton)) { b |= 0x02; } From 58b87387eb934976373f5febbf85261fa427c30e Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 11 May 2023 08:05:14 -0400 Subject: [PATCH 58/69] Cleanup of Qt GUI video driver viewport loading/unloading. Use QObject::deleteLater for a cleaner shutdown of a viewport. --- src/drivers/Qt/ConsoleViewerGL.cpp | 2 + src/drivers/Qt/ConsoleViewerQWidget.cpp | 2 + src/drivers/Qt/ConsoleViewerSDL.cpp | 2 + src/drivers/Qt/ConsoleWindow.cpp | 181 ++++++++++++++---------- src/drivers/Qt/ConsoleWindow.h | 2 + 5 files changed, 112 insertions(+), 77 deletions(-) diff --git a/src/drivers/Qt/ConsoleViewerGL.cpp b/src/drivers/Qt/ConsoleViewerGL.cpp index d7dbe7e3..b6210272 100644 --- a/src/drivers/Qt/ConsoleViewerGL.cpp +++ b/src/drivers/Qt/ConsoleViewerGL.cpp @@ -136,6 +136,8 @@ ConsoleViewGL_t::ConsoleViewGL_t(QWidget *parent) ConsoleViewGL_t::~ConsoleViewGL_t(void) { + //printf("Destroying GL Viewport\n"); + if ( localBuf ) { free( localBuf ); localBuf = NULL; diff --git a/src/drivers/Qt/ConsoleViewerQWidget.cpp b/src/drivers/Qt/ConsoleViewerQWidget.cpp index 427012cb..e461168d 100644 --- a/src/drivers/Qt/ConsoleViewerQWidget.cpp +++ b/src/drivers/Qt/ConsoleViewerQWidget.cpp @@ -120,6 +120,8 @@ ConsoleViewQWidget_t::ConsoleViewQWidget_t(QWidget *parent) ConsoleViewQWidget_t::~ConsoleViewQWidget_t(void) { + //printf("Destroying QPainter Viewport\n"); + if ( localBuf ) { free( localBuf ); localBuf = nullptr; diff --git a/src/drivers/Qt/ConsoleViewerSDL.cpp b/src/drivers/Qt/ConsoleViewerSDL.cpp index 339ec1c6..31cccffb 100644 --- a/src/drivers/Qt/ConsoleViewerSDL.cpp +++ b/src/drivers/Qt/ConsoleViewerSDL.cpp @@ -122,6 +122,8 @@ ConsoleViewSDL_t::ConsoleViewSDL_t(QWidget *parent) ConsoleViewSDL_t::~ConsoleViewSDL_t(void) { + //printf("Destroying SDL Viewport\n"); + if ( localBuf ) { free( localBuf ); localBuf = NULL; diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index f343a62f..7444b14a 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -152,31 +152,7 @@ consoleWin_t::consoleWin_t(QWidget *parent) g_config->getOption( "SDL.Sound.UseGlobalFocus", &soundUseGlobalFocus ); g_config->getOption ("SDL.VideoDriver", &videoDriver); - if ( videoDriver == ConsoleViewerBase::VIDEO_DRIVER_SDL) - { - viewport_SDL = new ConsoleViewSDL_t(this); - - setCentralWidget(viewport_SDL); - - viewport_Interface = static_cast(viewport_SDL); - } - else if ( videoDriver == ConsoleViewerBase::VIDEO_DRIVER_QPAINTER) - { - viewport_QWidget = new ConsoleViewQWidget_t(this); - - setCentralWidget(viewport_QWidget); - - viewport_Interface = static_cast(viewport_QWidget); - } - else - { - viewport_GL = new ConsoleViewGL_t(this); - - setCentralWidget(viewport_GL); - - viewport_Interface = static_cast(viewport_GL); - } - setViewportAspect(); + loadVideoDriver( videoDriver ); setWindowTitle( tr(FCEU_NAME_AND_VERSION) ); setWindowIcon(QIcon(":fceux1.png")); @@ -327,18 +303,8 @@ consoleWin_t::~consoleWin_t(void) //fceuWrapperClose(); //FCEU_WRAPPER_UNLOCK(); - if ( viewport_GL != NULL ) - { - delete viewport_GL; viewport_GL = NULL; - } - if ( viewport_SDL != NULL ) - { - delete viewport_SDL; viewport_SDL = NULL; - } - if ( viewport_QWidget != NULL ) - { - delete viewport_QWidget; viewport_QWidget = NULL; - } + unloadVideoDriver(); + delete mutex; // LoadGame() checks for an IP and if it finds one begins a network session @@ -1977,6 +1943,99 @@ void consoleWin_t::createMainMenu(void) #endif }; //--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +int consoleWin_t::unloadVideoDriver(void) +{ + viewport_Interface = NULL; + + if (viewport_GL != NULL) + { + if ( viewport_GL == centralWidget() ) + { + takeCentralWidget(); + } + else + { + printf("Error: Central Widget Failed!\n"); + } + viewport_GL->deleteLater(); + + viewport_GL = NULL; + } + + if (viewport_SDL != NULL) + { + if ( viewport_SDL == centralWidget() ) + { + takeCentralWidget(); + } + else + { + printf("Error: Central Widget Failed!\n"); + } + viewport_SDL->deleteLater(); + + viewport_SDL = NULL; + } + + if (viewport_QWidget != NULL) + { + if ( viewport_QWidget == centralWidget() ) + { + takeCentralWidget(); + } + else + { + printf("Error: Central Widget Failed!\n"); + } + viewport_QWidget->deleteLater(); + + viewport_QWidget = NULL; + } + return 0; +} +//--------------------------------------------------------------------------- +void consoleWin_t::videoDriverDestroyed(QObject* obj) +{ + if (viewport_GL == obj) + { + //printf("GL Video Driver Destroyed\n"); + + if (viewport_Interface == static_cast(viewport_GL)) + { + viewport_Interface = NULL; + } + viewport_GL = NULL; + } + + if (viewport_SDL == obj) + { + //printf("SDL Video Driver Destroyedi\n"); + + if (viewport_Interface == static_cast(viewport_SDL)) + { + viewport_Interface = NULL; + } + viewport_SDL = NULL; + } + + if (viewport_QWidget == obj) + { + //printf("QPainter Video Driver Destroyed\n"); + + if (viewport_Interface == static_cast(viewport_QWidget)) + { + viewport_Interface = NULL; + } + viewport_QWidget = NULL; + } + printf("Video Driver Destroyed: %p\n", obj); + //printf("viewport_GL: %p\n", viewport_GL); + //printf("viewport_SDL: %p\n", viewport_SDL); + //printf("viewport_Qt: %p\n", viewport_QWidget); + //printf("viewport_Interface: %p\n", viewport_Interface); +} +//--------------------------------------------------------------------------- int consoleWin_t::loadVideoDriver( int driverId, bool force ) { if (viewport_Interface) @@ -1985,46 +2044,7 @@ int consoleWin_t::loadVideoDriver( int driverId, bool force ) { // Already Loaded if (force) { - switch (viewport_Interface->driver()) - { - case ConsoleViewerBase::VIDEO_DRIVER_OPENGL: - { - if ( viewport_GL == centralWidget() ) - { - takeCentralWidget(); - } - delete viewport_GL; - - viewport_GL = NULL; - } - break; - case ConsoleViewerBase::VIDEO_DRIVER_SDL: - { - if ( viewport_SDL == centralWidget() ) - { - takeCentralWidget(); - } - delete viewport_SDL; - - viewport_SDL = NULL; - } - break; - case ConsoleViewerBase::VIDEO_DRIVER_QPAINTER: - { - if ( viewport_QWidget == centralWidget() ) - { - takeCentralWidget(); - } - delete viewport_QWidget; - - viewport_QWidget = NULL; - } - break; - default: - printf("Error: Invalid video driver\n"); - break; - } - + unloadVideoDriver(); } else { @@ -2046,6 +2066,8 @@ int consoleWin_t::loadVideoDriver( int driverId, bool force ) setViewportAspect(); viewport_SDL->init(); + + connect( viewport_SDL, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) ); } break; case ConsoleViewerBase::VIDEO_DRIVER_OPENGL: @@ -2059,8 +2081,11 @@ int consoleWin_t::loadVideoDriver( int driverId, bool force ) setViewportAspect(); viewport_GL->init(); + + connect( viewport_GL, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) ); } break; + default: case ConsoleViewerBase::VIDEO_DRIVER_QPAINTER: { viewport_QWidget = new ConsoleViewQWidget_t(this); @@ -2072,6 +2097,8 @@ int consoleWin_t::loadVideoDriver( int driverId, bool force ) setViewportAspect(); viewport_QWidget->init(); + + connect( viewport_QWidget, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) ); } break; } diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index 67a2a786..1fc69749 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -158,6 +158,7 @@ class consoleWin_t : public QMainWindow #endif int loadVideoDriver( int driverId, bool force = false ); + int unloadVideoDriver(void); double getRefreshRate(void){ return refreshRate; } @@ -466,6 +467,7 @@ class consoleWin_t : public QMainWindow void toggleUseBgPaletteForVideo(bool); void videoBgColorChanged( QColor &c ); void loadRomRequestCB( QString s ); + void videoDriverDestroyed( QObject *obj ); }; From 7573f1b7dcc5b67e19cf46999187cce4550a2be0 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 13 May 2023 20:26:56 -0400 Subject: [PATCH 59/69] Added profiler per thread logging. --- src/drivers/Qt/AviRecord.cpp | 1 + src/drivers/Qt/ConsoleWindow.cpp | 11 +++ src/drivers/Qt/TraceLogger.cpp | 1 + src/drivers/Qt/fceuWrapper.cpp | 7 ++ src/profiler.cpp | 130 ++++++++++++++++++++++++------- src/profiler.h | 51 ++++++++++-- 6 files changed, 165 insertions(+), 36 deletions(-) diff --git a/src/drivers/Qt/AviRecord.cpp b/src/drivers/Qt/AviRecord.cpp index 319cc93c..e6a695d9 100644 --- a/src/drivers/Qt/AviRecord.cpp +++ b/src/drivers/Qt/AviRecord.cpp @@ -2580,6 +2580,7 @@ int FCEUD_AviGetFormatOpts( std::vector &formatList ) AviRecordDiskThread_t::AviRecordDiskThread_t( QObject *parent ) : QThread(parent) { + setObjectName( QString("AviRecordDiskThread") ); } //---------------------------------------------------- AviRecordDiskThread_t::~AviRecordDiskThread_t(void) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 7444b14a..ce714dd0 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -125,6 +125,13 @@ consoleWin_t::consoleWin_t(QWidget *parent) printf("Running on Platform: %s\n", QGuiApplication::platformName().toStdString().c_str() ); + QThread *thread = QThread::currentThread(); + + if (thread) + { + thread->setObjectName( QString("MainThread") ); + } + QApplication::setStyle( new fceuStyle() ); initHotKeys(); @@ -4604,6 +4611,9 @@ void consoleWin_t::updatePeriodic(void) updateCounter++; +#ifdef __FCEU_PROFILER_ENABLE__ + FCEU_profiler_log_thread_activity(); +#endif return; } @@ -4614,6 +4624,7 @@ emulatorThread_t::emulatorThread_t( QObject *parent ) pself = 0; #endif + setObjectName( QString("EmulationThread") ); } #if defined(__linux__) diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index d23de1b8..95df40f2 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -2507,6 +2507,7 @@ void QTraceLogView::paintEvent(QPaintEvent *event) TraceLogDiskThread_t::TraceLogDiskThread_t( QObject *parent ) : QThread(parent) { + setObjectName( QString("TraceLogDiskThread") ); } //---------------------------------------------------- TraceLogDiskThread_t::~TraceLogDiskThread_t(void) diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index 9954987a..b906a870 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -53,6 +53,7 @@ #include "../../cheat.h" #include "../../movie.h" #include "../../state.h" +#include "../../profiler.h" #include "../../version.h" #ifdef _S9XLUA_H @@ -1465,6 +1466,9 @@ int fceuWrapperUpdate( void ) emulatorHasMutex = 0; +#ifdef __FCEU_PROFILER_ENABLE__ + FCEU_profiler_log_thread_activity(); +#endif while ( SpeedThrottle() ) { // Input device processing is in main thread @@ -1478,6 +1482,9 @@ int fceuWrapperUpdate( void ) emulatorHasMutex = 0; +#ifdef __FCEU_PROFILER_ENABLE__ + FCEU_profiler_log_thread_activity(); +#endif msleep( 100 ); } return 0; diff --git a/src/profiler.cpp b/src/profiler.cpp index 263b07bd..26bd9340 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -23,6 +23,10 @@ #include +#ifdef __QT_DRIVER__ +#include +#endif + #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) #include #endif @@ -37,6 +41,7 @@ namespace FCEU { +static thread_local profileExecVector execList; static thread_local profilerFuncMap threadProfileMap; FILE *profilerManager::pLog = nullptr; @@ -64,10 +69,8 @@ void timeStampRecord::readNew(void) { #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) clock_gettime( CLOCK_REALTIME, &ts ); -#elif defined(WIN32) - QueryPerformanceCounter((LARGE_INTEGER*)&ts); #else - ts = 0; + QueryPerformanceCounter((LARGE_INTEGER*)&ts); #endif tsc = rdtsc(); } @@ -121,16 +124,19 @@ funcProfileRecord::funcProfileRecord(const char *fileNameStringLiteral, : fileLineNum(fileLineNumber), fileName(fileNameStringLiteral), funcName(funcNameStringLiteral), comment(commentStringLiteral) { - min.zero(); + min.fromSeconds(9); max.zero(); sum.zero(); numCalls = 0; recursionCount = 0; + + threadProfileMap.addRecord( fileNameStringLiteral, fileLineNumber, + funcNameStringLiteral, commentStringLiteral, this); } //------------------------------------------------------------------------- void funcProfileRecord::reset(void) { - min.zero(); + min.fromSeconds(9); max.zero(); sum.zero(); numCalls = 0; @@ -149,20 +155,9 @@ double funcProfileRecord::average(void) //------------------------------------------------------------------------- //---- Profile Scoped Function Class //------------------------------------------------------------------------- -profileFuncScoped::profileFuncScoped(const char *fileNameStringLiteral, - const int fileLineNumber, - const char *funcNameStringLiteral, - const char *commentStringLiteral) +profileFuncScoped::profileFuncScoped( funcProfileRecord *recordIn ) { - rec = nullptr; - - //if (threadProfileMap == nullptr) - //{ - // threadProfileMap = new profilerFuncMap(); - //} - - rec = threadProfileMap.findRecord( fileNameStringLiteral, fileLineNumber, - funcNameStringLiteral, commentStringLiteral, true); + rec = recordIn; if (rec) { @@ -181,17 +176,70 @@ profileFuncScoped::~profileFuncScoped(void) ts.readNew(); dt = ts - start; + rec->last = dt; rec->sum += dt; if (dt < rec->min) rec->min = dt; if (dt > rec->max) rec->max = dt; rec->recursionCount--; - //printf("%s: %u %f %f %f %f\n", rec->funcName, rec->numCalls, dt.toSeconds(), rec->average(), rec->min.toSeconds(), rec->max.toSeconds()); + execList._vec.push_back(*rec); + threadProfileMap.popStack(rec); } } //------------------------------------------------------------------------- +//---- Profile Execution Vector +//------------------------------------------------------------------------- +profileExecVector::profileExecVector(void) +{ + _vec.reserve( 10000 ); + + char threadName[128]; + char fileName[256]; + + strcpy( threadName, "MainThread"); + +#ifdef __QT_DRIVER__ + QThread *thread = QThread::currentThread(); + + if (thread) + { + //printf("Thread: %s\n", thread->objectName().toStdString().c_str()); + strcpy( threadName, thread->objectName().toStdString().c_str()); + } +#endif + sprintf( fileName, "fceux-profile-%s.log", threadName); + + logFp = ::fopen(fileName, "w"); + + if (logFp == nullptr) + { + printf("Error: Failed to create profiler logfile: %s\n", fileName); + } +} +//------------------------------------------------------------------------- +profileExecVector::~profileExecVector(void) +{ + if (logFp) + { + ::fclose(logFp); + } +} +//------------------------------------------------------------------------- +void profileExecVector::update(void) +{ + size_t n = _vec.size(); + + for (size_t i=0; isecond; - } - _map.clear(); - } + // for (auto it = _map.begin(); it != _map.end(); it++) + // { + // delete it->second; + // } + // _map.clear(); + //} } //------------------------------------------------------------------------- void profilerFuncMap::pushStack(funcProfileRecord *rec) @@ -228,6 +276,26 @@ void profilerFuncMap::popStack(funcProfileRecord *rec) stack.pop_back(); } //------------------------------------------------------------------------- +int profilerFuncMap::addRecord(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral, + funcProfileRecord *rec ) +{ + autoScopedLock aLock(_mapMtx); + char lineString[64]; + + sprintf( lineString, ":%i", fileLineNumber); + + std::string fname(fileNameStringLiteral); + + fname.append( lineString ); + + _map[fname] = rec; + + return 0; +} +//------------------------------------------------------------------------- funcProfileRecord *profilerFuncMap::findRecord(const char *fileNameStringLiteral, const int fileLineNumber, const char *funcNameStringLiteral, @@ -363,6 +431,12 @@ int profilerManager::removeThreadProfiler( profilerFuncMap *m, bool shouldDestro return result; } //------------------------------------------------------------------------- -// +} // namespace FCEU + +//------------------------------------------------------------------------- +int FCEU_profiler_log_thread_activity(void) +{ + FCEU::execList.update(); + return 0; } #endif // __FCEU_PROFILER_ENABLE__ diff --git a/src/profiler.h b/src/profiler.h index ceef4b15..a48d7c73 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -32,6 +32,7 @@ */ #ifdef __FCEU_PROFILER_ENABLE__ +#include #include #include #include @@ -123,7 +124,7 @@ namespace FCEU } else if (ts.tv_sec > op.ts.tv_sec) { - res = (ts.tv_sec > op.ts.tv_sec); + res = true; } return res; } @@ -135,9 +136,9 @@ namespace FCEU { res = (ts.tv_nsec < op.ts.tv_nsec); } - else if (ts.tv_sec > op.ts.tv_sec) + else if (ts.tv_sec < op.ts.tv_sec) { - res = (ts.tv_sec < op.ts.tv_sec); + res = true; } return res; } @@ -149,6 +150,13 @@ namespace FCEU tsc = 0; } + void fromSeconds(unsigned int sec) + { + ts.tv_sec = sec; + ts.tv_nsec = 0; + tsc = 0; + } + double toSeconds(void) { double sec = static_cast(ts.tv_sec) + ( static_cast(ts.tv_nsec) * 1.0e-9 ); @@ -213,6 +221,12 @@ namespace FCEU tsc = 0; } + void fromSeconds(unsigned int sec) + { + ts = sec * qpcFreq; + tsc = 0; + } + double toSeconds(void) { double sec = static_cast(ts) / static_cast(qpcFreq); @@ -235,6 +249,7 @@ namespace FCEU timeStampRecord min; timeStampRecord max; timeStampRecord sum; + timeStampRecord last; unsigned int numCalls; unsigned int recursionCount; @@ -253,20 +268,35 @@ namespace FCEU funcProfileRecord *rec; timeStampRecord start; - profileFuncScoped(const char *fileNameStringLiteral, - const int fileLineNumber, - const char *funcNameStringLiteral, - const char *commentStringLiteral); + profileFuncScoped( funcProfileRecord *recordIn ); ~profileFuncScoped(void); }; + struct profileExecVector + { + profileExecVector(void); + ~profileExecVector(void); + + void update(void); + + std::vector _vec; + + FILE *logFp; + }; + class profilerFuncMap { public: profilerFuncMap(); ~profilerFuncMap(); + int addRecord(const char *fileNameStringLiteral, + const int fileLineNumber, + const char *funcNameStringLiteral, + const char *commentStringLiteral, + funcProfileRecord *rec ); + funcProfileRecord *findRecord(const char *fileNameStringLiteral, const int fileLineNumber, const char *funcNameStringLiteral, @@ -312,7 +342,12 @@ namespace FCEU #define __FCEU_PROFILE_FUNC_NAME__ __func__ #endif -#define FCEU_PROFILE_FUNC(id, comment) FCEU::profileFuncScoped id( __FILE__, __LINE__, __FCEU_PROFILE_FUNC_NAME__, comment ) +#define FCEU_PROFILE_FUNC(id, comment) \ + static thread_local FCEU::funcProfileRecord id( __FILE__, __LINE__, __FCEU_PROFILE_FUNC_NAME__, comment ); \ + FCEU::profileFuncScoped id ## _unique_scope( &id ) + + +int FCEU_profiler_log_thread_activity(void); #else // __FCEU_PROFILER_ENABLE__ not defined From 584593816f8f561c142d3b2d6f3f7c08273fb046 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 15 May 2023 20:42:17 -0400 Subject: [PATCH 60/69] Remove debug symbol save call from load symbols function so that live file edits are not overwriten. Added a save debug symbols debugger menu action so that this can be explicitly performed. Fixes issue #642. --- src/debugsymboltable.cpp | 1 - src/drivers/Qt/ConsoleDebugger.cpp | 17 +++++++++++++++++ src/drivers/Qt/ConsoleDebugger.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/debugsymboltable.cpp b/src/debugsymboltable.cpp index 60089fcd..a8f92b7c 100644 --- a/src/debugsymboltable.cpp +++ b/src/debugsymboltable.cpp @@ -756,7 +756,6 @@ int debugSymbolTable_t::loadGameSymbols(void) { int nPages, pageSize, romSize = 0x10000; - this->save(); this->clear(); if ( GameInfo != nullptr ) diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp index 76b0eda6..406b7a74 100644 --- a/src/drivers/Qt/ConsoleDebugger.cpp +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -879,6 +879,16 @@ QMenuBar *ConsoleDebugger::buildMenuBar(void) symMenu->addAction(act); + // Symbols -> Save + act = new QAction(tr("&Save"), this); + //act->setShortcut(QKeySequence( tr("F7") ) ); + act->setStatusTip(tr("&Save")); + //act->setCheckable(true); + //act->setChecked( break_on_unlogged_data ); + connect( act, SIGNAL(triggered(void)), this, SLOT(saveSymbolsCB(void)) ); + + symMenu->addAction(act); + symMenu->addSeparator(); // Symbols -> Symbolic Debug @@ -2935,6 +2945,13 @@ void ConsoleDebugger::reloadSymbolsCB(void) FCEU_WRAPPER_UNLOCK(); } //---------------------------------------------------------------------------- +void ConsoleDebugger::saveSymbolsCB(void) +{ + FCEU_WRAPPER_LOCK(); + debugSymbolTable.save(); + FCEU_WRAPPER_UNLOCK(); +} +//---------------------------------------------------------------------------- void ConsoleDebugger::pcSetPlaceTop(void) { asmView->setPC_placement( 0 ); diff --git a/src/drivers/Qt/ConsoleDebugger.h b/src/drivers/Qt/ConsoleDebugger.h index f78f7d22..65278c80 100644 --- a/src/drivers/Qt/ConsoleDebugger.h +++ b/src/drivers/Qt/ConsoleDebugger.h @@ -632,6 +632,7 @@ class ConsoleDebugger : public QDialog void resizeToMinimumSizeHint(void); void resetCountersCB (void); void reloadSymbolsCB(void); + void saveSymbolsCB(void); void displayByteCodesCB(bool value); void displayTraceDataCB(bool value); void displayROMoffsetCB(bool value); From be38e34a0627f20deb42988f4c5d30e708891fc4 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 15 May 2023 21:11:19 -0400 Subject: [PATCH 61/69] For Qt GUI, added code to save/restore window geometry for ROM selection from archive dialog. --- src/drivers/Qt/ConsoleWindow.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index ce714dd0..673f7d06 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -2324,6 +2325,7 @@ int consoleWin_t::showListSelectDialog( const char *title, std::vector Date: Mon, 15 May 2023 21:29:42 -0400 Subject: [PATCH 62/69] Compiler warning fix for potential uninitialized var. --- src/drivers/Qt/GuiConf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/drivers/Qt/GuiConf.cpp b/src/drivers/Qt/GuiConf.cpp index 2147658b..3bc6b7a9 100644 --- a/src/drivers/Qt/GuiConf.cpp +++ b/src/drivers/Qt/GuiConf.cpp @@ -989,6 +989,8 @@ static int readQPaletteFromFile( const char *path, QPalette *pal ) rTxtMatch = NULL; + r = QPalette::WindowText; + for (k=0; k<30; k++) { From e597f6a4a36de78da31574a72416aeba6b048c29 Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 19 May 2023 22:31:01 -0400 Subject: [PATCH 63/69] Moved time stamp functions to its own file. Use new common time stamp functions in Qt GUI timing. --- src/CMakeLists.txt | 1 + src/drivers/Qt/ConsoleWindow.cpp | 1 + src/drivers/Qt/sdl-throttle.cpp | 22 +-- src/profiler.cpp | 75 ---------- src/profiler.h | 192 +------------------------ src/utils/timeStamp.cpp | 112 +++++++++++++++ src/utils/timeStamp.h | 231 +++++++++++++++++++++++++++++++ vc/vc14_fceux.vcxproj | 2 + 8 files changed, 360 insertions(+), 276 deletions(-) create mode 100644 src/utils/timeStamp.cpp create mode 100644 src/utils/timeStamp.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0957b7b..0437eaff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -527,6 +527,7 @@ set(SRC_CORE ${CMAKE_CURRENT_SOURCE_DIR}/utils/md5.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils/memory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils/mutex.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/timeStamp.cpp ) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 673f7d06..1cd7d828 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -58,6 +58,7 @@ #include "../../profiler.h" #include "../../version.h" #include "common/os_utils.h" +#include "utils/timeStamp.h" #ifdef _S9XLUA_H #include "../../fceulua.h" diff --git a/src/drivers/Qt/sdl-throttle.cpp b/src/drivers/Qt/sdl-throttle.cpp index f720e1ed..5603eb6c 100644 --- a/src/drivers/Qt/sdl-throttle.cpp +++ b/src/drivers/Qt/sdl-throttle.cpp @@ -22,6 +22,7 @@ #include "Qt/sdl.h" #include "Qt/throttle.h" +#include "utils/timeStamp.h" #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) #include @@ -60,21 +61,22 @@ extern bool turbo; double getHighPrecTimeStamp(void) { -#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) - struct timespec ts; double t; - clock_gettime( CLOCK_REALTIME, &ts ); + if (FCEU::timeStampModuleInitialized()) + { + FCEU::timeStampRecord ts; - t = (double)ts.tv_sec + (double)(ts.tv_nsec * 1.0e-9); -#else - double t; + ts.readNew(); - t = (double)SDL_GetTicks(); - - t = t * 1e-3; -#endif + t = ts.toSeconds(); + } + else + { + t = (double)SDL_GetTicks(); + t = t * 1e-3; + } return t; } diff --git a/src/profiler.cpp b/src/profiler.cpp index 26bd9340..077605cc 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -27,18 +27,10 @@ #include #endif -#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) -#include -#endif - #include "utils/mutex.h" #include "fceu.h" #include "profiler.h" -#if defined(WIN32) -#include -#endif - namespace FCEU { static thread_local profileExecVector execList; @@ -48,71 +40,6 @@ FILE *profilerManager::pLog = nullptr; static profilerManager pMgr; -//------------------------------------------------------------------------- -//---- Time Stamp Record -//------------------------------------------------------------------------- -#if defined(WIN32) -uint64_t timeStampRecord::qpcFreq = 0; -#include -#pragma intrinsic(__rdtsc) -#else -#include -#endif -uint64_t timeStampRecord::tscFreq = 0; - -static uint64_t rdtsc() -{ - return __rdtsc(); -} - -void timeStampRecord::readNew(void) -{ -#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) - clock_gettime( CLOCK_REALTIME, &ts ); -#else - QueryPerformanceCounter((LARGE_INTEGER*)&ts); -#endif - tsc = rdtsc(); -} - -static void calibrateTSC(void) -{ - constexpr int numSamples = 1; - timeStampRecord t1, t2, td; - uint64_t td_sum = 0; - double td_avg; - -#if defined(WIN32) - if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0) - { - printf("QueryPerformanceFrequency FAILED!\n"); - } -#endif - FCEU_printf("Running TSC Calibration: %i sec...\n", numSamples); - - for (int i=0; i(td_sum); - - timeStampRecord::tscFreq = static_cast( td_avg / td.toSeconds() ); - - FCEU_printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(), - static_cast(td.tsc), static_cast(timeStampRecord::tscFreq) * 1.0e-6 ); - } -} - //------------------------------------------------------------------------- //---- Function Profile Record //------------------------------------------------------------------------- @@ -371,8 +298,6 @@ profilerManager* profilerManager::getInstance(void) //------------------------------------------------------------------------- profilerManager::profilerManager(void) { - calibrateTSC(); - //printf("profilerManager Constructor\n"); if (pLog == nullptr) { diff --git a/src/profiler.h b/src/profiler.h index a48d7c73..61b9f5ae 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -45,200 +45,10 @@ #endif #include "utils/mutex.h" +#include "utils/timeStamp.h" namespace FCEU { - struct timeStampRecord - { -#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) - struct timespec ts; - uint64_t tsc; - - timeStampRecord(void) - { - ts.tv_sec = 0; - ts.tv_nsec = 0; - tsc = 0; - } - - timeStampRecord& operator = (const timeStampRecord& in) - { - ts = in.ts; - tsc = in.tsc; - return *this; - } - - timeStampRecord& operator += (const timeStampRecord& op) - { - ts.tv_sec += op.ts.tv_sec; - ts.tv_nsec += op.ts.tv_nsec; - - if (ts.tv_nsec >= 1000000000) - { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - tsc += op.tsc; - return *this; - } - - timeStampRecord operator + (const timeStampRecord& op) - { - timeStampRecord res; - - res.ts.tv_sec = ts.tv_sec + op.ts.tv_sec; - res.ts.tv_nsec = ts.tv_nsec + op.ts.tv_nsec; - - if (res.ts.tv_nsec >= 1000000000) - { - res.ts.tv_nsec -= 1000000000; - res.ts.tv_sec++; - } - res.tsc = tsc + op.tsc; - return res; - } - - timeStampRecord operator - (const timeStampRecord& op) - { - timeStampRecord res; - - res.ts.tv_sec = ts.tv_sec - op.ts.tv_sec; - res.ts.tv_nsec = ts.tv_nsec - op.ts.tv_nsec; - - if (res.ts.tv_nsec < 0) - { - res.ts.tv_nsec += 1000000000; - res.ts.tv_sec--; - } - res.tsc = tsc - op.tsc; - - return res; - } - - bool operator > (const timeStampRecord& op) - { - bool res = false; - if (ts.tv_sec == op.ts.tv_sec) - { - res = (ts.tv_nsec > op.ts.tv_nsec); - } - else if (ts.tv_sec > op.ts.tv_sec) - { - res = true; - } - return res; - } - - bool operator < (const timeStampRecord& op) - { - bool res = false; - if (ts.tv_sec == op.ts.tv_sec) - { - res = (ts.tv_nsec < op.ts.tv_nsec); - } - else if (ts.tv_sec < op.ts.tv_sec) - { - res = true; - } - return res; - } - - void zero(void) - { - ts.tv_sec = 0; - ts.tv_nsec = 0; - tsc = 0; - } - - void fromSeconds(unsigned int sec) - { - ts.tv_sec = sec; - ts.tv_nsec = 0; - tsc = 0; - } - - double toSeconds(void) - { - double sec = static_cast(ts.tv_sec) + ( static_cast(ts.tv_nsec) * 1.0e-9 ); - return sec; - } -#else // WIN32 - uint64_t ts; - uint64_t tsc; - - timeStampRecord(void) - { - ts = 0; - tsc = 0; - } - - timeStampRecord& operator = (const timeStampRecord& in) - { - ts = in.ts; - tsc = in.tsc; - return *this; - } - - timeStampRecord& operator += (const timeStampRecord& op) - { - ts += op.ts; - tsc += op.tsc; - return *this; - } - - timeStampRecord operator + (const timeStampRecord& op) - { - timeStampRecord res; - - res.ts = ts + op.ts; - res.tsc = tsc + op.tsc; - return res; - } - - timeStampRecord operator - (const timeStampRecord& op) - { - timeStampRecord res; - - res.ts = ts - op.ts; - res.tsc = tsc - op.tsc; - - return res; - } - - bool operator > (const timeStampRecord& op) - { - return ts > op.ts; - } - - bool operator < (const timeStampRecord& op) - { - return ts < op.ts; - } - - void zero(void) - { - ts = 0; - tsc = 0; - } - - void fromSeconds(unsigned int sec) - { - ts = sec * qpcFreq; - tsc = 0; - } - - double toSeconds(void) - { - double sec = static_cast(ts) / static_cast(qpcFreq); - return sec; - } - static uint64_t qpcFreq; -#endif - static uint64_t tscFreq; - - void readNew(void); - }; - struct funcProfileRecord { const int fileLineNum; diff --git a/src/utils/timeStamp.cpp b/src/utils/timeStamp.cpp new file mode 100644 index 00000000..d0e34e5a --- /dev/null +++ b/src/utils/timeStamp.cpp @@ -0,0 +1,112 @@ +// timeStamp.cpp +#include + +#include "timeStamp.h" + +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) +#include +#endif + +#if defined(WIN32) +#include +#endif + +//------------------------------------------------------------------------- +//---- Time Stamp Record +//------------------------------------------------------------------------- +#if defined(WIN32) +uint64_t timeStampRecord::qpcFreq = 0; +#include +#pragma intrinsic(__rdtsc) +#else +#include +#endif + +static uint64_t rdtsc() +{ + return __rdtsc(); +} + +namespace FCEU +{ + +uint64_t timeStampRecord::tscFreq = 0; + +void timeStampRecord::readNew(void) +{ +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + clock_gettime( CLOCK_REALTIME, &ts ); +#else + QueryPerformanceCounter((LARGE_INTEGER*)&ts); +#endif + tsc = rdtsc(); +} + +static timeStampRecord cal_t1, cal_t2, cal_td; + +class timeStampModule +{ + public: + timeStampModule(void) + { + printf("timeStampModuleInit\n"); + #if defined(WIN32) + if (QueryPerformanceFrequency((LARGE_INTEGER*)&StampRecord::qpcFreq) == 0) + { + printf("QueryPerformanceFrequency FAILED!\n"); + } + #endif + } +}; + +static timeStampModule module; + +bool timeStampModuleInitialized(void) +{ + bool initialized = false; +#if defined(WIN32) + initialized = timeStampRecord::qpcFreq != 0; +#else + initialized = true; +#endif + return initialized; +} + +void timeStampModuleCalibrate(int numSamples) +{ + timeStampRecord t1, t2, td; + uint64_t td_sum = 0; + double td_avg; + +#if defined(WIN32) + if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0) + { + printf("QueryPerformanceFrequency FAILED!\n"); + } +#endif + printf("Running TSC Calibration: %i sec...\n", numSamples); + + for (int i=0; i(td_sum); + + timeStampRecord::tscFreq = static_cast( td_avg / td.toSeconds() ); + + printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(), + static_cast(td.tsc), static_cast(timeStampRecord::tscFreq) * 1.0e-6 ); + } +} + +} // namespace FCEU diff --git a/src/utils/timeStamp.h b/src/utils/timeStamp.h new file mode 100644 index 00000000..497c1ab5 --- /dev/null +++ b/src/utils/timeStamp.h @@ -0,0 +1,231 @@ +// timeStamp.h +#pragma once + +#include + +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) +#include +#endif + +namespace FCEU +{ + struct timeStampRecord + { +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + struct timespec ts; + uint64_t tsc; + + timeStampRecord(void) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + tsc = 0; + } + + timeStampRecord& operator = (const timeStampRecord& in) + { + ts = in.ts; + tsc = in.tsc; + return *this; + } + + timeStampRecord& operator += (const timeStampRecord& op) + { + ts.tv_sec += op.ts.tv_sec; + ts.tv_nsec += op.ts.tv_nsec; + + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + tsc += op.tsc; + return *this; + } + + timeStampRecord operator + (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts.tv_sec = ts.tv_sec + op.ts.tv_sec; + res.ts.tv_nsec = ts.tv_nsec + op.ts.tv_nsec; + + if (res.ts.tv_nsec >= 1000000000) + { + res.ts.tv_nsec -= 1000000000; + res.ts.tv_sec++; + } + res.tsc = tsc + op.tsc; + return res; + } + + timeStampRecord operator - (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts.tv_sec = ts.tv_sec - op.ts.tv_sec; + res.ts.tv_nsec = ts.tv_nsec - op.ts.tv_nsec; + + if (res.ts.tv_nsec < 0) + { + res.ts.tv_nsec += 1000000000; + res.ts.tv_sec--; + } + res.tsc = tsc - op.tsc; + + return res; + } + + bool operator > (const timeStampRecord& op) + { + bool res = false; + if (ts.tv_sec == op.ts.tv_sec) + { + res = (ts.tv_nsec > op.ts.tv_nsec); + } + else if (ts.tv_sec > op.ts.tv_sec) + { + res = true; + } + return res; + } + + bool operator < (const timeStampRecord& op) + { + bool res = false; + if (ts.tv_sec == op.ts.tv_sec) + { + res = (ts.tv_nsec < op.ts.tv_nsec); + } + else if (ts.tv_sec < op.ts.tv_sec) + { + res = true; + } + return res; + } + + void zero(void) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + tsc = 0; + } + + void fromSeconds(unsigned int sec) + { + ts.tv_sec = sec; + ts.tv_nsec = 0; + tsc = 0; + } + + double toSeconds(void) + { + double sec = static_cast(ts.tv_sec) + ( static_cast(ts.tv_nsec) * 1.0e-9 ); + return sec; + } + + uint64_t toCounts(void) + { + return (ts.tv_sec * 1000000000) + ts.tv_nsec; + } + + static uint64_t countFreq(void) + { + return 1000000000; + } +#else // WIN32 + uint64_t ts; + uint64_t tsc; + + timeStampRecord(void) + { + ts = 0; + tsc = 0; + } + + timeStampRecord& operator = (const timeStampRecord& in) + { + ts = in.ts; + tsc = in.tsc; + return *this; + } + + timeStampRecord& operator += (const timeStampRecord& op) + { + ts += op.ts; + tsc += op.tsc; + return *this; + } + + timeStampRecord operator + (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts = ts + op.ts; + res.tsc = tsc + op.tsc; + return res; + } + + timeStampRecord operator - (const timeStampRecord& op) + { + timeStampRecord res; + + res.ts = ts - op.ts; + res.tsc = tsc - op.tsc; + + return res; + } + + bool operator > (const timeStampRecord& op) + { + return ts > op.ts; + } + + bool operator < (const timeStampRecord& op) + { + return ts < op.ts; + } + + void zero(void) + { + ts = 0; + tsc = 0; + } + + void fromSeconds(unsigned int sec) + { + ts = sec * qpcFreq; + tsc = 0; + } + + double toSeconds(void) + { + double sec = static_cast(ts) / static_cast(qpcFreq); + return sec; + } + + uint64_t toCounts(void) + { + return ts; + } + + static uint64_t countFreq(void) + { + return qpcFreq; + } + static uint64_t qpcFreq; +#endif + static uint64_t tscFreq; + + static bool tscValid(void){ return tscFreq != 0; }; + + void readNew(void); + }; + + bool timeStampModuleInitialized(void); + + // Call this function to calibrate the estimated TSC frequency + void timeStampModuleCalibrate(int numSamples = 1); + +} // namespace FCEU + diff --git a/vc/vc14_fceux.vcxproj b/vc/vc14_fceux.vcxproj index d37cff0f..43e5dc9d 100644 --- a/vc/vc14_fceux.vcxproj +++ b/vc/vc14_fceux.vcxproj @@ -743,6 +743,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + @@ -1152,6 +1153,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + From 2d896c10bc06b0171dc9c473d610f0df9968c78b Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 19 May 2023 22:35:22 -0400 Subject: [PATCH 64/69] Build fix for win32. --- src/utils/timeStamp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/timeStamp.cpp b/src/utils/timeStamp.cpp index d0e34e5a..2f772409 100644 --- a/src/utils/timeStamp.cpp +++ b/src/utils/timeStamp.cpp @@ -15,7 +15,6 @@ //---- Time Stamp Record //------------------------------------------------------------------------- #if defined(WIN32) -uint64_t timeStampRecord::qpcFreq = 0; #include #pragma intrinsic(__rdtsc) #else @@ -31,6 +30,9 @@ namespace FCEU { uint64_t timeStampRecord::tscFreq = 0; +#if defined(WIN32) +uint64_t timeStampRecord::qpcFreq = 0; +#endif void timeStampRecord::readNew(void) { From 6fb899e9a1efd83f3936cf0a3facfc3e10b5ed14 Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 19 May 2023 22:37:13 -0400 Subject: [PATCH 65/69] Another win32 build fix. --- src/utils/timeStamp.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/timeStamp.cpp b/src/utils/timeStamp.cpp index 2f772409..e8eff27b 100644 --- a/src/utils/timeStamp.cpp +++ b/src/utils/timeStamp.cpp @@ -44,8 +44,6 @@ void timeStampRecord::readNew(void) tsc = rdtsc(); } -static timeStampRecord cal_t1, cal_t2, cal_td; - class timeStampModule { public: @@ -53,7 +51,7 @@ class timeStampModule { printf("timeStampModuleInit\n"); #if defined(WIN32) - if (QueryPerformanceFrequency((LARGE_INTEGER*)&StampRecord::qpcFreq) == 0) + if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0) { printf("QueryPerformanceFrequency FAILED!\n"); } From 1a3170fd680f62631231df738f0084a9d4d946df Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 21 May 2023 16:44:20 -0400 Subject: [PATCH 66/69] Changed Qt GUI frame throttling to use new time stamp record for computing timing. This fixes an issue where frame timing starts to lose precision when application time gets to float large values by performing calcultions using integer math --- src/drivers/Qt/sdl-throttle.cpp | 74 +++++++------ src/utils/timeStamp.cpp | 13 ++- src/utils/timeStamp.h | 183 ++++++++++++++++++++++++++++---- 3 files changed, 212 insertions(+), 58 deletions(-) diff --git a/src/drivers/Qt/sdl-throttle.cpp b/src/drivers/Qt/sdl-throttle.cpp index 5603eb6c..800ad121 100644 --- a/src/drivers/Qt/sdl-throttle.cpp +++ b/src/drivers/Qt/sdl-throttle.cpp @@ -37,7 +37,8 @@ static const double Fastest = 32; // 32x speed (around 1920 fps on NTSC) static const double Normal = 1.0; // 1x speed (around 60 fps on NTSC) static uint32 frameLateCounter = 0; -static double Lasttime=0, Nexttime=0, Latetime=0; +static FCEU::timeStampRecord Lasttime, Nexttime, Latetime; +static FCEU::timeStampRecord DesiredFrameTime, HalfFrameTime, QuarterFrameTime, DoubleFrameTime; static double desired_frametime = (1.0 / 60.099823); static double desired_frameRate = (60.099823); static double baseframeRate = (60.099823); @@ -119,9 +120,9 @@ static void setTimer( double hz ) //printf("Timer Set: %li ns\n", ispec.it_value.tv_nsec ); - Lasttime = getHighPrecTimeStamp(); - Nexttime = Lasttime + desired_frametime; - Latetime = Nexttime + (desired_frametime*0.50); + Lasttime.readNew(); + Nexttime = Lasttime + DesiredFrameTime; + Latetime = Nexttime + HalfFrameTime; } #endif @@ -270,10 +271,17 @@ RefreshThrottleFPS(void) if ( T < 0 ) T = 1; + DesiredFrameTime.fromSeconds( desired_frametime ); + HalfFrameTime = DesiredFrameTime / 2; + QuarterFrameTime = DesiredFrameTime / 4; + DoubleFrameTime = DesiredFrameTime * 2; + + //printf("FrameTime: %f %f %f %f \n", DesiredFrameTime.toSeconds(), + // HalfFrameTime.toSeconds(), QuarterFrameTime.toSeconds(), DoubleFrameTime.toSeconds() ); //printf("FrameTime: %llu %llu %f %lf \n", fps, fps >> 24, hz, desired_frametime ); - Lasttime=0; - Nexttime=0; + Lasttime.zero(); + Nexttime.zero(); InFrame=0; #ifdef __linux__ @@ -297,18 +305,17 @@ double getFrameRateAdjustmentRatio(void) return frmRateAdjRatio; } -int highPrecSleep( double timeSeconds ) +static int highPrecSleep( FCEU::timeStampRecord &ts ) { int ret = 0; #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) struct timespec req, rem; - req.tv_sec = (long)timeSeconds; - req.tv_nsec = (long)((timeSeconds - (double)req.tv_sec) * 1e9); + req = ts.toTimeSpec(); ret = nanosleep( &req, &rem ); #else - SDL_Delay( (long)(timeSeconds * 1e3) ); + SDL_Delay( ts.toMilliSeconds() ); #endif return ret; } @@ -323,39 +330,37 @@ SpeedThrottle(void) { return 0; /* Done waiting */ } - double time_left; - double cur_time, idleStart; - double frame_time = desired_frametime; - double halfFrame = 0.500 * frame_time; - double quarterFrame = 0.250 * frame_time; + FCEU::timeStampRecord cur_time, idleStart, time_left; - idleStart = cur_time = getHighPrecTimeStamp(); + cur_time.readNew(); + idleStart = cur_time; - if (Lasttime < 1.0) + if (Lasttime.isZero()) { + //printf("Lasttime Reset\n"); Lasttime = cur_time; - Latetime = Lasttime + 2.0*frame_time; + Latetime = Lasttime + DoubleFrameTime; } if (!InFrame) { InFrame = 1; - Nexttime = Lasttime + frame_time; - Latetime = Nexttime + halfFrame; + Nexttime = Lasttime + DesiredFrameTime; + Latetime = Nexttime + HalfFrameTime; } if (cur_time >= Nexttime) { - time_left = 0; + time_left.zero(); } else { time_left = Nexttime - cur_time; } - if (time_left > 50) + if (time_left.toMilliSeconds() > 50) { - time_left = 50; + time_left.fromMilliSeconds(50); /* In order to keep input responsive, don't wait too long at once */ /* 50 ms wait gives us a 20 Hz responsetime which is nice. */ } @@ -381,7 +386,7 @@ SpeedThrottle(void) } } } - else if ( time_left > 0 ) + else if ( !time_left.isZero() ) { highPrecSleep( time_left ); } @@ -394,7 +399,7 @@ SpeedThrottle(void) } } #else - if ( time_left > 0 ) + if ( !time_left.isZero() ) { highPrecSleep( time_left ); } @@ -408,14 +413,15 @@ SpeedThrottle(void) } #endif - cur_time = getHighPrecTimeStamp(); + cur_time.readNew(); - if ( cur_time >= (Nexttime - quarterFrame) ) + if ( cur_time >= (Nexttime - QuarterFrameTime) ) { if ( keepFrameTimeStats ) { + FCEU::timeStampRecord diffTime = (cur_time - Lasttime); - frameDeltaCur = (cur_time - Lasttime); + frameDeltaCur = diffTime.toSeconds(); if ( frameDeltaCur < frameDeltaMin ) { @@ -426,7 +432,9 @@ SpeedThrottle(void) frameDeltaMax = frameDeltaCur; } - frameIdleCur = (cur_time - idleStart); + diffTime = (cur_time - idleStart); + + frameIdleCur = diffTime.toSeconds(); if ( frameIdleCur < frameIdleMin ) { @@ -440,14 +448,14 @@ SpeedThrottle(void) //printf("Frame Sleep Time: %f Target Error: %f us\n", time_left * 1e6, (cur_time - Nexttime) * 1e6 ); } Lasttime = Nexttime; - Nexttime = Lasttime + frame_time; - Latetime = Nexttime + halfFrame; + Nexttime = Lasttime + DesiredFrameTime; + Latetime = Nexttime + HalfFrameTime; if ( cur_time >= Nexttime ) { Lasttime = cur_time; - Nexttime = Lasttime + frame_time; - Latetime = Nexttime + halfFrame; + Nexttime = Lasttime + DesiredFrameTime; + Latetime = Nexttime + HalfFrameTime; } return 0; /* Done waiting */ } diff --git a/src/utils/timeStamp.cpp b/src/utils/timeStamp.cpp index e8eff27b..b746fc5e 100644 --- a/src/utils/timeStamp.cpp +++ b/src/utils/timeStamp.cpp @@ -29,7 +29,7 @@ static uint64_t rdtsc() namespace FCEU { -uint64_t timeStampRecord::tscFreq = 0; +uint64_t timeStampRecord::_tscFreq = 0; #if defined(WIN32) uint64_t timeStampRecord::qpcFreq = 0; #endif @@ -63,16 +63,15 @@ static timeStampModule module; bool timeStampModuleInitialized(void) { - bool initialized = false; #if defined(WIN32) - initialized = timeStampRecord::qpcFreq != 0; + bool initialized = timeStampRecord::qpcFreq != 0; #else - initialized = true; + bool initialized = true; #endif return initialized; } -void timeStampModuleCalibrate(int numSamples) +void timeStampRecord::tscCalibrate(int numSamples) { timeStampRecord t1, t2, td; uint64_t td_sum = 0; @@ -102,10 +101,10 @@ void timeStampModuleCalibrate(int numSamples) td_avg = static_cast(td_sum); - timeStampRecord::tscFreq = static_cast( td_avg / td.toSeconds() ); + timeStampRecord::_tscFreq = static_cast( td_avg / td.toSeconds() ); printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(), - static_cast(td.tsc), static_cast(timeStampRecord::tscFreq) * 1.0e-6 ); + static_cast(td.tsc), static_cast(timeStampRecord::_tscFreq) * 1.0e-6 ); } } diff --git a/src/utils/timeStamp.h b/src/utils/timeStamp.h index 497c1ab5..0ac67c42 100644 --- a/src/utils/timeStamp.h +++ b/src/utils/timeStamp.h @@ -9,11 +9,10 @@ namespace FCEU { - struct timeStampRecord + class timeStampRecord { + public: #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) - struct timespec ts; - uint64_t tsc; timeStampRecord(void) { @@ -37,7 +36,7 @@ namespace FCEU if (ts.tv_nsec >= 1000000000) { ts.tv_nsec -= 1000000000; - ts.tv_sec++; + ts.tv_sec++; } tsc += op.tsc; return *this; @@ -53,7 +52,7 @@ namespace FCEU if (res.ts.tv_nsec >= 1000000000) { res.ts.tv_nsec -= 1000000000; - res.ts.tv_sec++; + res.ts.tv_sec++; } res.tsc = tsc + op.tsc; return res; @@ -76,30 +75,84 @@ namespace FCEU return res; } + timeStampRecord operator * (const unsigned int multiplier) + { + timeStampRecord res; + + res.ts.tv_sec = ts.tv_sec * multiplier; + res.ts.tv_nsec = ts.tv_nsec * multiplier; + + if (res.ts.tv_nsec >= 1000000000) + { + res.ts.tv_nsec -= 1000000000; + res.ts.tv_sec++; + } + res.tsc = tsc * multiplier; + + return res; + } + + timeStampRecord operator / (const unsigned int divisor) + { + timeStampRecord res; + + res.ts.tv_sec = ts.tv_sec / divisor; + res.ts.tv_nsec = ts.tv_nsec / divisor; + res.tsc = tsc / divisor; + + return res; + } + bool operator > (const timeStampRecord& op) { - bool res = false; + bool res; if (ts.tv_sec == op.ts.tv_sec) { res = (ts.tv_nsec > op.ts.tv_nsec); } - else if (ts.tv_sec > op.ts.tv_sec) + else { - res = true; + res = (ts.tv_sec > op.ts.tv_sec); + } + return res; + } + bool operator >= (const timeStampRecord& op) + { + bool res; + if (ts.tv_sec == op.ts.tv_sec) + { + res = (ts.tv_nsec >= op.ts.tv_nsec); + } + else + { + res = (ts.tv_sec >= op.ts.tv_sec); } return res; } bool operator < (const timeStampRecord& op) { - bool res = false; + bool res; if (ts.tv_sec == op.ts.tv_sec) { res = (ts.tv_nsec < op.ts.tv_nsec); } - else if (ts.tv_sec < op.ts.tv_sec) + else { - res = true; + res = (ts.tv_sec < op.ts.tv_sec); + } + return res; + } + bool operator <= (const timeStampRecord& op) + { + bool res; + if (ts.tv_sec == op.ts.tv_sec) + { + res = (ts.tv_nsec <= op.ts.tv_nsec); + } + else + { + res = (ts.tv_sec <= op.ts.tv_sec); } return res; } @@ -111,6 +164,11 @@ namespace FCEU tsc = 0; } + bool isZero(void) + { + return (ts.tv_sec == 0) && (ts.tv_nsec == 0); + } + void fromSeconds(unsigned int sec) { ts.tv_sec = sec; @@ -118,12 +176,33 @@ namespace FCEU tsc = 0; } + void fromSeconds(double sec) + { + double ns; + ts.tv_sec = static_cast(sec); + ns = (sec - static_cast(ts.tv_sec)) * 1.0e9; + ts.tv_nsec = static_cast(ns); + tsc = 0; + } + double toSeconds(void) { double sec = static_cast(ts.tv_sec) + ( static_cast(ts.tv_nsec) * 1.0e-9 ); return sec; } + void fromMilliSeconds(uint64_t ms) + { + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms * 1000000) - (ts.tv_sec * 1000000000); + } + + uint64_t toMilliSeconds(void) + { + uint64_t ms = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000 ); + return ms; + } + uint64_t toCounts(void) { return (ts.tv_sec * 1000000000) + ts.tv_nsec; @@ -133,9 +212,12 @@ namespace FCEU { return 1000000000; } + + struct timespec toTimeSpec(void) + { + return ts; + } #else // WIN32 - uint64_t ts; - uint64_t tsc; timeStampRecord(void) { @@ -176,15 +258,43 @@ namespace FCEU return res; } + timeStampRecord operator * (const unsigned int multiplier) + { + timeStampRecord res; + + res.ts = ts * multiplier; + res.tsc = tsc * multiplier; + + return res; + } + + timeStampRecord operator / (const unsigned int divisor) + { + timeStampRecord res; + + res.ts = ts / divisor; + res.tsc = tsc / divisor; + + return res; + } + bool operator > (const timeStampRecord& op) { return ts > op.ts; } + bool operator >= (const timeStampRecord& op) + { + return ts >= op.ts; + } bool operator < (const timeStampRecord& op) { return ts < op.ts; } + bool operator <= (const timeStampRecord& op) + { + return ts <= op.ts; + } void zero(void) { @@ -192,18 +302,41 @@ namespace FCEU tsc = 0; } + bool isZero(void) + { + return (ts == 0); + } + + void fromSeconds(unsigned int sec) { ts = sec * qpcFreq; tsc = 0; } + void fromSeconds(double sec) + { + ts = static_cast(sec * static_cast(qpcFreq)); + tsc = 0; + } + double toSeconds(void) { double sec = static_cast(ts) / static_cast(qpcFreq); return sec; } + void fromMilliSeconds(uint64_t ms) + { + ts = (ms * qpcFreq) / 1000; + } + + uint64_t toMilliSeconds(void) + { + uint64_t ms = (ts * 1000) / qpcFreq; + return ms; + } + uint64_t toCounts(void) { return ts; @@ -213,19 +346,33 @@ namespace FCEU { return qpcFreq; } - static uint64_t qpcFreq; #endif - static uint64_t tscFreq; + uint64_t getTSC(void){ return tsc; }; + + static uint64_t tscFreq(void) + { + return _tscFreq; + } static bool tscValid(void){ return tscFreq != 0; }; + // Call this function to calibrate the estimated TSC frequency + static void tscCalibrate(int numSamples = 0); + void readNew(void); + + private: +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + struct timespec ts; +#else // Win32 + uint64_t ts; + static uint64_t qpcFreq; +#endif + uint64_t tsc; + static uint64_t _tscFreq; }; bool timeStampModuleInitialized(void); - // Call this function to calibrate the estimated TSC frequency - void timeStampModuleCalibrate(int numSamples = 1); - } // namespace FCEU From 70aa850c156e67d25f6c8de3aa24dc9d0975cd62 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 21 May 2023 16:56:52 -0400 Subject: [PATCH 67/69] Build fix for win64 Qt GUI. --- src/utils/timeStamp.cpp | 16 +++++++++++----- src/utils/timeStamp.h | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/utils/timeStamp.cpp b/src/utils/timeStamp.cpp index b746fc5e..f62d019f 100644 --- a/src/utils/timeStamp.cpp +++ b/src/utils/timeStamp.cpp @@ -43,6 +43,15 @@ void timeStampRecord::readNew(void) #endif tsc = rdtsc(); } +#if defined(WIN32) +void timeStampRecord::qpcCalibrate(void) +{ + if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0) + { + printf("QueryPerformanceFrequency FAILED!\n"); + } +} +#endif class timeStampModule { @@ -51,10 +60,7 @@ class timeStampModule { printf("timeStampModuleInit\n"); #if defined(WIN32) - if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0) - { - printf("QueryPerformanceFrequency FAILED!\n"); - } + timeStampRecord::qpcCalibrate(); #endif } }; @@ -64,7 +70,7 @@ static timeStampModule module; bool timeStampModuleInitialized(void) { #if defined(WIN32) - bool initialized = timeStampRecord::qpcFreq != 0; + bool initialized = timeStampRecord::countFreq() != 0; #else bool initialized = true; #endif diff --git a/src/utils/timeStamp.h b/src/utils/timeStamp.h index 0ac67c42..e7458107 100644 --- a/src/utils/timeStamp.h +++ b/src/utils/timeStamp.h @@ -346,6 +346,8 @@ namespace FCEU { return qpcFreq; } + + static void qpcCalibrate(void); #endif uint64_t getTSC(void){ return tsc; }; From 43cd7ef60aead6b9e6b91c8f6c57c43bbd53ea81 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 21 May 2023 18:41:24 -0400 Subject: [PATCH 68/69] Updated Qt driver FCEUD_GetTime and FCEUD_GetTimeFreq functions to allow for a higher timing resolution. --- src/drivers/Qt/fceuWrapper.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index b906a870..c907b104 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -62,6 +62,7 @@ #include "common/os_utils.h" #include "common/configSys.h" +#include "utils/timeStamp.h" #include "../../oldmovie.h" #include "../../types.h" @@ -191,7 +192,23 @@ const char *FCEUD_GetCompilerString(void) uint64 FCEUD_GetTime(void) { - return SDL_GetTicks(); + uint64 t; + + if (FCEU::timeStampModuleInitialized()) + { + FCEU::timeStampRecord ts; + + ts.readNew(); + + t = ts.toCounts(); + } + else + { + t = (double)SDL_GetTicks(); + + t = t * 1e-3; + } + return t; } /** @@ -201,7 +218,13 @@ uint64 FCEUD_GetTimeFreq(void) { // SDL_GetTicks() is in milliseconds - return 1000; + uint64 f = 1000; + + if (FCEU::timeStampModuleInitialized()) + { + f = FCEU::timeStampRecord::countFreq(); + } + return f; } /** From 530fec6aff4d1dbe446f3e0a9ecc66c7d7340d82 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 28 May 2023 11:14:01 -0400 Subject: [PATCH 69/69] Cleanup a few hard coded constants in timeStamp.h --- src/utils/timeStamp.h | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/utils/timeStamp.h b/src/utils/timeStamp.h index e7458107..4af6b774 100644 --- a/src/utils/timeStamp.h +++ b/src/utils/timeStamp.h @@ -12,7 +12,11 @@ namespace FCEU class timeStampRecord { public: + static constexpr uint64_t ONE_SEC_TO_MILLI = 1000; + #if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + static constexpr long int ONE_SEC_TO_NANO = 1000000000; + static constexpr long int MILLI_TO_NANO = 1000000; timeStampRecord(void) { @@ -33,9 +37,9 @@ namespace FCEU ts.tv_sec += op.ts.tv_sec; ts.tv_nsec += op.ts.tv_nsec; - if (ts.tv_nsec >= 1000000000) + if (ts.tv_nsec >= ONE_SEC_TO_NANO) { - ts.tv_nsec -= 1000000000; + ts.tv_nsec -= ONE_SEC_TO_NANO; ts.tv_sec++; } tsc += op.tsc; @@ -49,9 +53,9 @@ namespace FCEU res.ts.tv_sec = ts.tv_sec + op.ts.tv_sec; res.ts.tv_nsec = ts.tv_nsec + op.ts.tv_nsec; - if (res.ts.tv_nsec >= 1000000000) + if (res.ts.tv_nsec >= ONE_SEC_TO_NANO) { - res.ts.tv_nsec -= 1000000000; + res.ts.tv_nsec -= ONE_SEC_TO_NANO; res.ts.tv_sec++; } res.tsc = tsc + op.tsc; @@ -67,7 +71,7 @@ namespace FCEU if (res.ts.tv_nsec < 0) { - res.ts.tv_nsec += 1000000000; + res.ts.tv_nsec += ONE_SEC_TO_NANO; res.ts.tv_sec--; } res.tsc = tsc - op.tsc; @@ -82,9 +86,9 @@ namespace FCEU res.ts.tv_sec = ts.tv_sec * multiplier; res.ts.tv_nsec = ts.tv_nsec * multiplier; - if (res.ts.tv_nsec >= 1000000000) + if (res.ts.tv_nsec >= ONE_SEC_TO_NANO) { - res.ts.tv_nsec -= 1000000000; + res.ts.tv_nsec -= ONE_SEC_TO_NANO; res.ts.tv_sec++; } res.tsc = tsc * multiplier; @@ -193,24 +197,24 @@ namespace FCEU void fromMilliSeconds(uint64_t ms) { - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms * 1000000) - (ts.tv_sec * 1000000000); + ts.tv_sec = ms / ONE_SEC_TO_MILLI; + ts.tv_nsec = (ms * MILLI_TO_NANO) - (ts.tv_sec * ONE_SEC_TO_NANO); } uint64_t toMilliSeconds(void) { - uint64_t ms = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000 ); + uint64_t ms = (ts.tv_sec * ONE_SEC_TO_MILLI) + (ts.tv_nsec / MILLI_TO_NANO ); return ms; } uint64_t toCounts(void) { - return (ts.tv_sec * 1000000000) + ts.tv_nsec; + return (ts.tv_sec * ONE_SEC_TO_NANO) + ts.tv_nsec; } static uint64_t countFreq(void) { - return 1000000000; + return ONE_SEC_TO_NANO; } struct timespec toTimeSpec(void) @@ -328,12 +332,12 @@ namespace FCEU void fromMilliSeconds(uint64_t ms) { - ts = (ms * qpcFreq) / 1000; + ts = (ms * qpcFreq) / ONE_SEC_TO_MILLI; } uint64_t toMilliSeconds(void) { - uint64_t ms = (ts * 1000) / qpcFreq; + uint64_t ms = (ts * ONE_SEC_TO_MILLI) / qpcFreq; return ms; }