diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml
deleted file mode 100644
index 7e7df583..00000000
--- a/.github/workflows/build-appimage.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-name: CMake Build (AppImage x86-64)
-
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-
-jobs:
- build:
-
- runs-on: ubuntu-20.04
-
- steps:
- - uses: actions/checkout@v1
- - name: Install dependencies
- run: |
- sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
- sudo apt update
- sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libqt5multimedia5-plugins qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades
- - name: Create build environment
- run: mkdir ${{runner.workspace}}/build
- - name: Configure
- working-directory: ${{runner.workspace}}/build
- run: cmake $GITHUB_WORKSPACE
- - name: Make
- working-directory: ${{runner.workspace}}/build
- run: |
- make -j$(nproc --all)
- - name: Prepare AppDir for AppImage
- working-directory: ${{runner.workspace}}/build
- run: |
- make install DESTDIR=AppDir
- mv ./AppDir/usr/local/bin ./AppDir/usr/bin
- mv ./AppDir/usr/local/share ./AppDir/usr/share
- rm -rf ./AppDir/usr/local
- - name: Prepare necessary Tools for building the AppImage
- working-directory: ${{runner.workspace}}/build
- run: |
- wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
- wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
- chmod a+x linuxdeploy-x86_64.AppImage
- chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
- - name: Build the AppImage
- working-directory: ${{runner.workspace}}/build
- run: |
- ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
- mkdir dist
- cp ./melonDS*.AppImage ./dist
- - uses: actions/upload-artifact@v1
- with:
- name: melonDS-appimage-x86_64
- path: ${{runner.workspace}}/build/dist
diff --git a/.github/workflows/build-macos-universal.yml b/.github/workflows/build-macos-universal.yml
deleted file mode 100644
index 4416ce7a..00000000
--- a/.github/workflows/build-macos-universal.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: CMake Build (macOS Universal)
-
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-
-jobs:
- prepare:
- runs-on: [self-hosted, macOS, ARM64]
-
- steps:
- - name: Clean workspace
- run: rm -rf ${{runner.workspace}}/build
-
- - uses: actions/checkout@v3
-
-
- build-arm64:
- needs: prepare
- runs-on: [self-hosted, macOS, ARM64]
- env:
- homebrew_prefix: /opt/homebrew
-
- steps:
- - name: Create build directory
- run: mkdir -p ${{runner.workspace}}/build/arm64
-
- - name: Configure
- working-directory: ${{runner.workspace}}/build/arm64
- run: arch -arm64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
-
- - name: Make
- working-directory: ${{runner.workspace}}/build/arm64
- run: arch -arm64 make -j$(sysctl -n hw.logicalcpu)
-
- build-x86_64:
- needs: prepare
- runs-on: [self-hosted, macOS, ARM64]
- env:
- homebrew_prefix: /usr/local
-
- steps:
- - name: Create build directory
- run: mkdir -p ${{runner.workspace}}/build/x86_64
-
- - name: Configure
- working-directory: ${{runner.workspace}}/build/x86_64
- run: arch -x86_64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
-
- - name: Make
- working-directory: ${{runner.workspace}}/build/x86_64
- run: arch -x86_64 make -j$(sysctl -n hw.logicalcpu)
-
- universal-binary:
- needs: [build-arm64, build-x86_64]
- runs-on: [self-hosted, macOS, ARM64]
-
- steps:
- - name: Merge binaries
- run: $GITHUB_WORKSPACE/tools/mac-universal.py ${{runner.workspace}}/build/arm64/melonDS.app ${{runner.workspace}}/build/x86_64/melonDS.app ${{runner.workspace}}/build/universal/melonDS.app
-
- - name: Codesign app
- run: codesign -s - --deep -f ${{runner.workspace}}/build/universal/melonDS.app
-
- - name: Create DMG
- run: hdiutil create -fs HFS+ -volname melonDS -srcfolder ${{runner.workspace}}/build/universal/melonDS.app -ov -format UDBZ ${{runner.workspace}}/build/universal/melonDS.dmg
-
- - uses: actions/upload-artifact@v3
- with:
- name: macOS-universal
- path: ${{runner.workspace}}/build/universal/melonDS.dmg
-
diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml
new file mode 100644
index 00000000..4178157d
--- /dev/null
+++ b/.github/workflows/build-macos.yml
@@ -0,0 +1,85 @@
+name: macOS
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ build-macos:
+ strategy:
+ matrix:
+ arch: [x86_64, arm64]
+
+ name: ${{ matrix.arch }}
+ runs-on: macos-14
+ steps:
+ - name: Check out sources
+ uses: actions/checkout@v3
+ - name: Install dependencies for package building
+ run: |
+ brew install autoconf automake autoconf-archive libtool python-setuptools
+ - name: Set up CMake
+ uses: lukka/get-cmake@latest
+ - name: Set up vcpkg
+ uses: lukka/run-vcpkg@v11
+ with:
+ vcpkgGitCommitId: 53bef8994c541b6561884a8395ea35715ece75db
+ - name: Build
+ uses: lukka/run-cmake@v10
+ with:
+ configurePreset: release-mac-${{ matrix.arch }}
+ buildPreset: release-mac-${{ matrix.arch }}
+ - name: Compress app bundle
+ shell: bash
+ run: |
+ cd build/release-mac-${{ matrix.arch }}
+ zip -r -y ../../macOS-${{ matrix.arch }}.zip melonDS.app
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: macOS-${{ matrix.arch }}
+ path: macOS-${{ matrix.arch }}.zip
+ retention-days: 1
+
+ universal-binary:
+ name: Universal binary
+ needs: [build-macos]
+ runs-on: macos-13
+ continue-on-error: true
+ steps:
+ - name: Download x86_64
+ uses: actions/download-artifact@v4
+ with:
+ name: macOS-x86_64
+ path: x86_64
+ - name: Download arm64
+ uses: actions/download-artifact@v4
+ with:
+ name: macOS-arm64
+ path: arm64
+ - name: Combine app bundles
+ shell: bash
+ run: |
+ unzip x86_64/*.zip -d x86_64
+ unzip arm64/*.zip -d arm64
+ lipo {x86_64,arm64}/melonDS.app/Contents/MacOS/melonDS -create -output melonDS
+ cp -a arm64/melonDS.app melonDS.app
+ cp melonDS melonDS.app/Contents/MacOS/melonDS
+ codesign -s - --deep melonDS.app
+ zip -r -y macOS-universal.zip melonDS.app
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: macOS-universal
+ path: macOS-universal.zip
+# - name: Clean up architecture-specific artifacts
+# uses: geekyeggo/delete-artifact@v4
+# with:
+# failOnError: false
+# name: |
+# macOS-x86_64
+# macOS-arm64
diff --git a/.github/workflows/build-ubuntu-aarch64.yml b/.github/workflows/build-ubuntu-aarch64.yml
deleted file mode 100644
index 096bc0b9..00000000
--- a/.github/workflows/build-ubuntu-aarch64.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-name: CMake Build (Ubuntu aarch64)
-
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-
-env:
- BUILD_TYPE: Release
-
-jobs:
- build:
- runs-on: ubuntu-20.04
- container: ubuntu:20.04
-
- steps:
- - name: Prepare system
- shell: bash
- run: |
- apt update
- apt -y full-upgrade
- apt -y install git
- - name: Check out source
- uses: actions/checkout@v1
- - name: Install dependencies
- shell: bash
- run: |
- dpkg --add-architecture arm64
- sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
- rm /etc/apt/sources.list
- mv /etc/apt/sources.list{.new,}
- apt update
- DEBIAN_FRONTEND=noninteractive apt install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu {libsdl2,qtbase5,qtbase5-private,qtmultimedia5,libslirp,libarchive,libzstd}-dev:arm64 zstd:arm64 cmake extra-cmake-modules dpkg-dev
- - name: Configure
- shell: bash
- run: |
- CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -B build
- - name: Make
- shell: bash
- run: |
- cmake --build build -j$(nproc --all)
- mkdir dist
- cp build/melonDS dist
- - uses: actions/upload-artifact@v1
- with:
- name: melonDS-ubuntu-aarch64
- path: dist
diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml
index b6c50e9a..0102d912 100644
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -1,4 +1,4 @@
-name: CMake Build (Ubuntu x86-64)
+name: Ubuntu
on:
push:
@@ -9,29 +9,77 @@ on:
- master
jobs:
- build:
-
- runs-on: ubuntu-20.04
+ build-x86_64:
+ name: x86_64
+ runs-on: ubuntu-22.04
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v4
+ name: Check out sources
- name: Install dependencies
run: |
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
sudo apt update
- sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades
- - name: Create build environment
- run: mkdir ${{runner.workspace}}/build
+ sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev \
+ qt6-{base,base-private,multimedia}-dev libarchive-dev libzstd-dev libfuse2
- name: Configure
- working-directory: ${{runner.workspace}}/build
- run: cmake $GITHUB_WORKSPACE
- - name: Make
- working-directory: ${{runner.workspace}}/build
+ run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr
+ - name: Build
run: |
- make -j$(nproc --all)
- mkdir dist
- cp melonDS dist
- - uses: actions/upload-artifact@v1
+ cmake --build build
+ DESTDIR=AppDir cmake --install build
+ - uses: actions/upload-artifact@v4
with:
name: melonDS-ubuntu-x86_64
- path: ${{runner.workspace}}/build/dist
+ path: AppDir/usr/bin/melonDS
+ - name: Fetch AppImage tools
+ run: |
+ wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
+ wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
+ chmod a+x linuxdeploy-*.AppImage
+ - name: Build the AppImage
+ env:
+ QMAKE: /usr/lib/qt6/bin/qmake
+ run: |
+ ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
+ - uses: actions/upload-artifact@v4
+ with:
+ name: melonDS-appimage-x86_64
+ path: melonDS*.AppImage
+
+ build-aarch64:
+ name: aarch64
+ runs-on: ubuntu-latest
+ container: ubuntu:22.04
+
+ steps:
+ - name: Prepare system
+ shell: bash
+ run: |
+ dpkg --add-architecture arm64
+ sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
+ rm /etc/apt/sources.list
+ mv /etc/apt/sources.list{.new,}
+ apt update
+ apt -y full-upgrade
+ apt -y install git {gcc-12,g++-12}-aarch64-linux-gnu cmake ninja-build extra-cmake-modules \
+ {libsdl2,qt6-{base,base-private,multimedia},libarchive,libzstd}-dev:arm64 \
+ pkg-config dpkg-dev
+ - name: Check out source
+ uses: actions/checkout@v4
+ - name: Configure
+ shell: bash
+ run: |
+ cmake -B build -G Ninja \
+ -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \
+ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \
+ -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \
+ -DUSE_QT6=ON
+ - name: Build
+ shell: bash
+ run: |
+ cmake --build build
+ - uses: actions/upload-artifact@v4
+ with:
+ name: melonDS-ubuntu-aarch64
+ path: build/melonDS
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index 70b11c05..a4d84a1c 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -1,4 +1,4 @@
-name: CMake Build (Windows x86-64)
+name: Windows
on:
push:
@@ -27,7 +27,7 @@ jobs:
update: true
- name: Install dependencies
- run: pacman -Sq --noconfirm git pkgconf mingw-w64-x86_64-{cmake,SDL2,qt5-static,libslirp,libarchive,toolchain}
+ run: pacman -Sq --noconfirm git pkgconf mingw-w64-x86_64-{cmake,SDL2,qt5-static,libarchive,toolchain}
- name: Configure
working-directory: ${{runner.workspace}}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 598b3bdb..20e37cfd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
+cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.15)
if (POLICY CMP0076)
@@ -8,6 +8,12 @@ endif()
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
+set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake")
+
+option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
+if (USE_VCPKG)
+ include(ConfigureVcpkg)
+endif()
project(melonDS
VERSION 0.9.5
@@ -20,6 +26,8 @@ include(CheckLibraryExists)
include(CMakeDependentOption)
include(CheckIPOSupported)
+include(SetupCCache)
+
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
set(CMAKE_C_STANDARD 11)
@@ -28,8 +36,6 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
-add_compile_definitions(MELONDS_VERSION="${melonDS_VERSION}")
-
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
@@ -73,11 +79,6 @@ if (ENABLE_LTO)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og")
-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
-string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
-string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
-
if (NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
endif()
@@ -92,13 +93,6 @@ endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-find_program(CCACHE "ccache")
-if (CCACHE)
- message(STATUS "Using CCache to speed up compilation")
- set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
- set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
-endif()
-
option(ENABLE_GDBSTUB "Enable GDB stub" ON)
if (ENABLE_GDBSTUB)
add_definitions(-DGDBSTUB_ENABLED)
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 00000000..e14eda24
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,88 @@
+{
+ "version": 6,
+ "configurePresets": [
+ {
+ "name": "release",
+ "displayName": "Release",
+ "description": "Default release build configuration.",
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/build/release"
+ },
+ {
+ "inherits": "release",
+ "name": "release-vcpkg",
+ "displayName": "Release (vcpkg)",
+ "description": "Release build with packages from vcpkg.",
+ "cacheVariables": {
+ "USE_VCPKG": {
+ "type": "BOOL",
+ "value": "ON"
+ }
+ }
+ },
+ {
+ "name": "release-mac-x86_64",
+ "inherits": "release-vcpkg",
+ "displayName": "macOS release (x86_64)",
+ "binaryDir": "${sourceDir}/build/release-mac-x86_64",
+ "cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "x86_64" }
+ },
+ {
+ "name": "release-mac-arm64",
+ "inherits": "release-vcpkg",
+ "displayName": "macOS release (arm64)",
+ "binaryDir": "${sourceDir}/build/release-mac-arm64",
+ "cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" }
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "release",
+ "configurePreset": "release"
+ },
+ {
+ "name": "release-vcpkg",
+ "configurePreset": "release-vcpkg"
+ },
+ {
+ "name": "release-mac-x86_64",
+ "configurePreset": "release-mac-x86_64"
+ },
+ {
+ "name": "release-mac-arm64",
+ "configurePreset": "release-mac-arm64"
+ }
+ ],
+ "workflowPresets": [
+ {
+ "name": "release",
+ "displayName": "Release",
+ "steps": [
+ { "type": "configure", "name": "release" },
+ { "type": "build", "name": "release" }
+ ]
+ },
+ {
+ "name": "release-vcpkg",
+ "displayName": "Release (vcpkg)",
+ "steps": [
+ { "type": "configure", "name": "release-vcpkg" },
+ { "type": "build", "name": "release-vcpkg" }
+ ]
+ },
+ {
+ "name": "release-mac-x86_64",
+ "steps": [
+ { "type": "configure", "name": "release-mac-x86_64" },
+ { "type": "build", "name": "release-mac-x86_64" }
+ ]
+ },
+ {
+ "name": "release-mac-arm64",
+ "steps": [
+ { "type": "configure", "name": "release-mac-arm64" },
+ { "type": "build", "name": "release-mac-arm64" }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index de494305..f02185ca 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,9 @@
-
-
-
-
+
+
+
DS emulator, sorta
@@ -35,9 +34,9 @@ As for the rest, the interface should be pretty straightforward. If you have a q
### Linux
1. Install dependencies:
- * Ubuntu 22.04: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libslirp-dev libarchive-dev libzstd-dev`
- * Older Ubuntu: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp-dev libarchive-dev libzstd-dev`
- * Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia libslirp libarchive zstd`
+ * Ubuntu 22.04: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libarchive-dev libzstd-dev`
+ * Older Ubuntu: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libarchive-dev libzstd-dev`
+ * Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia libarchive zstd`
3. Download the melonDS repository and prepare:
```bash
git clone https://github.com/melonDS-emu/melonDS
@@ -64,7 +63,7 @@ As for the rest, the interface should be pretty straightforward. If you have a q
cd melonDS
```
#### Dynamic builds (with DLLs)
-5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-base,qt5-svg,qt5-multimedia,qt5-tools,libslirp,libarchive,zstd}`
+5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-base,qt5-svg,qt5-multimedia,qt5-tools,libarchive,zstd}`
6. Compile:
```bash
cmake -B build
@@ -75,7 +74,7 @@ As for the rest, the interface should be pretty straightforward. If you have a q
If everything went well, melonDS and the libraries it needs should now be in the `dist` folder.
#### Static builds (without DLLs, standalone executable)
-5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-static,libslirp,libarchive,zstd}`
+5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-static,libarchive,zstd}`
6. Compile:
```bash
cmake -B build -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static
@@ -85,7 +84,7 @@ If everything went well, melonDS should now be in the `build` folder.
### macOS
1. Install the [Homebrew Package Manager](https://brew.sh)
-2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libslirp libarchive zstd`
+2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive zstd`
3. Download the melonDS repository and prepare:
```zsh
git clone https://github.com/melonDS-emu/melonDS
diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake
new file mode 100644
index 00000000..c9f3e92f
--- /dev/null
+++ b/cmake/ConfigureVcpkg.cmake
@@ -0,0 +1,91 @@
+include(FetchContent)
+
+set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg")
+set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository")
+
+if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
+ file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE)
+ FetchContent_Declare(vcpkg
+ GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
+ GIT_TAG 2024.01.12
+ SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
+ FetchContent_MakeAvailable(vcpkg)
+endif()
+
+set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets")
+
+option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
+
+if (CMAKE_OSX_ARCHITECTURES MATCHES ";")
+ message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.")
+endif()
+
+if (USE_RECOMMENDED_TRIPLETS)
+ execute_process(
+ COMMAND uname -m
+ OUTPUT_VARIABLE _HOST_PROCESSOR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ set(_CAN_TARGET_AS_HOST OFF)
+
+ if (APPLE)
+ if (NOT CMAKE_OSX_ARCHITECTURES)
+ if (_HOST_PROCESSOR STREQUAL arm64)
+ set(CMAKE_OSX_ARCHITECTURES arm64)
+ else()
+ set(CMAKE_OSX_ARCHITECTURES x86_64)
+ endif()
+ endif()
+
+ if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
+ set(_WANTED_TRIPLET arm64-osx-11-release)
+ set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
+ else()
+ set(_WANTED_TRIPLET x64-osx-1015-release)
+ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
+ endif()
+ elseif(WIN32)
+ # TODO Windows arm64 if possible
+ set(_CAN_TARGET_AS_HOST ON)
+ set(_WANTED_TRIPLET x64-mingw-static)
+ endif()
+
+ # Don't override it if the user set something else
+ if (NOT VCPKG_TARGET_TRIPLET)
+ set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}")
+ else()
+ set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}")
+ endif()
+
+ if (APPLE)
+ if (_HOST_PROCESSOR MATCHES arm64)
+ if (_WANTED_TRIPLET MATCHES "^arm64-osx-")
+ set(_CAN_TARGET_AS_HOST ON)
+ elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release")
+ # Use the default triplet for when building for arm64
+ # because we're probably making a universal build
+ set(VCPKG_HOST_TRIPLET arm64-osx-11-release)
+ endif()
+ else()
+ if (_WANTED_TRIPLET MATCHES "^x64-osx-")
+ set(_CAN_TARGET_AS_HOST ON)
+ elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release")
+ set(VCPKG_HOST_TRIPLET x64-osx-1015-release)
+ endif()
+ endif()
+ endif()
+
+ # If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries.
+ # In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant.
+ if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET)
+ option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON)
+ else()
+ option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF)
+ endif()
+
+ if (VCPKG_TARGET_AS_HOST)
+ set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg")
+ endif()
+endif()
+
+set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
diff --git a/cmake/DefaultBuildFlags.cmake b/cmake/DefaultBuildFlags.cmake
new file mode 100644
index 00000000..683767b3
--- /dev/null
+++ b/cmake/DefaultBuildFlags.cmake
@@ -0,0 +1,9 @@
+if (CMAKE_C_COMPILER_ID STREQUAL GNU)
+ set(CMAKE_C_FLAGS_DEBUG_INIT "-g -Og")
+endif()
+if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
+ set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og")
+endif()
+
+string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}")
+string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}")
diff --git a/cmake/FixInterfaceIncludes.cmake b/cmake/FixInterfaceIncludes.cmake
index 513c1117..5c285d7a 100644
--- a/cmake/FixInterfaceIncludes.cmake
+++ b/cmake/FixInterfaceIncludes.cmake
@@ -19,6 +19,13 @@ function(fix_interface_includes)
if (PARENT_DIR MATCHES "include$")
list(APPEND NEW_DIRS "${PARENT_DIR}")
endif()
+
+ # HACK
+ # The libarchive pkg-config file in MSYS2 seems to include a UNIX-style path for its
+ # include directory and CMake doesn't like that.
+ if (WIN32 AND MINGW AND target STREQUAL PkgConfig::LibArchive)
+ list(FILTER DIRS EXCLUDE REGEX "^/[^.]+64/.*")
+ endif()
endforeach()
list(APPEND DIRS ${NEW_DIRS})
diff --git a/cmake/SetupCCache.cmake b/cmake/SetupCCache.cmake
new file mode 100644
index 00000000..72388bf8
--- /dev/null
+++ b/cmake/SetupCCache.cmake
@@ -0,0 +1,19 @@
+include(FindPackageMessage)
+
+find_program(CCACHE "ccache")
+
+cmake_dependent_option(USE_CCACHE "Use CCache to speed up repeated builds." ON CCACHE OFF)
+
+if (NOT CCACHE OR NOT USE_CCACHE)
+ return()
+endif()
+
+# Fedora, and probably also Red Hat-based distros in general, use CCache by default if it's installed on the system.
+# We'll try to detect this here, and exit if that's the case.
+# Trying to launch ccache with ccache as we'd otherwise do seems to cause build issues.
+if (CMAKE_C_COMPILER MATCHES "ccache" OR CMAKE_CXX_COMPILER MATCHES "ccache")
+ return()
+endif()
+
+find_package_message(CCache "Using CCache to speed up compilation" "${USE_CCACHE}")
+set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE}")
\ No newline at end of file
diff --git a/cmake/overlay-triplets/arm64-osx-11-release.cmake b/cmake/overlay-triplets/arm64-osx-11-release.cmake
new file mode 100644
index 00000000..7c4b43ae
--- /dev/null
+++ b/cmake/overlay-triplets/arm64-osx-11-release.cmake
@@ -0,0 +1,12 @@
+set(VCPKG_TARGET_ARCHITECTURE arm64)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE static)
+
+set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
+set(VCPKG_CMAKE_SYSTEM_VERSION 11.0)
+set(VCPKG_OSX_ARCHITECTURES arm64)
+set(VCPKG_BUILD_TYPE release)
+set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0)
+
+set(VCPKG_C_FLAGS -mmacosx-version-min=11.0)
+set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0)
diff --git a/cmake/overlay-triplets/x64-osx-1015-release.cmake b/cmake/overlay-triplets/x64-osx-1015-release.cmake
new file mode 100644
index 00000000..fcb67a7a
--- /dev/null
+++ b/cmake/overlay-triplets/x64-osx-1015-release.cmake
@@ -0,0 +1,12 @@
+set(VCPKG_TARGET_ARCHITECTURE x64)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE static)
+
+set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
+set(VCPKG_CMAKE_SYSTEM_VERSION 10.15)
+set(VCPKG_OSX_ARCHITECTURES x86_64)
+set(VCPKG_BUILD_TYPE release)
+set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)
+
+set(VCPKG_C_FLAGS -mmacosx-version-min=10.15)
+set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15)
diff --git a/freebios/bios_common.S b/freebios/bios_common.S
index 56d349ef..308d67c4 100755
--- a/freebios/bios_common.S
+++ b/freebios/bios_common.S
@@ -517,6 +517,9 @@ swi_get_crc16:
mov const_0x1E, #0x1E
adr crc_table_ptr, crc_table
+ bic crc_value, crc_value, #0xFF000000
+ bic crc_value, crc_value, #0x00FF0000
+
movs crc_length, crc_length, lsr #1
beq 1f
diff --git a/freebios/drastic_bios_arm7.bin b/freebios/drastic_bios_arm7.bin
index e586eb9f..adffb53a 100755
Binary files a/freebios/drastic_bios_arm7.bin and b/freebios/drastic_bios_arm7.bin differ
diff --git a/freebios/drastic_bios_arm9.bin b/freebios/drastic_bios_arm9.bin
index 51a82829..95c330d0 100755
Binary files a/freebios/drastic_bios_arm9.bin and b/freebios/drastic_bios_arm9.bin differ
diff --git a/src/AREngine.cpp b/src/AREngine.cpp
index 4e13a39a..c7d49fe6 100644
--- a/src/AREngine.cpp
+++ b/src/AREngine.cpp
@@ -40,16 +40,16 @@ AREngine::AREngine(melonDS::NDS& nds) : NDS(nds)
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
-void AREngine::RunCheat(ARCode& arcode)
+void AREngine::RunCheat(const ARCode& arcode)
{
- u32* code = &arcode.Code[0];
+ const u32* code = &arcode.Code[0];
u32 offset = 0;
u32 datareg = 0;
u32 cond = 1;
u32 condstack = 0;
- u32* loopstart = code;
+ const u32* loopstart = code;
u32 loopcount = 0;
u32 loopcond = 1;
u32 loopcondstack = 0;
diff --git a/src/AREngine.h b/src/AREngine.h
index 1f2ee186..21044676 100644
--- a/src/AREngine.h
+++ b/src/AREngine.h
@@ -33,7 +33,7 @@ public:
void SetCodeFile(ARCodeFile* file) { CodeFile = file; }
void RunCheats();
- void RunCheat(ARCode& arcode);
+ void RunCheat(const ARCode& arcode);
private:
melonDS::NDS& NDS;
ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this
diff --git a/src/ARM.cpp b/src/ARM.cpp
index 659d303b..c2f6a6c2 100644
--- a/src/ARM.cpp
+++ b/src/ARM.cpp
@@ -17,6 +17,7 @@
*/
#include
+#include
#include
#include "NDS.h"
#include "DSi.h"
@@ -106,17 +107,17 @@ const u32 ARM::ConditionTable[16] =
0x0000 // NE
};
-ARM::ARM(u32 num, melonDS::NDS& nds) :
+ARM::ARM(u32 num, bool jit, std::optional gdb, melonDS::NDS& nds) :
#ifdef GDBSTUB_ENABLED
- GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)),
+ GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0),
#endif
Num(num), // well uh
NDS(nds)
{
#ifdef GDBSTUB_ENABLED
- if (Platform::GetConfigBool(Platform::GdbEnabled)
+ if (gdb
#ifdef JIT_ENABLED
- && !Platform::GetConfigBool(Platform::JIT_Enable)
+ && !jit // TODO: Should we support toggling the GdbStub without destroying the ARM?
#endif
)
GdbStub.Init();
@@ -129,14 +130,14 @@ ARM::~ARM()
// dorp
}
-ARMv5::ARMv5(melonDS::NDS& nds) : ARM(0, nds)
+ARMv5::ARMv5(melonDS::NDS& nds, std::optional gdb, bool jit) : ARM(0, jit, gdb, nds)
{
DTCM = NDS.JIT.Memory.GetARM9DTCM();
PU_Map = PU_PrivMap;
}
-ARMv4::ARMv4(melonDS::NDS& nds) : ARM(1, nds)
+ARMv4::ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit) : ARM(1, jit, gdb, nds)
{
//
}
@@ -187,8 +188,6 @@ void ARM::Reset()
#ifdef GDBSTUB_ENABLED
IsSingleStep = false;
BreakReq = false;
- BreakOnStartup = Platform::GetConfigBool(
- Num ? Platform::GdbARM7BreakOnStartup : Platform::GdbARM9BreakOnStartup);
#endif
// zorp
@@ -224,7 +223,7 @@ void ARM::DoSavestate(Savestate* file)
file->VarArray(R_UND, 3*sizeof(u32));
file->Var32(&CurInstr);
#ifdef JIT_ENABLED
- if (file->Saving && NDS.EnableJIT)
+ if (file->Saving && NDS.IsJITEnabled())
{
// hack, the JIT doesn't really pipeline
// but we still want JIT save states to be
diff --git a/src/ARM.h b/src/ARM.h
index 4becff02..1e0b71b8 100644
--- a/src/ARM.h
+++ b/src/ARM.h
@@ -20,9 +20,11 @@
#define ARM_H
#include
+#include
#include "types.h"
#include "MemRegion.h"
+#include "MemConstants.h"
#ifdef GDBSTUB_ENABLED
#include "debug/GdbStub.h"
@@ -41,10 +43,7 @@ enum
RWFlags_ForceUser = (1<<21),
};
-const u32 ITCMPhysicalSize = 0x8000;
-const u32 DTCMPhysicalSize = 0x4000;
-
-
+struct GDBArgs;
class ARMJIT;
class GPU;
class ARMJIT_Memory;
@@ -57,7 +56,7 @@ class ARM
#endif
{
public:
- ARM(u32 num, NDS& nds);
+ ARM(u32 num, bool jit, std::optional gdb, NDS& nds);
virtual ~ARM(); // destroy shit
virtual void Reset();
@@ -81,7 +80,7 @@ public:
virtual void ExecuteJIT() = 0;
#endif
- bool CheckCondition(u32 code)
+ bool CheckCondition(u32 code) const
{
if (code == 0xE) return true;
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
@@ -110,7 +109,7 @@ public:
if (v) CPSR |= 0x10000000;
}
- inline bool ModeIs(u32 mode)
+ inline bool ModeIs(u32 mode) const
{
u32 cm = CPSR & 0x1f;
mode &= 0x1f;
@@ -202,6 +201,7 @@ protected:
bool IsSingleStep;
bool BreakReq;
bool BreakOnStartup;
+ u16 Port;
public:
int GetCPU() const override { return Num ? 7 : 9; }
@@ -225,7 +225,7 @@ protected:
class ARMv5 : public ARM
{
public:
- ARMv5(melonDS::NDS& nds);
+ ARMv5(melonDS::NDS& nds, std::optional gdb, bool jit);
~ARMv5();
void Reset() override;
@@ -315,7 +315,7 @@ public:
void ICacheInvalidateAll();
void CP15Write(u32 id, u32 val);
- u32 CP15Read(u32 id);
+ u32 CP15Read(u32 id) const;
u32 CP15Control;
@@ -377,7 +377,7 @@ protected:
class ARMv4 : public ARM
{
public:
- ARMv4(melonDS::NDS& nds);
+ ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit);
void FillPipeline() override;
diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp
index b938dfb8..c3fcba26 100644
--- a/src/ARMJIT.cpp
+++ b/src/ARMJIT.cpp
@@ -63,12 +63,12 @@ const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] =
0,
ITCMPhysicalSize,
0,
- sizeof(NDS::ARM9BIOS),
+ ARM9BIOSSize,
MainRAMMaxSize,
SharedWRAMSize,
0,
0x100000,
- sizeof(NDS::ARM7BIOS),
+ ARM7BIOSSize,
ARM7WRAMSize,
0,
0,
@@ -237,16 +237,6 @@ ARMJIT::~ARMJIT() noexcept
void ARMJIT::Reset() noexcept
{
- MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize);
- LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations);
- BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations);
- FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory);
-
- if (MaxBlockSize < 1)
- MaxBlockSize = 1;
- if (MaxBlockSize > 32)
- MaxBlockSize = 32;
-
JitEnableWrite();
ResetBlockCache();
@@ -491,6 +481,56 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
}
}
+void ARMJIT::SetJITArgs(JITArgs args) noexcept
+{
+ args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
+
+ if (MaxBlockSize != args.MaxBlockSize
+ || LiteralOptimizations != args.LiteralOptimizations
+ || BranchOptimizations != args.BranchOptimizations
+ || FastMemory != args.FastMemory)
+ ResetBlockCache();
+
+ MaxBlockSize = args.MaxBlockSize;
+ LiteralOptimizations = args.LiteralOptimizations;
+ BranchOptimizations = args.BranchOptimizations;
+ FastMemory = args.FastMemory;
+}
+
+void ARMJIT::SetMaxBlockSize(int size) noexcept
+{
+ size = std::clamp(size, 1, 32);
+
+ if (size != MaxBlockSize)
+ ResetBlockCache();
+
+ MaxBlockSize = size;
+}
+
+void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
+{
+ if (LiteralOptimizations != enabled)
+ ResetBlockCache();
+
+ LiteralOptimizations = enabled;
+}
+
+void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
+{
+ if (BranchOptimizations != enabled)
+ ResetBlockCache();
+
+ BranchOptimizations = enabled;
+}
+
+void ARMJIT::SetFastMemory(bool enabled) noexcept
+{
+ if (FastMemory != enabled)
+ ResetBlockCache();
+
+ FastMemory = enabled;
+}
+
void ARMJIT::CompileBlock(ARM* cpu) noexcept
{
bool thumb = cpu->CPSR & 0x20;
diff --git a/src/ARMJIT.h b/src/ARMJIT.h
index 9e1ca074..7619f234 100644
--- a/src/ARMJIT.h
+++ b/src/ARMJIT.h
@@ -19,10 +19,15 @@
#ifndef ARMJIT_H
#define ARMJIT_H
+#include
+#include
#include
#include "types.h"
-
+#include "MemConstants.h"
+#include "Args.h"
#include "ARMJIT_Memory.h"
+
+#ifdef JIT_ENABLED
#include "JitBlock.h"
#if defined(__APPLE__) && defined(__aarch64__)
@@ -30,7 +35,6 @@
#endif
#include "ARMJIT_Compiler.h"
-#include "MemConstants.h"
namespace melonDS
{
@@ -40,18 +44,25 @@ class JitBlock;
class ARMJIT
{
public:
- ARMJIT(melonDS::NDS& nds) noexcept : NDS(nds), Memory(nds), JITCompiler(nds) {};
- ~ARMJIT() noexcept NOOP_IF_NO_JIT;
- void InvalidateByAddr(u32) noexcept NOOP_IF_NO_JIT;
- void CheckAndInvalidateWVRAM(int) noexcept NOOP_IF_NO_JIT;
- void CheckAndInvalidateITCM() noexcept NOOP_IF_NO_JIT;
- void Reset() noexcept NOOP_IF_NO_JIT;
- void JitEnableWrite() noexcept NOOP_IF_NO_JIT;
- void JitEnableExecute() noexcept NOOP_IF_NO_JIT;
- void CompileBlock(ARM* cpu) noexcept NOOP_IF_NO_JIT;
- void ResetBlockCache() noexcept NOOP_IF_NO_JIT;
+ ARMJIT(melonDS::NDS& nds, std::optional jit) noexcept :
+ NDS(nds),
+ Memory(nds),
+ JITCompiler(nds),
+ MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
+ LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
+ BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
+ FastMemory(jit.has_value() ? jit->FastMemory : false)
+ {}
+ ~ARMJIT() noexcept;
+ void InvalidateByAddr(u32) noexcept;
+ void CheckAndInvalidateWVRAM(int) noexcept;
+ void CheckAndInvalidateITCM() noexcept;
+ void Reset() noexcept;
+ void JitEnableWrite() noexcept;
+ void JitEnableExecute() noexcept;
+ void CompileBlock(ARM* cpu) noexcept;
+ void ResetBlockCache() noexcept;
-#ifdef JIT_ENABLED
template
void CheckAndInvalidate(u32 addr) noexcept
{
@@ -62,23 +73,31 @@ public:
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept;
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept;
u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept;
-#else
- template
- void CheckAndInvalidate(u32) noexcept {}
-#endif
ARMJIT_Memory Memory;
+private:
int MaxBlockSize {};
bool LiteralOptimizations = false;
bool BranchOptimizations = false;
bool FastMemory = false;
-
+public:
melonDS::NDS& NDS;
TinyVector InvalidLiterals {};
friend class ARMJIT_Memory;
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept;
void RetireJitBlock(JitBlock* block) noexcept;
+ int GetMaxBlockSize() const noexcept { return MaxBlockSize; }
+ bool LiteralOptimizationsEnabled() const noexcept { return LiteralOptimizations; }
+ bool BranchOptimizationsEnabled() const noexcept { return BranchOptimizations; }
+ bool FastMemoryEnabled() const noexcept { return FastMemory; }
+
+ void SetJITArgs(JITArgs args) noexcept;
+ void SetMaxBlockSize(int size) noexcept;
+ void SetLiteralOptimizations(bool enabled) noexcept;
+ void SetBranchOptimizations(bool enabled) noexcept;
+ void SetFastMemory(bool enabled) noexcept;
+
Compiler JITCompiler;
std::unordered_map JitBlocks9 {};
std::unordered_map JitBlocks7 {};
@@ -162,5 +181,33 @@ public:
// Defined in assembly
extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry);
+#else
+namespace melonDS
+{
+class ARM;
+
+// This version is a stub; the methods all do nothing,
+// but there's still a Memory member.
+class ARMJIT
+{
+public:
+ ARMJIT(melonDS::NDS& nds, std::optional) noexcept : Memory(nds) {}
+ ~ARMJIT() noexcept {}
+ void InvalidateByAddr(u32) noexcept {}
+ void CheckAndInvalidateWVRAM(int) noexcept {}
+ void CheckAndInvalidateITCM() noexcept {}
+ void Reset() noexcept {}
+ void JitEnableWrite() noexcept {}
+ void JitEnableExecute() noexcept {}
+ void CompileBlock(ARM*) noexcept {}
+ void ResetBlockCache() noexcept {}
+ template
+ void CheckAndInvalidate(u32 addr) noexcept {}
+
+ ARMJIT_Memory Memory;
+};
+}
+#endif // JIT_ENABLED
+
+#endif // ARMJIT_H
-#endif
diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp
index c306dd84..7981ed67 100644
--- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp
+++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp
@@ -276,7 +276,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
#elif defined(__APPLE__)
pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
- JitEnableWrite();
+ nds.JIT.JitEnableWrite();
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h
index 72dd7bcb..2b0048a9 100644
--- a/src/ARMJIT_A64/ARMJIT_Compiler.h
+++ b/src/ARMJIT_A64/ARMJIT_Compiler.h
@@ -19,6 +19,8 @@
#ifndef ARMJIT_A64_COMPILER_H
#define ARMJIT_A64_COMPILER_H
+#if defined(JIT_ENABLED) && defined(__aarch64__)
+
#include "../ARM.h"
#include "../dolphin/Arm64Emitter.h"
@@ -67,7 +69,7 @@ struct Op2
bool IsSimpleReg()
{ return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; }
bool ImmFits12Bit()
- { return IsImm && (Imm & 0xFFF == Imm); }
+ { return IsImm && ((Imm & 0xFFF) == Imm); }
bool IsZero()
{ return IsImm && !Imm; }
@@ -96,12 +98,8 @@ class Compiler : public Arm64Gen::ARM64XEmitter
public:
typedef void (Compiler::*CompileFunc)();
-#ifdef JIT_ENABLED
explicit Compiler(melonDS::NDS& nds);
-#else
- explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {}
-#endif
- ~Compiler();
+ ~Compiler() override;
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
@@ -116,7 +114,7 @@ public:
bool CanCompile(bool thumb, u16 kind);
- bool FlagsNZNeeded()
+ bool FlagsNZNeeded() const
{
return CurInstr.SetFlags & 0xC;
}
@@ -236,7 +234,7 @@ public:
return (u8*)entry - GetRXBase();
}
- bool IsJITFault(u8* pc);
+ bool IsJITFault(const u8* pc);
u8* RewriteMemAccess(u8* pc);
void SwapCodeRegion()
@@ -291,3 +289,5 @@ public:
}
#endif
+
+#endif
diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp
index 22a410ae..e108b7b4 100644
--- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp
+++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp
@@ -28,7 +28,7 @@ using namespace Arm64Gen;
namespace melonDS
{
-bool Compiler::IsJITFault(u8* pc)
+bool Compiler::IsJITFault(const u8* pc)
{
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
}
@@ -112,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (size == 16)
addressMask = ~1;
- if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
+ if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@@ -147,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(W0, rnMapped);
}
- bool addrIsStatic = NDS.JIT.LiteralOptimizations
+ bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@@ -189,7 +189,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
- if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
+ if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
{
ptrdiff_t memopStart = GetCodeOffset();
LoadStorePatch patch;
@@ -453,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel()
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
u32 addr = (R15 & ~0x2) + offset;
- if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
+ if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@@ -498,7 +498,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
- bool compileFastPath = NDS.JIT.FastMemory
+ bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
{
diff --git a/src/ARMJIT_Compiler.h b/src/ARMJIT_Compiler.h
index 4ece834f..ff4f8ff7 100644
--- a/src/ARMJIT_Compiler.h
+++ b/src/ARMJIT_Compiler.h
@@ -20,10 +20,6 @@
#define ARMJIT_COMPILER_H
#ifdef JIT_ENABLED
-#define NOOP_IF_NO_JIT
-#else
-#define NOOP_IF_NO_JIT {}
-#endif
#if defined(__x86_64__)
#include "ARMJIT_x64/ARMJIT_Compiler.h"
@@ -34,3 +30,5 @@
#endif
#endif
+
+#endif
diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h
index 72d40a5f..8429bade 100644
--- a/src/ARMJIT_Internal.h
+++ b/src/ARMJIT_Internal.h
@@ -85,7 +85,7 @@ typedef void (*InterpreterFunc)(ARM* cpu);
extern InterpreterFunc InterpretARM[];
extern InterpreterFunc InterpretTHUMB[];
-inline bool PageContainsCode(AddressRange* range)
+inline bool PageContainsCode(const AddressRange* range)
{
for (int i = 0; i < 8; i++)
{
diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp
index bf1fb063..c8969aee 100644
--- a/src/ARMJIT_Memory.cpp
+++ b/src/ARMJIT_Memory.cpp
@@ -473,8 +473,10 @@ void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept
void ARMJIT_Memory::RemapNWRAM(int num) noexcept
{
- auto* dsi = dynamic_cast(&NDS);
- assert(dsi != nullptr);
+ if (NDS.ConsoleType == 0)
+ return;
+
+ auto* dsi = static_cast(&NDS);
for (int i = 0; i < Mappings[memregion_SharedWRAM].Length;)
{
Mapping& mapping = Mappings[memregion_SharedWRAM][i];
@@ -1071,15 +1073,19 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept
}
else
{
- auto& dsi = static_cast(NDS); // ONLY use this if ConsoleType == 1!
- if (NDS.ConsoleType == 1 && addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1)))
+ if (NDS.ConsoleType == 1)
{
- if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0)))
- return memregion_Other;
+ auto& dsi = static_cast(NDS);
+ if (addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1)))
+ {
+ if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0)))
+ return memregion_Other;
- return memregion_BIOS9DSi;
+ return memregion_BIOS9DSi;
+ }
}
- else if ((addr & 0xFFFFF000) == 0xFFFF0000)
+
+ if ((addr & 0xFFFFF000) == 0xFFFF0000)
{
return memregion_BIOS9;
}
@@ -1091,6 +1097,7 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept
case 0x03000000:
if (NDS.ConsoleType == 1)
{
+ auto& dsi = static_cast(NDS);
if (addr >= dsi.NWRAMStart[0][0] && addr < dsi.NWRAMEnd[0][0])
return memregion_NewSharedWRAM_A;
if (addr >= dsi.NWRAMStart[0][1] && addr < dsi.NWRAMEnd[0][1])
@@ -1116,15 +1123,19 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept
int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept
{
- auto& dsi = static_cast(NDS);
- if (NDS.ConsoleType == 1 && addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9)))
+ if (NDS.ConsoleType == 1)
{
- if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8))
- return memregion_Other;
+ auto& dsi = static_cast(NDS);
+ if (addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9)))
+ {
+ if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8))
+ return memregion_Other;
- return memregion_BIOS7DSi;
+ return memregion_BIOS7DSi;
+ }
}
- else if (addr < 0x00004000)
+
+ if (addr < 0x00004000)
{
return memregion_BIOS7;
}
@@ -1138,6 +1149,7 @@ int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept
case 0x03000000:
if (NDS.ConsoleType == 1)
{
+ auto& dsi = static_cast(NDS);
if (addr >= dsi.NWRAMStart[1][0] && addr < dsi.NWRAMEnd[1][0])
return memregion_NewSharedWRAM_A;
if (addr >= dsi.NWRAMStart[1][1] && addr < dsi.NWRAMEnd[1][1])
diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h
index 487005b2..d36f6032 100644
--- a/src/ARMJIT_Memory.h
+++ b/src/ARMJIT_Memory.h
@@ -20,33 +20,33 @@
#define ARMJIT_MEMORY
#include "types.h"
-#include "TinyVector.h"
-
-#include "ARM.h"
#include "MemConstants.h"
-#if defined(__SWITCH__)
-#include
-#elif defined(_WIN32)
+#ifdef JIT_ENABLED
+# include "TinyVector.h"
+# include "ARM.h"
+# if defined(__SWITCH__)
+# include
+# elif defined(_WIN32)
#include
+# else
+# include
+# include
+# include
+# include
+# include
+# endif
#else
-#include
-#include
-#include
-#include
-#include
-#endif
-
-#ifndef JIT_ENABLED
-#include
-#include "NDS.h"
+# include
#endif
namespace melonDS
{
+#ifdef JIT_ENABLED
namespace Platform { struct DynamicLibrary; }
class Compiler;
class ARMJIT;
+#endif
constexpr u32 RoundUp(u32 size) noexcept
{
diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h
index 3cb0f79f..e5f28dd6 100644
--- a/src/ARMJIT_RegisterCache.h
+++ b/src/ARMJIT_RegisterCache.h
@@ -99,7 +99,7 @@ public:
LiteralsLoaded &= ~(1 << reg);
}
- bool IsLiteral(int reg)
+ bool IsLiteral(int reg) const
{
return LiteralsLoaded & (1 << reg);
}
diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
index eec4d7d1..b18837f3 100644
--- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp
+++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
@@ -651,7 +651,7 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
};
#undef F
-bool Compiler::CanCompile(bool thumb, u16 kind)
+bool Compiler::CanCompile(bool thumb, u16 kind) const
{
return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL;
}
@@ -667,7 +667,7 @@ void Compiler::Reset()
LoadStorePatches.clear();
}
-bool Compiler::IsJITFault(u8* addr)
+bool Compiler::IsJITFault(const u8* addr)
{
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
}
diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h
index fa6d78a4..941d8924 100644
--- a/src/ARMJIT_x64/ARMJIT_Compiler.h
+++ b/src/ARMJIT_x64/ARMJIT_Compiler.h
@@ -19,6 +19,8 @@
#ifndef ARMJIT_X64_COMPILER_H
#define ARMJIT_X64_COMPILER_H
+#if defined(JIT_ENABLED) && defined(__x86_64__)
+
#include "../dolphin/x64Emitter.h"
#include "../ARMJIT_Internal.h"
@@ -81,11 +83,7 @@ struct Op2
class Compiler : public Gen::XEmitter
{
public:
-#ifdef JIT_ENABLED
explicit Compiler(melonDS::NDS& nds);
-#else
- explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {}
-#endif
void Reset();
@@ -94,7 +92,7 @@ public:
void LoadReg(int reg, Gen::X64Reg nativeReg);
void SaveReg(int reg, Gen::X64Reg nativeReg);
- bool CanCompile(bool thumb, u16 kind);
+ bool CanCompile(bool thumb, u16 kind) const;
typedef void (Compiler::*CompileFunc)();
@@ -236,7 +234,7 @@ public:
SetCodePtr(FarCode);
}
- bool IsJITFault(u8* addr);
+ bool IsJITFault(const u8* addr);
u8* RewriteMemAccess(u8* pc);
@@ -284,5 +282,6 @@ public:
};
}
+#endif
#endif
diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
index 72a073db..8520bebc 100644
--- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
+++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp
@@ -119,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (size == 16)
addressMask = ~1;
- if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
+ if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@@ -136,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
Comp_AddCycles_CDI();
}
- bool addrIsStatic = NDS.JIT.LiteralOptimizations
+ bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@@ -200,7 +200,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
- if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
+ if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
{
if (rdMapped.IsImm())
{
@@ -431,7 +431,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
else
Comp_AddCycles_CD();
- bool compileFastPath = NDS.JIT.FastMemory
+ bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
// we need to make sure that the stack stays aligned to 16 bytes
@@ -809,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel()
{
u32 offset = (CurInstr.Instr & 0xFF) << 2;
u32 addr = (R15 & ~0x2) + offset;
- if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
+ if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
diff --git a/src/Args.h b/src/Args.h
new file mode 100644
index 00000000..0b20bbf0
--- /dev/null
+++ b/src/Args.h
@@ -0,0 +1,152 @@
+/*
+ Copyright 2016-2023 melonDS team
+
+ This file is part of melonDS.
+
+ melonDS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#ifndef MELONDS_ARGS_H
+#define MELONDS_ARGS_H
+
+#include
+#include
+#include
+
+#include "NDSCart.h"
+#include "GBACart.h"
+#include "types.h"
+#include "MemConstants.h"
+#include "DSi_NAND.h"
+#include "FATStorage.h"
+#include "FreeBIOS.h"
+#include "GPU3D_Soft.h"
+#include "SPI_Firmware.h"
+#include "SPU.h"
+
+namespace melonDS
+{
+namespace NDSCart { class CartCommon; }
+namespace GBACart { class CartCommon; }
+
+template
+constexpr std::array BrokenBIOS = []() constexpr {
+ std::array broken {};
+
+ for (int i = 0; i < 16; i++)
+ {
+ broken[i*4+0] = 0xE7;
+ broken[i*4+1] = 0xFF;
+ broken[i*4+2] = 0xDE;
+ broken[i*4+3] = 0xFF;
+ }
+
+ return broken;
+}();
+
+/// Arguments that configure the JIT.
+/// Ignored in builds that don't have the JIT included.
+struct JITArgs
+{
+ unsigned MaxBlockSize = 32;
+ bool LiteralOptimizations = true;
+ bool BranchOptimizations = true;
+
+ /// Ignored in builds that have fast memory excluded
+ /// (even if the JIT is otherwise available).
+ /// Enabled by default, but frontends should disable this when debugging
+ /// so the constants segfaults don't hinder debugging.
+ bool FastMemory = true;
+};
+
+using ARM9BIOSImage = std::array;
+using ARM7BIOSImage = std::array;
+using DSiBIOSImage = std::array;
+
+struct GDBArgs
+{
+ u16 PortARM7 = 0;
+ u16 PortARM9 = 0;
+ bool ARM7BreakOnStartup = false;
+ bool ARM9BreakOnStartup = false;
+};
+
+/// Arguments to pass into the NDS constructor.
+/// New fields here should have default values if possible.
+struct NDSArgs
+{
+ /// NDS ROM to install.
+ /// Defaults to nullptr, which means no cart.
+ /// Should be populated with the desired save data beforehand,
+ /// including an SD card if applicable.
+ std::unique_ptr NDSROM = nullptr;
+
+ /// GBA ROM to install.
+ /// Defaults to nullptr, which means no cart.
+ /// Should be populated with the desired save data beforehand.
+ /// Ignored in DSi mode.
+ std::unique_ptr GBAROM = nullptr;
+
+ /// NDS ARM9 BIOS to install.
+ /// Defaults to FreeBIOS, which is not compatible with DSi mode.
+ std::unique_ptr ARM9BIOS = std::make_unique(bios_arm9_bin);
+
+ /// NDS ARM7 BIOS to install.
+ /// Defaults to FreeBIOS, which is not compatible with DSi mode.
+ std::unique_ptr ARM7BIOS = std::make_unique(bios_arm7_bin);
+
+ /// Firmware image to install.
+ /// Defaults to generated NDS firmware.
+ /// Generated firmware is not compatible with DSi mode.
+ melonDS::Firmware Firmware {0};
+
+ /// How the JIT should be configured when initializing.
+ /// Defaults to enabled, with default settings.
+ /// To disable the JIT, set this to std::nullopt.
+ /// Ignored in builds that don't have the JIT included.
+ std::optional JIT = JITArgs();
+
+ AudioBitDepth BitDepth = AudioBitDepth::Auto;
+ AudioInterpolation Interpolation = AudioInterpolation::None;
+
+ /// How the GDB stub should be handled.
+ /// Defaults to disabled.
+ /// Ignored in builds that don't have the GDB stub included.
+ std::optional GDB = std::nullopt;
+
+ /// The 3D renderer to initialize the DS with.
+ /// Defaults to the software renderer.
+ /// Can be changed later at any time.
+ std::unique_ptr Renderer3D = std::make_unique();
+};
+
+/// Arguments to pass into the DSi constructor.
+/// New fields here should have default values if possible.
+/// Contains no virtual methods, so there's no vtable.
+struct DSiArgs final : public NDSArgs
+{
+ std::unique_ptr ARM9iBIOS = std::make_unique(BrokenBIOS);
+ std::unique_ptr ARM7iBIOS = std::make_unique(BrokenBIOS);
+
+ /// NAND image to install.
+ /// Required, there is no default value.
+ DSi_NAND::NANDImage NANDImage;
+
+ /// SD card to install.
+ /// Defaults to std::nullopt, which means no SD card.
+ std::optional DSiSDCard;
+
+ bool FullBIOSBoot = false;
+};
+}
+#endif //MELONDS_ARGS_H
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9fe93ae5..50cd7708 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -35,9 +35,12 @@ add_library(core STATIC
GPU2D_Soft.cpp
GPU3D.cpp
GPU3D_Soft.cpp
+ GPU3D_Texcache.cpp
+ GPU3D_Texcache.h
melonDLDI.h
NDS.cpp
NDSCart.cpp
+ NDSCartR4.cpp
Platform.h
ROMList.h
ROMList.cpp
@@ -49,7 +52,8 @@ add_library(core STATIC
SPI_Firmware.cpp
SPU.cpp
types.h
- version.h
+ Utils.cpp
+ Utils.h
Wifi.cpp
WifiAP.cpp
@@ -76,6 +80,9 @@ if (ENABLE_OGLRENDERER)
GPU_OpenGL.cpp
GPU_OpenGL_shaders.h
GPU3D_OpenGL.cpp
+ GPU3D_Compute.cpp
+ GPU3D_TexcacheOpenGL.cpp
+ GPU3D_TexcacheOpenGL.h
GPU3D_OpenGL_shaders.h
OpenGLSupport.cpp)
@@ -120,11 +127,26 @@ if (ENABLE_JIT)
endif()
endif()
+set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version")
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
+target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h")
+target_include_directories(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
+
add_subdirectory(teakra EXCLUDE_FROM_ALL)
# Workaround for building teakra with -O0 on Windows either failing or hanging forever
target_compile_options(teakra PRIVATE "$<$:-Og>")
target_link_libraries(core PRIVATE teakra)
+if (NOT MSVC)
+ # MSVC has its own compiler flag syntax; if we ever support it,
+ # be sure to silence any equivalent warnings there.
+
+ target_compile_options(core PRIVATE "$<$:-Wno-invalid-offsetof>")
+ # These warnings are excessive, and are only triggered in the ARMJIT code
+ # (which is fundamentally non-portable, so this is fine)
+endif()
+
find_library(m MATH_LIBRARY)
if (MATH_LIBRARY)
@@ -142,11 +164,13 @@ endif()
if (WIN32)
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
-elseif(NOT APPLE)
+elseif(NOT APPLE AND NOT HAIKU)
check_library_exists(rt shm_open "" NEED_LIBRT)
if (NEED_LIBRT)
target_link_libraries(core PRIVATE rt)
endif()
+elseif(HAIKU)
+ target_link_libraries(core PRIVATE network)
endif()
if (ENABLE_JIT_PROFILING)
diff --git a/src/CP15.cpp b/src/CP15.cpp
index 7cea845d..5e5b35ea 100644
--- a/src/CP15.cpp
+++ b/src/CP15.cpp
@@ -186,10 +186,14 @@ void ARMv5::UpdatePURegion(u32 n)
return;
}
- u32 start = rgn >> 12;
- u32 sz = 2 << ((rgn >> 1) & 0x1F);
- u32 end = start + (sz >> 12);
- // TODO: check alignment of start
+ // notes:
+ // * min size of a pu region is 4KiB (12 bits)
+ // * size is calculated as size + 1, but the 12 lsb of address space are ignored, therefore we need it as size + 1 - 12, or size - 11
+ // * pu regions are aligned based on their size
+ u32 size = std::max((int)((rgn>>1) & 0x1F) - 11, 0); // obtain the size, subtract 11 and clamp to a min of 0.
+ u32 start = ((rgn >> 12) >> size) << size; // determine the start offset, and use shifts to force alignment with a multiple of the size.
+ u32 end = start + (1<> 4) & 0xF,
val,
val & 1 ? "enabled" : "disabled",
val & 0xFFFFF000,
- (val & 0xFFFFF000) + (2 << ((val & 0x3E) >> 1))
+ (val & 0x3E) >> 1
);
Log(LogLevel::Debug, "%s", log_output);
// Some implementations of Log imply a newline, so we build up the line before printing it
@@ -668,7 +672,7 @@ void ARMv5::CP15Write(u32 id, u32 val)
Log(LogLevel::Debug, "unknown CP15 write op %03X %08X\n", id, val);
}
-u32 ARMv5::CP15Read(u32 id)
+u32 ARMv5::CP15Read(u32 id) const
{
//printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]);
diff --git a/src/DMA.cpp b/src/DMA.cpp
index 717b38fa..0fc6cf05 100644
--- a/src/DMA.cpp
+++ b/src/DMA.cpp
@@ -21,6 +21,7 @@
#include "DSi.h"
#include "DMA.h"
#include "GPU.h"
+#include "GPU3D.h"
#include "DMA_Timings.h"
#include "Platform.h"
diff --git a/src/DSi.cpp b/src/DSi.cpp
index f2781e48..306c5d1c 100644
--- a/src/DSi.cpp
+++ b/src/DSi.cpp
@@ -17,8 +17,10 @@
*/
#include
+#include
#include
#include
+#include "Args.h"
#include "NDS.h"
#include "DSi.h"
#include "ARM.h"
@@ -68,8 +70,8 @@ const u32 NDMAModes[] =
0xFF, // wifi / GBA cart slot (TODO)
};
-DSi::DSi() noexcept :
- NDS(1),
+DSi::DSi(DSiArgs&& args) noexcept :
+ NDS(std::move(args), 1),
NDMAs {
DSi_NDMA(0, 0, *this),
DSi_NDMA(0, 1, *this),
@@ -80,9 +82,11 @@ DSi::DSi() noexcept :
DSi_NDMA(1, 2, *this),
DSi_NDMA(1, 3, *this),
},
+ ARM7iBIOS(*args.ARM7iBIOS),
+ ARM9iBIOS(*args.ARM9iBIOS),
DSP(*this),
- SDMMC(*this, 0),
- SDIO(*this, 1),
+ SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)),
+ SDIO(*this),
I2C(*this),
CamModule(*this),
AES(*this)
@@ -90,7 +94,7 @@ DSi::DSi() noexcept :
// Memory is owned by ARMJIT_Memory, don't free it
NWRAM_A = JIT.Memory.GetNWRAM_A();
NWRAM_B = JIT.Memory.GetNWRAM_B();
- NWRAM_C = NDS::JIT.Memory.GetNWRAM_C();
+ NWRAM_C = JIT.Memory.GetNWRAM_C();
}
DSi::~DSi() noexcept
@@ -108,6 +112,8 @@ void DSi::Reset()
//ARM9.CP15Write(0x100, ARM9.CP15Read(0x100) | 0x00050000);
NDS::Reset();
+ // The SOUNDBIAS register does nothing on DSi
+ SPU.SetApplyBias(false);
KeyInput &= ~(1 << (16+6));
MapSharedWRAM(3);
@@ -118,9 +124,6 @@ void DSi::Reset()
CamModule.Reset();
DSP.Reset();
- SDMMC.CloseHandles();
- SDIO.CloseHandles();
-
LoadNAND();
SDMMC.Reset();
@@ -128,7 +131,7 @@ void DSi::Reset()
AES.Reset();
- if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
+ if (FullBIOSBoot)
{
SCFG_BIOS = 0x0000;
}
@@ -162,25 +165,23 @@ void DSi::Stop(Platform::StopReason reason)
CamModule.Stop();
}
-bool DSi::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
+void DSi::SetNDSCart(std::unique_ptr&& cart)
{
- if (NDS::LoadCart(romdata, romlen, savedata, savelen))
- {
- SetCartInserted(true);
- return true;
- }
-
- return false;
+ NDS::SetNDSCart(std::move(cart));
+ SetCartInserted(NDSCartSlot.GetCart() != nullptr);
}
-void DSi::EjectCart()
+std::unique_ptr DSi::EjectCart()
{
- NDS::EjectCart();
+ auto oldcart = NDS::EjectCart();
SetCartInserted(false);
+
+ return oldcart;
}
-void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb)
+
+void DSi::CamInputFrame(int cam, const u32* data, int width, int height, bool rgb)
{
switch (cam)
{
@@ -276,7 +277,7 @@ void DSi::SetCartInserted(bool inserted)
SCFG_MC |= 1;
}
-void DSi::DecryptModcryptArea(u32 offset, u32 size, u8* iv)
+void DSi::DecryptModcryptArea(u32 offset, u32 size, const u8* iv)
{
AES_ctx ctx;
u8 key[16];
@@ -509,9 +510,9 @@ void DSi::SetupDirectBoot()
ARM9Write32(0x02FFE000+i, tmp);
}
- if (NANDImage && *NANDImage)
+ if (DSi_NAND::NANDImage* image = SDMMC.GetNAND(); image && *image)
{ // If a NAND image is installed, and it's valid...
- if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*NANDImage))
+ if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*image))
{
DSi_NAND::DSiFirmwareSystemSettings userdata {};
nand.ReadUserData(userdata);
@@ -531,7 +532,7 @@ void DSi::SetupDirectBoot()
}
}
- Firmware::WifiBoard nwifiver = SPI.GetFirmware()->GetHeader().WifiBoard;
+ Firmware::WifiBoard nwifiver = SPI.GetFirmware().GetHeader().WifiBoard;
ARM9Write8(0x020005E0, static_cast(nwifiver));
// TODO: these should be taken from the wifi firmware in NAND
@@ -674,9 +675,6 @@ void DSi::SoftReset()
// the DSP most likely gets reset
DSP.Reset();
- SDMMC.CloseHandles();
- SDIO.CloseHandles();
-
LoadNAND();
SDMMC.Reset();
@@ -684,7 +682,7 @@ void DSi::SoftReset()
AES.Reset();
- if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
+ if (FullBIOSBoot)
{
SCFG_BIOS = 0x0000;
}
@@ -709,21 +707,22 @@ void DSi::SoftReset()
bool DSi::LoadNAND()
{
- if (!NANDImage)
+ DSi_NAND::NANDImage* image = SDMMC.GetNAND();
+ if (!(image && *image))
{
Log(LogLevel::Error, "No NAND image loaded\n");
return false;
}
Log(LogLevel::Info, "Loading DSi NAND\n");
- DSi_NAND::NANDMount nandmount(*NANDImage);
+ DSi_NAND::NANDMount nandmount(*SDMMC.GetNAND());
if (!nandmount)
{
Log(LogLevel::Error, "Failed to load DSi NAND\n");
return false;
}
- FileHandle* nand = NANDImage->GetFile();
+ FileHandle* nand = image->GetFile();
// Make sure NWRAM is accessible.
// The Bits are set to the startup values in Reset() and we might
@@ -745,7 +744,7 @@ bool DSi::LoadNAND()
memset(NWRAMMask, 0, sizeof(NWRAMMask));
u32 bootparams[8];
- if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
+ if (FullBIOSBoot)
{
// TODO: figure out default MBK mapping
// MBK1..5: disable mappings
@@ -879,11 +878,11 @@ bool DSi::LoadNAND()
}
}
- const DSi_NAND::DSiKey& emmccid = NANDImage->GetEMMCID();
+ const DSi_NAND::DSiKey& emmccid = image->GetEMMCID();
Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]);
- Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", NANDImage->GetConsoleID());
+ Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", image->GetConsoleID());
- if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
+ if (FullBIOSBoot)
{
// point CPUs to boot ROM reset vectors
ARM9.JumpTo(0xFFFF0000);
@@ -958,21 +957,21 @@ void DSi::StallNDMAs()
}
-bool DSi::DMAsInMode(u32 cpu, u32 mode)
+bool DSi::DMAsInMode(u32 cpu, u32 mode) const
{
if (NDS::DMAsInMode(cpu, mode)) return true;
return NDMAsInMode(cpu, NDMAModes[mode]);
}
-bool DSi::DMAsRunning(u32 cpu)
+bool DSi::DMAsRunning(u32 cpu) const
{
if (NDS::DMAsRunning(cpu)) return true;
return NDMAsRunning(cpu);
}
-bool DSi::NDMAsInMode(u32 cpu, u32 mode)
+bool DSi::NDMAsInMode(u32 cpu, u32 mode) const
{
cpu <<= 2;
if (NDMAs[cpu+0].IsInMode(mode)) return true;
@@ -982,7 +981,7 @@ bool DSi::NDMAsInMode(u32 cpu, u32 mode)
return false;
}
-bool DSi::NDMAsRunning(u32 cpu)
+bool DSi::NDMAsRunning(u32 cpu) const
{
cpu <<= 2;
if (NDMAs[cpu+0].IsRunning()) return true;
@@ -1728,12 +1727,12 @@ bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
return false;
}
- region->Mem = ARM9BIOS;
+ region->Mem = &ARM9BIOS[0];
region->Mask = 0xFFF;
}
else
{
- region->Mem = ARM9iBIOS;
+ region->Mem = &ARM9iBIOS[0];
region->Mask = 0xFFFF;
}
return true;
@@ -2678,14 +2677,14 @@ u8 DSi::ARM7IORead8(u32 addr)
case 0x04004500: return I2C.ReadData();
case 0x04004501: return I2C.ReadCnt();
- case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF;
- case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF;
- case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFF;
- case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 24) & 0xFF;
- case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFF;
- case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 40) & 0xFF;
- case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 48) & 0xFF;
- case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56;
+ case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFF;
+ case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 8) & 0xFF;
+ case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFF;
+ case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 24) & 0xFF;
+ case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFF;
+ case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 40) & 0xFF;
+ case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 48) & 0xFF;
+ case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56;
case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF;
@@ -2726,10 +2725,10 @@ u16 DSi::ARM7IORead16(u32 addr)
CASE_READ16_32BIT(0x0400405C, MBK[1][7])
CASE_READ16_32BIT(0x04004060, MBK[1][8])
- case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFF;
- case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFFFF;
- case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFFFF;
- case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48;
+ case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFF;
+ case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFFFF;
+ case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFFFF;
+ case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48;
case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt();
@@ -2806,8 +2805,8 @@ u32 DSi::ARM7IORead32(u32 addr)
case 0x04004400: return AES.ReadCnt();
case 0x0400440C: return AES.ReadOutputFIFO();
- case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF;
- case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32;
+ case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFFFFFF;
+ case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32;
case 0x04004D08: return 0;
case 0x4004700:
diff --git a/src/DSi.h b/src/DSi.h
index a221b5d7..1d010e0f 100644
--- a/src/DSi.h
+++ b/src/DSi.h
@@ -33,6 +33,7 @@ class DSi_I2CHost;
class DSi_CamModule;
class DSi_AES;
class DSi_DSP;
+class DSiArgs;
namespace DSi_NAND
{
@@ -48,9 +49,8 @@ public:
u16 SCFG_Clock9;
u32 SCFG_EXT[2];
- u8 ARM9iBIOS[0x10000];
- u8 ARM7iBIOS[0x10000];
- std::unique_ptr NANDImage;
+ std::array ARM9iBIOS;
+ std::array ARM7iBIOS;
DSi_SDHost SDMMC;
DSi_SDHost SDIO;
@@ -87,8 +87,8 @@ public:
void RunNDMAs(u32 cpu);
void StallNDMAs();
- bool NDMAsInMode(u32 cpu, u32 mode);
- bool NDMAsRunning(u32 cpu);
+ bool NDMAsInMode(u32 cpu, u32 mode) const;
+ bool NDMAsRunning(u32 cpu) const;
void CheckNDMAs(u32 cpu, u32 mode);
void StopNDMAs(u32 cpu, u32 mode);
@@ -130,22 +130,32 @@ public:
void ARM7IOWrite32(u32 addr, u32 val) override;
public:
- DSi() noexcept;
+ DSi(DSiArgs&& args) noexcept;
~DSi() noexcept override;
DSi(const DSi&) = delete;
DSi& operator=(const DSi&) = delete;
DSi(DSi&&) = delete;
DSi& operator=(DSi&&) = delete;
- bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) override;
- void EjectCart() override;
- bool NeedsDirectBoot() override
+ void SetNDSCart(std::unique_ptr&& cart) override;
+ std::unique_ptr EjectCart() override;
+ bool NeedsDirectBoot() const override
{
// for now, DSi mode requires original BIOS/NAND
return false;
}
- void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) override;
- bool DMAsInMode(u32 cpu, u32 mode) override;
- bool DMAsRunning(u32 cpu) override;
+
+ [[nodiscard]] const DSi_NAND::NANDImage& GetNAND() const noexcept { return *SDMMC.GetNAND(); }
+ [[nodiscard]] DSi_NAND::NANDImage& GetNAND() noexcept { return *SDMMC.GetNAND(); }
+ void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { SDMMC.SetNAND(std::move(nand)); }
+ u64 GetConsoleID() const noexcept { return SDMMC.GetNAND()->GetConsoleID(); }
+
+ [[nodiscard]] const FATStorage* GetSDCard() const noexcept { return SDMMC.GetSDCard(); }
+ void SetSDCard(FATStorage&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
+ void SetSDCard(std::optional&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
+
+ void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) override;
+ bool DMAsInMode(u32 cpu, u32 mode) const override;
+ bool DMAsRunning(u32 cpu) const override;
void StopDMAs(u32 cpu, u32 mode) override;
void CheckDMAs(u32 cpu, u32 mode) override;
u16 SCFG_Clock7;
@@ -162,10 +172,13 @@ public:
u8 GPIO_IE;
u8 GPIO_WiFi;
+ bool GetFullBIOSBoot() const noexcept { return FullBIOSBoot; }
+ void SetFullBIOSBoot(bool full) noexcept { FullBIOSBoot = full; }
private:
+ bool FullBIOSBoot;
void Set_SCFG_Clock9(u16 val);
void Set_SCFG_MC(u32 val);
- void DecryptModcryptArea(u32 offset, u32 size, u8* iv);
+ void DecryptModcryptArea(u32 offset, u32 size, const u8* iv);
void ApplyNewRAMSize(u32 size);
};
diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp
index 8e04586a..379dea13 100644
--- a/src/DSi_AES.cpp
+++ b/src/DSi_AES.cpp
@@ -78,7 +78,7 @@ void DSi_AES::Reset()
OutputMACDue = false;
// initialize keys
- u64 consoleid = DSi.NANDImage->GetConsoleID();
+ u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID();
// slot 0: modcrypt
*(u32*)&KeyX[0][0] = 0x746E694E;
@@ -235,7 +235,7 @@ void DSi_AES::ProcessBlock_CTR()
}
-u32 DSi_AES::ReadCnt()
+u32 DSi_AES::ReadCnt() const
{
u32 ret = Cnt;
diff --git a/src/DSi_AES.h b/src/DSi_AES.h
index 4df82695..d83c870e 100644
--- a/src/DSi_AES.h
+++ b/src/DSi_AES.h
@@ -54,7 +54,7 @@ public:
void Reset();
void DoSavestate(Savestate* file);
- u32 ReadCnt();
+ u32 ReadCnt() const;
void WriteCnt(u32 val);
void WriteBlkCnt(u32 val);
diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp
index 225bea8b..a1cdbe0a 100644
--- a/src/DSi_Camera.cpp
+++ b/src/DSi_Camera.cpp
@@ -438,7 +438,7 @@ void DSi_Camera::Stop()
Platform::Camera_Stop(Num);
}
-bool DSi_Camera::IsActivated()
+bool DSi_Camera::IsActivated() const
{
if (StandbyCnt & (1<<14)) return false; // standby
if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled
@@ -477,7 +477,7 @@ void DSi_Camera::StartTransfer()
Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true);
}
-bool DSi_Camera::TransferDone()
+bool DSi_Camera::TransferDone() const
{
return TransferY >= FrameHeight;
}
@@ -590,7 +590,7 @@ void DSi_Camera::Write(u8 val, bool last)
else DataPos++;
}
-u16 DSi_Camera::I2C_ReadReg(u16 addr)
+u16 DSi_Camera::I2C_ReadReg(u16 addr) const
{
switch (addr)
{
@@ -695,7 +695,7 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
// TODO: not sure at all what is the accessible range
// or if there is any overlap in the address range
-u8 DSi_Camera::MCU_Read(u16 addr)
+u8 DSi_Camera::MCU_Read(u16 addr) const
{
addr &= 0x7FFF;
@@ -724,7 +724,7 @@ void DSi_Camera::MCU_Write(u16 addr, u8 val)
}
-void DSi_Camera::InputFrame(u32* data, int width, int height, bool rgb)
+void DSi_Camera::InputFrame(const u32* data, int width, int height, bool rgb)
{
// TODO: double-buffering?
diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h
index ec409223..363cea43 100644
--- a/src/DSi_Camera.h
+++ b/src/DSi_Camera.h
@@ -38,10 +38,10 @@ public:
void Reset() override;
void Stop();
- bool IsActivated();
+ bool IsActivated() const;
void StartTransfer();
- bool TransferDone();
+ bool TransferDone() const;
// lengths in words
int TransferScanline(u32* buffer, int maxlen);
@@ -50,7 +50,7 @@ public:
u8 Read(bool last) override;
void Write(u8 val, bool last) override;
- void InputFrame(u32* data, int width, int height, bool rgb);
+ void InputFrame(const u32* data, int width, int height, bool rgb);
u32 Num;
@@ -59,7 +59,7 @@ private:
u32 RegAddr;
u16 RegData;
- u16 I2C_ReadReg(u16 addr);
+ u16 I2C_ReadReg(u16 addr) const;
void I2C_WriteReg(u16 addr, u16 val);
u16 PLLDiv;
@@ -72,7 +72,7 @@ private:
u16 MCUAddr;
u8 MCURegs[0x8000];
- u8 MCU_Read(u16 addr);
+ u8 MCU_Read(u16 addr) const;
void MCU_Write(u16 addr, u8 val);
u16 FrameWidth, FrameHeight;
@@ -91,7 +91,9 @@ public:
void Stop();
void DoSavestate(Savestate* file);
+ const DSi_Camera* GetOuterCamera() const { return Camera0; }
DSi_Camera* GetOuterCamera() { return Camera0; }
+ const DSi_Camera* GetInnerCamera() const { return Camera1; }
DSi_Camera* GetInnerCamera() { return Camera1; }
void IRQ(u32 param);
diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp
index 088943a9..25abd474 100644
--- a/src/DSi_DSP.cpp
+++ b/src/DSi_DSP.cpp
@@ -34,7 +34,7 @@ const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h
// NOTE: ^ IS IN DSP WORDS, NOT IN BYTES!
-u16 DSi_DSP::GetPSTS()
+u16 DSi_DSP::GetPSTS() const
{
u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit
//r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers
@@ -182,7 +182,7 @@ void DSi_DSP::Reset()
SNDExCnt = 0;
}
-bool DSi_DSP::IsRstReleased()
+bool DSi_DSP::IsRstReleased() const
{
return SCFG_RST;
}
@@ -193,12 +193,12 @@ void DSi_DSP::SetRstLine(bool release)
DSPTimestamp = DSi.ARM9Timestamp; // only start now!
}
-inline bool DSi_DSP::IsDSPCoreEnabled()
+inline bool DSi_DSP::IsDSPCoreEnabled() const
{
return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0)));
}
-inline bool DSi_DSP::IsDSPIOEnabled()
+inline bool DSi_DSP::IsDSPIOEnabled() const
{
return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST;
}
diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h
index a18dabf1..f76b4202 100644
--- a/src/DSi_DSP.h
+++ b/src/DSi_DSP.h
@@ -41,7 +41,7 @@ public:
void DSPCatchUpU32(u32 _);
// SCFG_RST bit0
- bool IsRstReleased();
+ bool IsRstReleased() const;
void SetRstLine(bool release);
// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT)
@@ -54,7 +54,7 @@ public:
u32 Read32(u32 addr);
void Write32(u32 addr, u32 val);
- u16 ReadSNDExCnt() { return SNDExCnt; }
+ u16 ReadSNDExCnt() const { return SNDExCnt; }
void WriteSNDExCnt(u16 val, u16 mask);
// NOTE: checks SCFG_CLK9
@@ -93,10 +93,10 @@ private:
static const u32 DataMemoryOffset;
- u16 GetPSTS();
+ u16 GetPSTS() const;
- inline bool IsDSPCoreEnabled();
- inline bool IsDSPIOEnabled();
+ inline bool IsDSPCoreEnabled() const;
+ inline bool IsDSPIOEnabled() const;
bool DSPCatchUp();
diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp
index d5ea60c6..28f98dc8 100644
--- a/src/DSi_I2C.cpp
+++ b/src/DSi_I2C.cpp
@@ -117,20 +117,20 @@ void DSi_BPTWL::DoSavestate(Savestate* file)
}
// TODO: Needs more investigation on the other bits
-inline bool DSi_BPTWL::GetIRQMode()
+inline bool DSi_BPTWL::GetIRQMode() const
{
return Registers[0x12] & 0x01;
}
-u8 DSi_BPTWL::GetBootFlag() { return Registers[0x70]; }
+u8 DSi_BPTWL::GetBootFlag() const { return Registers[0x70]; }
-bool DSi_BPTWL::GetBatteryCharging() { return Registers[0x20] >> 7; }
+bool DSi_BPTWL::GetBatteryCharging() const { return Registers[0x20] >> 7; }
void DSi_BPTWL::SetBatteryCharging(bool charging)
{
Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F));
}
-u8 DSi_BPTWL::GetBatteryLevel() { return Registers[0x20] & 0xF; }
+u8 DSi_BPTWL::GetBatteryLevel() const { return Registers[0x20] & 0xF; }
void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel)
{
Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F));
@@ -143,13 +143,13 @@ void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel)
}
-u8 DSi_BPTWL::GetVolumeLevel() { return Registers[0x40]; }
+u8 DSi_BPTWL::GetVolumeLevel() const { return Registers[0x40]; }
void DSi_BPTWL::SetVolumeLevel(u8 volume)
{
Registers[0x40] = volume & 0x1F;
}
-u8 DSi_BPTWL::GetBacklightLevel() { return Registers[0x41]; }
+u8 DSi_BPTWL::GetBacklightLevel() const { return Registers[0x41]; }
void DSi_BPTWL::SetBacklightLevel(u8 backlight)
{
Registers[0x41] = backlight > 4 ? 4 : backlight;
@@ -246,7 +246,7 @@ void DSi_BPTWL::SetVolumeSwitchReleased(u32 key)
VolumeSwitchRepeatTime = 0.0;
}
-inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid()
+inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() const
{
bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up);
bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down);
diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h
index 51fe78e6..5dfeebd0 100644
--- a/src/DSi_I2C.h
+++ b/src/DSi_I2C.h
@@ -86,20 +86,20 @@ public:
void Reset() override;
void DoSavestate(Savestate* file) override;
- u8 GetBootFlag();
+ u8 GetBootFlag() const;
- bool GetBatteryCharging();
+ bool GetBatteryCharging() const;
void SetBatteryCharging(bool charging);
- u8 GetBatteryLevel();
+ u8 GetBatteryLevel() const;
void SetBatteryLevel(u8 batteryLevel);
// 0-31
- u8 GetVolumeLevel();
+ u8 GetVolumeLevel() const;
void SetVolumeLevel(u8 volume);
// 0-4
- u8 GetBacklightLevel();
+ u8 GetBacklightLevel() const;
void SetBacklightLevel(u8 backlight);
void DoHardwareReset(bool direct);
@@ -144,10 +144,10 @@ private:
u8 Registers[0x100];
u32 CurPos;
- bool GetIRQMode();
+ bool GetIRQMode() const;
void ResetButtonState();
- bool CheckVolumeSwitchKeysValid();
+ bool CheckVolumeSwitchKeysValid() const;
};
diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp
index 59f582fd..8da02540 100644
--- a/src/DSi_NAND.cpp
+++ b/src/DSi_NAND.cpp
@@ -122,7 +122,8 @@ NANDImage::NANDImage(NANDImage&& other) noexcept :
ConsoleID(other.ConsoleID),
FATIV(other.FATIV),
FATKey(other.FATKey),
- ESKey(other.ESKey)
+ ESKey(other.ESKey),
+ Length(other.Length)
{
other.CurFile = nullptr;
}
@@ -131,12 +132,16 @@ NANDImage& NANDImage::operator=(NANDImage&& other) noexcept
{
if (this != &other)
{
+ if (CurFile)
+ CloseFile(CurFile);
+
CurFile = other.CurFile;
eMMC_CID = other.eMMC_CID;
ConsoleID = other.ConsoleID;
FATIV = other.FATIV;
FATKey = other.FATKey;
ESKey = other.ESKey;
+ Length = other.Length;
other.CurFile = nullptr;
}
@@ -362,7 +367,7 @@ bool NANDImage::ESEncrypt(u8* data, u32 len) const
return true;
}
-bool NANDImage::ESDecrypt(u8* data, u32 len)
+bool NANDImage::ESDecrypt(u8* data, u32 len) const
{
AES_ctx ctx;
u8 iv[16];
diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h
index 699397bd..104845d5 100644
--- a/src/DSi_NAND.h
+++ b/src/DSi_NAND.h
@@ -71,7 +71,7 @@ private:
u32 ReadFATBlock(u64 addr, u32 len, u8* buf);
u32 WriteFATBlock(u64 addr, u32 len, const u8* buf);
bool ESEncrypt(u8* data, u32 len) const;
- bool ESDecrypt(u8* data, u32 len);
+ bool ESDecrypt(u8* data, u32 len) const;
Platform::FileHandle* CurFile = nullptr;
DSiKey eMMC_CID;
u64 ConsoleID;
diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp
index fe1f0ba7..7c77c9ad 100644
--- a/src/DSi_NDMA.cpp
+++ b/src/DSi_NDMA.cpp
@@ -22,6 +22,7 @@
#include "DSi_NDMA.h"
#include "GPU.h"
#include "DSi_AES.h"
+#include "GPU3D.h"
namespace melonDS
{
diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h
index 7e87da7b..fb34dbdf 100644
--- a/src/DSi_NDMA.h
+++ b/src/DSi_NDMA.h
@@ -44,12 +44,12 @@ public:
void Run9();
void Run7();
- bool IsInMode(u32 mode)
+ bool IsInMode(u32 mode) const
{
return ((mode == StartMode) && (Cnt & 0x80000000));
}
- bool IsRunning() { return Running!=0; }
+ bool IsRunning() const { return Running!=0; }
void StartIfNeeded(u32 mode)
{
diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp
index 9e006e2c..a6177dec 100644
--- a/src/DSi_NWifi.cpp
+++ b/src/DSi_NWifi.cpp
@@ -165,13 +165,13 @@ void DSi_NWifi::Reset()
for (int i = 0; i < 9; i++)
Mailbox[i].Clear();
- const Firmware* fw = DSi.SPI.GetFirmware();
+ const Firmware& fw = DSi.SPI.GetFirmware();
- MacAddress mac = fw->GetHeader().MacAddr;
+ MacAddress mac = fw.GetHeader().MacAddr;
Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
- Firmware::WifiBoard type = fw->GetHeader().WifiBoard;
+ Firmware::WifiBoard type = fw.GetHeader().WifiBoard;
switch (type)
{
case Firmware::WifiBoard::W015: // AR6002
diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp
index 4cbf595d..72fe3756 100644
--- a/src/DSi_SD.cpp
+++ b/src/DSi_SD.cpp
@@ -18,6 +18,7 @@
#include
#include
+#include "Args.h"
#include "DSi.h"
#include "DSi_SD.h"
#include "DSi_NAND.h"
@@ -26,6 +27,10 @@
namespace melonDS
{
+using std::holds_alternative;
+using std::unique_ptr;
+using std::get_if;
+using std::get;
using namespace Platform;
// observed IRQ behavior during transfers
@@ -57,36 +62,38 @@ enum
};
-DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, u32 num) : DSi(dsi)
+DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard) noexcept : DSi(dsi), Num(0)
{
- Num = num;
-
- DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
+ DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
- DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
+ DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
- Ports[0] = nullptr;
+ Ports[0] = sdcard ? std::make_unique(DSi, this, std::move(*sdcard)) : nullptr;
+ sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
+ Ports[1] = std::make_unique(DSi, this, std::move(nand));
+}
+
+// Creates an SDIO host
+DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
+{
+ DSi.RegisterEventFunc(Event_DSi_SDIOTransfer ,
+ Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
+ DSi.RegisterEventFunc(Event_DSi_SDIOTransfer,
+ Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
+
+ Ports[0] = std::make_unique(DSi, this);
Ports[1] = nullptr;
}
DSi_SDHost::~DSi_SDHost()
{
- if (Ports[0]) delete Ports[0];
- if (Ports[1]) delete Ports[1];
-
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_TX);
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_RX);
-}
-void DSi_SDHost::CloseHandles()
-{
- if (Ports[0]) delete Ports[0];
- if (Ports[1]) delete Ports[1];
- Ports[0] = nullptr;
- Ports[1] = nullptr;
+ // unique_ptr's destructor will clean up the ports
}
void DSi_SDHost::Reset()
@@ -129,48 +136,70 @@ void DSi_SDHost::Reset()
TXReq = false;
- CloseHandles();
+ if (Ports[0]) Ports[0]->Reset();
+ if (Ports[1]) Ports[1]->Reset();
+}
- if (Num == 0)
+FATStorage* DSi_SDHost::GetSDCard() noexcept
+{
+ if (Num != 0) return nullptr;
+ return static_cast(Ports[0].get())->GetSDCard();
+}
+
+const FATStorage* DSi_SDHost::GetSDCard() const noexcept
+{
+ if (Num != 0) return nullptr;
+ return static_cast(Ports[0].get())->GetSDCard();
+}
+
+DSi_NAND::NANDImage* DSi_SDHost::GetNAND() noexcept
+{
+ if (Num != 0) return nullptr;
+ return static_cast(Ports[1].get())->GetNAND();
+}
+
+const DSi_NAND::NANDImage* DSi_SDHost::GetNAND() const noexcept
+{
+ if (Num != 0) return nullptr;
+ return static_cast(Ports[1].get())->GetNAND();
+}
+
+void DSi_SDHost::SetSDCard(FATStorage&& sdcard) noexcept
+{
+ if (Num != 0) return;
+
+ static_cast(Ports[0].get())->SetSDCard(std::move(sdcard));
+}
+
+void DSi_SDHost::SetSDCard(std::optional&& sdcard) noexcept
+{
+ if (Num != 0) return;
+
+ if (sdcard)
{
- DSi_MMCStorage* sd;
- DSi_MMCStorage* mmc;
-
- if (Platform::GetConfigBool(Platform::DSiSD_Enable))
+ if (!Ports[0])
{
- std::string folderpath;
- if (Platform::GetConfigBool(Platform::DSiSD_FolderSync))
- folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath);
- else
- folderpath = "";
-
- sd = new DSi_MMCStorage(this,
- false,
- Platform::GetConfigString(Platform::DSiSD_ImagePath),
- (u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024,
- Platform::GetConfigBool(Platform::DSiSD_ReadOnly),
- folderpath);
- u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
- sd->SetCID(sd_cid);
+ Ports[0] = std::make_unique(DSi, this, std::move(*sdcard));
}
else
- sd = nullptr;
-
- mmc = new DSi_MMCStorage(this, *DSi.NANDImage);
- mmc->SetCID(DSi.NANDImage->GetEMMCID().data());
-
- Ports[0] = sd;
- Ports[1] = mmc;
+ {
+ static_cast(Ports[0].get())->SetSDCard(std::move(*sdcard));
+ }
}
else
{
- DSi_NWifi* nwifi = new DSi_NWifi(DSi, this);
-
- Ports[0] = nwifi;
+ Ports[0] = nullptr;
}
- if (Ports[0]) Ports[0]->Reset();
- if (Ports[1]) Ports[1]->Reset();
+ sdcard = std::nullopt;
+ // a moved-from optional isn't empty, it contains a moved-from object
+}
+
+void DSi_SDHost::SetNAND(DSi_NAND::NANDImage&& nand) noexcept
+{
+ if (Num != 0) return;
+
+ static_cast(Ports[1].get())->SetNAND(std::move(nand));
}
void DSi_SDHost::DoSavestate(Savestate* file)
@@ -261,7 +290,7 @@ void DSi_SDHost::SetCardIRQ()
if (!(CardIRQCtl & (1<<0))) return;
u16 oldflags = CardIRQStatus & ~CardIRQMask;
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
+ DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev->IRQ) CardIRQStatus |= (1<<0);
else CardIRQStatus &= ~(1<<0);
@@ -307,7 +336,7 @@ void DSi_SDHost::FinishRX(u32 param)
SetIRQ(24);
}
-u32 DSi_SDHost::DataRX(u8* data, u32 len)
+u32 DSi_SDHost::DataRX(const u8* data, u32 len)
{
if (len != BlockLen16) { Log(LogLevel::Warn, "!! BAD BLOCKLEN\n"); len = BlockLen16; }
@@ -332,7 +361,7 @@ u32 DSi_SDHost::DataRX(u8* data, u32 len)
void DSi_SDHost::FinishTX(u32 param)
{
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
+ DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (BlockCountInternal == 0)
{
@@ -411,7 +440,7 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len)
return len;
}
-u32 DSi_SDHost::GetTransferrableLen(u32 len)
+u32 DSi_SDHost::GetTransferrableLen(u32 len) const
{
if (len > BlockLen16) len = BlockLen16; // checkme
return len;
@@ -419,7 +448,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len)
void DSi_SDHost::CheckRX()
{
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
+ DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
CheckSwapFIFO();
@@ -459,7 +488,7 @@ void DSi_SDHost::CheckTX()
return;
}
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
+ DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev) dev->ContinueTransfer();
}
@@ -550,7 +579,6 @@ u16 DSi_SDHost::ReadFIFO16()
return 0;
}
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u16 ret = DataFIFO[f].Read();
if (DataFIFO[f].IsEmpty())
@@ -571,7 +599,6 @@ u32 DSi_SDHost::ReadFIFO32()
return 0;
}
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 ret = DataFIFO32.Read();
if (DataFIFO32.IsEmpty())
@@ -593,7 +620,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
Command = val;
u8 cmd = Command & 0x3F;
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
+ DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev)
{
// CHECKME
@@ -707,7 +734,6 @@ void DSi_SDHost::Write(u32 addr, u16 val)
void DSi_SDHost::WriteFIFO16(u16 val)
{
- DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 f = CurFIFO;
if (DataFIFO[f].IsFull())
{
@@ -780,34 +806,23 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard")
-DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand)
- : DSi_SDDevice(host), Internal(true), NAND(&nand), SD(nullptr)
+DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept
+ : DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand))
{
ReadOnly = false;
+ SetCID(get(Storage).GetEMMCID().data());
}
-DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir)
- : DSi_SDDevice(host)
+DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept
+ : DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard))
{
- Internal = internal;
- NAND = nullptr;
-
- SD = new FATStorage(filename, size, readonly, sourcedir);
- SD->Open();
-
- ReadOnly = readonly;
+ ReadOnly = get(Storage).IsReadOnly();
+ SetCID(DSiSDCardCID);
}
-DSi_MMCStorage::~DSi_MMCStorage()
-{
- if (SD)
- {
- SD->Close();
- delete SD;
- }
-
- // Do not close the NANDImage, it's not owned by this object
-}
+// The FATStorage or NANDImage is owned by this object;
+// std::variant's destructor will clean it up.
+DSi_MMCStorage::~DSi_MMCStorage() = default;
void DSi_MMCStorage::Reset()
{
@@ -836,7 +851,7 @@ void DSi_MMCStorage::Reset()
void DSi_MMCStorage::DoSavestate(Savestate* file)
{
- file->Section(Internal ? "NAND" : "SDCR");
+ file->Section(holds_alternative(Storage) ? "NAND" : "SDCR");
file->VarArray(CID, 16);
file->VarArray(CSD, 16);
@@ -871,7 +886,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 1: // SEND_OP_COND
// CHECKME!!
// also TODO: it's different for the SD card
- if (Internal)
+ if (std::holds_alternative(Storage))
{
param &= ~(1<<30);
OCR &= 0xBF000000;
@@ -895,7 +910,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
return;
case 3: // get/set RCA
- if (Internal)
+ if (holds_alternative(Storage))
{
RCA = param >> 16;
Host->SendResponse(CSR|0x10000, true); // huh??
@@ -930,7 +945,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 12: // stop operation
SetState(0x04);
- if (NAND) FileFlush(NAND->GetFile());
+ if (auto* nand = get_if(&Storage))
+ FileFlush(nand->GetFile());
RWCommand = 0;
Host->SendResponse(CSR, true);
return;
@@ -1011,7 +1027,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
// DSi boot2 sets this to 0x40100000 (hardcoded)
// then has two codepaths depending on whether bit30 did get set
// is it settable at all on the MMC? probably not.
- if (Internal) param &= ~(1<<30);
+ if (holds_alternative(Storage)) param &= ~(1<<30);
OCR &= 0xBF000000;
OCR |= (param & 0x40FFFFFF);
Host->SendResponse(OCR, true);
@@ -1057,14 +1073,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
len = Host->GetTransferrableLen(len);
u8 data[0x200];
- if (SD)
+ if (auto* sd = std::get_if(&Storage))
{
- SD->ReadSectors((u32)(addr >> 9), 1, data);
+ sd->ReadSectors((u32)(addr >> 9), 1, data);
}
- else if (NAND)
+ else if (auto* nand = std::get_if(&Storage))
{
- FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start);
- FileRead(&data[addr & 0x1FF], 1, len, NAND->GetFile());
+ FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
+ FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile());
}
return Host->DataRX(&data[addr & 0x1FF], len);
@@ -1078,23 +1094,23 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
u8 data[0x200];
if (len < 0x200)
{
- if (SD)
+ if (auto* sd = get_if(&Storage))
{
- SD->ReadSectors((u32)(addr >> 9), 1, data);
+ sd->ReadSectors((u32)(addr >> 9), 1, data);
}
}
if ((len = Host->DataTX(&data[addr & 0x1FF], len)))
{
if (!ReadOnly)
{
- if (SD)
+ if (auto* sd = get_if(&Storage))
{
- SD->WriteSectors((u32)(addr >> 9), 1, data);
+ sd->WriteSectors((u32)(addr >> 9), 1, data);
}
- else if (NAND)
+ else if (auto* nand = get_if(&Storage))
{
- FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start);
- FileWrite(&data[addr & 0x1FF], 1, len, NAND->GetFile());
+ FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
+ FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile());
}
}
}
diff --git a/src/DSi_SD.h b/src/DSi_SD.h
index 17ba8d30..29620dc5 100644
--- a/src/DSi_SD.h
+++ b/src/DSi_SD.h
@@ -20,28 +20,30 @@
#define DSI_SD_H
#include
+#include
#include "FIFO.h"
#include "FATStorage.h"
+#include "DSi_NAND.h"
#include "Savestate.h"
namespace melonDS
{
-namespace DSi_NAND
-{
- class NANDImage;
-}
-
class DSi_SDDevice;
class DSi;
+using Nothing = std::monostate;
+using DSiStorage = std::variant;
class DSi_SDHost
{
public:
- DSi_SDHost(melonDS::DSi& dsi, u32 num);
+ /// Creates an SDMMC host.
+ DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard = std::nullopt) noexcept;
+
+ /// Creates an SDIO host
+ explicit DSi_SDHost(melonDS::DSi& dsi) noexcept;
~DSi_SDHost();
- void CloseHandles();
void Reset();
void DoSavestate(Savestate* file);
@@ -49,9 +51,9 @@ public:
void FinishRX(u32 param);
void FinishTX(u32 param);
void SendResponse(u32 val, bool last);
- u32 DataRX(u8* data, u32 len);
+ u32 DataRX(const u8* data, u32 len);
u32 DataTX(u8* data, u32 len);
- u32 GetTransferrableLen(u32 len);
+ u32 GetTransferrableLen(u32 len) const;
void CheckRX();
void CheckTX();
@@ -59,6 +61,15 @@ public:
void SetCardIRQ();
+ [[nodiscard]] FATStorage* GetSDCard() noexcept;
+ [[nodiscard]] const FATStorage* GetSDCard() const noexcept;
+ [[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept;
+ [[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept;
+
+ void SetSDCard(FATStorage&& sdcard) noexcept;
+ void SetSDCard(std::optional&& sdcard) noexcept;
+ void SetNAND(DSi_NAND::NANDImage&& nand) noexcept;
+
u16 Read(u32 addr);
void Write(u32 addr, u16 val);
u16 ReadFIFO16();
@@ -96,7 +107,7 @@ private:
u32 Param;
u16 ResponseBuffer[8];
- DSi_SDDevice* Ports[2];
+ std::array, 2> Ports {};
u32 CurFIFO; // FIFO accessible for read/write
FIFO DataFIFO[2];
@@ -134,25 +145,53 @@ protected:
class DSi_MMCStorage : public DSi_SDDevice
{
public:
- DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand);
- DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir);
- ~DSi_MMCStorage();
+ DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept;
+ DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept;
+ ~DSi_MMCStorage() override;
- void Reset();
+ [[nodiscard]] FATStorage* GetSDCard() noexcept { return std::get_if(&Storage); }
+ [[nodiscard]] const FATStorage* GetSDCard() const noexcept { return std::get_if(&Storage); }
+ [[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept { return std::get_if(&Storage); }
+ [[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept { return std::get_if(&Storage); }
- void DoSavestate(Savestate* file);
+ void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { Storage = std::move(nand); }
+ void SetSDCard(FATStorage&& sdcard) noexcept { Storage = std::move(sdcard); }
+ void SetSDCard(std::optional&& sdcard) noexcept
+ {
+ if (sdcard)
+ { // If we're setting a new SD card...
+ Storage = std::move(*sdcard);
+ sdcard = std::nullopt;
+ }
+ else
+ {
+ Storage = Nothing();
+ }
+ }
+
+ void SetStorage(DSiStorage&& storage) noexcept
+ {
+ Storage = std::move(storage);
+ storage = Nothing();
+ // not sure if a moved-from variant is empty or contains a moved-from object;
+ // better to be safe than sorry
+ }
+
+ void Reset() override;
+
+ void DoSavestate(Savestate* file) override;
void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); }
- void SendCMD(u8 cmd, u32 param);
+ void SendCMD(u8 cmd, u32 param) override;
void SendACMD(u8 cmd, u32 param);
- void ContinueTransfer();
+ void ContinueTransfer() override;
private:
- bool Internal;
- DSi_NAND::NANDImage* NAND;
- FATStorage* SD;
+ static constexpr u8 DSiSDCardCID[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
+ melonDS::DSi& DSi;
+ DSiStorage Storage;
u8 CID[16];
u8 CSD[16];
diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp
index 6c7a15c7..d515db9f 100644
--- a/src/DSi_SPI_TSC.cpp
+++ b/src/DSi_SPI_TSC.cpp
@@ -121,7 +121,7 @@ void DSi_TSC::SetTouchCoords(u16 x, u16 y)
}
}
-void DSi_TSC::MicInputFrame(s16* data, int samples)
+void DSi_TSC::MicInputFrame(const s16* data, int samples)
{
if (TSCMode == 0x00) return TSC::MicInputFrame(data, samples);
diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h
index 47777da5..d1a71063 100644
--- a/src/DSi_SPI_TSC.h
+++ b/src/DSi_SPI_TSC.h
@@ -40,7 +40,7 @@ public:
void SetMode(u8 mode);
void SetTouchCoords(u16 x, u16 y) override;
- void MicInputFrame(s16* data, int samples) override;
+ void MicInputFrame(const s16* data, int samples) override;
void Write(u8 val) override;
void Release() override;
diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp
index cd0a03c8..f735d4b8 100644
--- a/src/FATStorage.cpp
+++ b/src/FATStorage.cpp
@@ -29,47 +29,80 @@ namespace melonDS
{
namespace fs = std::filesystem;
using namespace Platform;
+using std::string;
-FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir)
+FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional& sourcedir) :
+ FATStorage(FATStorageArgs { filename, size, readonly, sourcedir })
{
- ReadOnly = readonly;
- Load(filename, size, sourcedir);
+}
- File = nullptr;
+FATStorage::FATStorage(const FATStorageArgs& args) noexcept :
+ FATStorage(args.Filename, args.Size, args.ReadOnly, args.SourceDir)
+{
+}
+
+FATStorage::FATStorage(FATStorageArgs&& args) noexcept :
+ FilePath(std::move(args.Filename)),
+ FileSize(args.Size),
+ ReadOnly(args.ReadOnly),
+ SourceDir(std::move(args.SourceDir))
+{
+ Load(FilePath, FileSize, SourceDir);
+}
+
+FATStorage::FATStorage(FATStorage&& other) noexcept
+{
+ FilePath = std::move(other.FilePath);
+ IndexPath = std::move(other.IndexPath);
+ SourceDir = std::move(other.SourceDir);
+ ReadOnly = other.ReadOnly;
+ File = other.File;
+ FileSize = other.FileSize;
+ DirIndex = std::move(other.DirIndex);
+ FileIndex = std::move(other.FileIndex);
+
+ other.File = nullptr;
+}
+
+FATStorage& FATStorage::operator=(FATStorage&& other) noexcept
+{
+ if (this != &other)
+ {
+ if (File)
+ { // Sync this file's contents to the host (if applicable) before closing it
+ if (!ReadOnly) Save();
+ CloseFile(File);
+ }
+
+ FilePath = std::move(other.FilePath);
+ IndexPath = std::move(other.IndexPath);
+ SourceDir = std::move(other.SourceDir);
+ ReadOnly = other.ReadOnly;
+ File = other.File;
+ FileSize = other.FileSize;
+ DirIndex = std::move(other.DirIndex);
+ FileIndex = std::move(other.FileIndex);
+
+ other.File = nullptr;
+ other.SourceDir = std::nullopt;
+ }
+
+ return *this;
}
FATStorage::~FATStorage()
{
if (!ReadOnly) Save();
-}
-
-bool FATStorage::Open()
-{
- File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
- if (!File)
- {
- return false;
- }
-
- return true;
-}
-
-void FATStorage::Close()
-{
if (File) CloseFile(File);
File = nullptr;
}
-
bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
{
if (!File) return false;
- if (FF_File) return false;
- FF_File = File;
- FF_FileSize = FileSize;
- ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9));
+ ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
FRESULT res;
FATFS fs;
@@ -77,8 +110,8 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
res = f_mount(&fs, "0:", 1);
if (res != FR_OK)
{
+ f_unmount("0:");
ff_disk_close();
- FF_File = nullptr;
return false;
}
@@ -90,7 +123,6 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
{
f_unmount("0:");
ff_disk_close();
- FF_File = nullptr;
return false;
}
@@ -100,34 +132,75 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
f_unmount("0:");
ff_disk_close();
- FF_File = nullptr;
return nwrite==len;
}
+u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data)
+{
+ if (!File) return false;
-u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data)
+ ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
+
+ FRESULT res;
+ FATFS fs;
+
+ res = f_mount(&fs, "0:", 1);
+ if (res != FR_OK)
+ {
+ f_unmount("0:");
+ ff_disk_close();
+ return false;
+ }
+
+ std::string prefixedPath("0:/");
+ prefixedPath += path;
+ FF_FIL file;
+ res = f_open(&file, prefixedPath.c_str(), FA_READ);
+ if (res != FR_OK)
+ {
+ f_unmount("0:");
+ ff_disk_close();
+ return false;
+ }
+
+ u32 nread;
+ f_lseek(&file, start);
+ f_read(&file, data, len, &nread);
+ f_close(&file);
+
+ f_unmount("0:");
+ ff_disk_close();
+ return nread;
+}
+
+u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) const
{
return ReadSectorsInternal(File, FileSize, start, num, data);
}
-u32 FATStorage::WriteSectors(u32 start, u32 num, u8* data)
+u32 FATStorage::WriteSectors(u32 start, u32 num, const u8* data)
{
if (ReadOnly) return 0;
return WriteSectorsInternal(File, FileSize, start, num, data);
}
-
-FileHandle* FATStorage::FF_File;
-u64 FATStorage::FF_FileSize;
-
-UINT FATStorage::FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num)
+u64 FATStorage::GetSectorCount() const
{
- return ReadSectorsInternal(FF_File, FF_FileSize, sector, num, buf);
+ return FileSize / 0x200;
}
-UINT FATStorage::FF_WriteStorage(const BYTE* buf, LBA_t sector, UINT num)
+ff_disk_read_cb FATStorage::FF_ReadStorage() const noexcept
{
- return WriteSectorsInternal(FF_File, FF_FileSize, sector, num, buf);
+ return [this](BYTE* buf, LBA_t sector, UINT num) {
+ return ReadSectorsInternal(File, FileSize, sector, num, buf);
+ };
+}
+
+ff_disk_write_cb FATStorage::FF_WriteStorage() const noexcept
+{
+ return [this](const BYTE* buf, LBA_t sector, UINT num) {
+ return WriteSectorsInternal(File, FileSize, sector, num, buf);
+ };
}
@@ -907,7 +980,7 @@ bool FATStorage::ImportDirectory(const std::string& sourcedir)
return true;
}
-u64 FATStorage::GetDirectorySize(fs::path sourcedir)
+u64 FATStorage::GetDirectorySize(fs::path sourcedir) const
{
u64 ret = 0;
u32 csize = 0x1000; // this is an estimate
@@ -930,19 +1003,15 @@ u64 FATStorage::GetDirectorySize(fs::path sourcedir)
return ret;
}
-bool FATStorage::Load(const std::string& filename, u64 size, const std::string& sourcedir)
+bool FATStorage::Load(const std::string& filename, u64 size, const std::optional& sourcedir)
{
- FilePath = filename;
- FileSize = size;
- SourceDir = sourcedir;
-
- bool hasdir = !sourcedir.empty();
- if (hasdir)
+ bool hasdir = sourcedir && !sourcedir->empty();
+ if (sourcedir)
{
- if (!fs::is_directory(fs::u8path(sourcedir)))
+ if (!fs::is_directory(fs::u8path(*sourcedir)))
{
hasdir = false;
- SourceDir = "";
+ SourceDir = std::nullopt;
}
}
@@ -953,8 +1022,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
// with a minimum 128MB extra, otherwise size is defaulted to 512MB
bool isnew = !Platform::LocalFileExists(filename);
- FF_File = Platform::OpenLocalFile(filename, static_cast(FileMode::ReadWrite | FileMode::Preserve));
- if (!FF_File)
+ File = Platform::OpenLocalFile(filename, static_cast(FileMode::ReadWrite | FileMode::Preserve));
+ if (!File)
return false;
IndexPath = FilePath + ".idx";
@@ -970,7 +1039,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
if (FileSize == 0)
{
- FileSize = FileLength(FF_File);
+ FileSize = FileLength(File);
}
}
@@ -984,8 +1053,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
}
else
{
- FF_FileSize = FileSize;
- ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9));
+ ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
res = f_mount(&fs, "0:", 1);
if (res != FR_OK)
@@ -1005,7 +1073,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
{
if (hasdir)
{
- FileSize = GetDirectorySize(fs::u8path(sourcedir));
+ FileSize = GetDirectorySize(fs::u8path(*sourcedir));
FileSize += 0x8000000ULL; // 128MB leeway
// make it a power of two
@@ -1021,9 +1089,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
FileSize = 0x20000000ULL; // 512MB
}
- FF_FileSize = FileSize;
ff_disk_close();
- ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9));
+ ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
DirIndex.clear();
FileIndex.clear();
@@ -1054,33 +1121,24 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
if (res == FR_OK)
{
if (hasdir)
- ImportDirectory(sourcedir);
+ ImportDirectory(*sourcedir);
}
f_unmount("0:");
ff_disk_close();
- CloseFile(FF_File);
- FF_File = nullptr;
return true;
}
bool FATStorage::Save()
{
- if (SourceDir.empty())
- {
- return true;
+ if (!SourceDir)
+ { // If we're not syncing the SD card image to a host directory...
+ return true; // Not an error.
}
- FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
- if (!FF_File)
- {
- return false;
- }
-
- FF_FileSize = FileSize;
- ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9));
+ ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
FRESULT res;
FATFS fs;
@@ -1088,21 +1146,18 @@ bool FATStorage::Save()
res = f_mount(&fs, "0:", 1);
if (res != FR_OK)
{
+ f_unmount("0:");
ff_disk_close();
- CloseFile(FF_File);
- FF_File = nullptr;
return false;
}
- ExportChanges(SourceDir);
+ ExportChanges(*SourceDir);
SaveIndex();
f_unmount("0:");
ff_disk_close();
- CloseFile(FF_File);
- FF_File = nullptr;
return true;
}
diff --git a/src/FATStorage.h b/src/FATStorage.h
index 2bdafad8..00628461 100644
--- a/src/FATStorage.h
+++ b/src/FATStorage.h
@@ -22,41 +22,63 @@
#include
#include
#include