From 277b151ada2545b042aeed4a97cc6432055c0069 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 6 Sep 2024 13:39:42 +0200 Subject: [PATCH 01/29] update vcpkg and nixpkgs --- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 4 ++-- cmake/ConfigureVcpkg.cmake | 2 +- flake.lock | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 9a5830e0..b2dda12d 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -28,7 +28,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 1de2026f28ead93ff1773e6e680387643e914ea1 + vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da - name: Build uses: lukka/run-cmake@v10 with: diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 5470e7b8..30dbd2a8 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -4,7 +4,7 @@ on: push: branches: - master - - ci/vcpkg-update + - ci/* pull_request: branches: - master @@ -27,7 +27,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 1de2026f28ead93ff1773e6e680387643e914ea1 + vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da - name: Configure run: cmake --preset=release-mingw-x86_64 - name: Build diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index fa6e2b00..bd256d91 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -9,7 +9,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") endif() FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" - GIT_TAG 2024.07.12 + GIT_TAG 2024.08.23 SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") FetchContent_MakeAvailable(vcpkg) endif() diff --git a/flake.lock b/flake.lock index 6e99d27f..9f0fe239 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1723175592, - "narHash": "sha256-M0xJ3FbDUc4fRZ84dPGx5VvgFsOzds77KiBMW/mMTnI=", + "lastModified": 1725432240, + "narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5e0ca22929f3342b19569b21b2f3462f053e497b", + "rev": "ad416d066ca1222956472ab7d0555a6946746a80", "type": "github" }, "original": { From d18524d5ace2897ad4bf68d6fe097c818575afe7 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 6 Sep 2024 19:44:48 +0200 Subject: [PATCH 02/29] Nix: Add dev shell for building using vcpkg --- flake.nix | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 487bf197..d670ea76 100644 --- a/flake.nix +++ b/flake.nix @@ -65,8 +65,27 @@ apps.default = flake-utils.lib.mkApp { drv = self.packages.${system}.default; }; - devShells.default = pkgs.mkShell { - inputsFrom = [ self.packages.${system}.default ]; + devShells = { + default = pkgs.mkShell { + inputsFrom = [ self.packages.${system}.default ]; + }; + + # Shell for building static melonDS release builds with vcpkg + # Use mkShellNoCC to ensure Nix's gcc/clang and stdlib isn't used + vcpkg = pkgs.mkShellNoCC { + packages = with pkgs; [ + autoconf + autoconf-archive + automake + cmake + cups.dev # Needed by qtbase despite not enabling print support + git + iconv.dev + libtool + ninja + pkg-config + ]; + }; }; } ); From 268c4f14c194b72ced33f520688fb0d3d096fad5 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 6 Sep 2024 22:50:12 +0200 Subject: [PATCH 03/29] vcpkg: support building on Linux --- cmake/ConfigureVcpkg.cmake | 13 +++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 7 ++++++- vcpkg.json | 27 ++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index bd256d91..f8c33fd4 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -25,6 +25,11 @@ else() option(USE_QT6 "Build using Qt 6 instead of 5" OFF) endif() +# Since the Linux build pulls in glib anyway, we can just use upstream libslirp +if (UNIX AND NOT APPLE) + option(USE_SYSTEM_LIBSLIRP "Use system libslirp instead of the bundled version" ON) +endif() + if (NOT USE_QT6) list(APPEND VCPKG_MANIFEST_FEATURES qt5) set(VCPKG_MANIFEST_NO_DEFAULT_FEATURES ON) @@ -62,6 +67,14 @@ if (USE_RECOMMENDED_TRIPLETS) # TODO Windows arm64 if possible set(_CAN_TARGET_AS_HOST ON) set(_WANTED_TRIPLET x64-mingw-static-release) + elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) + # Can't really detect cross compiling here. + set(_CAN_TARGET_AS_HOST ON) + if (_HOST_PROCESSOR STREQUAL x86_64) + set(_WANTED_TRIPLET x64-linux-release) + elseif(_HOST_PROCESSOR STREQUAL "aarch64") + set(_WANTED_TRIPLET arm64-linux-release) + endif() endif() # Don't override it if the user set something else diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 9cd784aa..524fa13d 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -160,7 +160,12 @@ if (BUILD_STATIC) if (WIN32 AND USE_QT6) qt_import_plugins(melonDS INCLUDE Qt::QModernWindowsStylePlugin) endif() - target_link_options(melonDS PRIVATE -static) + if (USE_VCPKG AND UNIX AND NOT APPLE) + pkg_check_modules(ALSA REQUIRED IMPORTED_TARGET alsa) + target_link_libraries(melonDS PRIVATE PkgConfig::ALSA) + else() + target_link_options(melonDS PRIVATE -static) + endif() endif() target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/vcpkg.json b/vcpkg.json index eb8790c8..cd734cce 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -2,9 +2,22 @@ "default-features": ["qt6"], "dependencies": [ "sdl2", + { + "name": "sdl2", + "platform": "linux", + "features": [ "alsa" ] + }, "libarchive", "zstd", - "enet" + "enet", + { + "name": "ecm", + "platform": "linux" + }, + { + "name": "libslirp", + "platform": "linux" + } ], "features": { "qt6": { @@ -15,6 +28,12 @@ "default-features": false, "features": ["gui", "png", "thread", "widgets", "opengl", "zstd", "harfbuzz"] }, + { + "name": "qtbase", + "platform": "linux", + "default-features": false, + "features": ["dbus", "xcb", "xkb", "xcb-xlib", "freetype", "fontconfig"] + }, { "name": "qtbase", "host": true, @@ -24,6 +43,12 @@ "name": "qtmultimedia", "default-features": false }, + { + "name": "qtmultimedia", + "platform": "linux", + "features": ["gstreamer"], + "default-features": false + }, "qtsvg" ] }, From f719438a6e0d796eccf4fce22df7a6c0157fc77d Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:13:51 -0400 Subject: [PATCH 04/29] Improve calculation of light colors (#1967) * maintain precision until all lights are calculated fixes lugia on the soul silver title screen * small optimization * small note * small cleanup/notes shouldn't need to check that every time, since the variable shouldn't be able to overflow * hw doesn't cap difflevel at 255 Should it cap at all? Can vtx colors overflow...? * diffuse level appears to be shifted right by 9 fixes some minor inaccuracies * improve specular lighting a little * small improvement to diffuse lighting fixes a few off by ones - finding by azusa * small tweaks * handle overflows of diffuse lighting properly -credits to azusa once more * attempt at improving specular lighting calcs still far from correct, but its a start. fixes: https://github.com/melonDS-emu/melonDS/issues/1545 * meh * improve specular lighting further * add notes * theory: add half vec instead of subt 1 * implement azusa's specular lighting algorithm * fix minor edge case with spec lighting * give proper credit in comments * fix some bugs/misc tweaks * more quirky overflow/underflow handling * fix a spec lighting edgecase remove some redundant parentheses * fix an edge case with light vector calcs * spec recip uses a different calc for light dir? also remove a check that shouldn't be mathematically possible to trigger * nvm that thing i thought couldn't trigger was required also move reciprocal calc into the light vector calc function since i might as well now ig * replace a bunch of stuff with much *much* simpler algorithms * misc cleanup PARENTHESES WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO * leave a note abt shininess table's default value being incorrect --- src/GPU3D.cpp | 117 +++++++++++++++++++++++++++++++------------------- src/GPU3D.h | 1 + 2 files changed, 73 insertions(+), 45 deletions(-) diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 865b00a1..4a1426aa 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -274,6 +274,8 @@ void GPU3D::Reset() noexcept memset(MatEmission, 0, sizeof(MatSpecular)); UseShininessTable = false; + // Shininess table seems to be uninitialized garbage, at least on n3dsxl hw? + // Also doesn't seem to be cleared properly unless the system is fully powered off? memset(ShininessTable, 0, sizeof(ShininessTable)); PolygonAttr = 0; @@ -1459,67 +1461,86 @@ void GPU3D::CalculateLighting() noexcept TexCoords[1] = RawTexCoords[1] + (((s64)Normal[0]*TexMatrix[1] + (s64)Normal[1]*TexMatrix[5] + (s64)Normal[2]*TexMatrix[9]) >> 21); } - s32 normaltrans[3]; - normaltrans[0] = (Normal[0]*VecMatrix[0] + Normal[1]*VecMatrix[4] + Normal[2]*VecMatrix[8]) >> 12; - normaltrans[1] = (Normal[0]*VecMatrix[1] + Normal[1]*VecMatrix[5] + Normal[2]*VecMatrix[9]) >> 12; - normaltrans[2] = (Normal[0]*VecMatrix[2] + Normal[1]*VecMatrix[6] + Normal[2]*VecMatrix[10]) >> 12; - - VertexColor[0] = MatEmission[0]; - VertexColor[1] = MatEmission[1]; - VertexColor[2] = MatEmission[2]; + s32 normaltrans[3]; // should be 1 bit sign 10 bits frac + normaltrans[0] = ((Normal[0]*VecMatrix[0] + Normal[1]*VecMatrix[4] + Normal[2]*VecMatrix[8]) << 9) >> 21; + normaltrans[1] = ((Normal[0]*VecMatrix[1] + Normal[1]*VecMatrix[5] + Normal[2]*VecMatrix[9]) << 9) >> 21; + normaltrans[2] = ((Normal[0]*VecMatrix[2] + Normal[1]*VecMatrix[6] + Normal[2]*VecMatrix[10]) << 9) >> 21; s32 c = 0; + u32 vtxbuff[3] = + { + (u32)MatEmission[0] << 14, + (u32)MatEmission[1] << 14, + (u32)MatEmission[2] << 14 + }; for (int i = 0; i < 4; i++) { if (!(CurPolygonAttr & (1<1) - // according to some hardware tests - // * diffuse level is saturated to 255 - // * shininess level mirrors back to 0 and is ANDed with 0xFF, that before being squared - // TODO: check how it behaves when the computed shininess is >=0x200 + // (credit to azusa for working out most of the details of the diff. algorithm, and essentially the entire spec. algorithm) + + // calculate dot product + // bottom 9 bits are discarded after multiplying and before adding + s32 dot = ((LightDirection[i][0]*normaltrans[0]) >> 9) + + ((LightDirection[i][1]*normaltrans[1]) >> 9) + + ((LightDirection[i][2]*normaltrans[2]) >> 9); - s32 difflevel = (-(LightDirection[i][0]*normaltrans[0] + - LightDirection[i][1]*normaltrans[1] + - LightDirection[i][2]*normaltrans[2])) >> 10; - if (difflevel < 0) difflevel = 0; - else if (difflevel > 255) difflevel = 255; + s32 shinelevel; + if (dot > 0) + { + // -- diffuse lighting -- + + // convert dot to signed 11 bit int + // then we truncate the result of the multiplications to an unsigned 20 bits before adding to the vtx color + s32 diffdot = (dot << 21) >> 21; + vtxbuff[0] += (MatDiffuse[0] * LightColor[i][0] * diffdot) & 0xFFFFF; + vtxbuff[1] += (MatDiffuse[1] * LightColor[i][1] * diffdot) & 0xFFFFF; + vtxbuff[2] += (MatDiffuse[2] * LightColor[i][2] * diffdot) & 0xFFFFF; - s32 shinelevel = -(((LightDirection[i][0]>>1)*normaltrans[0] + - (LightDirection[i][1]>>1)*normaltrans[1] + - ((LightDirection[i][2]-0x200)>>1)*normaltrans[2]) >> 10); - if (shinelevel < 0) shinelevel = 0; - else if (shinelevel > 255) shinelevel = (0x100 - shinelevel) & 0xFF; - shinelevel = ((shinelevel * shinelevel) >> 7) - 0x100; // really (2*shinelevel*shinelevel)-1 - if (shinelevel < 0) shinelevel = 0; + // -- specular lighting -- + + // reuse the dot product from diffuse lighting + dot += normaltrans[2]; + // convert to s11, then square it, and truncate to 10 bits + dot = (dot << 21) >> 21; + dot = ((dot * dot) >> 10) & 0x3FF; + + // multiply dot and reciprocal, the subtract '1' + shinelevel = ((dot * SpecRecip[i]) >> 8) - (1<<9); + + if (shinelevel < 0) shinelevel = 0; + else + { + // sign extend to convert to signed 14 bit integer + shinelevel = (shinelevel << 18) >> 18; + if (shinelevel < 0) shinelevel = 0; // for some reason there seems to be a redundant check for <0? + else if (shinelevel > 0x1FF) shinelevel = 0x1FF; + } + } + else shinelevel = 0; + + // convert shinelevel to use for lookup in the shininess table if enabled. if (UseShininessTable) { - // checkme - shinelevel >>= 1; + shinelevel >>= 2; shinelevel = ShininessTable[shinelevel]; + shinelevel <<= 1; } - VertexColor[0] += ((MatSpecular[0] * LightColor[i][0] * shinelevel) >> 13); - VertexColor[0] += ((MatDiffuse[0] * LightColor[i][0] * difflevel) >> 13); - VertexColor[0] += ((MatAmbient[0] * LightColor[i][0]) >> 5); - - VertexColor[1] += ((MatSpecular[1] * LightColor[i][1] * shinelevel) >> 13); - VertexColor[1] += ((MatDiffuse[1] * LightColor[i][1] * difflevel) >> 13); - VertexColor[1] += ((MatAmbient[1] * LightColor[i][1]) >> 5); - - VertexColor[2] += ((MatSpecular[2] * LightColor[i][2] * shinelevel) >> 13); - VertexColor[2] += ((MatDiffuse[2] * LightColor[i][2] * difflevel) >> 13); - VertexColor[2] += ((MatAmbient[2] * LightColor[i][2]) >> 5); - - if (VertexColor[0] > 31) VertexColor[0] = 31; - if (VertexColor[1] > 31) VertexColor[1] = 31; - if (VertexColor[2] > 31) VertexColor[2] = 31; + // Note: ambient seems to be a plain bitshift + vtxbuff[0] += ((MatSpecular[0] * shinelevel) + (MatAmbient[0] << 9)) * LightColor[i][0]; + vtxbuff[1] += ((MatSpecular[1] * shinelevel) + (MatAmbient[1] << 9)) * LightColor[i][1]; + vtxbuff[2] += ((MatSpecular[2] * shinelevel) + (MatAmbient[2] << 9)) * LightColor[i][2]; c++; } + VertexColor[0] = (vtxbuff[0] >> 14 > 31) ? 31 : (vtxbuff[0] >> 14); + VertexColor[1] = (vtxbuff[1] >> 14 > 31) ? 31 : (vtxbuff[1] >> 14); + VertexColor[2] = (vtxbuff[2] >> 14 > 31) ? 31 : (vtxbuff[2] >> 14); + if (c < 1) c = 1; NormalPipeline = 7; AddCycles(c); @@ -2012,9 +2033,15 @@ void GPU3D::ExecuteCommand() noexcept dir[0] = (s16)((entry.Param & 0x000003FF) << 6) >> 6; dir[1] = (s16)((entry.Param & 0x000FFC00) >> 4) >> 6; dir[2] = (s16)((entry.Param & 0x3FF00000) >> 14) >> 6; - LightDirection[l][0] = (dir[0]*VecMatrix[0] + dir[1]*VecMatrix[4] + dir[2]*VecMatrix[8]) >> 12; - LightDirection[l][1] = (dir[0]*VecMatrix[1] + dir[1]*VecMatrix[5] + dir[2]*VecMatrix[9]) >> 12; - LightDirection[l][2] = (dir[0]*VecMatrix[2] + dir[1]*VecMatrix[6] + dir[2]*VecMatrix[10]) >> 12; + // the order of operations here is very specific: discard bottom 12 bits -> negate -> then sign extend to convert to 11 bit signed int + // except for when used to calculate the specular reciprocal; then it's: sign extend -> discard lsb -> negate. + LightDirection[l][0] = (-((dir[0]*VecMatrix[0] + dir[1]*VecMatrix[4] + dir[2]*VecMatrix[8] ) >> 12) << 21) >> 21; + LightDirection[l][1] = (-((dir[0]*VecMatrix[1] + dir[1]*VecMatrix[5] + dir[2]*VecMatrix[9] ) >> 12) << 21) >> 21; + LightDirection[l][2] = (-((dir[0]*VecMatrix[2] + dir[1]*VecMatrix[6] + dir[2]*VecMatrix[10]) >> 12) << 21) >> 21; + s32 den = -(((dir[0]*VecMatrix[2] + dir[1]*VecMatrix[6] + dir[2]*VecMatrix[10]) << 9) >> 21) + (1<<9); + + if (den == 0) SpecRecip[l] = 0; + else SpecRecip[l] = (1<<18) / den; } AddCycles(5); break; diff --git a/src/GPU3D.h b/src/GPU3D.h index a12d4bc0..d10df55f 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -286,6 +286,7 @@ public: s16 Normal[3] {}; s16 LightDirection[4][3] {}; + s32 SpecRecip[4] {}; u8 LightColor[4][3] {}; u8 MatDiffuse[3] {}; u8 MatAmbient[3] {}; From 74f479ce6d956375f9bf9d2c3fd0b0b7a6f4ad56 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Wed, 11 Sep 2024 14:34:58 +0200 Subject: [PATCH 05/29] gdb stub config setup fixes --- src/frontend/qt_sdl/Config.cpp | 2 +- src/frontend/qt_sdl/EmuInstance.cpp | 4 ++-- src/frontend/qt_sdl/EmuSettingsDialog.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 5997d543..580af72e 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -71,7 +71,7 @@ DefaultList DefaultInts = #ifdef GDBSTUB_ENABLED {"Instance*.Gdb.ARM7.Port", 3334}, {"Instance*.Gdb.ARM9.Port", 3333}, -#endif, +#endif {"LAN.HostNumPlayers", 16}, }; diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index b29d2b09..e48f29a2 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1153,14 +1153,14 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB #endif #ifdef GDBSTUB_ENABLED - Config::Table gdbopt = globalCfg.GetTable("Gdb"); + Config::Table gdbopt = localCfg.GetTable("Gdb"); GDBArgs _gdbargs { static_cast(gdbopt.GetInt("ARM7.Port")), static_cast(gdbopt.GetInt("ARM9.Port")), gdbopt.GetBool("ARM7.BreakOnStartup"), gdbopt.GetBool("ARM9.BreakOnStartup"), }; - auto gdbargs = gdbopt.GetBool("Enable") ? std::make_optional(_gdbargs) : std::nullopt; + auto gdbargs = gdbopt.GetBool("Enabled") ? std::make_optional(_gdbargs) : std::nullopt; #else optional gdbargs = std::nullopt; #endif diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index d612ac83..7a6c0f40 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -95,7 +95,7 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new #endif #ifdef GDBSTUB_ENABLED - ui->cbGdbEnabled->setChecked(cfg.GetBool("Gdb.Enabled")); + ui->cbGdbEnabled->setChecked(instcfg.GetBool("Gdb.Enabled")); ui->intGdbPortA7->setValue(instcfg.GetInt("Gdb.ARM7.Port")); ui->intGdbPortA9->setValue(instcfg.GetInt("Gdb.ARM9.Port")); ui->cbGdbBOSA7->setChecked(instcfg.GetBool("Gdb.ARM7.BreakOnStartup")); @@ -286,7 +286,7 @@ void EmuSettingsDialog::done(int r) cfg.SetBool("JIT.FastMemory", ui->chkJITFastMemory->isChecked()); #endif #ifdef GDBSTUB_ENABLED - cfg.SetBool("Gdb.Enabled", ui->cbGdbEnabled->isChecked()); + instcfg.SetBool("Gdb.Enabled", ui->cbGdbEnabled->isChecked()); instcfg.SetInt("Gdb.ARM7.Port", ui->intGdbPortA7->value()); instcfg.SetInt("Gdb.ARM9.Port", ui->intGdbPortA9->value()); instcfg.SetBool("Gdb.ARM7.BreakOnStartup", ui->cbGdbBOSA7->isChecked()); From 50d32f3c96945d35840888a126de48c7739872cc Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 13 Sep 2024 05:42:20 +0200 Subject: [PATCH 06/29] flake: clean up dependencies a bit * qt6.* instead of kdePackages.* * use an extra-cmake-modules that depends on Qt6 rather than 5, and exclude it on macOS --- flake.nix | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index d670ea76..8236ccd3 100644 --- a/flake.nix +++ b/flake.nix @@ -25,23 +25,23 @@ cmake ninja pkg-config - kdePackages.wrapQtAppsHook + qt6.wrapQtAppsHook ]; buildInputs = (with pkgs; [ - kdePackages.qtbase - kdePackages.qtmultimedia - extra-cmake-modules + qt6.qtbase + qt6.qtmultimedia SDL2 zstd libarchive libGL libslirp enet - ]) ++ optionals isLinux [ - pkgs.wayland - pkgs.kdePackages.qtwayland - ]; + ]) ++ optionals (!isDarwin) (with pkgs; [ + kdePackages.extra-cmake-modules + qt6.qtwayland + wayland + ]); cmakeFlags = [ (cmakeBool "USE_QT6" true) From a3d696121e8d0643bbe45d6bdecdb99e2d62cbe1 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sun, 15 Sep 2024 07:30:53 +0200 Subject: [PATCH 07/29] rework gdb packet parsing it should be a bit more robust now --- src/debug/GdbCmds.cpp | 6 +- src/debug/GdbProto.cpp | 319 ++++++++++++++++++----------------------- src/debug/GdbProto.h | 41 ------ src/debug/GdbStub.cpp | 38 ++--- src/debug/GdbStub.h | 27 +++- 5 files changed, 184 insertions(+), 247 deletions(-) delete mode 100644 src/debug/GdbProto.h diff --git a/src/debug/GdbCmds.cpp b/src/debug/GdbCmds.cpp index c4706138..c2b1f9c4 100644 --- a/src/debug/GdbCmds.cpp +++ b/src/debug/GdbCmds.cpp @@ -1,12 +1,13 @@ #include #include +#include #include "../CRC32.h" #include "../Platform.h" #include "hexutil.h" -#include "GdbProto.h" +#include "GdbStub.h" using namespace melonDS; using Platform::Log; @@ -878,6 +879,7 @@ ExecResult GdbStub::Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len) ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len) { + printf("must reply empty\n"); stub->Resp(NULL, 0); return ExecResult::Ok; } @@ -886,6 +888,7 @@ ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len) { if (len < 1) { + printf("insufficient length"); stub->RespStr("E01"); return ExecResult::Ok; } @@ -902,6 +905,7 @@ ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len) stub->RespStr("OK"); return ExecResult::MustBreak; default: + printf("invalid continue %c %s\n", cmd[0], cmd); stub->RespStr("E01"); return ExecResult::Ok; } diff --git a/src/debug/GdbProto.cpp b/src/debug/GdbProto.cpp index bd7e6e65..25afb590 100644 --- a/src/debug/GdbProto.cpp +++ b/src/debug/GdbProto.cpp @@ -5,12 +5,14 @@ #include #endif +#include #include #include #include #include #include #include +#include #ifndef _WIN32 #include @@ -21,8 +23,7 @@ #include "../Platform.h" #include "hexutil.h" -#include "GdbProto.h" - +#include "GdbStub.h" using namespace melonDS; using Platform::Log; @@ -42,87 +43,128 @@ namespace Gdb * vKill;pid * qRcmd? qSupported? */ -u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; -ssize_t Cmdlen; -namespace Proto + +Gdb::ReadResult GdbStub::TryParsePacket(size_t start, size_t& packetStart, size_t& packetSize, size_t& packetContentSize) { - -u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY]; -u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5]; - -ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]) -{ - static ssize_t dataoff = 0; - - ssize_t recv_total = dataoff; - ssize_t cksumoff = -1; - u8 sum = 0; - - bool first = true; - - //printf("--- dataoff=%zd\n", dataoff); - if (dataoff != 0) { - printf("--- got preexisting: %s\n", PacketBuf); - - ssize_t datastart = 0; - while (true) + RecvBuffer[RecvBufferFilled] = '\0'; + //Log(LogLevel::Debug, "[GDB] Trying to parse packet %s %d %d\n", &RecvBuffer[0], start, RecvBufferFilled); + size_t i = start; + while (i < RecvBufferFilled) + { + char curChar = RecvBuffer[i++]; + if (curChar == '\x04') return ReadResult::Eof; + else if (curChar == '\x03') { - if (PacketBuf[datastart] == '\x04') return ReadResult::Eof; - else if (PacketBuf[datastart] == '+' || PacketBuf[datastart] == '-') + packetStart = i - 1; + packetSize = packetContentSize = 1; + return ReadResult::Break; + } + else if (curChar == '+' || curChar == '-') continue; + else if (curChar == '$') + { + packetStart = i; + uint8_t checksumGot = 0; + while (i < RecvBufferFilled) { - /*if (PacketBuf[datastart] == '+') SendAck(connfd); - else SendNak(connfd);*/ - ++datastart; - continue; - } - else if (PacketBuf[datastart] == '$') - { - ++datastart; - break; - } - else - { - __builtin_trap(); - return ReadResult::Wut; + curChar = RecvBuffer[i]; + if (curChar == '#' && i + 2 < RecvBufferFilled) + { + u8 checksumShould = (hex2nyb(RecvBuffer[i+1]) << 4) + | hex2nyb(RecvBuffer[i+2]); + + Log(LogLevel::Debug, "[GDB] found pkt, checksumGot: %02x vs %02x\n", checksumShould, checksumGot); + + if (checksumShould != checksumGot) + { + return ReadResult::CksumErr; + } + + packetContentSize = i - packetStart; + packetSize = packetContentSize + 3; + return ReadResult::CmdRecvd; + } + else + { + checksumGot += curChar; + } + + i++; } } - printf("--- datastart=%zd\n", datastart); - - for (ssize_t i = datastart; i < dataoff; ++i) + else { - if (PacketBuf[i] == '#') - { - cksumoff = i + 1; - printf("--- cksumoff=%zd\n", cksumoff); - break; - } - - sum += PacketBuf[i]; - } - - if (cksumoff >= 0) - { - recv_total = dataoff - datastart + 1; - dataoff = cksumoff + 2 - datastart + 1; - cksumoff -= datastart - 1; - - memmove(&PacketBuf[1], &PacketBuf[datastart], recv_total); - PacketBuf[0] = '$'; - PacketBuf[recv_total] = 0; - - printf("=== cksumoff=%zi recv_total=%zi datastart=%zi dataoff=%zi\n==> %s\n", - cksumoff, recv_total, datastart, dataoff, PacketBuf); - //break; + Log(LogLevel::Error, "[GDB] Received unknown character %c (%d)\n", curChar, curChar); + return ReadResult::Wut; } } - while (cksumoff < 0) - { - u8* pkt = &PacketBuf[dataoff]; - ssize_t n, blehoff = 0; + return ReadResult::NoPacket; +} - memset(pkt, 0, sizeof(PacketBuf) - dataoff); +Gdb::ReadResult GdbStub::ParseAndSetupPacket() +{ + // This complicated logic seems to be unfortunately necessary + // to handle the case of packet resends when we answered too slowly. + // GDB only expects a single response (as it assumes the previous packet was dropped) + size_t i = 0; + size_t prevPacketStart = SIZE_MAX, prevPacketSize, prevPacketContentSize; + size_t packetStart, packetSize, packetContentSize; + ReadResult result, prevResult; + while (true) + { + ReadResult result = TryParsePacket(i, packetStart, packetSize, packetContentSize); + if (result == ReadResult::NoPacket) + break; + if (result != ReadResult::CmdRecvd && result != ReadResult::Break) + return result; + + // looks like there is a different packet coming up + // so we quit here + if (prevPacketStart != SIZE_MAX && + (packetContentSize != prevPacketContentSize || + memcmp(&RecvBuffer[packetStart], &RecvBuffer[prevPacketStart], prevPacketContentSize) != 0)) + { + Log(LogLevel::Debug, "[GDB] found differing packet further back %zu %zu\n", packetContentSize, prevPacketContentSize); + break; + } + + i = packetStart + packetSize; + prevPacketStart = packetStart; + prevPacketSize = packetSize; + prevPacketContentSize = packetContentSize; + prevResult = result; + } + + if (prevPacketStart != SIZE_MAX) + { + memcpy(&Cmdbuf[0], &RecvBuffer[prevPacketStart], prevPacketContentSize); + Cmdbuf[prevPacketContentSize] = '\0'; + Cmdlen = static_cast(prevPacketContentSize); + + RecvBufferFilled -= prevPacketStart + prevPacketSize; + if (RecvBufferFilled > 0) + memmove(&RecvBuffer[0], &RecvBuffer[prevPacketStart + prevPacketSize], RecvBufferFilled); + + assert(prevResult == ReadResult::CmdRecvd || prevResult == ReadResult::Break); + return prevResult; + } + + assert(result == ReadResult::NoPacket); + return ReadResult::NoPacket; +} + +ReadResult GdbStub::MsgRecv() +{ + { + ReadResult result = ParseAndSetupPacket(); + if (result != ReadResult::NoPacket) + return result; + } + + bool first = true; + while (true) + { int flag = 0; #if MOCKTEST static bool FIRST = false; @@ -142,113 +184,35 @@ ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]) #else #ifndef _WIN32 if (first) flag |= MSG_DONTWAIT; - n = recv(connfd, pkt, sizeof(PacketBuf) - dataoff, flag); + ssize_t receivedNum = recv(ConnFd, &RecvBuffer[RecvBufferFilled], sizeof(RecvBuffer) - RecvBufferFilled, flag); + Log(LogLevel::Debug, "[GDB] receiving from stream %d\n", receivedNum); #else // fuck windows - n = recv(connfd, (char*)pkt, sizeof(PacketBuf) - dataoff, flag); + ssize_t receivedNum = recv(ConnFd, (char*)&RecvBuffer[RecvBufferFilled], sizeof(PacketBuf) - RecvBufferFilled, flag); #endif #endif - if (n <= 0) + if (receivedNum <= 0) { if (first) return ReadResult::NoPacket; else { - Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", n, errno, strerror(errno)); + Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", receivedNum, errno, strerror(errno)); return ReadResult::Eof; } } + RecvBufferFilled += static_cast(receivedNum); - Log(LogLevel::Debug, "[GDB] recv() %zd bytes: '%s' (%02x)\n", n, pkt, pkt[0]); - first = false; - - do - { - if (dataoff == 0) - { - if (pkt[blehoff] == '\x04') return ReadResult::Eof; - else if (pkt[blehoff] == '\x03') return ReadResult::Break; - else if (pkt[blehoff] != '$') - { - ++blehoff; - --n; - } - else break; - - if (n == 0) goto next_outer; - } - } - while (true); - - if (blehoff > 0) - { - memmove(pkt, &pkt[blehoff], n - blehoff + 1); - n -= blehoff - 1; // ??? - } - - recv_total += n; - - Log(LogLevel::Debug, "[GDB] recv() after skipping: n=%zd, recv_total=%zd\n", n, recv_total); - - for (ssize_t i = (dataoff == 0) ? 1 : 0; i < n; ++i) - { - u8 v = pkt[i]; - if (v == '#') - { - cksumoff = dataoff + i + 1; - break; - } - - sum += pkt[i]; - } - - if (cksumoff < 0) - { - // oops, need more data - dataoff += n; - } - - next_outer:; + ReadResult result = ParseAndSetupPacket(); + if (result != ReadResult::NoPacket) + return result; } - - u8 ck = (hex2nyb(PacketBuf[cksumoff+0]) << 4) - | hex2nyb(PacketBuf[cksumoff+1]); - - Log(LogLevel::Debug, "[GDB] got pkt, checksum: %02x vs %02x\n", ck, sum); - - if (ck != sum) - { - //__builtin_trap(); - return ReadResult::CksumErr; - } - - if (cksumoff + 2 > recv_total) - { - Log(LogLevel::Error, "[GDB] BIG MISTAKE: %zi > %zi which shouldn't happen!\n", cksumoff + 2, recv_total); - //__builtin_trap(); - return ReadResult::Wut; - } - else - { - Cmdlen = cksumoff - 2; - memcpy(Cmdbuf, &PacketBuf[1], Cmdlen); - Cmdbuf[Cmdlen] = 0; - - if (cksumoff + 2 < recv_total) { - // huh, we have the start of the next packet - dataoff = recv_total - (cksumoff + 2); - memmove(PacketBuf, &PacketBuf[cksumoff + 2], (size_t)dataoff); - PacketBuf[dataoff] = 0; - Log(LogLevel::Debug, "[GDB] got more: cksumoff=%zd, recvtotal=%zd, remain=%zd\n==> %s\n", cksumoff, recv_total, dataoff, PacketBuf); - } - else dataoff = 0; - } - - return ReadResult::CmdRecvd; } -int SendAck(int connfd) +int GdbStub::SendAck() { + if (NoAck) return 1; + Log(LogLevel::Debug, "[GDB] send ack\n"); u8 v = '+'; #if MOCKTEST @@ -257,14 +221,16 @@ int SendAck(int connfd) #ifdef _WIN32 // fuck windows - return send(connfd, (const char*)&v, 1, 0); + return send(ConnFd, (const char*)&v, 1, 0); #else - return send(connfd, &v, 1, 0); + return send(ConnFd, &v, 1, 0); #endif } -int SendNak(int connfd) +int GdbStub::SendNak() { + if (NoAck) return 1; + Log(LogLevel::Debug, "[GDB] send nak\n"); u8 v = '-'; #if MOCKTEST @@ -273,13 +239,13 @@ int SendNak(int connfd) #ifdef _WIN32 // fuck windows - return send(connfd, (const char*)&v, 1, 0); + return send(ConnFd, (const char*)&v, 1, 0); #else - return send(connfd, &v, 1, 0); + return send(ConnFd, &v, 1, 0); #endif } -int WaitAckBlocking(int connfd, u8* ackp, int to_ms) +int GdbStub::WaitAckBlocking(u8* ackp, int to_ms) { #if MOCKTEST *ackp = '+'; @@ -309,7 +275,7 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms) #else struct pollfd pfd; - pfd.fd = connfd; + pfd.fd = ConnFd; pfd.events = POLLIN; pfd.revents = 0; @@ -319,14 +285,14 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms) if (pfd.revents & (POLLHUP|POLLERR)) return -69; - r = recv(connfd, ackp, 1, 0); + r = recv(ConnFd, ackp, 1, 0); if (r < 0) return r; return (r == 1) ? 0 : -1; #endif } -int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack) +int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack) { u8 cksum = 0; int tries = 0; @@ -359,22 +325,22 @@ int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, ssize_t r; u8 ack; - Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", RespBuf); + Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", &RespBuf[0]); #if MOCKTEST r = totallen+4; #else #ifdef _WIN32 - r = send(connfd, (const char*)RespBuf, totallen+4, 0); + r = send(ConnFd, (const char*)&RespBuf[0], totallen+4, 0); #else - r = send(connfd, RespBuf, totallen+4, 0); + r = send(ConnFd, &RespBuf[0], totallen+4, 0); #endif #endif if (r < 0) return r; if (noack) break; - r = WaitAckBlocking(connfd, &ack, 2000); - //Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack); + r = WaitAckBlocking(&ack, 2000); + Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack); if (r == 0 && ack == '+') break; ++tries; @@ -386,5 +352,4 @@ int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, } -} diff --git a/src/debug/GdbProto.h b/src/debug/GdbProto.h deleted file mode 100644 index 68122f06..00000000 --- a/src/debug/GdbProto.h +++ /dev/null @@ -1,41 +0,0 @@ - -#ifndef GDBPROTO_H_ -#define GDBPROTO_H_ - -#include -#include - -#include "GdbStub.h" /* class GdbStub */ - - -#define MOCKTEST 0 - - -namespace Gdb { - -using namespace melonDS; -constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128; - -extern u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; -extern ssize_t Cmdlen; - -namespace Proto { - -extern u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY]; -extern u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5]; - -Gdb::ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]); - -int SendAck(int connfd); -int SendNak(int connfd); - -int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack); - -int WaitAckBlocking(int connfd, u8* ackp, int to_ms); - -} - -} - -#endif - diff --git a/src/debug/GdbStub.cpp b/src/debug/GdbStub.cpp index 53101cec..14a8670a 100644 --- a/src/debug/GdbStub.cpp +++ b/src/debug/GdbStub.cpp @@ -23,7 +23,7 @@ #include "../Platform.h" -#include "GdbProto.h" +#include "GdbStub.h" using namespace melonDS; using Platform::Log; @@ -304,7 +304,7 @@ StubState GdbStub::Poll(bool wait) if (ConnFd < 0) return StubState::NoConn; u8 a; - if (Proto::WaitAckBlocking(ConnFd, &a, 1000) < 0) + if (WaitAckBlocking(&a, 1000) < 0) { Log(LogLevel::Error, "[GDB] inital handshake: didn't receive inital ack!\n"); close(ConnFd); @@ -380,7 +380,7 @@ StubState GdbStub::Poll(bool wait) #endif #endif - ReadResult res = Proto::MsgRecv(ConnFd, Cmdbuf); + ReadResult res = MsgRecv(); switch (res) { @@ -422,11 +422,12 @@ ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* // check if prefix matches if (!strncmp((const char*)cmd, handlers[i].SubStr, strlen(handlers[i].SubStr))) { - if (SendAck() < 0) + // ack should have already been sent by CmdExec + /*if (SendAck() < 0) { Log(LogLevel::Error, "[GDB] send packet ack failed!\n"); return ExecResult::NetErr; - } + }*/ return handlers[i].Handler(this, &cmd[strlen(handlers[i].SubStr)], len-strlen(handlers[i].SubStr)); } } @@ -444,7 +445,7 @@ ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* ExecResult GdbStub::CmdExec(const CmdHandler* handlers) { - Log(LogLevel::Debug, "[GDB] command in: '%s'\n", Cmdbuf); + Log(LogLevel::Debug, "[GDB] command in: '%s'\n", &Cmdbuf[0]); for (size_t i = 0; handlers[i].Handler != NULL; ++i) { @@ -644,24 +645,13 @@ StubState GdbStub::CheckWatchpt(u32 addr, int kind, bool enter, bool stay) return StubState::CheckNoHit; } -int GdbStub::SendAck() -{ - if (NoAck) return 1; - return Proto::SendAck(ConnFd); -} -int GdbStub::SendNak() -{ - if (NoAck) return 1; - return Proto::SendNak(ConnFd); -} - int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2) { - return Proto::Resp(ConnFd, data1, len1, data2, len2, NoAck); + return Resp(data1, len1, data2, len2, NoAck); } int GdbStub::RespC(const char* data1, size_t len1, const u8* data2, size_t len2) { - return Proto::Resp(ConnFd, (const u8*)data1, len1, data2, len2, NoAck); + return Resp((const u8*)data1, len1, data2, len2, NoAck); } #if defined(__GCC__) || defined(__clang__) __attribute__((__format__(printf, 2/*includes implicit this*/, 3))) @@ -670,19 +660,19 @@ int GdbStub::RespFmt(const char* fmt, ...) { va_list args; va_start(args, fmt); - int r = vsnprintf((char*)&Proto::RespBuf[1], sizeof(Proto::RespBuf)-5, fmt, args); + int r = vsnprintf((char*)&RespBuf[1], sizeof(RespBuf)-5, fmt, args); va_end(args); if (r < 0) return r; - if ((size_t)r >= sizeof(Proto::RespBuf)-5) + if ((size_t)r >= sizeof(RespBuf)-5) { Log(LogLevel::Error, "[GDB] truncated response in send_fmt()! (lost %zd bytes)\n", - (ssize_t)r - (ssize_t)(sizeof(Proto::RespBuf)-5)); - r = sizeof(Proto::RespBuf)-5; + (ssize_t)r - (ssize_t)(sizeof(RespBuf)-5)); + r = sizeof(RespBuf)-5; } - return Resp(&Proto::RespBuf[1], r); + return Resp(&RespBuf[1], r); } int GdbStub::RespStr(const char* str) diff --git a/src/debug/GdbStub.h b/src/debug/GdbStub.h index 6461a354..3e4a0efe 100644 --- a/src/debug/GdbStub.h +++ b/src/debug/GdbStub.h @@ -86,6 +86,8 @@ enum class ExecResult Continue }; +constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128; + class GdbStub; typedef ExecResult (*GdbProtoCmd)(GdbStub* stub, const u8* cmd, ssize_t len); @@ -141,9 +143,6 @@ public: Gdb::ExecResult CmdExec(const CmdHandler* handlers); public: - int SendAck(); - int SendNak(); - int Resp(const u8* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0); int RespC(const char* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0); #if defined(__GCC__) || defined(__clang__) @@ -158,7 +157,20 @@ private: void Disconnect(); StubState HandlePacket(); -private: + Gdb::ReadResult MsgRecv(); + + Gdb::ReadResult TryParsePacket(size_t start, size_t& packetStart, size_t& packetSize, size_t& packetContentSize); + Gdb::ReadResult ParseAndSetupPacket(); + + void SetupCommand(size_t packetStart, size_t packetSize); + + int SendAck(); + int SendNak(); + + int Resp(const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack); + + int WaitAckBlocking(u8* ackp, int to_ms); + StubCallbacks* Cb; //struct sockaddr_in server, client; @@ -172,6 +184,13 @@ private: bool StatFlag; bool NoAck; + std::array RecvBuffer; + u32 RecvBufferFilled = 0; + std::array RespBuf; + + std::array Cmdbuf; + ssize_t Cmdlen; + std::map BpList; std::vector WpList; From 7ac2eb2d7137caad07801fb4ebe49901d40e42bd Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sun, 15 Sep 2024 07:38:28 +0200 Subject: [PATCH 08/29] attempt at fixing Windows build --- src/debug/GdbProto.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/debug/GdbProto.cpp b/src/debug/GdbProto.cpp index 25afb590..a69538db 100644 --- a/src/debug/GdbProto.cpp +++ b/src/debug/GdbProto.cpp @@ -188,7 +188,7 @@ ReadResult GdbStub::MsgRecv() Log(LogLevel::Debug, "[GDB] receiving from stream %d\n", receivedNum); #else // fuck windows - ssize_t receivedNum = recv(ConnFd, (char*)&RecvBuffer[RecvBufferFilled], sizeof(PacketBuf) - RecvBufferFilled, flag); + ssize_t receivedNum = recv(ConnFd, (char*)&RecvBuffer[RecvBufferFilled], sizeof(RecvBuffer) - RecvBufferFilled, flag); #endif #endif @@ -255,18 +255,18 @@ int GdbStub::WaitAckBlocking(u8* ackp, int to_ms) #ifdef _WIN32 fd_set infd, outfd, errfd; FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); - FD_SET(connfd, &infd); + FD_SET(ConnFd, &infd); struct timeval to; to.tv_sec = to_ms / 1000; to.tv_usec = (to_ms % 1000) * 1000; - int r = select(connfd+1, &infd, &outfd, &errfd, &to); + int r = select(ConnFd+1, &infd, &outfd, &errfd, &to); - if (FD_ISSET(connfd, &errfd)) return -1; - else if (FD_ISSET(connfd, &infd)) + if (FD_ISSET(ConnFd, &errfd)) return -1; + else if (FD_ISSET(ConnFd, &infd)) { - r = recv(connfd, (char*)ackp, 1, 0); + r = recv(ConnFd, (char*)ackp, 1, 0); if (r < 0) return r; return 0; } From 3b9a9e4eb3d8de840ceab9e0ff57c8dc1092d6a5 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:23:15 -0400 Subject: [PATCH 09/29] multiply instructions can't write to r15 --- src/ARMInterpreter_ALU.cpp | 62 ++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 37c79904..350ed168 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -766,7 +766,9 @@ void A_MUL(ARM* cpu) u32 res = rm * rs; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; + if (((cpu->CurInstr >> 16) & 0xF) != 15) // check arm7 + cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; + if (cpu->CurInstr & (1<<20)) { cpu->SetNZ(res & 0x80000000, @@ -795,8 +797,10 @@ void A_MLA(ARM* cpu) u32 rn = cpu->R[(cpu->CurInstr >> 12) & 0xF]; u32 res = (rm * rs) + rn; + + if (((cpu->CurInstr >> 16) & 0xF) != 15) // check arm7 + cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; if (cpu->CurInstr & (1<<20)) { cpu->SetNZ(res & 0x80000000, @@ -825,8 +829,11 @@ void A_UMULL(ARM* cpu) u64 res = (u64)rm * (u64)rs; - cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); + if (cpu->CurInstr & (1<<20)) { cpu->SetNZ((u32)(res >> 63ULL), @@ -857,9 +864,12 @@ void A_UMLAL(ARM* cpu) u64 rd = (u64)cpu->R[(cpu->CurInstr >> 12) & 0xF] | ((u64)cpu->R[(cpu->CurInstr >> 16) & 0xF] << 32ULL); res += rd; + + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); - cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); if (cpu->CurInstr & (1<<20)) { cpu->SetNZ((u32)(res >> 63ULL), @@ -887,9 +897,12 @@ void A_SMULL(ARM* cpu) u32 rs = cpu->R[(cpu->CurInstr >> 8) & 0xF]; s64 res = (s64)(s32)rm * (s64)(s32)rs; + + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); - cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); if (cpu->CurInstr & (1<<20)) { cpu->SetNZ((u32)(res >> 63ULL), @@ -920,9 +933,12 @@ void A_SMLAL(ARM* cpu) s64 rd = (s64)((u64)cpu->R[(cpu->CurInstr >> 12) & 0xF] | ((u64)cpu->R[(cpu->CurInstr >> 16) & 0xF] << 32ULL)); res += rd; + + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); - cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); if (cpu->CurInstr & (1<<20)) { cpu->SetNZ((u32)(res >> 63ULL), @@ -959,8 +975,10 @@ void A_SMLAxy(ARM* cpu) u32 res_mul = ((s16)rm * (s16)rs); u32 res = res_mul + rn; + + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; if (OverflowAdd(res_mul, rn)) cpu->CPSR |= 0x08000000; @@ -980,8 +998,9 @@ void A_SMLAWy(ARM* cpu) u32 res_mul = ((s64)(s32)rm * (s16)rs) >> 16; u32 res = res_mul + rn; - - cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; + + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; if (OverflowAdd(res_mul, rn)) cpu->CPSR |= 0x08000000; @@ -1001,8 +1020,9 @@ void A_SMULxy(ARM* cpu) else rs &= 0xFFFF; u32 res = ((s16)rm * (s16)rs); - - cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; + + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; cpu->AddCycles_C(); // TODO: interlock?? } @@ -1017,8 +1037,9 @@ void A_SMULWy(ARM* cpu) else rs &= 0xFFFF; u32 res = ((s64)(s32)rm * (s16)rs) >> 16; - - cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; + + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; cpu->AddCycles_C(); // TODO: interlock?? } @@ -1039,8 +1060,11 @@ void A_SMLALxy(ARM* cpu) s64 rd = (s64)((u64)cpu->R[(cpu->CurInstr >> 12) & 0xF] | ((u64)cpu->R[(cpu->CurInstr >> 16) & 0xF] << 32ULL)); res += rd; - cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; - cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res; + + if (((cpu->CurInstr >> 16) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL); cpu->AddCycles_CI(1); // TODO: interlock?? } From ac8c942565f956402f681c9cc8fa8b6eb6e0e74b Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:10:13 -0400 Subject: [PATCH 10/29] sat add/sub also fail to jump --- src/ARMInterpreter_ALU.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 350ed168..54c1d6d3 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -1110,7 +1110,9 @@ void A_QADD(ARM* cpu) cpu->CPSR |= 0x08000000; } - cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; + cpu->AddCycles_C(); // TODO: interlock?? } @@ -1127,8 +1129,10 @@ void A_QSUB(ARM* cpu) res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000; cpu->CPSR |= 0x08000000; } + + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; - cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; cpu->AddCycles_C(); // TODO: interlock?? } @@ -1153,8 +1157,10 @@ void A_QDADD(ARM* cpu) res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000; cpu->CPSR |= 0x08000000; } + + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; - cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; cpu->AddCycles_C(); // TODO: interlock?? } @@ -1179,8 +1185,10 @@ void A_QDSUB(ARM* cpu) res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000; cpu->CPSR |= 0x08000000; } + + if (((cpu->CurInstr >> 12) & 0xF) != 15) + cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; - cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; cpu->AddCycles_C(); // TODO: interlock?? } From e2f3dd1e6f1ae6602c5dc63f65ffb4908203ad7f Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:27:36 -0400 Subject: [PATCH 11/29] clarify --- src/ARMInterpreter_ALU.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 54c1d6d3..46c703cd 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -766,7 +766,8 @@ void A_MUL(ARM* cpu) u32 res = rm * rs; - if (((cpu->CurInstr >> 16) & 0xF) != 15) // check arm7 + // all multiply instructions fail writes to r15 on arm7/9 + if (((cpu->CurInstr >> 16) & 0xF) != 15) cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; if (cpu->CurInstr & (1<<20)) @@ -798,7 +799,7 @@ void A_MLA(ARM* cpu) u32 res = (rm * rs) + rn; - if (((cpu->CurInstr >> 16) & 0xF) != 15) // check arm7 + if (((cpu->CurInstr >> 16) & 0xF) != 15) cpu->R[(cpu->CurInstr >> 16) & 0xF] = res; if (cpu->CurInstr & (1<<20)) @@ -1110,6 +1111,7 @@ void A_QADD(ARM* cpu) cpu->CPSR |= 0x08000000; } + // all saturated math instructions fail writes to r15 if (((cpu->CurInstr >> 12) & 0xF) != 15) cpu->R[(cpu->CurInstr >> 12) & 0xF] = res; From e5654ec541528f606b27c854bb4d2ae981ab79d2 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:50:09 -0400 Subject: [PATCH 12/29] r15 mrc mrs --- src/ARMInterpreter.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 15ec42db..8ce15db1 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -201,7 +201,12 @@ void A_MRS(ARM* cpu) else psr = cpu->CPSR; - cpu->R[(cpu->CurInstr>>12) & 0xF] = psr; + if (((cpu->CurInstr>>12) & 0xF) == 15) + { + if (cpu->Num == 1) // doesn't seem to jump on the arm9? checkme + cpu->JumpTo(psr & ~0x1); // checkme: this shouldn't be able to switch to thumb? + } + else cpu->R[(cpu->CurInstr>>12) & 0xF] = psr; if (cpu->Num != 1) cpu->AddCycles_CI(1); // arm9 else cpu->AddCycles_C(); // arm7 @@ -248,12 +253,13 @@ void A_MRC(ARM* cpu) u32 cn = (cpu->CurInstr >> 16) & 0xF; u32 cm = cpu->CurInstr & 0xF; u32 cpinfo = (cpu->CurInstr >> 5) & 0x7; + u32 rd = (cpu->CurInstr>>12) & 0xF; - if (cpu->Num==0 && cp==15) + if (cpu->Num==0 && cp==15 && rd!=15) { - cpu->R[(cpu->CurInstr>>12)&0xF] = ((ARMv5*)cpu)->CP15Read((cn<<8)|(cm<<4)|cpinfo); + cpu->R[rd] = ((ARMv5*)cpu)->CP15Read((cn<<8)|(cm<<4)|cpinfo); } - else if (cpu->Num==1 && cp==14) + else if (cpu->Num==1 && cp==14 && rd!=15) { Log(LogLevel::Debug, "MRC p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo); } From 89e8549a556c0172feece95ce35dfeb61b01f2c1 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:27:31 -0400 Subject: [PATCH 13/29] implement comparison instrs w/ rd == 15 --- src/ARMInterpreter_ALU.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 46c703cd..f04ab9b5 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -583,6 +583,11 @@ A_IMPLEMENT_ALU_OP(RSC,) u32 res = a & b; \ cpu->SetNZ(res & 0x80000000, \ !res); \ + if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + { \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING TST w/ rd == 15???"); \ + } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); A_IMPLEMENT_ALU_TEST(TST,_S) @@ -593,6 +598,11 @@ A_IMPLEMENT_ALU_TEST(TST,_S) u32 res = a ^ b; \ cpu->SetNZ(res & 0x80000000, \ !res); \ + if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + { \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING TEQ w/ rd == 15???"); \ + } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); A_IMPLEMENT_ALU_TEST(TEQ,_S) @@ -605,6 +615,11 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S) !res, \ CarrySub(a, b), \ OverflowSub(a, b)); \ + if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + { \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING CMP w/ rd == 15???"); \ + } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); A_IMPLEMENT_ALU_TEST(CMP,) @@ -617,6 +632,11 @@ A_IMPLEMENT_ALU_TEST(CMP,) !res, \ CarryAdd(a, b), \ OverflowAdd(a, b)); \ + if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + { \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING CMN w/ rd == 15???"); \ + } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); A_IMPLEMENT_ALU_TEST(CMN,) @@ -1569,6 +1589,11 @@ void T_CMP_HIREG(ARM* cpu) !res, CarrySub(a, b), OverflowSub(a, b)); + if (rd == 15) \ + { \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP HIREG w/ rd == 15."); \ + } \ cpu->AddCycles_C(); } From 2179ca2a417e356f23a09cd88707b20c1bcaf66f Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:58:55 -0700 Subject: [PATCH 14/29] Set the correct save type for Puzzler World USA (#2156) Fixes #1804 --- src/ROMList.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ROMList.cpp b/src/ROMList.cpp index e4ce7d2a..a00b0b22 100644 --- a/src/ROMList.cpp +++ b/src/ROMList.cpp @@ -1830,7 +1830,7 @@ const ROMListEntry ROMList[] = {0x45564E43, 0x10000000, 0x00000005}, {0x45564F59, 0x00800000, 0x00000001}, {0x45565041, 0x00800000, 0x00000002}, - {0x45565042, 0x00800000, 0x00000004}, + {0x45565042, 0x00800000, 0x00000002}, {0x45565043, 0x04000000, 0x00000002}, {0x45565056, 0x04000000, 0x00000002}, {0x45565059, 0x04000000, 0x00000001}, @@ -6804,4 +6804,4 @@ const ROMListEntry ROMList[] = const size_t ROMListEntryCount = sizeof(ROMList) / sizeof(ROMListEntry); -} \ No newline at end of file +} From 6ebabde39217e948406f4aff123f8a4afdf7f30b Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:23:23 -0400 Subject: [PATCH 15/29] implement changing thumb bit. and bkpt ig probably wrong --- src/ARM.cpp | 33 ++++++++++++++++++++++++++++++--- src/ARM.h | 6 +++++- src/ARMInterpreter.cpp | 20 ++++++++++++++------ src/ARMInterpreter.h | 1 + src/ARM_InstrInfo.cpp | 1 + src/ARM_InstrTable.h | 2 +- 6 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index a7c6c11e..c7fea92d 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -201,6 +201,13 @@ void ARMv5::Reset() ARM::Reset(); } +void ARMv4::Reset() +{ + Thumb = false; + + ARM::Reset(); +} + void ARM::DoSavestate(Savestate* file) { @@ -395,6 +402,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) Cycles += NDS.ARM7MemTimings[CodeCycles][0] + NDS.ARM7MemTimings[CodeCycles][1]; CPSR |= 0x20; + Thumb = true; } else { @@ -408,6 +416,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) Cycles += NDS.ARM7MemTimings[CodeCycles][2] + NDS.ARM7MemTimings[CodeCycles][3]; CPSR &= ~0x20; + Thumb = false; } } @@ -724,7 +733,12 @@ void ARMv5::Execute() ARMInterpreter::A_BLX_IMM(this); } else - AddCycles_C(); + { + if ((((CurInstr >> 4) & 0xF) | ((CurInstr >> 16) & 0xFF0)) == 0x127) + ARMInterpreter::A_BKPT(this); // always passes regardless of condition code + else + AddCycles_C(); + } } // TODO optimize this shit!!! @@ -826,8 +840,11 @@ void ARMv4::Execute() else #endif { - if (CPSR & 0x20) // THUMB + if (Thumb) // THUMB { + Thumb = (CPSR & 0x20); + bool fix = !Thumb; + if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); @@ -841,12 +858,22 @@ void ARMv4::Execute() else { // actually execute - u32 icode = (CurInstr >> 6); + u32 icode = (CurInstr >> 6) & 0x3FF; ARMInterpreter::THUMBInstrTable[icode](this); } + + if (fix) [[unlikely]] + { + // probably wrong? + // fixup + R[15] &= ~0x3; + NextInstr[1] = CodeRead32(R[15]); + } } else { + Thumb = (CPSR & 0x20); + if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); diff --git a/src/ARM.h b/src/ARM.h index 26080b51..8d640a30 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -385,6 +385,8 @@ class ARMv4 : public ARM public: ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit); + void Reset() override; + void FillPipeline() override; void JumpTo(u32 addr, bool restorecpsr = false) override; @@ -393,7 +395,7 @@ public: template void Execute(); - u16 CodeRead16(u32 addr) + u32 CodeRead16(u32 addr) { return BusRead16(addr); } @@ -403,6 +405,8 @@ public: return BusRead32(addr); } + bool Thumb; + bool DataRead8(u32 addr, u32* val) override; bool DataRead16(u32 addr, u32* val) override; bool DataRead32(u32 addr, u32* val) override; diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 8ce15db1..979e3bb8 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -69,6 +69,14 @@ void T_UNK(ARM* cpu) cpu->JumpTo(cpu->ExceptionBase + 0x04); } +void A_BKPT(ARM* cpu) +{ + if (cpu->Num == 1) A_UNK(cpu); // checkme + + Log(LogLevel::Warn, "BKPT: "); // combine with the prefetch abort warning message + ((ARMv5*)cpu)->PrefetchAbort(); +} + void A_MSR_IMM(ARM* cpu) @@ -105,9 +113,6 @@ void A_MSR_IMM(ARM* cpu) //if (cpu->CurInstr & (1<<18)) mask |= 0x00FF0000; // unused by arm 7 & 9 if (cpu->CurInstr & (1<<19)) mask |= ((cpu->Num==1) ? 0xF0000000 : 0xF8000000); - if (!(cpu->CurInstr & (1<<22))) - mask &= 0xFFFFFFDF; - if ((cpu->CPSR & 0x1F) == 0x10) mask &= 0xFFFFFF00; u32 val = ROR((cpu->CurInstr & 0xFF), ((cpu->CurInstr >> 7) & 0x1E)); @@ -121,6 +126,9 @@ void A_MSR_IMM(ARM* cpu) if (!(cpu->CurInstr & (1<<22))) cpu->UpdateMode(oldpsr, cpu->CPSR); + if (cpu->Num == 0) + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this + cpu->AddCycles_C(); } @@ -158,9 +166,6 @@ void A_MSR_REG(ARM* cpu) //if (cpu->CurInstr & (1<<18)) mask |= 0x00FF0000; // unused by arm 7 & 9 if (cpu->CurInstr & (1<<19)) mask |= ((cpu->Num==1) ? 0xF0000000 : 0xF8000000); - if (!(cpu->CurInstr & (1<<22))) - mask &= 0xFFFFFFDF; - if ((cpu->CPSR & 0x1F) == 0x10) mask &= 0xFFFFFF00; u32 val = cpu->R[cpu->CurInstr & 0xF]; @@ -173,6 +178,9 @@ void A_MSR_REG(ARM* cpu) if (!(cpu->CurInstr & (1<<22))) cpu->UpdateMode(oldpsr, cpu->CPSR); + + if (cpu->Num == 0) + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this cpu->AddCycles_C(); } diff --git a/src/ARMInterpreter.h b/src/ARMInterpreter.h index 1066ac69..4c5ddafe 100644 --- a/src/ARMInterpreter.h +++ b/src/ARMInterpreter.h @@ -36,6 +36,7 @@ void A_MRS(ARM* cpu); void A_MCR(ARM* cpu); void A_MRC(ARM* cpu); void A_SVC(ARM* cpu); +void A_BKPT(ARM* cpu); void T_SVC(ARM* cpu); diff --git a/src/ARM_InstrInfo.cpp b/src/ARM_InstrInfo.cpp index 58838307..d1be9761 100644 --- a/src/ARM_InstrInfo.cpp +++ b/src/ARM_InstrInfo.cpp @@ -194,6 +194,7 @@ const u32 A_BX = A_BranchAlways | A_Read0 | ak(ak_BX); const u32 A_BLX_REG = A_BranchAlways | A_Link | A_Read0 | ak(ak_BLX_REG); const u32 A_UNK = A_BranchAlways | A_Link | ak(ak_UNK); +const u32 A_BKPT = A_BranchAlways | A_Link | ak(ak_UNK); const u32 A_MSR_IMM = ak(ak_MSR_IMM); const u32 A_MSR_REG = A_Read0 | ak(ak_MSR_REG); const u32 A_MRS = A_Write12 | ak(ak_MRS); diff --git a/src/ARM_InstrTable.h b/src/ARM_InstrTable.h index 8213c2e0..2c480f8d 100644 --- a/src/ARM_InstrTable.h +++ b/src/ARM_InstrTable.h @@ -130,7 +130,7 @@ INSTRFUNC_PROTO(ARMInstrTable[4096]) = // 0001 0010 0000 A_MSR_REG, A_BX, A_UNK, A_BLX_REG, - A_UNK, A_QSUB, A_UNK, A_UNK, + A_UNK, A_QSUB, A_UNK, A_BKPT, A_SMLAWy, A_UNK, A_SMULWy, A_STRH_REG, A_SMLAWy, A_LDRD_REG, A_SMULWy, A_STRD_REG, From 45f87a1c8d529289f031619e3b13d4e6d67c3d57 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:57:55 -0400 Subject: [PATCH 16/29] prevent t bit changes without pipeline flush on arm7 idk what's happening fully and its gonna be slow to emulate most likely we'll figure this out later --- src/ARM.cpp | 26 ++------------ src/ARM.h | 6 +--- src/ARMInterpreter.cpp | 24 ++++++++++--- src/ARMInterpreter_ALU.cpp | 71 +++++++++++++++++++++++++++++++------- 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index c7fea92d..6518b751 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -201,13 +201,6 @@ void ARMv5::Reset() ARM::Reset(); } -void ARMv4::Reset() -{ - Thumb = false; - - ARM::Reset(); -} - void ARM::DoSavestate(Savestate* file) { @@ -402,7 +395,6 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) Cycles += NDS.ARM7MemTimings[CodeCycles][0] + NDS.ARM7MemTimings[CodeCycles][1]; CPSR |= 0x20; - Thumb = true; } else { @@ -416,7 +408,6 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) Cycles += NDS.ARM7MemTimings[CodeCycles][2] + NDS.ARM7MemTimings[CodeCycles][3]; CPSR &= ~0x20; - Thumb = false; } } @@ -840,11 +831,8 @@ void ARMv4::Execute() else #endif { - if (Thumb) // THUMB + if (CPSR & 0x20) // THUMB { - Thumb = (CPSR & 0x20); - bool fix = !Thumb; - if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); @@ -858,22 +846,12 @@ void ARMv4::Execute() else { // actually execute - u32 icode = (CurInstr >> 6) & 0x3FF; + u32 icode = (CurInstr >> 6); ARMInterpreter::THUMBInstrTable[icode](this); } - - if (fix) [[unlikely]] - { - // probably wrong? - // fixup - R[15] &= ~0x3; - NextInstr[1] = CodeRead32(R[15]); - } } else { - Thumb = (CPSR & 0x20); - if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); diff --git a/src/ARM.h b/src/ARM.h index 8d640a30..26080b51 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -385,8 +385,6 @@ class ARMv4 : public ARM public: ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit); - void Reset() override; - void FillPipeline() override; void JumpTo(u32 addr, bool restorecpsr = false) override; @@ -395,7 +393,7 @@ public: template void Execute(); - u32 CodeRead16(u32 addr) + u16 CodeRead16(u32 addr) { return BusRead16(addr); } @@ -405,8 +403,6 @@ public: return BusRead32(addr); } - bool Thumb; - bool DataRead8(u32 addr, u32* val) override; bool DataRead16(u32 addr, u32* val) override; bool DataRead32(u32 addr, u32* val) override; diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 979e3bb8..b11913ef 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -126,8 +126,15 @@ void A_MSR_IMM(ARM* cpu) if (!(cpu->CurInstr & (1<<22))) cpu->UpdateMode(oldpsr, cpu->CPSR); - if (cpu->Num == 0) - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) + { + if (cpu->Num == 0) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this + else + { + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR IMM T bit change on ARM7\n"); + cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); // keep it from crashing the emulator at least + } + } cpu->AddCycles_C(); } @@ -178,9 +185,16 @@ void A_MSR_REG(ARM* cpu) if (!(cpu->CurInstr & (1<<22))) cpu->UpdateMode(oldpsr, cpu->CPSR); - - if (cpu->Num == 0) - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this + + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) + { + if (cpu->Num == 0) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this + else + { + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); + cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); // keep it from crashing the emulator at least + } + } cpu->AddCycles_C(); } diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index f04ab9b5..fd60b5f0 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -585,8 +585,17 @@ A_IMPLEMENT_ALU_OP(RSC,) !res); \ if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING TST w/ rd == 15???"); \ + if (cpu->Num == 1) \ + { \ + u32 oldcpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST T bit change on ARM7\n"); \ + cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + } \ + } \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -600,8 +609,17 @@ A_IMPLEMENT_ALU_TEST(TST,_S) !res); \ if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING TEQ w/ rd == 15???"); \ + if (cpu->Num == 1) \ + { \ + u32 oldcpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ T bit change on ARM7\n"); \ + cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + } \ + } \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -617,8 +635,17 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S) OverflowSub(a, b)); \ if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING CMP w/ rd == 15???"); \ + if (cpu->Num == 1) \ + { \ + u32 oldcpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP T bit change on ARM7\n"); \ + cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + } \ + } \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -634,8 +661,17 @@ A_IMPLEMENT_ALU_TEST(CMP,) OverflowAdd(a, b)); \ if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: WHO IS USING CMN w/ rd == 15???"); \ + if (cpu->Num == 1) \ + { \ + u32 oldcpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN T bit change on ARM7\n"); \ + cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + } \ + } \ + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -1589,11 +1625,20 @@ void T_CMP_HIREG(ARM* cpu) !res, CarrySub(a, b), OverflowSub(a, b)); - if (rd == 15) \ - { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP HIREG w/ rd == 15."); \ - } \ + if (rd == 15) + { + if (cpu->Num == 1) + { + u32 oldcpsr = cpu->CPSR; + cpu->RestoreCPSR(); // ARM7TDMI restores cpsr and does ___not___ flush the pipeline. + if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) + { + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); + cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); // keep it from crashing the emulator at least + } + } + else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP HIREG w/ rd == 15 on ARM9\n"); + } cpu->AddCycles_C(); } From c1338147137dab672034c9f3074c1bace7b31e4f Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Fri, 20 Sep 2024 04:39:16 -0400 Subject: [PATCH 17/29] some day i will remember to test before pushing --- src/ARMInterpreter_ALU.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index fd60b5f0..a638a49c 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -587,7 +587,7 @@ A_IMPLEMENT_ALU_OP(RSC,) { \ if (cpu->Num == 1) \ { \ - u32 oldcpsr = cpu->CPSR; \ + u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ { \ @@ -611,7 +611,7 @@ A_IMPLEMENT_ALU_TEST(TST,_S) { \ if (cpu->Num == 1) \ { \ - u32 oldcpsr = cpu->CPSR; \ + u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ { \ @@ -637,7 +637,7 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S) { \ if (cpu->Num == 1) \ { \ - u32 oldcpsr = cpu->CPSR; \ + u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ { \ @@ -663,7 +663,7 @@ A_IMPLEMENT_ALU_TEST(CMP,) { \ if (cpu->Num == 1) \ { \ - u32 oldcpsr = cpu->CPSR; \ + u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ { \ @@ -1629,7 +1629,7 @@ void T_CMP_HIREG(ARM* cpu) { if (cpu->Num == 1) { - u32 oldcpsr = cpu->CPSR; + u32 oldpsr = cpu->CPSR; cpu->RestoreCPSR(); // ARM7TDMI restores cpsr and does ___not___ flush the pipeline. if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) { From 7afa805afc3719b145424c642f3858d3be75169a Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Fri, 20 Sep 2024 05:37:51 -0400 Subject: [PATCH 18/29] slightly better code --- src/ARMInterpreter.cpp | 10 +++++----- src/ARMInterpreter_ALU.cpp | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index b11913ef..72d1e189 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -126,13 +126,13 @@ void A_MSR_IMM(ARM* cpu) if (!(cpu->CurInstr & (1<<22))) cpu->UpdateMode(oldpsr, cpu->CPSR); - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) + if (cpu->CPSR & 0x20) [[unlikely]] { if (cpu->Num == 0) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this else { - Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR IMM T bit change on ARM7\n"); - cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); // keep it from crashing the emulator at least + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); + cpu->CPSR &= ~0x20; // keep it from crashing the emulator at least } } @@ -186,13 +186,13 @@ void A_MSR_REG(ARM* cpu) if (!(cpu->CurInstr & (1<<22))) cpu->UpdateMode(oldpsr, cpu->CPSR); - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) + if (cpu->CPSR & 0x20) [[unlikely]] { if (cpu->Num == 0) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this else { Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); - cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); // keep it from crashing the emulator at least + cpu->CPSR &= ~0x20; // keep it from crashing the emulator at least } } diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index a638a49c..9305fc42 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -583,16 +583,16 @@ A_IMPLEMENT_ALU_OP(RSC,) u32 res = a & b; \ cpu->SetNZ(res & 0x80000000, \ !res); \ - if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ if (cpu->Num == 1) \ { \ u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + if (cpu->CPSR & 0x20) \ { \ Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST T bit change on ARM7\n"); \ - cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ } \ } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST w/ rd == 15 on ARM9\n"); \ @@ -607,16 +607,16 @@ A_IMPLEMENT_ALU_TEST(TST,_S) u32 res = a ^ b; \ cpu->SetNZ(res & 0x80000000, \ !res); \ - if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ if (cpu->Num == 1) \ { \ u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + if (cpu->CPSR & 0x20) \ { \ Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ T bit change on ARM7\n"); \ - cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ } \ } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ w/ rd == 15 on ARM9\n"); \ @@ -633,16 +633,16 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S) !res, \ CarrySub(a, b), \ OverflowSub(a, b)); \ - if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ if (cpu->Num == 1) \ { \ u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + if (cpu->CPSR & 0x20) \ { \ Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP T bit change on ARM7\n"); \ - cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ } \ } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP w/ rd == 15 on ARM9\n"); \ @@ -659,16 +659,16 @@ A_IMPLEMENT_ALU_TEST(CMP,) !res, \ CarryAdd(a, b), \ OverflowAdd(a, b)); \ - if (((cpu->CurInstr>>12) & 0xF) == 15) /* yes this instruction has a secret rd for some reason */ \ + if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ if (cpu->Num == 1) \ { \ u32 oldpsr = cpu->CPSR; \ cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) \ + if (cpu->CPSR & 0x20) \ { \ Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN T bit change on ARM7\n"); \ - cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); /* keep it from crashing the emulator at least */ \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ } \ } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN w/ rd == 15 on ARM9\n"); \ @@ -1625,16 +1625,16 @@ void T_CMP_HIREG(ARM* cpu) !res, CarrySub(a, b), OverflowSub(a, b)); - if (rd == 15) + if (rd == 15) [[unlikely]] { if (cpu->Num == 1) { u32 oldpsr = cpu->CPSR; cpu->RestoreCPSR(); // ARM7TDMI restores cpsr and does ___not___ flush the pipeline. - if (!(oldpsr & 0x20) && (cpu->CPSR & 0x20)) + if (!(cpu->CPSR & 0x20)) { Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); - cpu->CPSR = (cpu->CPSR & ~0x20) | (oldpsr & 0x20); // keep it from crashing the emulator at least + cpu->CPSR |= 0x20; // keep it from crashing the emulator at least } } else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP HIREG w/ rd == 15 on ARM9\n"); From 157e9c5b046199658d5c5e12a3a5b29bf944a451 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:34:27 -0400 Subject: [PATCH 19/29] reimplement changing t bit with arm7 kinda slow though? --- src/ARM.cpp | 52 +++++++++++++++++++++++++++-------- src/ARM.h | 3 +++ src/ARMInterpreter.cpp | 4 +-- src/ARMInterpreter_ALU.cpp | 55 ++++---------------------------------- 4 files changed, 51 insertions(+), 63 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index 6518b751..ade9649f 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -385,6 +385,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) if (addr & 0x1) { + Thumb = true; addr &= ~0x1; R[15] = addr+2; @@ -398,6 +399,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) } else { + Thumb = false; addr &= ~0x3; R[15] = addr+4; @@ -831,35 +833,63 @@ void ARMv4::Execute() else #endif { - if (CPSR & 0x20) // THUMB + if (Thumb) // THUMB { + // attempt to delay t bit changes without a pipeline flush (msr) by one instruction + Thumb = CPSR & 0x20; if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); // prefetch - R[15] += 2; - CurInstr = NextInstr[0]; - NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead16(R[15]); + // thumb bit can change without a flush and is usually delayed 1 instruction + // but if the code fetch takes more than 1 cycle(?) it can take effect early for just the code fetch + if (!Thumb && (NDS.ARM7MemTimings[CodeCycles][2] > 1)) [[unlikely]] // checkme + { + R[15] = (R[15] + 4) & ~0x3; + CurInstr = NextInstr[0]; + NextInstr[0] = NextInstr[1]; + NextInstr[1] = CodeRead32(R[15]); + } + else + { + R[15] += 2; + CurInstr = NextInstr[0]; + NextInstr[0] = NextInstr[1]; + NextInstr[1] = CodeRead16(R[15]); + } if (IRQ && !(CPSR & 0x80)) TriggerIRQ(); else { // actually execute - u32 icode = (CurInstr >> 6); + u32 icode = (CurInstr >> 6) & 0x3FF; ARMInterpreter::THUMBInstrTable[icode](this); } } else { + // attempt to delay t bit changes without a pipeline flush (msr) by one instruction + Thumb = CPSR & 0x20; if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); - + // prefetch - R[15] += 4; - CurInstr = NextInstr[0]; - NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead32(R[15]); + // thumb bit can change without a flush and is usually delayed 1 instruction + // but if the code fetch takes more than 1 cycle(?) it can take effect early for just the code fetch + if (Thumb && (NDS.ARM7MemTimings[CodeCycles][2] > 1)) [[unlikely]] // checkme? + { + R[15] = (R[15] + 4) & ~0x3; + CurInstr = NextInstr[0]; + NextInstr[0] = NextInstr[1]; + NextInstr[1] = CodeRead16(R[15]); + } + else + { + R[15] = (R[15] + 4) & ~0x3; + CurInstr = NextInstr[0]; + NextInstr[0] = NextInstr[1]; + NextInstr[1] = CodeRead32(R[15]); + } if (IRQ && !(CPSR & 0x80)) TriggerIRQ(); else if (CheckCondition(CurInstr >> 28)) // actually execute diff --git a/src/ARM.h b/src/ARM.h index 26080b51..81d6be39 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -416,6 +416,9 @@ public: void AddCycles_CDI() override; void AddCycles_CD() override; +private: + bool Thumb; + protected: u8 BusRead8(u32 addr) override; u16 BusRead16(u32 addr) override; diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 72d1e189..cc19df3b 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -129,11 +129,11 @@ void A_MSR_IMM(ARM* cpu) if (cpu->CPSR & 0x20) [[unlikely]] { if (cpu->Num == 0) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this - else + /*else { Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); cpu->CPSR &= ~0x20; // keep it from crashing the emulator at least - } + }*/ } cpu->AddCycles_C(); diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 9305fc42..abe2bce0 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -585,16 +585,7 @@ A_IMPLEMENT_ALU_OP(RSC,) !res); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) \ - { \ - u32 oldpsr = cpu->CPSR; \ - cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (cpu->CPSR & 0x20) \ - { \ - Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST T bit change on ARM7\n"); \ - cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ - } \ - } \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -609,16 +600,7 @@ A_IMPLEMENT_ALU_TEST(TST,_S) !res); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) \ - { \ - u32 oldpsr = cpu->CPSR; \ - cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (cpu->CPSR & 0x20) \ - { \ - Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ T bit change on ARM7\n"); \ - cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ - } \ - } \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -635,16 +617,7 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S) OverflowSub(a, b)); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) \ - { \ - u32 oldpsr = cpu->CPSR; \ - cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (cpu->CPSR & 0x20) \ - { \ - Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP T bit change on ARM7\n"); \ - cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ - } \ - } \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -661,16 +634,7 @@ A_IMPLEMENT_ALU_TEST(CMP,) OverflowAdd(a, b)); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) \ - { \ - u32 oldpsr = cpu->CPSR; \ - cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ - if (cpu->CPSR & 0x20) \ - { \ - Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN T bit change on ARM7\n"); \ - cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ - } \ - } \ + if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -1627,16 +1591,7 @@ void T_CMP_HIREG(ARM* cpu) OverflowSub(a, b)); if (rd == 15) [[unlikely]] { - if (cpu->Num == 1) - { - u32 oldpsr = cpu->CPSR; - cpu->RestoreCPSR(); // ARM7TDMI restores cpsr and does ___not___ flush the pipeline. - if (!(cpu->CPSR & 0x20)) - { - Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); - cpu->CPSR |= 0x20; // keep it from crashing the emulator at least - } - } + if (cpu->Num == 1) cpu->RestoreCPSR(); // ARM7 restores cpsr and does ___not___ flush the pipeline. else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP HIREG w/ rd == 15 on ARM9\n"); } cpu->AddCycles_C(); From 8d451dff48b3932225d9d2b222ec7bdeedbda265 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:47:40 -0400 Subject: [PATCH 20/29] misaligned pc.......... --- src/ARM.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index ade9649f..509027c5 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -837,6 +837,7 @@ void ARMv4::Execute() { // attempt to delay t bit changes without a pipeline flush (msr) by one instruction Thumb = CPSR & 0x20; + bool fix = !Thumb; if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); @@ -845,17 +846,17 @@ void ARMv4::Execute() // but if the code fetch takes more than 1 cycle(?) it can take effect early for just the code fetch if (!Thumb && (NDS.ARM7MemTimings[CodeCycles][2] > 1)) [[unlikely]] // checkme { - R[15] = (R[15] + 4) & ~0x3; + R[15] += 4; CurInstr = NextInstr[0]; NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead32(R[15]); + NextInstr[1] = CodeRead32(R[15] & ~3); } - else + else [[likely]] { R[15] += 2; CurInstr = NextInstr[0]; NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead16(R[15]); + NextInstr[1] = CodeRead16(R[15] & ~1); } if (IRQ && !(CPSR & 0x80)) TriggerIRQ(); @@ -865,6 +866,12 @@ void ARMv4::Execute() u32 icode = (CurInstr >> 6) & 0x3FF; ARMInterpreter::THUMBInstrTable[icode](this); } + + if (fix) [[unlikely]] // attempt at fixing flushless t bit changes + { + R[15] += 2; // yes it can end up misaligned. that's correct. + NextInstr[1] = CodeRead32(R[15] & ~3); + } } else { @@ -878,17 +885,17 @@ void ARMv4::Execute() // but if the code fetch takes more than 1 cycle(?) it can take effect early for just the code fetch if (Thumb && (NDS.ARM7MemTimings[CodeCycles][2] > 1)) [[unlikely]] // checkme? { - R[15] = (R[15] + 4) & ~0x3; + R[15] += 4; CurInstr = NextInstr[0]; NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead16(R[15]); + NextInstr[1] = CodeRead16(R[15] & ~1); } - else + else [[likely]] { - R[15] = (R[15] + 4) & ~0x3; + R[15] += 4; CurInstr = NextInstr[0]; NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead32(R[15]); + NextInstr[1] = CodeRead32(R[15] & ~3); } if (IRQ && !(CPSR & 0x80)) TriggerIRQ(); From 7b0d71dbbedcea1ab0a311147a75cdd3909d0995 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Sun, 22 Sep 2024 19:57:33 -0400 Subject: [PATCH 21/29] Revert T bit changing support for arm7 i cannot comprehend what is happening currently --- src/ARM.cpp | 59 +++++++------------------------------- src/ARM.h | 3 -- src/ARMInterpreter.cpp | 4 +-- src/ARMInterpreter_ALU.cpp | 55 +++++++++++++++++++++++++++++++---- 4 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index 509027c5..6518b751 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -385,7 +385,6 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) if (addr & 0x1) { - Thumb = true; addr &= ~0x1; R[15] = addr+2; @@ -399,7 +398,6 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) } else { - Thumb = false; addr &= ~0x3; R[15] = addr+4; @@ -833,70 +831,35 @@ void ARMv4::Execute() else #endif { - if (Thumb) // THUMB + if (CPSR & 0x20) // THUMB { - // attempt to delay t bit changes without a pipeline flush (msr) by one instruction - Thumb = CPSR & 0x20; - bool fix = !Thumb; if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); // prefetch - // thumb bit can change without a flush and is usually delayed 1 instruction - // but if the code fetch takes more than 1 cycle(?) it can take effect early for just the code fetch - if (!Thumb && (NDS.ARM7MemTimings[CodeCycles][2] > 1)) [[unlikely]] // checkme - { - R[15] += 4; - CurInstr = NextInstr[0]; - NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead32(R[15] & ~3); - } - else [[likely]] - { - R[15] += 2; - CurInstr = NextInstr[0]; - NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead16(R[15] & ~1); - } + R[15] += 2; + CurInstr = NextInstr[0]; + NextInstr[0] = NextInstr[1]; + NextInstr[1] = CodeRead16(R[15]); if (IRQ && !(CPSR & 0x80)) TriggerIRQ(); else { // actually execute - u32 icode = (CurInstr >> 6) & 0x3FF; + u32 icode = (CurInstr >> 6); ARMInterpreter::THUMBInstrTable[icode](this); } - - if (fix) [[unlikely]] // attempt at fixing flushless t bit changes - { - R[15] += 2; // yes it can end up misaligned. that's correct. - NextInstr[1] = CodeRead32(R[15] & ~3); - } } else { - // attempt to delay t bit changes without a pipeline flush (msr) by one instruction - Thumb = CPSR & 0x20; if constexpr (mode == CPUExecuteMode::InterpreterGDB) GdbCheckC(); - + // prefetch - // thumb bit can change without a flush and is usually delayed 1 instruction - // but if the code fetch takes more than 1 cycle(?) it can take effect early for just the code fetch - if (Thumb && (NDS.ARM7MemTimings[CodeCycles][2] > 1)) [[unlikely]] // checkme? - { - R[15] += 4; - CurInstr = NextInstr[0]; - NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead16(R[15] & ~1); - } - else [[likely]] - { - R[15] += 4; - CurInstr = NextInstr[0]; - NextInstr[0] = NextInstr[1]; - NextInstr[1] = CodeRead32(R[15] & ~3); - } + R[15] += 4; + CurInstr = NextInstr[0]; + NextInstr[0] = NextInstr[1]; + NextInstr[1] = CodeRead32(R[15]); if (IRQ && !(CPSR & 0x80)) TriggerIRQ(); else if (CheckCondition(CurInstr >> 28)) // actually execute diff --git a/src/ARM.h b/src/ARM.h index 81d6be39..26080b51 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -416,9 +416,6 @@ public: void AddCycles_CDI() override; void AddCycles_CD() override; -private: - bool Thumb; - protected: u8 BusRead8(u32 addr) override; u16 BusRead16(u32 addr) override; diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index cc19df3b..72d1e189 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -129,11 +129,11 @@ void A_MSR_IMM(ARM* cpu) if (cpu->CPSR & 0x20) [[unlikely]] { if (cpu->Num == 0) cpu->NextInstr[1] &= 0xFFFF; // checkme: probably not the right way to handle this - /*else + else { Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); cpu->CPSR &= ~0x20; // keep it from crashing the emulator at least - }*/ + } } cpu->AddCycles_C(); diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index abe2bce0..9305fc42 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -585,7 +585,16 @@ A_IMPLEMENT_ALU_OP(RSC,) !res); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->Num == 1) \ + { \ + u32 oldpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->CPSR & 0x20) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST T bit change on ARM7\n"); \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ + } \ + } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TST w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -600,7 +609,16 @@ A_IMPLEMENT_ALU_TEST(TST,_S) !res); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->Num == 1) \ + { \ + u32 oldpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->CPSR & 0x20) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ T bit change on ARM7\n"); \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ + } \ + } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: TEQ w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -617,7 +635,16 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S) OverflowSub(a, b)); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->Num == 1) \ + { \ + u32 oldpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->CPSR & 0x20) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP T bit change on ARM7\n"); \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ + } \ + } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -634,7 +661,16 @@ A_IMPLEMENT_ALU_TEST(CMP,) OverflowAdd(a, b)); \ if (((cpu->CurInstr>>12) & 0xF) == 15) [[unlikely]] /* yes this instruction has a secret rd for some reason */ \ { \ - if (cpu->Num == 1) cpu->RestoreCPSR(); /* ARM7 restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->Num == 1) \ + { \ + u32 oldpsr = cpu->CPSR; \ + cpu->RestoreCPSR(); /* ARM7TDMI restores cpsr and does ___not___ flush the pipeline. */ \ + if (cpu->CPSR & 0x20) \ + { \ + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN T bit change on ARM7\n"); \ + cpu->CPSR &= ~0x20; /* keep it from crashing the emulator at least */ \ + } \ + } \ else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMN w/ rd == 15 on ARM9\n"); \ } \ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); @@ -1591,7 +1627,16 @@ void T_CMP_HIREG(ARM* cpu) OverflowSub(a, b)); if (rd == 15) [[unlikely]] { - if (cpu->Num == 1) cpu->RestoreCPSR(); // ARM7 restores cpsr and does ___not___ flush the pipeline. + if (cpu->Num == 1) + { + u32 oldpsr = cpu->CPSR; + cpu->RestoreCPSR(); // ARM7TDMI restores cpsr and does ___not___ flush the pipeline. + if (!(cpu->CPSR & 0x20)) + { + Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: MSR REG T bit change on ARM7\n"); + cpu->CPSR |= 0x20; // keep it from crashing the emulator at least + } + } else Platform::Log(Platform::LogLevel::Warn, "UNIMPLEMENTED: CMP HIREG w/ rd == 15 on ARM9\n"); } cpu->AddCycles_C(); From 8af790beeec9e1a74648e8cb01a3492efcb6d340 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:00:35 -0400 Subject: [PATCH 22/29] ldm/str with empty rlist --- src/ARMInterpreter_LoadStore.cpp | 88 ++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index bf187aca..f181476b 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -434,7 +434,59 @@ void A_SWPB(ARM* cpu) SWP(cpu); } +void ReglessLDMSTM(ARM* cpu, const bool load, const u8 baseid, const bool writeback, const bool decrement, bool preinc, const bool usermode) +{ + if (cpu->Num == 1) + { + u32 base = cpu->R[baseid]; + if (decrement) + { + preinc = !preinc; + base -= 0x40; + } + if (preinc) base+=4; + + if (load) + { + u32 pc; + if (cpu->DataRead32(base, &pc)) + { + cpu->AddCycles_CDI(); + cpu->JumpTo(pc, usermode); // checkme can we restore cpsr? + } + else + { + cpu->AddCycles_CDI(); + ((ARMv5*)cpu)->DataAbort(); + return; + } + } + else + { + if (!cpu->DataWrite32(base, cpu->R[15])) + { + cpu->AddCycles_CD(); + ((ARMv5*)cpu)->DataAbort(); + return; + } + else + { + cpu->AddCycles_CD(); + } + } + } + else + { + cpu->AddCycles_C(); // checkme + } + + if (writeback) + { + if (decrement) cpu->R[baseid] -= 0x40; + else cpu->R[baseid] += 0x40; + } +} void A_LDM(ARM* cpu) { @@ -445,6 +497,12 @@ void A_LDM(ARM* cpu) u32 preinc = (cpu->CurInstr & (1<<24)); bool first = true; bool dabort = false; + + if (!(cpu->CurInstr & 0xFFFF)) [[unlikely]] + { + ReglessLDMSTM(cpu, true, baseid, cpu->CurInstr & (1<<21), !(cpu->CurInstr & (1<<23)), preinc, cpu->CurInstr & (1<<22)); + return; + } if (!(cpu->CurInstr & (1<<23))) // decrement { @@ -545,6 +603,12 @@ void A_STM(ARM* cpu) u32 preinc = (cpu->CurInstr & (1<<24)); bool first = true; bool dabort = false; + + if (!(cpu->CurInstr & 0xFFFF)) [[unlikely]] + { + ReglessLDMSTM(cpu, false, baseid, cpu->CurInstr & (1<<21), !(cpu->CurInstr & (1<<23)), preinc, false); + return; + } if (!(cpu->CurInstr & (1<<23))) { @@ -737,6 +801,12 @@ void T_PUSH(ARM* cpu) if (cpu->CurInstr & (1<<8)) nregs++; + + if (!nregs) [[unlikely]] + { + ReglessLDMSTM(cpu, false, 13, true, true, true, false); + return; + } u32 base = cpu->R[13]; base -= (nregs<<2); @@ -777,6 +847,12 @@ void T_POP(ARM* cpu) u32 base = cpu->R[13]; bool first = true; bool dabort = false; + + if (!(cpu->CurInstr & 0x1FF)) [[unlikely]] + { + ReglessLDMSTM(cpu, true, 13, true, false, false, false); + return; + } for (int i = 0; i < 8; i++) { @@ -823,6 +899,12 @@ void T_STMIA(ARM* cpu) u32 base = cpu->R[(cpu->CurInstr >> 8) & 0x7]; bool first = true; bool dabort = false; + + if (!(cpu->CurInstr & 0xFF)) [[unlikely]] + { + ReglessLDMSTM(cpu, false, (cpu->CurInstr >> 8) & 0x7, true, false, false, false); + return; + } for (int i = 0; i < 8; i++) { @@ -853,6 +935,12 @@ void T_LDMIA(ARM* cpu) u32 base = cpu->R[(cpu->CurInstr >> 8) & 0x7]; bool first = true; bool dabort = false; + + if (!(cpu->CurInstr & 0xFF)) [[unlikely]] + { + ReglessLDMSTM(cpu, true, (cpu->CurInstr >> 8) & 0x7, true, false, false, false); + return; + } for (int i = 0; i < 8; i++) { From 3b73f21bb7b5836bea24d82b5ce8326dee6ac7f9 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:12:23 -0400 Subject: [PATCH 23/29] str r15 is incremented by +2/+4 oop --- src/ARMInterpreter_LoadStore.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index f181476b..e8e6accc 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -434,7 +434,7 @@ void A_SWPB(ARM* cpu) SWP(cpu); } -void ReglessLDMSTM(ARM* cpu, const bool load, const u8 baseid, const bool writeback, const bool decrement, bool preinc, const bool usermode) +void ReglessLDMSTM(ARM* cpu, const bool load, const u8 baseid, const bool writeback, const bool decrement, bool preinc, const bool usermode, const bool thumb) { if (cpu->Num == 1) { @@ -464,7 +464,7 @@ void ReglessLDMSTM(ARM* cpu, const bool load, const u8 baseid, const bool writeb } else { - if (!cpu->DataWrite32(base, cpu->R[15])) + if (!cpu->DataWrite32(base, cpu->R[15] + (thumb ? 2 : 4))) { cpu->AddCycles_CD(); ((ARMv5*)cpu)->DataAbort(); @@ -500,7 +500,7 @@ void A_LDM(ARM* cpu) if (!(cpu->CurInstr & 0xFFFF)) [[unlikely]] { - ReglessLDMSTM(cpu, true, baseid, cpu->CurInstr & (1<<21), !(cpu->CurInstr & (1<<23)), preinc, cpu->CurInstr & (1<<22)); + ReglessLDMSTM(cpu, true, baseid, cpu->CurInstr & (1<<21), !(cpu->CurInstr & (1<<23)), preinc, cpu->CurInstr & (1<<22), false); return; } @@ -606,7 +606,7 @@ void A_STM(ARM* cpu) if (!(cpu->CurInstr & 0xFFFF)) [[unlikely]] { - ReglessLDMSTM(cpu, false, baseid, cpu->CurInstr & (1<<21), !(cpu->CurInstr & (1<<23)), preinc, false); + ReglessLDMSTM(cpu, false, baseid, cpu->CurInstr & (1<<21), !(cpu->CurInstr & (1<<23)), preinc, false, false); return; } @@ -804,7 +804,7 @@ void T_PUSH(ARM* cpu) if (!nregs) [[unlikely]] { - ReglessLDMSTM(cpu, false, 13, true, true, true, false); + ReglessLDMSTM(cpu, false, 13, true, true, true, false, true); return; } @@ -850,7 +850,7 @@ void T_POP(ARM* cpu) if (!(cpu->CurInstr & 0x1FF)) [[unlikely]] { - ReglessLDMSTM(cpu, true, 13, true, false, false, false); + ReglessLDMSTM(cpu, true, 13, true, false, false, false, true); return; } @@ -902,7 +902,7 @@ void T_STMIA(ARM* cpu) if (!(cpu->CurInstr & 0xFF)) [[unlikely]] { - ReglessLDMSTM(cpu, false, (cpu->CurInstr >> 8) & 0x7, true, false, false, false); + ReglessLDMSTM(cpu, false, (cpu->CurInstr >> 8) & 0x7, true, false, false, false, true); return; } @@ -938,7 +938,7 @@ void T_LDMIA(ARM* cpu) if (!(cpu->CurInstr & 0xFF)) [[unlikely]] { - ReglessLDMSTM(cpu, true, (cpu->CurInstr >> 8) & 0x7, true, false, false, false); + ReglessLDMSTM(cpu, true, (cpu->CurInstr >> 8) & 0x7, true, false, false, false, true); return; } From 7fb18b11552374df2cd51454cca7ce8fbdc583bc Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 23 Sep 2024 20:03:58 -0400 Subject: [PATCH 24/29] clean up code --- src/ARMInterpreter_LoadStore.cpp | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index e8e6accc..59b9bc30 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -450,30 +450,16 @@ void ReglessLDMSTM(ARM* cpu, const bool load, const u8 baseid, const bool writeb if (load) { u32 pc; - if (cpu->DataRead32(base, &pc)) - { - cpu->AddCycles_CDI(); - cpu->JumpTo(pc, usermode); // checkme can we restore cpsr? - } - else - { - cpu->AddCycles_CDI(); - ((ARMv5*)cpu)->DataAbort(); - return; - } + cpu->DataRead32(base, &pc); + + cpu->AddCycles_CDI(); + cpu->JumpTo(pc, usermode); } else { - if (!cpu->DataWrite32(base, cpu->R[15] + (thumb ? 2 : 4))) - { - cpu->AddCycles_CD(); - ((ARMv5*)cpu)->DataAbort(); - return; - } - else - { - cpu->AddCycles_CD(); - } + cpu->DataWrite32(base, cpu->R[15] + (thumb ? 2 : 4)); + + cpu->AddCycles_CD(); } } else From e1d4fbef750ce25e0b0c2c3f0f69bef7b5c79e85 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:47:32 -0400 Subject: [PATCH 25/29] i can't reproduce this anymore --- src/ARM.cpp | 26 -------------------------- src/ARM.h | 3 --- 2 files changed, 29 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index 6518b751..c194cc71 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -346,27 +346,6 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr) NDS.MonitorARM9Jump(addr); } -void ARMv5::JumpTo8_16Bit(const u32 addr) -{ - // 8 and 16 loads (signed included) to pc - if (!(CP15Control & 0x1)) - { - // if the pu is disabled it behaves like a normal jump - JumpTo((CP15Control & (1<<15)) ? (addr & ~0x1) : addr); - } - else - { - if (addr & 0x3) - { - // if the pu is enabled it will always prefetch abort if not word aligned - // although it will still attempt (and fail) to enter thumb mode if enabled - if ((addr & 0x1) && !(CP15Control & (1<<15))) CPSR |= 0x20; - PrefetchAbort(); - } - else JumpTo(addr); - } -} - void ARMv4::JumpTo(u32 addr, bool restorecpsr) { if (restorecpsr) @@ -411,11 +390,6 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) } } -void ARMv4::JumpTo8_16Bit(const u32 addr) -{ - JumpTo(addr & ~1); // checkme? -} - void ARM::RestoreCPSR() { u32 oldcpsr = CPSR; diff --git a/src/ARM.h b/src/ARM.h index 26080b51..e7156d72 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -75,7 +75,6 @@ public: virtual void FillPipeline() = 0; virtual void JumpTo(u32 addr, bool restorecpsr = false) = 0; - virtual void JumpTo8_16Bit(u32 addr) = 0; void RestoreCPSR(); void Halt(u32 halt) @@ -244,7 +243,6 @@ public: void FillPipeline() override; void JumpTo(u32 addr, bool restorecpsr = false) override; - void JumpTo8_16Bit(const u32 addr) override; void PrefetchAbort(); void DataAbort(); @@ -388,7 +386,6 @@ public: void FillPipeline() override; void JumpTo(u32 addr, bool restorecpsr = false) override; - void JumpTo8_16Bit(const u32 addr) override; template void Execute(); From 2eb6d44c2c5e366e05946e3e36ec596bd73eed10 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Tue, 24 Sep 2024 20:08:07 +0200 Subject: [PATCH 26/29] prevent use after free through focusOutEvent when window is closed --- src/frontend/qt_sdl/Window.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 79b37e31..d40a062e 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -780,6 +780,10 @@ void MainWindow::closeEvent(QCloseEvent* event) Config::Save(); emuInstance->deleteWindow(windowID, false); + + // emuInstance may be deleted + // prevent use after free from us + emuInstance = nullptr; QMainWindow::closeEvent(event); } @@ -970,7 +974,10 @@ void MainWindow::focusInEvent(QFocusEvent* event) void MainWindow::focusOutEvent(QFocusEvent* event) { - emuInstance->audioMute(); + // focusOutEvent is called through the window close event handler + // prevent use after free + if (emuInstance) + emuInstance->audioMute(); } void MainWindow::onAppStateChanged(Qt::ApplicationState state) From 3065141ed751778523876b92c54f9b89c33becec Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:03:18 -0400 Subject: [PATCH 27/29] probably not faster --- src/ARM.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index c194cc71..ac3fe200 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -697,13 +697,12 @@ void ARMv5::Execute() { ARMInterpreter::A_BLX_IMM(this); } - else + else if ((CurInstr & 0x0FF000F0) == 0x01200070) { - if ((((CurInstr >> 4) & 0xF) | ((CurInstr >> 16) & 0xFF0)) == 0x127) - ARMInterpreter::A_BKPT(this); // always passes regardless of condition code - else - AddCycles_C(); + ARMInterpreter::A_BKPT(this); // always passes regardless of condition code } + else + AddCycles_C(); } // TODO optimize this shit!!! From a11208ec6db98722579c047a78717408db1463be Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Tue, 24 Sep 2024 21:02:17 -0400 Subject: [PATCH 28/29] oops --- src/ARM.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ARM.cpp b/src/ARM.cpp index ac3fe200..f97c26e2 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -523,9 +523,11 @@ void ARM::TriggerIRQ() UpdateMode(oldcpsr, CPSR); R_IRQ[2] = oldcpsr; +#ifdef JIT_ENABLED if constexpr (mode == CPUExecuteMode::JIT) R[14] = R[15] + (oldcpsr & 0x20 ? 2 : 0); else +#endif R[14] = R[15] - (oldcpsr & 0x20 ? 0 : 4); JumpTo(ExceptionBase + 0x18); From e9446fa9dc3ba64c22f8e5bb611ad394b5729eda Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Sun, 29 Sep 2024 03:30:13 -0400 Subject: [PATCH 29/29] implement 3 configurable and toggleable framerate targets (#2159) This pr allows for configuring the framerate target and adds support for two other framerate targets, a "fastforward" and "slowmo" target which can be enabled via either a toggle or holding a button. this allows for supporting a more accurate framerate target and allows for users to slow down the speed of gameplay if they so desire --- src/frontend/qt_sdl/Config.cpp | 48 +++- src/frontend/qt_sdl/Config.h | 4 + src/frontend/qt_sdl/EmuInstance.cpp | 26 +- src/frontend/qt_sdl/EmuInstance.h | 12 +- src/frontend/qt_sdl/EmuInstanceInput.cpp | 7 +- src/frontend/qt_sdl/EmuThread.cpp | 43 ++-- .../qt_sdl/InputConfig/InputConfigDialog.h | 6 + .../qt_sdl/InterfaceSettingsDialog.cpp | 52 +++- src/frontend/qt_sdl/InterfaceSettingsDialog.h | 10 + .../qt_sdl/InterfaceSettingsDialog.ui | 236 +++++++++++++++--- src/frontend/qt_sdl/Window.cpp | 5 +- 11 files changed, 394 insertions(+), 55 deletions(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 580af72e..0a161b6f 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -55,7 +55,6 @@ DefaultList DefaultInts = {"Screen.VSyncInterval", 1}, {"3D.Renderer", renderer3D_Software}, {"3D.GL.ScaleFactor", 1}, - {"MaxFPS", 1000}, #ifdef JIT_ENABLED {"JIT.MaxBlockSize", 32}, #endif @@ -120,6 +119,13 @@ DefaultList DefaultStrings = {"Instance*.Firmware.Username", "melonDS"} }; +DefaultList DefaultDoubles = +{ + {"TargetFPS", 60.0}, + {"FastForwardFPS", 1000.0}, + {"SlowmoFPS", 30.0}, +}; + LegacyEntry LegacyFile[] = { {"Key_A", 0, "Keyboard.A", true}, @@ -153,7 +159,7 @@ LegacyEntry LegacyFile[] = {"HKKey_Pause", 0, "Keyboard.HK_Pause", true}, {"HKKey_Reset", 0, "Keyboard.HK_Reset", true}, {"HKKey_FastForward", 0, "Keyboard.HK_FastForward", true}, - {"HKKey_FastForwardToggle", 0, "Keyboard.HK_FastForwardToggle", true}, + {"HKKey_FastForwardToggle", 0, "Keyboard.HK_FrameLimitToggle", true}, {"HKKey_FullscreenToggle", 0, "Keyboard.HK_FullscreenToggle", true}, {"HKKey_SwapScreens", 0, "Keyboard.HK_SwapScreens", true}, {"HKKey_SwapScreenEmphasis", 0, "Keyboard.HK_SwapScreenEmphasis", true}, @@ -169,7 +175,7 @@ LegacyEntry LegacyFile[] = {"HKJoy_Pause", 0, "Joystick.HK_Pause", true}, {"HKJoy_Reset", 0, "Joystick.HK_Reset", true}, {"HKJoy_FastForward", 0, "Joystick.HK_FastForward", true}, - {"HKJoy_FastForwardToggle", 0, "Joystick.HK_FastForwardToggle", true}, + {"HKJoy_FastForwardToggle", 0, "Joystick.HK_FrameLimitToggle", true}, {"HKJoy_FullscreenToggle", 0, "Joystick.HK_FullscreenToggle", true}, {"HKJoy_SwapScreens", 0, "Joystick.HK_SwapScreens", true}, {"HKJoy_SwapScreenEmphasis", 0, "Joystick.HK_SwapScreenEmphasis", true}, @@ -434,6 +440,18 @@ std::string Array::GetString(const int id) return tval.as_string(); } +double Array::GetDouble(const int id) +{ + while (Data.size() < id+1) + Data.push_back(0.0); + + toml::value& tval = Data[id]; + if (!tval.is_floating()) + tval = 0.0; + + return tval.as_floating(); +} + void Array::SetInt(const int id, int val) { while (Data.size() < id+1) @@ -470,6 +488,15 @@ void Array::SetString(const int id, const std::string& val) tval = val; } +void Array::SetDouble(const int id, double val) +{ + while (Data.size() < id+1) + Data.push_back(0.0); + + toml::value& tval = Data[id]; + tval = val; +} + /*Table::Table()// : Data(toml::value()) { @@ -562,6 +589,15 @@ std::string Table::GetString(const std::string& path) return tval.as_string(); } +double Table::GetDouble(const std::string& path) +{ + toml::value& tval = ResolvePath(path); + if (!tval.is_floating()) + tval = FindDefault(path, 0.0, DefaultDoubles); + + return tval.as_floating(); +} + void Table::SetInt(const std::string& path, int val) { std::string rngkey = GetDefaultKey(PathPrefix+path); @@ -593,6 +629,12 @@ void Table::SetString(const std::string& path, const std::string& val) tval = val; } +void Table::SetDouble(const std::string& path, double val) +{ + toml::value& tval = ResolvePath(path); + tval = val; +} + toml::value& Table::ResolvePath(const std::string& path) { toml::value* ret = &Data; diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 4ca88b46..9e6d3ea4 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -61,11 +61,13 @@ public: int64_t GetInt64(const int id); bool GetBool(const int id); std::string GetString(const int id); + double GetDouble(const int id); void SetInt(const int id, int val); void SetInt64(const int id, int64_t val); void SetBool(const int id, bool val); void SetString(const int id, const std::string& val); + void SetDouble(const int id, double val); // convenience @@ -99,11 +101,13 @@ public: int64_t GetInt64(const std::string& path); bool GetBool(const std::string& path); std::string GetString(const std::string& path); + double GetDouble(const std::string& path); void SetInt(const std::string& path, int val); void SetInt64(const std::string& path, int64_t val); void SetBool(const std::string& path, bool val); void SetString(const std::string& path, const std::string& val); + void SetDouble(const std::string& path, double val); // convenience diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index e48f29a2..54bae531 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -88,7 +88,31 @@ EmuInstance::EmuInstance(int inst) : deleting(false), cheatsOn = localCfg.GetBool("EnableCheats"); doLimitFPS = globalCfg.GetBool("LimitFPS"); - maxFPS = globalCfg.GetInt("MaxFPS"); + + double val = globalCfg.GetDouble("TargetFPS"); + if (val == 0.0) + { + Platform::Log(Platform::LogLevel::Error, "Target FPS in config invalid\n"); + targetFPS = 1.0 / 60.0; + } + else targetFPS = 1.0 / val; + + val = globalCfg.GetDouble("FastForwardFPS"); + if (val == 0.0) + { + Platform::Log(Platform::LogLevel::Error, "Fast-Forward FPS in config invalid\n"); + fastForwardFPS = 1.0 / 60.0; + } + else fastForwardFPS = 1.0 / val; + + val = globalCfg.GetDouble("SlowmoFPS"); + if (val == 0.0) + { + Platform::Log(Platform::LogLevel::Error, "Slow-Mo FPS in config invalid\n"); + slowmoFPS = 1.0 / 60.0; + } + else slowmoFPS = 1.0 / val; + doAudioSync = globalCfg.GetBool("AudioSync"); mpAudioMode = globalCfg.GetInt("MP.AudioMode"); diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 39c187c2..04290f56 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -36,7 +36,7 @@ enum HK_Pause, HK_Reset, HK_FastForward, - HK_FastForwardToggle, + HK_FrameLimitToggle, HK_FullscreenToggle, HK_SwapScreens, HK_SwapScreenEmphasis, @@ -46,6 +46,9 @@ enum HK_PowerButton, HK_VolumeUp, HK_VolumeDown, + HK_SlowMo, + HK_FastForwardToggle, + HK_SlowMoToggle, HK_MAX }; @@ -252,7 +255,12 @@ public: std::unique_ptr firmwareSave; bool doLimitFPS; - int maxFPS; + double curFPS; + double targetFPS; + double fastForwardFPS; + double slowmoFPS; + bool fastForwardToggled; + bool slowmoToggled; bool doAudioSync; private: diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index ddaca8f0..bb06c242 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -47,7 +47,7 @@ const char* EmuInstance::hotkeyNames[HK_MAX] = "HK_Pause", "HK_Reset", "HK_FastForward", - "HK_FastForwardToggle", + "HK_FrameLimitToggle", "HK_FullscreenToggle", "HK_SwapScreens", "HK_SwapScreenEmphasis", @@ -56,7 +56,10 @@ const char* EmuInstance::hotkeyNames[HK_MAX] = "HK_FrameStep", "HK_PowerButton", "HK_VolumeUp", - "HK_VolumeDown" + "HK_VolumeDown", + "HK_SlowMo", + "HK_FastForwardToggle", + "HK_SlowMoToggle" }; diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index ae66e1ba..4ce4efda 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -149,12 +149,17 @@ void EmuThread::run() char melontitle[100]; + bool fastforward = false; + bool slowmo = false; + emuInstance->fastForwardToggled = false; + emuInstance->slowmoToggled = false; + while (emuStatus != emuStatus_Exit) { MPInterface::Get().Process(); emuInstance->inputProcess(); - if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); + if (emuInstance->hotkeyPressed(HK_FrameLimitToggle)) emit windowLimitFPSChange(); if (emuInstance->hotkeyPressed(HK_Pause)) emuTogglePause(); if (emuInstance->hotkeyPressed(HK_Reset)) emuReset(); @@ -332,22 +337,34 @@ void EmuThread::run() emit windowUpdate(); winUpdateCount = 0; } + + if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emuInstance->fastForwardToggled = !emuInstance->fastForwardToggled; + if (emuInstance->hotkeyPressed(HK_SlowMoToggle)) emuInstance->slowmoToggled = !emuInstance->slowmoToggled; - bool fastforward = emuInstance->hotkeyDown(HK_FastForward); + bool enablefastforward = emuInstance->hotkeyDown(HK_FastForward) | emuInstance->fastForwardToggled; + bool enableslowmo = emuInstance->hotkeyDown(HK_SlowMo) | emuInstance->slowmoToggled; if (useOpenGL) { - // when using OpenGL: when toggling fast-forward, change the vsync interval - if (emuInstance->hotkeyPressed(HK_FastForward)) + // when using OpenGL: when toggling fast-forward or slowmo, change the vsync interval + if ((enablefastforward || enableslowmo) && !(fastforward || slowmo)) { emuInstance->setVSyncGL(false); } - else if (emuInstance->hotkeyReleased(HK_FastForward)) + else if (!(enablefastforward || enableslowmo) && (fastforward || slowmo)) { emuInstance->setVSyncGL(true); } } + fastforward = enablefastforward; + slowmo = enableslowmo; + + if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS; + else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS; + else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1.0 / 1000.0; + else emuInstance->curFPS = emuInstance->targetFPS; + if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1) { DSi* dsi = static_cast(emuInstance->nds); @@ -361,23 +378,19 @@ void EmuThread::run() emuInstance->audioVolume = volumeLevel * (256.0 / 31.0); } - if (emuInstance->doAudioSync && !fastforward) + if (emuInstance->doAudioSync && !(fastforward || slowmo)) emuInstance->audioSync(); double frametimeStep = nlines / (60.0 * 263.0); { - bool limitfps = emuInstance->doLimitFPS && !fastforward; - - double practicalFramelimit = limitfps ? frametimeStep : 1.0 / emuInstance->maxFPS; - double curtime = SDL_GetPerformanceCounter() * perfCountsSec; - frameLimitError += practicalFramelimit - (curtime - lastTime); - if (frameLimitError < -practicalFramelimit) - frameLimitError = -practicalFramelimit; - if (frameLimitError > practicalFramelimit) - frameLimitError = practicalFramelimit; + frameLimitError += emuInstance->curFPS - (curtime - lastTime); + if (frameLimitError < -emuInstance->curFPS) + frameLimitError = -emuInstance->curFPS; + if (frameLimitError > emuInstance->curFPS) + frameLimitError = emuInstance->curFPS; if (round(frameLimitError * 1000.0) > 0.0) { diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 64986d62..3337228f 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -49,6 +49,9 @@ static constexpr std::initializer_list hk_general = HK_FrameStep, HK_FastForward, HK_FastForwardToggle, + HK_SlowMo, + HK_SlowMoToggle, + HK_FrameLimitToggle, HK_FullscreenToggle, HK_Lid, HK_Mic, @@ -65,6 +68,9 @@ static constexpr std::initializer_list hk_general_labels = "Reset", "Frame step", "Fast forward", + "Toggle fast forward", + "Slow mo", + "Toggle slow mo", "Toggle FPS limit", "Toggle fullscreen", "Close/open lid", diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index dfe30d12..2e7e75c9 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -39,7 +39,9 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked()); ui->spinMouseHideSeconds->setValue(cfg.GetInt("MouseHideSeconds")); ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus")); - ui->spinMaxFPS->setValue(cfg.GetInt("MaxFPS")); + ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS")); + ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS")); + ui->spinSlow->setValue(cfg.GetDouble("SlowmoFPS")); const QList themeKeys = QStyleFactory::keys(); const QString currentTheme = qApp->style()->objectName(); @@ -65,6 +67,41 @@ void InterfaceSettingsDialog::on_cbMouseHide_clicked() ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked()); } +void InterfaceSettingsDialog::on_pbClean_clicked() +{ + ui->spinTargetFPS->setValue(60.0000); +} + +void InterfaceSettingsDialog::on_pbAccurate_clicked() +{ + ui->spinTargetFPS->setValue(59.8261); +} + +void InterfaceSettingsDialog::on_pb2x_clicked() +{ + ui->spinFFW->setValue(ui->spinTargetFPS->value() * 2.0); +} + +void InterfaceSettingsDialog::on_pb3x_clicked() +{ + ui->spinFFW->setValue(ui->spinTargetFPS->value() * 3.0); +} + +void InterfaceSettingsDialog::on_pbMAX_clicked() +{ + ui->spinFFW->setValue(1000.0); +} + +void InterfaceSettingsDialog::on_pbHalf_clicked() +{ + ui->spinSlow->setValue(ui->spinTargetFPS->value() / 2.0); +} + +void InterfaceSettingsDialog::on_pbQuarter_clicked() +{ + ui->spinSlow->setValue(ui->spinTargetFPS->value() / 4.0); +} + void InterfaceSettingsDialog::done(int r) { if (r == QDialog::Accepted) @@ -74,7 +111,18 @@ void InterfaceSettingsDialog::done(int r) cfg.SetBool("MouseHide", ui->cbMouseHide->isChecked()); cfg.SetInt("MouseHideSeconds", ui->spinMouseHideSeconds->value()); cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked()); - cfg.SetInt("MaxFPS", ui->spinMaxFPS->value()); + + double val = ui->spinTargetFPS->value(); + if (val == 0.0) cfg.SetDouble("TargetFPS", 0.0001); + else cfg.SetDouble("TargetFPS", val); + + val = ui->spinFFW->value(); + if (val == 0.0) cfg.SetDouble("FastForwardFPS", 0.0001); + else cfg.SetDouble("FastForwardFPS", val); + + val = ui->spinSlow->value(); + if (val == 0.0) cfg.SetDouble("SlowmoFPS", 0.0001); + else cfg.SetDouble("SlowmoFPS", val); QString themeName = ui->cbxUITheme->currentData().toString(); cfg.SetQString("UITheme", themeName); diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.h b/src/frontend/qt_sdl/InterfaceSettingsDialog.h index bbc62df9..c960e560 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.h +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.h @@ -60,6 +60,16 @@ private slots: void on_cbMouseHide_clicked(); + void on_pbClean_clicked(); + void on_pbAccurate_clicked(); + + void on_pb2x_clicked(); + void on_pb3x_clicked(); + void on_pbMAX_clicked(); + + void on_pbHalf_clicked(); + void on_pbQuarter_clicked(); + private: Ui::InterfaceSettingsDialog* ui; diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui index 21d8434e..460a6e99 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui @@ -6,12 +6,12 @@ 0 0 - 337 - 275 + 389 + 356 - + 0 0 @@ -20,6 +20,9 @@ Interface settings - melonDS + + QLayout::SizeConstraint::SetFixedSize + @@ -95,32 +98,209 @@ Framerate - + - - - Fast-forward limit + + + 6 - - spinMaxFPS + + 2 - - - - - - FPS - - - 60 - - - 1000 - - - 1000 - - + + + + Target FPS + + + + + + + Fast-Forward + + + spinFFW + + + + + + + FPS + + + 4 + + + 0.000100000000000 + + + 1000.000000000000000 + + + 59.826099999999997 + + + + + + + 2 + + + + + + 63 + 16777215 + + + + 1/4 + + + + + + + + 62 + 16777215 + + + + 1/2 + + + + + + + + + FPS + + + 4 + + + 0.000100000000000 + + + 1000.000000000000000 + + + 29.913000000000000 + + + + + + + FPS + + + 4 + + + 0.000100000000000 + + + 1000.000000000000000 + + + 1000.000000000000000 + + + + + + + Slow-Mo + + + + + + + 2 + + + + + + 63 + 16777215 + + + + Accurate + + + + + + + + 62 + 16777215 + + + + Clean + + + + + + + + + 2 + + + + + + 41 + 16777215 + + + + 2x + + + + + + + + 41 + 16777215 + + + + 3x + + + + + + + + 41 + 16777215 + + + + MAX + + + + + + @@ -128,10 +308,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index d40a062e..3020defd 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1946,8 +1946,9 @@ void MainWindow::onOpenInterfaceSettings() void MainWindow::onUpdateInterfaceSettings() { pauseOnLostFocus = globalCfg.GetBool("PauseLostFocus"); - emuInstance->maxFPS = globalCfg.GetInt("MaxFPS"); - + emuInstance->targetFPS = 1.0 / globalCfg.GetDouble("TargetFPS"); + emuInstance->fastForwardFPS = 1.0 / globalCfg.GetDouble("FastForwardFPS"); + emuInstance->slowmoFPS = 1.0 / globalCfg.GetDouble("SlowmoFPS"); panel->setMouseHide(globalCfg.GetBool("MouseHide"), globalCfg.GetInt("MouseHideSeconds")*1000); }