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 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/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 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; 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 2d2f94ad..0437eaff 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} ) @@ -41,12 +47,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 ) @@ -93,6 +102,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 ) @@ -110,6 +125,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} ) @@ -293,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 @@ -504,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 ) @@ -526,6 +550,8 @@ 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/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 @@ -551,6 +577,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 @@ -635,14 +662,15 @@ 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} ${${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/boards/addrlatch.cpp b/src/boards/addrlatch.cpp index b3f44c88..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; @@ -220,6 +221,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) { @@ -341,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); @@ -392,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 --------------------------- diff --git a/src/boards/coolgirl.cpp b/src/boards/coolgirl.cpp index d9a22331..3e24994f 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 @@ -103,14 +103,12 @@ 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* WRAM = NULL; +static uint8* SAVE_FLASH = NULL; static uint8* CFI; static uint8 sram_enabled = 0; @@ -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; } } @@ -509,9 +507,8 @@ 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; - } + } // write byte if ((flash_state == 4) && @@ -557,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: @@ -573,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]; @@ -886,7 +881,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; } @@ -1176,7 +1171,6 @@ static DECLFW(COOLGIRL_WRITE) { // Mapper #1 - MMC1 /* - r0 - load register flag0 - 16KB of SRAM (SOROM) */ if (mapper == 0b010000) @@ -1230,7 +1224,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; @@ -1397,9 +1391,6 @@ static DECLFW(COOLGIRL_WRITE) { } // Mapper #112 - /* - r0[2:0] - internal register - */ if (mapper == 0b010101) { switch (get_bits(A, "14:13")) @@ -1464,6 +1455,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; @@ -1813,7 +1805,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]; } } @@ -1963,32 +1955,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]; } } } @@ -2238,15 +2217,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) { @@ -2256,12 +2233,8 @@ 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_"); +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); if (WRAM_SIZE > 0) { @@ -2271,7 +2244,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); } } @@ -2280,7 +2253,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); 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) { diff --git a/src/boards/mmc3.cpp b/src/boards/mmc3.cpp index 17d9509b..e7e229cf 100644 --- a/src/boards/mmc3.cpp +++ b/src/boards/mmc3.cpp @@ -1136,20 +1136,73 @@ 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); + 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) { -// GN-30A - начальная маска должна быть FF + uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x7F : 0xFF); + 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) { + 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, 0, 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 +1212,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 +1221,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/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..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); } } @@ -139,7 +165,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 +179,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 +195,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/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/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/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/drivers/Qt/AboutWindow.cpp b/src/drivers/Qt/AboutWindow.cpp index 81871444..20719222 100644 --- a/src/drivers/Qt/AboutWindow.cpp +++ b/src/drivers/Qt/AboutWindow.cpp @@ -30,9 +30,14 @@ #endif #ifdef _USE_X264 +#include #include "x264.h" #endif +#ifdef _USE_LIBARCHIVE +#include +#endif + #ifdef _USE_LIBAV #ifdef __cplusplus extern "C" @@ -201,6 +206,23 @@ 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]) + { + if (libArcVersion[i]) + { + sprintf( stmp, " %s %s\n", libArcName[i], libArcVersion[i]); + credits->insertPlainText( stmp ); + } + i++; + } +#endif #ifdef _S9XLUA_H sprintf( stmp, " Compiled with %s\n", LUA_RELEASE ); 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/CheatsConf.cpp b/src/drivers/Qt/CheatsConf.cpp index a67ef5af..7c8b3ec9 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); @@ -705,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)); @@ -738,8 +733,6 @@ void GuiCheatsDialog_t::showActiveCheatList(bool redraw) enaCheats->setChecked(!globalCheatDisabled); - actvCheatRedraw = redraw; - if (redraw) { actvCheatList->clear(); @@ -747,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/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 6f64cb4f..406b7a74 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; } @@ -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 @@ -1834,28 +1844,26 @@ void ConsoleDebugger::selBmAddrChanged(const QString &txt) //printf("selBmAddrVal = %04X\n", selBmAddrVal ); } //---------------------------------------------------------------------------- -void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool forceAccept ) +DebuggerBreakpointEditor::DebuggerBreakpointEditor(int editIndex, watchpointinfo *wpIn, QWidget *parent) + : QDialog(parent) { - int ret; - QDialog dialog(this); + editIdx = editIndex; + wp = wpIn; + 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") ); + setWindowTitle( tr("Edit Breakpoint") ); } else { - dialog.setWindowTitle( tr("Add Breakpoint") ); + setWindowTitle( tr("Add Breakpoint") ); } hbox = new QHBoxLayout(); @@ -1874,6 +1882,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo 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 ); @@ -1906,6 +1917,11 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo 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 ); @@ -1917,6 +1933,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo 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 ); @@ -1928,15 +1947,17 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo 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( cancelButton ); - hbox->addWidget( okButton ); + hbox->addWidget( msgLbl, 5 ); + hbox->addWidget( cancelButton, 1 ); + hbox->addWidget( okButton, 1 ); - connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); - connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); + 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 ) ); @@ -2036,99 +2057,270 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo ebp->setChecked(true); } - dialog.setLayout( mainLayout ); + 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) +{ + 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 ); + + if (allEntriesValid) + { + 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")); + } + else + { + msgLbl->clear(); + } +} +//---------------------------------------------------------------------------- +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 ) + { + 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(); + ret = dialog->exec(); } - if ( ret == QDialog::Accepted ) + 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 ); - } - } + bpListUpdate( false ); } } //---------------------------------------------------------------------------- @@ -2439,7 +2631,7 @@ static void DeleteBreak(int sel) if (watchpoint[sel].cond) { - freeTree(watchpoint[sel].cond); + delete watchpoint[sel].cond; } if (watchpoint[sel].condText) { @@ -2488,7 +2680,7 @@ void debuggerClearAllBreakpoints(void) { if (watchpoint[i].cond) { - freeTree(watchpoint[i].cond); + delete watchpoint[i].cond; } if (watchpoint[i].condText) { @@ -2499,7 +2691,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; @@ -2753,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 ); @@ -2898,7 +3097,7 @@ void ConsoleDebugger::debugStepBackCB(void) { FCEU_WRAPPER_LOCK(); FCEUD_TraceLoggerBackUpInstruction(); - updateWindowData(); + updateWindowData(QAsmView::UPDATE_ALL); hexEditorUpdateMemoryValues(true); hexEditorRequestUpdateAll(); lastBpIdx = BREAK_TYPE_STEP; @@ -3093,8 +3292,7 @@ void ConsoleDebugger::seekPCCB (void) setRegsFromEntry(); //updateAllDebugWindows(); } - windowUpdateReq = true; - //asmView->scrollToPC(); + windowUpdateReq = QAsmView::UPDATE_ALL; } //---------------------------------------------------------------------------- void ConsoleDebugger::openChangePcDialog(void) @@ -3148,7 +3346,7 @@ void ConsoleDebugger::openChangePcDialog(void) { X.PC = sbox->value(); - windowUpdateReq = true; + windowUpdateReq = QAsmView::UPDATE_ALL; } } //---------------------------------------------------------------------------- @@ -4142,20 +4340,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) @@ -4173,10 +4376,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(); @@ -4346,7 +4549,7 @@ void ConsoleDebugger::breakPointNotify( int bpNum ) } } - windowUpdateReq = true; + windowUpdateReq = QAsmView::UPDATE_ALL; } //---------------------------------------------------------------------------- void ConsoleDebugger::hbarChanged(int value) @@ -4461,7 +4664,6 @@ void debuggerWindowSetFocus(bool val) { dbgWin->activateWindow(); dbgWin->raise(); - dbgWin->setFocus(); } } //---------------------------------------------------------------------------- @@ -4470,11 +4672,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 be5b2e0b..65278c80 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; @@ -420,6 +422,51 @@ 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 typeChanged(bool checked); + void addressTextChanged( const QString &text ); + void conditionTextChanged( const QString &text ); +}; + class ConsoleDebugger : public QDialog { Q_OBJECT @@ -428,7 +475,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); @@ -437,7 +484,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; @@ -530,7 +577,8 @@ class ConsoleDebugger : public QDialog ColorMenuItem *pcColorAct; int selBmAddrVal; - bool windowUpdateReq; + enum QAsmView::UpdateType windowUpdateReq; + bool startedTraceLogger; private: @@ -584,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); @@ -625,6 +674,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/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.cpp b/src/drivers/Qt/ConsoleViewerGL.cpp index c877cefe..b6210272 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; @@ -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; @@ -502,7 +504,7 @@ void ConsoleViewGL_t::transfer2LocalBuffer(void) } else { - memcpy( localBuf, src, cpSize ); + copyPixels32( dest, src, cpSize, alphaMask); } } 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.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 +#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; + + 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 new file mode 100644 index 00000000..e461168d --- /dev/null +++ b/src/drivers/Qt/ConsoleViewerQWidget.cpp @@ -0,0 +1,505 @@ +/* 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 "../../profiler.h" +#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("Initializing 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 ) + { + memset32( localBuf, alphaMask, 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) +{ + //printf("Destroying QPainter Viewport\n"); + + 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 ); + } + painter.setRenderHint( QPainter::SmoothPixmapTransform, linearFilter ); + + int rowPitch = nesWidth * sizeof(uint32_t); + + QImage tmpImage( (const uchar*)localBuf, nesWidth, nesHeight, rowPitch, QImage::Format_ARGB32); + + //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.cpp b/src/drivers/Qt/ConsoleViewerSDL.cpp index 04216592..31cccffb 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; @@ -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; @@ -240,7 +242,7 @@ void ConsoleViewSDL_t::transfer2LocalBuffer(void) } else { - memcpy( localBuf, src, cpSize ); + copyPixels32( dest, src, cpSize, alphaMask); } } 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 0613edc9..1cd7d828 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -53,8 +54,11 @@ #include "../../input.h" #include "../../movie.h" #include "../../wave.h" +#include "../../state.h" +#include "../../profiler.h" #include "../../version.h" #include "common/os_utils.h" +#include "utils/timeStamp.h" #ifdef _S9XLUA_H #include "../../fceulua.h" @@ -78,6 +82,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" @@ -110,7 +115,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); @@ -122,6 +127,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(); @@ -129,8 +141,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; @@ -145,21 +159,9 @@ 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 ) - { - viewport_SDL = new ConsoleViewSDL_t(this); - - setCentralWidget(viewport_SDL); - } - else - { - viewport_GL = new ConsoleViewGL_t(this); - - setCentralWidget(viewport_GL); - } - setViewportAspect(); + loadVideoDriver( videoDriver ); setWindowTitle( tr(FCEU_NAME_AND_VERSION) ); setWindowIcon(QIcon(":fceux1.png")); @@ -225,13 +227,9 @@ 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 ); } //this->resize( reqSize ); } @@ -274,17 +272,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() ); @@ -325,14 +312,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; - } + unloadVideoDriver(); + delete mutex; // LoadGame() checks for an IP and if it finds one begins a network session @@ -365,26 +346,18 @@ 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(); } 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(); } return; } @@ -487,21 +460,13 @@ 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(); } dw = 0; @@ -572,13 +537,9 @@ 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 ); } } @@ -648,25 +609,17 @@ 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); } } 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); } } @@ -674,13 +627,9 @@ 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(); } return s; } @@ -692,14 +641,11 @@ 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 ) ); } + firstResize = false; } //printf("%i x %i \n", event->size().width(), event->size().height() ); @@ -784,9 +730,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; @@ -920,6 +878,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) @@ -1214,6 +1175,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"))); @@ -1982,85 +1952,164 @@ 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 ( 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() ) - { - takeCentralWidget(); - } - delete viewport_SDL; - - viewport_SDL = NULL; + unloadVideoDriver(); } 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(); + + connect( viewport_SDL, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) ); } - 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(); + + connect( viewport_GL, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) ); + } + break; + default: + case ConsoleViewerBase::VIDEO_DRIVER_QPAINTER: + { + viewport_QWidget = new ConsoleViewQWidget_t(this); + + viewport_Interface = static_cast(viewport_QWidget); + + setCentralWidget(viewport_QWidget); + + setViewportAspect(); + + viewport_QWidget->init(); + + connect( viewport_QWidget, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) ); + } + break; } // Reload Viewport Cursor Type and Visibility @@ -2256,15 +2305,10 @@ 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(); } } //--------------------------------------------------------------------------- @@ -2282,6 +2326,7 @@ int consoleWin_t::showListSelectDialog( const char *title, std::vector grabWindow( viewport_SDL->winId() ); } + else if ( viewport_QWidget ) + { + image = screen->grabWindow( viewport_QWidget->winId() ); + } for (u = 0; u < 99999; ++u) { @@ -3173,17 +3243,11 @@ 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(); } dw = w.width() - v.width(); @@ -3846,6 +3910,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; @@ -4435,19 +4508,15 @@ int consoleWin_t::getPeriodicInterval(void) void consoleWin_t::transferVideoBuffer(void) { + FCEU_PROFILE_FUNC(prof, "VideoXfer"); if ( nes_shm->blitUpdated ) { 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(); } } } @@ -4476,6 +4545,7 @@ void consoleWin_t::emuFrameFinish(void) void consoleWin_t::updatePeriodic(void) { + FCEU_PROFILE_FUNC(prof, "updatePeriodic"); static bool eventProcessingInProg = false; if ( eventProcessingInProg ) @@ -4551,6 +4621,9 @@ void consoleWin_t::updatePeriodic(void) updateCounter++; +#ifdef __FCEU_PROFILER_ENABLE__ + FCEU_profiler_log_thread_activity(); +#endif return; } @@ -4561,6 +4634,7 @@ emulatorThread_t::emulatorThread_t( QObject *parent ) pself = 0; #endif + setObjectName( QString("EmulationThread") ); } #if defined(__linux__) diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index eda9fd34..1fc69749 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 ); @@ -155,6 +158,7 @@ class consoleWin_t : public QMainWindow #endif int loadVideoDriver( int driverId, bool force = false ); + int unloadVideoDriver(void); double getRefreshRate(void){ return refreshRate; } @@ -211,6 +215,7 @@ class consoleWin_t : public QMainWindow QAction *hotkeyConfig; QAction *paletteConfig; QAction *guiConfig; + QAction *stateRecordConfig; QAction *timingConfig; QAction *movieConfig; QAction *autoResume; @@ -344,6 +349,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); @@ -439,6 +445,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*); @@ -459,6 +467,7 @@ class consoleWin_t : public QMainWindow void toggleUseBgPaletteForVideo(bool); void videoBgColorChanged( QColor &c ); void loadRomRequestCB( QString s ); + void videoDriverDestroyed( QObject *obj ); }; 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/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(); } 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++) { diff --git a/src/drivers/Qt/HexEditor.cpp b/src/drivers/Qt/HexEditor.cpp index 04f1365d..b1c5d8aa 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); } } //---------------------------------------------------------------------------- @@ -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 @@ -2830,6 +2836,7 @@ void QHexEdit::keyPressEvent(QKeyEvent *event) } else { // Edit Area is Hex + key = int(event->text()[0].toUpper().toLatin1()); if ( ::isxdigit( key ) ) @@ -4222,7 +4229,6 @@ int hexEditorOpenFromDebugger( int mode, int addr ) { win->activateWindow(); win->raise(); - win->setFocus(); } win->editor->setMode( mode ); 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/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/StateRecorderConf.cpp b/src/drivers/Qt/StateRecorderConf.cpp new file mode 100644 index 00000000..d0cfcba3 --- /dev/null +++ b/src/drivers/Qt/StateRecorderConf.cpp @@ -0,0 +1,674 @@ +/* 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 + +#include "Qt/throttle.h" +#include "Qt/fceuWrapper.h" +#include "Qt/StateRecorderConf.h" + +#include "../../fceu.h" +#include "../../state.h" +#include "../../driver.h" +#include "../../emufile.h" + +//---------------------------------------------------------------------------- +StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) + : QDialog(parent) +{ + QVBoxLayout *mainLayout, *vbox1; + QHBoxLayout *hbox, *hbox1; + QGroupBox *frame, *frame1; + QGridLayout *grid, *memStatsGrid, *sysStatusGrid; + QSettings settings; + int opt; + + setWindowTitle("State Recorder Config"); + + mainLayout = new QVBoxLayout(); + 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); + snapMinutes->setMaximum(60); + 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); + + opt = 0; + g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &opt); + snapMinutes->setValue(opt); + + opt = 3; + 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)) ); + + 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 ); + + 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); + + connect( cmprLvlCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(compressionLevelChanged(int)) ); + + hbox->addWidget(cmprLvlCbox); + + frame->setLayout(hbox); + grid->addWidget( frame, 1, 1 ); + + 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(); + + 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); + + 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"), 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(opt); + + 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, 4, 3, 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); + 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 ); + + frame = new QGroupBox( tr("CPU Usage:") ); + hbox = new QHBoxLayout(); + frame->setLayout(hbox); + + 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))); + connect(snapFrameSelBtn, SIGNAL(clicked(void)), this, SLOT(snapFrameModeClicked(void))); + connect(snapTimeSelBtn , SIGNAL(clicked(void)), this, SLOT(snapTimeModeClicked(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); + + closeButton = new QPushButton( tr("Close") ); + applyButton = new QPushButton( tr("Apply") ); + + 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); + + setLayout(mainLayout); + + restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray()); + + 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) +{ + //printf("Destroy State Recorder Config Window\n"); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::closeEvent(QCloseEvent *event) +{ + QSettings settings; + settings.setValue("stateRecorderWindow/geometry", saveGeometry()); + + if (dataSavedCheck()) + { + done(0); + deleteLater(); + event->accept(); + } +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::closeWindow(void) +{ + QSettings settings; + settings.setValue("stateRecorderWindow/geometry", saveGeometry()); + + if (dataSavedCheck()) + { + done(0); + deleteLater(); + } +} +//---------------------------------------------------------------------------- +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 ); + 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()) + { + 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(); + + 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); + g_config->setOption("SDL.StateRecorderPauseOnLoad", config.pauseOnLoad); + g_config->setOption("SDL.StateRecorderPauseDuration", config.loadPauseTimeSeconds); + g_config->setOption("SDL.StateRecorderEnable", recorderEnable->isChecked() ); + 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::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(); + + 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; + + FCEU_WRAPPER_LOCK(); + FCEU_StateRecorderSetEnabled( ena ); + FCEU_WRAPPER_UNLOCK(); + + g_config->setOption("SDL.StateRecorderEnable", ena ); + g_config->save(); +} +//---------------------------------------------------------------------------- +void StateRecorderDialog_t::spinBoxValueChanged(int newValue) +{ + recalcMemoryUsage(); +} +//---------------------------------------------------------------------------- +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]; + + float fsnapMin = 1.0; + float fhistMin = static_cast( historyDuration->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; + 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) ); + + saveTimeMs = 0.0; + + if (GameInfo) + { + constexpr int numIterations = 10; + double ts_start, ts_end; + + FCEU_WRAPPER_LOCK(); + + EMUFILE_MEMORY em; + int compressionLevel = cmprLvlCbox->currentData().toInt(); + + ts_start = getHighPrecTimeStamp(); + + // 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(); + } + + 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) ); + + sprintf( stmp, "%.02f ms", saveTimeMs); + saveTimeLbl->setText( tr(stmp) ); +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/StateRecorderConf.h b/src/drivers/Qt/StateRecorderConf.h new file mode 100644 index 00000000..e57faf2f --- /dev/null +++ b/src/drivers/Qt/StateRecorderConf.h @@ -0,0 +1,83 @@ +// StateRecorderConf.h +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct StateRecorderConfigData; + +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 *snapFrames; + QSpinBox *historyDuration; + QSpinBox *pauseDuration; + QCheckBox *recorderEnable; + QLineEdit *numSnapsLbl; + QLineEdit *snapMemSizeLbl; + QLineEdit *totalMemUsageLbl; + QLineEdit *saveTimeLbl; + QPushButton *applyButton; + QPushButton *closeButton; + QComboBox *cmprLvlCbox; + QComboBox *pauseOnLoadCbox; + QGroupBox *snapTimeGroup; + QGroupBox *snapFramesGroup; + QRadioButton *snapFrameSelBtn; + QRadioButton *snapTimeSelBtn; + QLineEdit *recStatusLbl; + QLineEdit *recBufSizeLbl; + QPushButton *startStopButton; + QProgressBar *bufUsage; + QTimer *updateTimer; + + double saveTimeMs; + bool snapUseTime; + + 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); +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); + void pauseOnLoadChanged(int index); +}; 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..95df40f2 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" @@ -1211,7 +1212,6 @@ void openTraceLoggerWindow(QWidget *parent) { traceLogWindow->activateWindow(); traceLogWindow->raise(); - traceLogWindow->setFocus(); return; } //printf("Open Trace Logger Window\n"); @@ -2188,7 +2188,7 @@ void QTraceLogView::openBpEditWindow(int editIdx, watchpointinfo *wp, traceRecor numWPs++; } - updateAllDebuggerWindows(); + updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL); } } } @@ -2233,7 +2233,7 @@ void QTraceLogView::openDebugSymbolEditWindow(int addr, int bank) if (ret == QDialog::Accepted) { - updateAllDebuggerWindows(); + updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL); } } //---------------------------------------------------------------------------- @@ -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/config.cpp b/src/drivers/Qt/config.cpp index fc314283..949c591e 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; @@ -748,6 +754,16 @@ 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.StateRecorderTimingMode", 0); + config->addOption("SDL.StateRecorderFramesBetweenSnaps", 60); + 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/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/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index ef80bd2a..c907b104 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -52,6 +52,8 @@ #include "../../fceu.h" #include "../../cheat.h" #include "../../movie.h" +#include "../../state.h" +#include "../../profiler.h" #include "../../version.h" #ifdef _S9XLUA_H @@ -60,6 +62,7 @@ #include "common/os_utils.h" #include "common/configSys.h" +#include "utils/timeStamp.h" #include "../../oldmovie.h" #include "../../types.h" @@ -189,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; } /** @@ -199,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; } /** @@ -972,6 +997,45 @@ int fceuWrapperInit( int argc, char *argv[] ) // load the hotkeys from the config life setHotKeys(); + // 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; + int pauseOnLoadTime = 3; + 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); + g_config->getOption("SDL.StateRecorderPauseOnLoad", &pauseOnLoad); + g_config->getOption("SDL.StateRecorderPauseDuration", &pauseOnLoadTime); + + 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); + + FCEU_StateRecorderSetEnabled( srEnable ); + FCEU_StateRecorderSetConfigData( srConfig ); + } + + // Rom Load if (romIndex >= 0) { QFileInfo fi( argv[romIndex] ); @@ -1425,6 +1489,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 @@ -1438,25 +1505,27 @@ int fceuWrapperUpdate( void ) emulatorHasMutex = 0; +#ifdef __FCEU_PROFILER_ENABLE__ + FCEU_profiler_log_thread_activity(); +#endif msleep( 100 ); } 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; @@ -1486,18 +1555,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 ) @@ -1547,75 +1911,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; } @@ -1628,82 +1931,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; } 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; } diff --git a/src/drivers/Qt/ppuViewer.cpp b/src/drivers/Qt/ppuViewer.cpp index d151da42..39071698 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(); @@ -637,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 ] ) { @@ -3338,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 ); diff --git a/src/drivers/Qt/sdl-throttle.cpp b/src/drivers/Qt/sdl-throttle.cpp index f720e1ed..800ad121 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 @@ -36,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); @@ -60,21 +62,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; } @@ -117,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 @@ -268,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__ @@ -295,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; } @@ -321,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. */ } @@ -379,7 +386,7 @@ SpeedThrottle(void) } } } - else if ( time_left > 0 ) + else if ( !time_left.isZero() ) { highPrecSleep( time_left ); } @@ -392,7 +399,7 @@ SpeedThrottle(void) } } #else - if ( time_left > 0 ) + if ( !time_left.isZero() ) { highPrecSleep( time_left ); } @@ -406,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 ) { @@ -424,7 +432,9 @@ SpeedThrottle(void) frameDeltaMax = frameDeltaCur; } - frameIdleCur = (cur_time - idleStart); + diffTime = (cur_time - idleStart); + + frameIdleCur = diffTime.toSeconds(); if ( frameIdleCur < frameIdleMin ) { @@ -438,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/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) 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..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" @@ -116,6 +117,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() @@ -208,6 +210,8 @@ static void FCEU_CloseGame(void) GameInterface(GI_CLOSE); + FCEU_StateRecorderStop(); + FCEUI_StopMovie(); ResetExState(0, 0); @@ -592,6 +596,12 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen } FCEU_fclose(fp); + + if ( FCEU_StateRecorderIsEnabled() ) + { + FCEU_StateRecorderStart(); + } + return GameInfo; } @@ -725,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; @@ -756,6 +767,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 @@ -779,7 +806,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); @@ -793,6 +820,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski AutoFire(); UpdateAutosave(); + FCEU_StateRecorderUpdate(); #ifdef _S9XLUA_H FCEU_LuaFrameBoundary(); @@ -1254,6 +1282,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/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/ines.cpp b/src/ines.cpp index 477f0abe..e533a0f8 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}, @@ -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 1a59a324..8c335b55 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 *); @@ -280,6 +281,7 @@ void Mapper406_Init(CartInfo *); void Mapper474_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/lua-engine.cpp b/src/lua-engine.cpp index 6aa16a52..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,53 +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); } -FCEU_MAYBE_UNUSED 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! @@ -6137,7 +6096,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/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) { diff --git a/src/profiler.cpp b/src/profiler.cpp new file mode 100644 index 00000000..077605cc --- /dev/null +++ b/src/profiler.cpp @@ -0,0 +1,367 @@ +/* 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 + +#ifdef __QT_DRIVER__ +#include +#endif + +#include "utils/mutex.h" +#include "fceu.h" +#include "profiler.h" + +namespace FCEU +{ +static thread_local profileExecVector execList; +static thread_local profilerFuncMap threadProfileMap; + +FILE *profilerManager::pLog = nullptr; + +static profilerManager pMgr; + +//------------------------------------------------------------------------- +//---- 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.fromSeconds(9); + max.zero(); + sum.zero(); + numCalls = 0; + recursionCount = 0; + + threadProfileMap.addRecord( fileNameStringLiteral, fileLineNumber, + funcNameStringLiteral, commentStringLiteral, this); +} +//------------------------------------------------------------------------- +void funcProfileRecord::reset(void) +{ + min.fromSeconds(9); + 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( funcProfileRecord *recordIn ) +{ + rec = recordIn; + + 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->last = dt; + rec->sum += dt; + if (dt < rec->min) rec->min = dt; + if (dt > rec->max) rec->max = dt; + + rec->recursionCount--; + + 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(); + //} +} +//------------------------------------------------------------------------- +void profilerFuncMap::pushStack(funcProfileRecord *rec) +{ + stack.push_back(rec); +} +//------------------------------------------------------------------------- +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, + const char *commentStringLiteral, + bool create) +{ + autoScopedLock aLock(_mapMtx); + 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; +} +//------------------------------------------------------------------------- +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) +{ + //printf("profilerManager Constructor\n"); + if (pLog == nullptr) + { + pLog = stdout; + } + + if (instance == nullptr) + { + instance = this; + } +} + +profilerManager::~profilerManager(void) +{ + //printf("profilerManager Destructor\n"); + { + autoScopedLock aLock(threadListMtx); + threadList.clear(); + } + + if (pLog && (pLog != stdout)) + { + fclose(pLog); pLog = nullptr; + } + if (instance == this) + { + instance = 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; +} +//------------------------------------------------------------------------- +} // 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 new file mode 100644 index 00000000..61b9f5ae --- /dev/null +++ b/src/profiler.h @@ -0,0 +1,167 @@ +/* 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 +#include + + +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) +#include +#endif + +#include "utils/mutex.h" +#include "utils/timeStamp.h" + +namespace FCEU +{ + struct funcProfileRecord + { + const int fileLineNum; + const char *fileName; + const char *funcName; + const char *comment; + + timeStampRecord min; + timeStampRecord max; + timeStampRecord sum; + timeStampRecord last; + 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( 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, + 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; + }; + + class profilerManager + { + public: + profilerManager(void); + ~profilerManager(void); + + int addThreadProfiler( profilerFuncMap *m ); + int removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy = false ); + + static FILE *pLog; + + static profilerManager *getInstance(); + private: + + mutex threadListMtx; + std::list threadList; + static profilerManager *instance; + }; +} + +#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) \ + 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 + +#define FCEU_PROFILE_FUNC(id, comment) + +#endif // __FCEU_PROFILER_ENABLE__ + diff --git a/src/state.cpp b/src/state.cpp index 0ef771e8..622168a2 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]; @@ -1179,3 +1179,389 @@ void RedoLoadState() redoLS = false; //Flag that RedoLoadState can not be run again undoLS = true; //Flag that LoadBackup can be run again } + +//----------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------- +//----------- Save State History ---------------- +//----------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------- +static StateRecorderConfigData stateRecorderConfig; + +class StateRecorder +{ + public: + StateRecorder(void) + { + loadConfig( stateRecorderConfig ); + + for (int i=0; i config.historyDurationMinutes) + { + config.historyDurationMinutes = config.timeBetweenSnapsMinutes; + } + + if (config.timingMode) + { + 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 ); + } + 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 ); + + compressionLevel = config.compressionLevel; + loadPauseTime = config.loadPauseTimeSeconds; + pauseOnLoad = config.pauseOnLoad; + } + + void update(void) + { + bool isPaused = EmulationPaused ? true : false; + + unsigned int curFrame = static_cast(currFrameCounter); + + if (!isPaused && loadIndexReset) + { + ringHead = (lastState + 1) % ringBufSize; + + frameCounter = curFrame; + + loadIndexReset = false; + } + + if (!isPaused && (curFrame > frameCounter) ) + { + frameCounter = curFrame; + + if ( (frameCounter % framesPerSnap) == 0 ) + { + 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 ); + + lastState = ringHead; + + ringHead = (ringHead + 1) % ringBufSize; + + if (ringStart == ringHead) + { + ringStart = (ringHead + 1) % ringBufSize; + } + } + } + } + + int loadStateRelativeToEnd( int numSnapsFromLatest ) + { + if (numSnapsFromLatest < 0) + { + numSnapsFromLatest = 0; + } + numSnapsFromLatest = numSnapsFromLatest % ringBufSize; + + int snapIdx = ringHead - numSnapsFromLatest - 1; + + loadStateByIndex(snapIdx); + + return 0; + } + + int loadStateByIndex( int snapIdx ) + { + if (snapIdx < 0) + { + snapIdx = snapIdx + ringBufSize; + } + snapIdx = snapIdx % ringBufSize; + + EMUFILE_MEMORY *em = ringBuf[ snapIdx ]; + + em->fseek(SEEK_SET, 0); + + FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP ); + + frameCounter = lastLoadFrame = static_cast(currFrameCounter); + + lastState = snapIdx; + loadIndexReset = true; + + 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; + } + + 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; + } + + int getStartIndex(void) + { + return ringStart; + } + + 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(); + } + + size_t ringBufferSize(void) + { + return ringBuf.size(); + } + static bool enabled; + static int lastState; + private: + + void doSnap(void) + { + + } + + std::vector ringBuf; + int ringHead; + int ringTail; + int ringStart; + int ringBufSize; + int compressionLevel; + int loadPauseTime; + StateRecorderConfigData::PauseType pauseOnLoad; + 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) +{ + 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; +} + +void FCEU_StateRecorderSetEnabled(bool enabled) +{ + StateRecorder::enabled = enabled; +} + +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; + + if (stateRecorder != nullptr) + { + ret = stateRecorder->loadStateByIndex(snapIndex); + } + return ret; +} + +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; +} +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 4157dc9e..705d7e86 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 @@ -78,3 +79,56 @@ 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) + +struct StateRecorderConfigData +{ + float historyDurationMinutes; + float timeBetweenSnapsMinutes; + int framesBetweenSnaps; + int compressionLevel; + int loadPauseTimeSeconds; + + enum TimingType + { + FRAMES = 0, + TIME, + } timingMode; + + enum PauseType + { + NO_PAUSE = 0, + TEMPORARY_PAUSE, + FULL_PAUSE, + } pauseOnLoad; + + 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 ) + { + return memcmp( this, &other, sizeof(StateRecorderConfigData) ) == 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_StateRecorderGetMaxSnaps(void); +int FCEU_StateRecorderGetNumSnapsSaved(void); +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); diff --git a/src/unif.cpp b/src/unif.cpp index 9bcc44f7..e47a8c54 100644 --- a/src/unif.cpp +++ b/src/unif.cpp @@ -475,7 +475,8 @@ 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 } }; 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..e7a2968d 100644 --- a/src/utils/mutex.h +++ b/src/utils/mutex.h @@ -1,4 +1,5 @@ // mutex.h +#pragma once #ifdef __QT_DRIVER__ #include @@ -32,6 +33,7 @@ namespace FCEU { public: autoScopedLock( mutex *mtx ); + autoScopedLock( mutex &mtx ); ~autoScopedLock(void); private: diff --git a/src/utils/timeStamp.cpp b/src/utils/timeStamp.cpp new file mode 100644 index 00000000..f62d019f --- /dev/null +++ b/src/utils/timeStamp.cpp @@ -0,0 +1,117 @@ +// 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) +#include +#pragma intrinsic(__rdtsc) +#else +#include +#endif + +static uint64_t rdtsc() +{ + return __rdtsc(); +} + +namespace FCEU +{ + +uint64_t timeStampRecord::_tscFreq = 0; +#if defined(WIN32) +uint64_t timeStampRecord::qpcFreq = 0; +#endif + +void timeStampRecord::readNew(void) +{ +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + clock_gettime( CLOCK_REALTIME, &ts ); +#else + QueryPerformanceCounter((LARGE_INTEGER*)&ts); +#endif + tsc = rdtsc(); +} +#if defined(WIN32) +void timeStampRecord::qpcCalibrate(void) +{ + if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0) + { + printf("QueryPerformanceFrequency FAILED!\n"); + } +} +#endif + +class timeStampModule +{ + public: + timeStampModule(void) + { + printf("timeStampModuleInit\n"); + #if defined(WIN32) + timeStampRecord::qpcCalibrate(); + #endif + } +}; + +static timeStampModule module; + +bool timeStampModuleInitialized(void) +{ +#if defined(WIN32) + bool initialized = timeStampRecord::countFreq() != 0; +#else + bool initialized = true; +#endif + return initialized; +} + +void timeStampRecord::tscCalibrate(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..4af6b774 --- /dev/null +++ b/src/utils/timeStamp.h @@ -0,0 +1,384 @@ +// timeStamp.h +#pragma once + +#include + +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) +#include +#endif + +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) + { + 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 >= ONE_SEC_TO_NANO) + { + ts.tv_nsec -= ONE_SEC_TO_NANO; + 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 >= ONE_SEC_TO_NANO) + { + res.ts.tv_nsec -= ONE_SEC_TO_NANO; + 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 += ONE_SEC_TO_NANO; + res.ts.tv_sec--; + } + res.tsc = tsc - op.tsc; + + 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 >= ONE_SEC_TO_NANO) + { + res.ts.tv_nsec -= ONE_SEC_TO_NANO; + 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; + 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; + 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; + 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; + 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; + } + + void zero(void) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + tsc = 0; + } + + bool isZero(void) + { + return (ts.tv_sec == 0) && (ts.tv_nsec == 0); + } + + void fromSeconds(unsigned int sec) + { + ts.tv_sec = sec; + ts.tv_nsec = 0; + 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 / 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 * ONE_SEC_TO_MILLI) + (ts.tv_nsec / MILLI_TO_NANO ); + return ms; + } + + uint64_t toCounts(void) + { + return (ts.tv_sec * ONE_SEC_TO_NANO) + ts.tv_nsec; + } + + static uint64_t countFreq(void) + { + return ONE_SEC_TO_NANO; + } + + struct timespec toTimeSpec(void) + { + return ts; + } +#else // WIN32 + + 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; + } + + 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) + { + ts = 0; + 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) / ONE_SEC_TO_MILLI; + } + + uint64_t toMilliSeconds(void) + { + uint64_t ms = (ts * ONE_SEC_TO_MILLI) / qpcFreq; + return ms; + } + + uint64_t toCounts(void) + { + return ts; + } + + static uint64_t countFreq(void) + { + return qpcFreq; + } + + static void qpcCalibrate(void); +#endif + + 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); + +} // namespace FCEU + 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); + } + } + } +} 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) diff --git a/vc/vc14_fceux.vcxproj b/vc/vc14_fceux.vcxproj index d9b7fb3e..6d40ff05 100644 --- a/vc/vc14_fceux.vcxproj +++ b/vc/vc14_fceux.vcxproj @@ -744,6 +744,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + @@ -1008,6 +1009,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + @@ -1137,6 +1139,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + @@ -1151,6 +1154,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + 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)