Merge branch 'master' into i2s
This commit is contained in:
commit
975de7fe11
|
@ -4,15 +4,17 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
- ci/vcpkg-update
|
||||
- ci/*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
VCPKG_COMMIT: 2ad004460f5db4d3b66f62f5799ff66c265c4b5d
|
||||
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||
# MELONDS_VERSION_SUFFIX: " RC"
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
|
@ -33,7 +35,7 @@ jobs:
|
|||
- name: Set up vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
vcpkgGitCommitId: 10b7a178346f3f0abef60cecd5130e295afd8da4
|
||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||
- name: Build
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
- ci/*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
@ -12,80 +13,51 @@ env:
|
|||
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||
# MELONDS_VERSION_SUFFIX: " RC"
|
||||
|
||||
jobs:
|
||||
build-x86_64:
|
||||
name: x86_64
|
||||
runs-on: ubuntu-22.04
|
||||
build:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- runner: ubuntu-22.04
|
||||
name: x86_64
|
||||
- runner: ubuntu-22.04-arm
|
||||
name: aarch64
|
||||
|
||||
name: ${{ matrix.arch.name }}
|
||||
runs-on: ${{ matrix.arch.runner }}
|
||||
|
||||
steps:
|
||||
- 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 --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \
|
||||
qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2
|
||||
- name: Configure
|
||||
run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON
|
||||
run: cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build
|
||||
DESTDIR=AppDir cmake --install build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-ubuntu-x86_64
|
||||
name: melonDS-ubuntu-${{ matrix.arch.name }}
|
||||
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
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.arch.name }}.AppImage
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch.name }}.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
|
||||
./linuxdeploy-${{ matrix.arch.name }}.AppImage --appdir AppDir --plugin qt --output appimage
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-appimage-x86_64
|
||||
name: melonDS-appimage-${{ matrix.arch.name }}
|
||||
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},libqt6svg6,libarchive,libzstd,libenet}-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 \
|
||||
-DMELONDS_EMBED_BUILD_INFO=ON
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-ubuntu-aarch64
|
||||
path: build/melonDS
|
||||
|
|
|
@ -10,9 +10,11 @@ on:
|
|||
- master
|
||||
|
||||
env:
|
||||
VCPKG_COMMIT: 2ad004460f5db4d3b66f62f5799ff66c265c4b5d
|
||||
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||
# MELONDS_VERSION_SUFFIX: " RC"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -32,7 +34,7 @@ jobs:
|
|||
- name: Set up vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
vcpkgGitCommitId: 10b7a178346f3f0abef60cecd5130e295afd8da4
|
||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||
- name: Configure
|
||||
run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON
|
||||
- name: Build
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
# Building melonDS
|
||||
|
||||
* [Linux](#linux)
|
||||
* [Windows](#windows)
|
||||
* [macOS](#macos)
|
||||
|
||||
## Linux
|
||||
1. Install dependencies:
|
||||
* Ubuntu:
|
||||
* All versions: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libarchive-dev libenet-dev libzstd-dev`
|
||||
* 24.04: `sudo apt install qt6-{base,base-private,multimedia,svg}-dev`
|
||||
* 22.04: `sudo apt install qtbase6-dev qtbase6-private-dev qtmultimedia6-dev libqt6svg6-dev`
|
||||
* Older versions: `sudo apt install qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev`
|
||||
Also add `-DUSE_QT6=OFF` to the first CMake command below.
|
||||
* Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtbase-private,qtmultimedia,qtsvg}-devel wayland-devel`
|
||||
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt6-{base,multimedia,svg} libarchive enet zstd`
|
||||
2. Download the melonDS repository and prepare:
|
||||
```bash
|
||||
git clone https://github.com/melonDS-emu/melonDS
|
||||
cd melonDS
|
||||
```
|
||||
3. Compile:
|
||||
```bash
|
||||
cmake -B build
|
||||
cmake --build build -j$(nproc --all)
|
||||
```
|
||||
|
||||
## Windows
|
||||
1. Install [MSYS2](https://www.msys2.org/)
|
||||
2. Open the MSYS2 terminal from the Start menu:
|
||||
* For x64 systems (most common), use **MSYS2 UCRT64**
|
||||
* For ARM64 systems, use **MSYS2 CLANGARM64**
|
||||
3. Update the packages using `pacman -Syu` and reopen the same terminal if it asks you to
|
||||
4. Install git and clone the repository
|
||||
```bash
|
||||
pacman -S git
|
||||
git clone https://github.com/melonDS-emu/melonDS
|
||||
cd melonDS
|
||||
```
|
||||
5. Install dependencies:
|
||||
Replace `<prefix>` below with `mingw-w64-ucrt-x86_64` on x64 systems, or `mingw-w64-clang-aarch64` on ARM64 systems.
|
||||
```bash
|
||||
pacman -S <prefix>-{toolchain,cmake,SDL2,libarchive,enet,zstd}
|
||||
```
|
||||
6. Install Qt and configure the build directory
|
||||
* Dynamic builds (with DLLs)
|
||||
1. Install Qt: `pacman -S <prefix>-{qt6-base,qt6-svg,qt6-multimedia,qt6-svg,qt6-tools}`
|
||||
2. Set up the build directory with `cmake -B build`
|
||||
* Static builds (without DLLs, standalone executable)
|
||||
1. Install Qt: `pacman -S <prefix>-qt5-static`
|
||||
(Note: As of writing, the `qt6-static` package does not work.)
|
||||
2. Set up the build directory with `cmake -B build -DBUILD_STATIC=ON -DUSE_QT6=OFF -DCMAKE_PREFIX_PATH=$MSYSTEM_PREFIX/qt5-static`
|
||||
7. Compile: `cmake --build build`
|
||||
|
||||
If everything went well, melonDS should now be in the `build` folder. For dynamic builds, you may need to run melonDS from the MSYS2 terminal in order for it to find the required DLLs.
|
||||
|
||||
## macOS
|
||||
1. Install the [Homebrew Package Manager](https://brew.sh)
|
||||
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd`
|
||||
3. Download the melonDS repository and prepare:
|
||||
```zsh
|
||||
git clone https://github.com/melonDS-emu/melonDS
|
||||
cd melonDS
|
||||
```
|
||||
4. Compile:
|
||||
```zsh
|
||||
cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)"
|
||||
cmake --build build -j$(sysctl -n hw.logicalcpu)
|
||||
```
|
||||
If everything went well, melonDS.app should now be in the `build` directory.
|
||||
|
||||
### Self-contained app bundle
|
||||
If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run `
|
||||
../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
|
||||
|
||||
## Nix (macOS/Linux)
|
||||
|
||||
melonDS provides a Nix flake with support for both macOS and Linux. The [Nix package manager](https://nixos.org) needs to be installed to use it.
|
||||
|
||||
* To run melonDS, just type `nix run github:melonDS-emu/melonDS`.
|
||||
* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory.
|
|
@ -9,6 +9,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
|
|||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
|
||||
if (USE_VCPKG)
|
||||
|
@ -16,7 +17,7 @@ if (USE_VCPKG)
|
|||
endif()
|
||||
|
||||
project(melonDS
|
||||
VERSION 0.9.5
|
||||
VERSION 1.0
|
||||
DESCRIPTION "DS emulator, sorta"
|
||||
HOMEPAGE_URL "https://melonds.kuribo64.net"
|
||||
LANGUAGES C CXX)
|
||||
|
@ -29,8 +30,6 @@ include(CheckIPOSupported)
|
|||
include(SetupCCache)
|
||||
include(Sanitizers)
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
|
|
@ -27,10 +27,6 @@
|
|||
"binaryDir": "${sourceDir}/build/release-mingw-x86_64",
|
||||
"generator": "Ninja",
|
||||
"cacheVariables": {
|
||||
"USE_QT6": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"BUILD_STATIC": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
|
|
72
README.md
72
README.md
|
@ -2,7 +2,7 @@
|
|||
<h2 align="center"><b>melonDS</b></h2>
|
||||
<p align="center">
|
||||
<a href="http://melonds.kuribo64.net/" alt="melonDS website"><img src="https://img.shields.io/badge/website-melonds.kuribo64.net-%2331352e.svg"></a>
|
||||
<a href="http://melonds.kuribo64.net/downloads.php" alt="Release: 0.9.5"><img src="https://img.shields.io/badge/release-0.9.5-%235c913b.svg"></a>
|
||||
<a href="http://melonds.kuribo64.net/downloads.php" alt="Release: 1.0"><img src="https://img.shields.io/badge/release-1.0-%235c913b.svg"></a>
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/License-GPL%20v3-%23ff554d.svg"></a>
|
||||
<a href="https://kiwiirc.com/client/irc.badnik.net/?nick=IRC-Source_?#melonds" alt="IRC channel: #melonds"><img src="https://img.shields.io/badge/IRC%20chat-%23melonds-%23dd2e44.svg"></a>
|
||||
<a href="https://discord.gg/pAMAtExcqV" alt="Discord"><img src="https://img.shields.io/badge/Discord-Kuribo64-7289da?logo=discord&logoColor=white"></a>
|
||||
|
@ -32,75 +32,7 @@ DS BIOS dumps from a DSi or 3DS can be used with no compatibility issues. DSi BI
|
|||
As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though!
|
||||
|
||||
## How to build
|
||||
|
||||
### 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 libqt5svg5-dev libarchive-dev libenet-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 libqt5svg5-dev libarchive-dev libenet-dev libzstd-dev`
|
||||
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia qt5-svg libarchive enet zstd`
|
||||
3. Download the melonDS repository and prepare:
|
||||
```bash
|
||||
git clone https://github.com/melonDS-emu/melonDS
|
||||
cd melonDS
|
||||
```
|
||||
|
||||
3. Compile:
|
||||
```bash
|
||||
cmake -B build
|
||||
cmake --build build -j$(nproc --all)
|
||||
```
|
||||
|
||||
### Windows
|
||||
1. Install [MSYS2](https://www.msys2.org/)
|
||||
2. Open the **MSYS2 MinGW 64-bit** terminal
|
||||
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to
|
||||
4. Install git to clone the repository
|
||||
```bash
|
||||
pacman -S git
|
||||
```
|
||||
5. Download the melonDS repository and prepare:
|
||||
```bash
|
||||
git clone https://github.com/melonDS-emu/melonDS
|
||||
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-svg,qt5-tools,libarchive,enet,zstd}`
|
||||
6. Compile:
|
||||
```bash
|
||||
cmake -B build
|
||||
cmake --build build
|
||||
cd build
|
||||
../tools/msys-dist.sh
|
||||
```
|
||||
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,libarchive,enet,zstd}`
|
||||
6. Compile:
|
||||
```bash
|
||||
cmake -B build -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static
|
||||
cmake --build build
|
||||
```
|
||||
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 libarchive enet zstd`
|
||||
3. Download the melonDS repository and prepare:
|
||||
```zsh
|
||||
git clone https://github.com/melonDS-emu/melonDS
|
||||
cd melonDS
|
||||
```
|
||||
4. Compile:
|
||||
```zsh
|
||||
cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)"
|
||||
cmake --build build -j$(sysctl -n hw.logicalcpu)
|
||||
```
|
||||
If everything went well, melonDS.app should now be in the `build` directory.
|
||||
|
||||
#### Self-contained app bundle
|
||||
If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run `
|
||||
../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
|
||||
See [BUILD.md](./BUILD.md) for build instructions.
|
||||
|
||||
## TODO LIST
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
|
|||
endif()
|
||||
FetchContent_Declare(vcpkg
|
||||
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
|
||||
GIT_TAG 2024.10.21
|
||||
GIT_TAG 2ad004460f5db4d3b66f62f5799ff66c265c4b5d
|
||||
EXCLUDE_FROM_ALL
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||
FetchContent_MakeAvailable(vcpkg)
|
||||
endif()
|
||||
|
@ -19,11 +20,7 @@ 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)
|
||||
|
||||
# Duplicated here because it needs to be set before project()
|
||||
if (NOT WIN32)
|
||||
option(USE_QT6 "Build using Qt 6 instead of 5" ON)
|
||||
else()
|
||||
option(USE_QT6 "Build using Qt 6 instead of 5" OFF)
|
||||
endif()
|
||||
option(USE_QT6 "Use Qt 6 instead of Qt 5" ON)
|
||||
|
||||
# Since the Linux build pulls in glib anyway, we can just use upstream libslirp
|
||||
if (UNIX AND NOT APPLE)
|
||||
|
|
|
@ -7,3 +7,9 @@ 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}")
|
||||
|
||||
# Building with LTO causes an internal compiler error in GCC 15.1.0
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.1.1)
|
||||
set(ENABLE_LTO_RELEASE OFF CACHE BOOL "Enable LTO for release builds" FORCE)
|
||||
set(ENABLE_LTO OFF CACHE BOOL "Enable LTO" FORCE)
|
||||
endif()
|
||||
|
|
12
flake.lock
12
flake.lock
|
@ -5,11 +5,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1729665710,
|
||||
"narHash": "sha256-AlcmCXJZPIlO5dmFzV3V2XF6x/OpNWUV8Y/FMPGd8Z4=",
|
||||
"lastModified": 1739020877,
|
||||
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2768c7d042a37de65bb1b5b3268fc987e534c49d",
|
||||
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
15
flake.nix
15
flake.nix
|
@ -19,9 +19,9 @@
|
|||
then sourceInfo.dirtyShortRev
|
||||
else sourceInfo.shortRev;
|
||||
|
||||
melonDS = pkgs.qt6.qtbase.stdenv.mkDerivation {
|
||||
melonDS = pkgs.stdenv.mkDerivation {
|
||||
pname = "melonDS";
|
||||
version = "0.9.5-${shortRevision}";
|
||||
version = "1.0-${shortRevision}";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
|
@ -74,8 +74,11 @@
|
|||
drv = self.packages.${system}.default;
|
||||
};
|
||||
devShells = {
|
||||
default = pkgs.mkShell.override { stdenv = pkgs.qt6.qtbase.stdenv; } {
|
||||
default = pkgs.mkShell {
|
||||
inputsFrom = [ self.packages.${system}.default ];
|
||||
packages = with pkgs; [
|
||||
qt6.qttools
|
||||
];
|
||||
};
|
||||
|
||||
# Shell for building static melonDS release builds with vcpkg
|
||||
|
@ -92,7 +95,13 @@
|
|||
libtool
|
||||
ninja
|
||||
pkg-config
|
||||
python3
|
||||
];
|
||||
|
||||
# Undo the SDK setup done by nixpkgs so we can use AppleClang
|
||||
shellHook = ''
|
||||
unset DEVELOPER_DIR SDKROOT MACOSX_DEPLOYMENT_TARGET
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
AS = arm-none-eabi-as
|
||||
LD = arm-none-eabi-ld
|
||||
OBJCOPY = arm-none-eabi-objcopy
|
||||
|
||||
BIN = melonDLDI
|
||||
|
||||
all:
|
||||
$(AS) $(BIN).s -o $(BIN).o
|
||||
$(LD) $(BIN).o -Ttext 0xBF800000 -e 0xBF800000 -o $(BIN).elf
|
||||
$(OBJCOPY) -O binary $(BIN).elf $(BIN).bin
|
||||
xxd -i -n $(BIN) -c 16 $(BIN).bin $(BIN).h
|
||||
|
||||
clean:
|
||||
rm -f $(BIN).h $(BIN).bin $(BIN).elf $(BIN).o
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
.arm
|
||||
.text
|
||||
|
||||
.align 2
|
||||
|
||||
_start:
|
||||
.word 0xBF8DA5ED
|
||||
.string " Chishm"
|
||||
.byte 1
|
||||
.byte 9 @ size
|
||||
.byte 0
|
||||
.byte 0
|
||||
.string "melonDS DLDI driver"
|
||||
.align 6, 0
|
||||
.word _start, melon_end
|
||||
.word 0, 0
|
||||
.word 0, 0
|
||||
.word 0, 0
|
||||
.ascii "MELN"
|
||||
.word 0x23
|
||||
.word melon_startup
|
||||
.word melon_isInserted
|
||||
.word melon_readSectors
|
||||
.word melon_writeSectors
|
||||
.word melon_clearStatus
|
||||
.word melon_shutdown
|
||||
|
||||
|
||||
.align 2
|
||||
|
||||
melon_startup:
|
||||
mov r0, #1
|
||||
bx lr
|
||||
|
||||
|
||||
melon_isInserted:
|
||||
mov r0, #1
|
||||
bx lr
|
||||
|
||||
|
||||
@ r0=cmd r1=sector r2=out (0=none)
|
||||
_sendcmd:
|
||||
mov r12, #0x04000000
|
||||
add r12, r12, #0x1A0
|
||||
@ init
|
||||
mov r3, #0x8000
|
||||
strh r3, [r12]
|
||||
@ set cmd
|
||||
strb r0, [r12, #0x8]
|
||||
strb r1, [r12, #0xC]
|
||||
mov r1, r1, lsr #8
|
||||
strb r1, [r12, #0xB]
|
||||
mov r1, r1, lsr #8
|
||||
strb r1, [r12, #0xA]
|
||||
mov r1, r1, lsr #8
|
||||
strb r1, [r12, #0x9]
|
||||
mov r1, r1, lsr #8
|
||||
strb r1, [r12, #0xD]
|
||||
strh r1, [r12, #0xE]
|
||||
@ send
|
||||
mov r3, #0xA0000000
|
||||
orr r3, r3, r0, lsl #30
|
||||
cmp r2, #0
|
||||
orrne r3, r3, #0x01000000 @ block size
|
||||
orr r3, r3, #0x00400000 @ KEY2
|
||||
str r3, [r12, #0x4]
|
||||
mov r3, #0x04100000
|
||||
tst r0, #0x01
|
||||
bne __send_write
|
||||
@ receive data
|
||||
tst r2, #0x3
|
||||
bne __read_unal_loop
|
||||
__read_busyloop:
|
||||
ldr r0, [r12, #0x4]
|
||||
tst r0, #0x80000000
|
||||
bxeq lr
|
||||
tst r0, #0x00800000
|
||||
ldrne r1, [r3, #0x10] @ load data
|
||||
strne r1, [r2], #4
|
||||
b __read_busyloop
|
||||
__read_unal_loop:
|
||||
ldr r0, [r12, #0x4]
|
||||
tst r0, #0x80000000
|
||||
bxeq lr
|
||||
tst r0, #0x00800000
|
||||
beq __read_unal_loop
|
||||
ldr r1, [r3, #0x10] @ load data
|
||||
strb r1, [r2], #1
|
||||
mov r1, r1, lsr #8
|
||||
strb r1, [r2], #1
|
||||
mov r1, r1, lsr #8
|
||||
strb r1, [r2], #1
|
||||
mov r1, r1, lsr #8
|
||||
strb r1, [r2], #1
|
||||
b __read_unal_loop
|
||||
@ send data
|
||||
__send_write:
|
||||
mov r1, #0
|
||||
tst r2, #0x3
|
||||
bne __write_unal_loop
|
||||
__write_busyloop:
|
||||
ldr r0, [r12, #0x4]
|
||||
tst r0, #0x80000000
|
||||
bxeq lr
|
||||
tst r0, #0x00800000
|
||||
ldrne r1, [r2], #4
|
||||
strne r1, [r3, #0x10] @ store data
|
||||
b __write_busyloop
|
||||
__write_unal_loop:
|
||||
ldr r0, [r12, #0x4]
|
||||
tst r0, #0x80000000
|
||||
bxeq lr
|
||||
tst r0, #0x00800000
|
||||
beq __write_unal_loop
|
||||
ldrb r1, [r2], #1
|
||||
ldrb r0, [r2], #1
|
||||
orr r1, r1, r0, lsl #8
|
||||
ldrb r0, [r2], #1
|
||||
orr r1, r1, r0, lsl #16
|
||||
ldrb r0, [r2], #1
|
||||
orr r1, r1, r0, lsl #24
|
||||
str r1, [r3, #0x10] @ store data
|
||||
b __write_unal_loop
|
||||
|
||||
|
||||
@ r0=sector r1=numsectors r2=out
|
||||
melon_readSectors:
|
||||
stmdb sp!, {r3-r6, lr}
|
||||
mov r4, r0
|
||||
mov r5, r1
|
||||
mov r6, #0
|
||||
_readloop:
|
||||
mov r0, #0xC0
|
||||
add r1, r4, r6
|
||||
bl _sendcmd
|
||||
add r6, r6, #1
|
||||
cmp r6, r5
|
||||
bcc _readloop
|
||||
ldmia sp!, {r3-r6, lr}
|
||||
mov r0, #1
|
||||
bx lr
|
||||
|
||||
|
||||
@ r0=sector r1=numsectors r2=out
|
||||
melon_writeSectors:
|
||||
stmdb sp!, {r3-r6, lr}
|
||||
mov r4, r0
|
||||
mov r5, r1
|
||||
mov r6, #0
|
||||
_writeloop:
|
||||
mov r0, #0xC1
|
||||
add r1, r4, r6
|
||||
bl _sendcmd
|
||||
add r6, r6, #1
|
||||
cmp r6, r5
|
||||
bcc _writeloop
|
||||
ldmia sp!, {r3-r6, lr}
|
||||
mov r0, #1
|
||||
bx lr
|
||||
|
||||
|
||||
melon_clearStatus:
|
||||
mov r0, #1
|
||||
bx lr
|
||||
|
||||
|
||||
melon_shutdown:
|
||||
mov r0, #1
|
||||
bx lr
|
||||
|
||||
|
||||
melon_end:
|
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
|
@ -2,6 +2,6 @@
|
|||
<RCC version="1.0">
|
||||
<qresource>
|
||||
<file alias="melon-icon">icon/melon_256x256.png</file>
|
||||
<file alias="melon-logo">melon.svg</file>
|
||||
<file alias="melon-logo">melon384.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
//include version information in .exe, modify these values to match your needs
|
||||
1 VERSIONINFO
|
||||
FILEVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
|
||||
PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
|
||||
FILEVERSION ${MELON_RC_VERSION}
|
||||
PRODUCTVERSION ${MELON_RC_VERSION}
|
||||
FILETYPE VFT_APP
|
||||
{
|
||||
BLOCK "StringFileInfo"
|
||||
|
@ -18,7 +18,7 @@ FILETYPE VFT_APP
|
|||
VALUE "FileVersion", "${melonDS_VERSION}"
|
||||
VALUE "FileDescription", "melonDS emulator"
|
||||
VALUE "InternalName", "SDnolem"
|
||||
VALUE "LegalCopyright", "2016-2023 melonDS team"
|
||||
VALUE "LegalCopyright", "2016-2025 melonDS team"
|
||||
VALUE "LegalTrademarks", ""
|
||||
VALUE "OriginalFilename", "melonDS.exe"
|
||||
VALUE "ProductName", "melonDS"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -58,7 +58,8 @@ void AREngine::RunCheat(const ARCode& arcode)
|
|||
|
||||
for (;;)
|
||||
{
|
||||
if (code >= &arcode.Code[arcode.Code.size()])
|
||||
if (code > &arcode.Code[arcode.Code.size() - 1])
|
||||
// If the instruction pointer is past the end of the cheat code...
|
||||
break;
|
||||
|
||||
u32 a = *code++;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
30
src/ARM.cpp
30
src/ARM.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -109,21 +109,13 @@ const u32 ARM::ConditionTable[16] =
|
|||
|
||||
ARM::ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, melonDS::NDS& nds) :
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0),
|
||||
BreakOnStartup(gdb ? (num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup) : false),
|
||||
GdbStub(this),
|
||||
BreakOnStartup(false),
|
||||
#endif
|
||||
Num(num), // well uh
|
||||
NDS(nds)
|
||||
{
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
if (gdb
|
||||
#ifdef JIT_ENABLED
|
||||
&& !jit // TODO: Should we support toggling the GdbStub without destroying the ARM?
|
||||
#endif
|
||||
)
|
||||
GdbStub.Init();
|
||||
IsSingleStep = false;
|
||||
#endif
|
||||
SetGdbArgs(jit ? std::nullopt : gdb);
|
||||
}
|
||||
|
||||
ARM::~ARM()
|
||||
|
@ -148,6 +140,20 @@ ARMv5::~ARMv5()
|
|||
// DTCM is owned by Memory, not going to delete it
|
||||
}
|
||||
|
||||
void ARM::SetGdbArgs(std::optional<GDBArgs> gdb)
|
||||
{
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
GdbStub.Close();
|
||||
if (gdb)
|
||||
{
|
||||
int port = Num ? gdb->PortARM7 : gdb->PortARM9;
|
||||
GdbStub.Init(port);
|
||||
BreakOnStartup = Num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup;
|
||||
}
|
||||
IsSingleStep = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ARM::Reset()
|
||||
{
|
||||
Cycles = 0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -68,6 +68,8 @@ public:
|
|||
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
|
||||
virtual ~ARM(); // destroy shit
|
||||
|
||||
void SetGdbArgs(std::optional<GDBArgs> gdb);
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual void DoSavestate(Savestate* file);
|
||||
|
@ -323,6 +325,7 @@ public:
|
|||
u32 CP15Control;
|
||||
|
||||
u32 RNGSeed;
|
||||
u32 TraceProcessID;
|
||||
|
||||
u32 DTCMSetting, ITCMSetting;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
|||
#include "ARMJIT_Internal.h"
|
||||
#include "ARMJIT_Memory.h"
|
||||
#include "ARMJIT_Compiler.h"
|
||||
#include "ARMJIT_Global.h"
|
||||
|
||||
#include "ARMInterpreter_ALU.h"
|
||||
#include "ARMInterpreter_LoadStore.h"
|
||||
|
@ -467,6 +468,16 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
|
|||
};
|
||||
#undef F
|
||||
|
||||
ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> 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_Memory::IsFastMemSupported())
|
||||
{}
|
||||
|
||||
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
|
||||
{
|
||||
auto it = RestoreCandidates.find(block->InstrHash);
|
||||
|
@ -483,6 +494,7 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
|
|||
|
||||
void ARMJIT::SetJITArgs(JITArgs args) noexcept
|
||||
{
|
||||
args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported();
|
||||
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
|
||||
|
||||
if (MaxBlockSize != args.MaxBlockSize
|
||||
|
@ -499,36 +511,22 @@ void ARMJIT::SetJITArgs(JITArgs args) noexcept
|
|||
|
||||
void ARMJIT::SetMaxBlockSize(int size) noexcept
|
||||
{
|
||||
size = std::clamp(size, 1, 32);
|
||||
|
||||
if (size != MaxBlockSize)
|
||||
ResetBlockCache();
|
||||
|
||||
MaxBlockSize = size;
|
||||
SetJITArgs(JITArgs{static_cast<unsigned>(size), LiteralOptimizations, LiteralOptimizations, FastMemory});
|
||||
}
|
||||
|
||||
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
|
||||
{
|
||||
if (LiteralOptimizations != enabled)
|
||||
ResetBlockCache();
|
||||
|
||||
LiteralOptimizations = enabled;
|
||||
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), enabled, BranchOptimizations, FastMemory});
|
||||
}
|
||||
|
||||
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
|
||||
{
|
||||
if (BranchOptimizations != enabled)
|
||||
ResetBlockCache();
|
||||
|
||||
BranchOptimizations = enabled;
|
||||
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, enabled, FastMemory});
|
||||
}
|
||||
|
||||
void ARMJIT::SetFastMemory(bool enabled) noexcept
|
||||
{
|
||||
if (FastMemory != enabled)
|
||||
ResetBlockCache();
|
||||
|
||||
FastMemory = enabled;
|
||||
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled});
|
||||
}
|
||||
|
||||
void ARMJIT::CompileBlock(ARM* cpu) noexcept
|
||||
|
@ -918,7 +916,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
|
|||
|
||||
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
|
||||
|
||||
if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512]))
|
||||
if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
|
||||
|
||||
AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512];
|
||||
|
@ -971,7 +969,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
|
|||
range->Blocks.Remove(i);
|
||||
|
||||
if (range->Blocks.Length == 0
|
||||
&& !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512]))
|
||||
&& !PageContainsCode(®ion[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||
{
|
||||
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
|
||||
}
|
||||
|
@ -1005,7 +1003,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
|
|||
|
||||
if (otherRange->Blocks.Length == 0)
|
||||
{
|
||||
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512]))
|
||||
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
|
||||
|
||||
otherRange->Code = 0;
|
||||
|
|
13
src/ARMJIT.h
13
src/ARMJIT.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -44,15 +44,7 @@ class JitBlock;
|
|||
class ARMJIT
|
||||
{
|
||||
public:
|
||||
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> 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(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept;
|
||||
~ARMJIT() noexcept;
|
||||
void InvalidateByAddr(u32) noexcept;
|
||||
void CheckAndInvalidateWVRAM(int) noexcept;
|
||||
|
@ -80,6 +72,7 @@ private:
|
|||
bool LiteralOptimizations = false;
|
||||
bool BranchOptimizations = false;
|
||||
bool FastMemory = false;
|
||||
|
||||
public:
|
||||
melonDS::NDS& NDS;
|
||||
TinyVector<u32> InvalidLiterals {};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -83,7 +83,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
|||
// doesn't matter if we put garbage in the MSbs there
|
||||
if (addr & 0x2)
|
||||
{
|
||||
cpu9->CodeRead32(addr-2, true) >> 16;
|
||||
cpu9->CodeRead32(addr-2, true);
|
||||
cycles += cpu9->CodeCycles;
|
||||
cpu9->CodeRead32(addr+2, false);
|
||||
cycles += CurCPU->CodeCycles;
|
||||
|
@ -437,4 +437,4 @@ void Compiler::T_Comp_BL_Merged()
|
|||
Comp_JumpTo(target);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,17 +22,7 @@
|
|||
#include "../ARMInterpreter.h"
|
||||
#include "../ARMJIT.h"
|
||||
#include "../NDS.h"
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
#include <switch.h>
|
||||
|
||||
extern char __start__;
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "../ARMJIT_Global.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -66,11 +56,6 @@ const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
|
|||
|
||||
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
|
||||
|
||||
const int JitMemSize = 16 * 1024 * 1024;
|
||||
#ifndef __SWITCH__
|
||||
u8 JitMem[JitMemSize];
|
||||
#endif
|
||||
|
||||
void Compiler::MovePC()
|
||||
{
|
||||
ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4);
|
||||
|
@ -260,29 +245,13 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
|
|||
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
|
||||
JitMemMainSize = JitMemSize;
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
ARMJIT_Global::Init();
|
||||
|
||||
u64 pageSize = (u64)sysInfo.dwPageSize;
|
||||
#else
|
||||
u64 pageSize = sysconf(_SC_PAGE_SIZE);
|
||||
#endif
|
||||
u8* pageAligned = (u8*)(((u64)JitMem & ~(pageSize - 1)) + pageSize);
|
||||
u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
|
||||
CodeMemBase = ARMJIT_Global::AllocateCodeMem();
|
||||
nds.JIT.JitEnableWrite();
|
||||
|
||||
#if defined(_WIN32)
|
||||
DWORD dummy;
|
||||
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);
|
||||
nds.JIT.JitEnableWrite();
|
||||
#else
|
||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
SetCodeBase(pageAligned, pageAligned);
|
||||
JitMemMainSize = alignedSize;
|
||||
SetCodeBase(reinterpret_cast<u8*>(CodeMemBase), reinterpret_cast<u8*>(CodeMemBase));
|
||||
JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize;
|
||||
#endif
|
||||
SetCodePtr(0);
|
||||
|
||||
|
@ -493,6 +462,9 @@ Compiler::~Compiler()
|
|||
free(JitRWBase);
|
||||
}
|
||||
#endif
|
||||
|
||||
ARMJIT_Global::FreeCodeMem(CodeMemBase);
|
||||
ARMJIT_Global::DeInit();
|
||||
}
|
||||
|
||||
void Compiler::LoadCycles()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -275,6 +275,7 @@ public:
|
|||
void* JitRWStart;
|
||||
void* JitRXStart;
|
||||
#endif
|
||||
void* CodeMemBase;
|
||||
|
||||
void* ReadBanked, *WriteBanked;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
#include "ARMJIT_Global.h"
|
||||
#include "ARMJIT_Memory.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
namespace ARMJIT_Global
|
||||
{
|
||||
|
||||
std::mutex globalMutex;
|
||||
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
#define APPLE_AARCH64
|
||||
#endif
|
||||
|
||||
#ifndef APPLE_AARCH64
|
||||
static constexpr size_t NumCodeMemSlices = 4;
|
||||
static constexpr size_t CodeMemoryAlignedSize = NumCodeMemSlices * CodeMemorySliceSize;
|
||||
|
||||
// I haven't heard of pages larger than 16 KB
|
||||
u8 CodeMemory[CodeMemoryAlignedSize + 16*1024];
|
||||
|
||||
u32 AvailableCodeMemSlices = (1 << NumCodeMemSlices) - 1;
|
||||
|
||||
u8* GetAlignedCodeMemoryStart()
|
||||
{
|
||||
return reinterpret_cast<u8*>((reinterpret_cast<intptr_t>(CodeMemory) + (16*1024-1)) & ~static_cast<intptr_t>(16*1024-1));
|
||||
}
|
||||
#endif
|
||||
|
||||
int RefCounter = 0;
|
||||
|
||||
void* AllocateCodeMem()
|
||||
{
|
||||
std::lock_guard guard(globalMutex);
|
||||
|
||||
#ifndef APPLE_AARCH64
|
||||
if (AvailableCodeMemSlices)
|
||||
{
|
||||
int slice = __builtin_ctz(AvailableCodeMemSlices);
|
||||
AvailableCodeMemSlices &= ~(1 << slice);
|
||||
//printf("allocating slice %d\n", slice);
|
||||
return &GetAlignedCodeMemoryStart()[slice * CodeMemorySliceSize];
|
||||
}
|
||||
#endif
|
||||
|
||||
// allocate
|
||||
#ifdef _WIN32
|
||||
return VirtualAlloc(nullptr, CodeMemorySliceSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#elif defined(APPLE_AARCH64)
|
||||
return mmap(NULL, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
|
||||
#else
|
||||
//printf("mmaping...\n");
|
||||
return mmap(nullptr, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FreeCodeMem(void* codeMem)
|
||||
{
|
||||
std::lock_guard guard(globalMutex);
|
||||
|
||||
#ifndef APPLE_AARCH64
|
||||
for (int i = 0; i < NumCodeMemSlices; i++)
|
||||
{
|
||||
if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i])
|
||||
{
|
||||
//printf("freeing slice\n");
|
||||
AvailableCodeMemSlices |= 1 << i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
VirtualFree(codeMem, CodeMemorySliceSize, MEM_RELEASE|MEM_DECOMMIT);
|
||||
#else
|
||||
munmap(codeMem, CodeMemorySliceSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
std::lock_guard guard(globalMutex);
|
||||
|
||||
RefCounter++;
|
||||
if (RefCounter == 1)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD dummy;
|
||||
VirtualProtect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
||||
#elif defined(APPLE_AARCH64)
|
||||
// Apple aarch64 always uses dynamic allocation
|
||||
#else
|
||||
mprotect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
ARMJIT_Memory::RegisterFaultHandler();
|
||||
}
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
std::lock_guard guard(globalMutex);
|
||||
|
||||
RefCounter--;
|
||||
if (RefCounter == 0)
|
||||
{
|
||||
ARMJIT_Memory::UnregisterFaultHandler();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright 2016-2025 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 ARMJIT_GLOBAL_H
|
||||
#define ARMJIT_GLOBAL_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
namespace ARMJIT_Global
|
||||
{
|
||||
|
||||
static constexpr size_t CodeMemorySliceSize = 1024*1024*32;
|
||||
|
||||
void Init();
|
||||
void DeInit();
|
||||
|
||||
void* AllocateCodeMem();
|
||||
void FreeCodeMem(void* codeMem);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -85,9 +85,9 @@ typedef void (*InterpreterFunc)(ARM* cpu);
|
|||
extern InterpreterFunc InterpretARM[];
|
||||
extern InterpreterFunc InterpretTHUMB[];
|
||||
|
||||
inline bool PageContainsCode(const AddressRange* range)
|
||||
inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int i = 0; i < pageSize / 512; i++)
|
||||
{
|
||||
if (range[i].Blocks.Length > 0)
|
||||
return true;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
|||
|
||||
#include "ARMJIT_Internal.h"
|
||||
#include "ARMJIT_Compiler.h"
|
||||
#include "ARMJIT_Global.h"
|
||||
|
||||
#include "DSi.h"
|
||||
#include "GPU.h"
|
||||
|
@ -100,6 +101,9 @@
|
|||
namespace melonDS
|
||||
{
|
||||
|
||||
static constexpr u64 AddrSpaceSize = 0x100000000;
|
||||
static constexpr u64 VirtmemAreaSize = AddrSpaceSize * 2 + MemoryTotalSize;
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
|
@ -152,6 +156,15 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx)
|
|||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
static LPVOID ExceptionHandlerHandle = nullptr;
|
||||
static HMODULE KernelBaseDll = nullptr;
|
||||
|
||||
using VirtualAlloc2Type = PVOID WINAPI (*)(HANDLE Process, PVOID BaseAddress, SIZE_T Size, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
|
||||
using MapViewOfFile3Type = PVOID WINAPI (*)(HANDLE FileMapping, HANDLE Process, PVOID BaseAddress, ULONG64 Offset, SIZE_T ViewSize, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
|
||||
|
||||
static VirtualAlloc2Type virtualAlloc2Ptr;
|
||||
static MapViewOfFile3Type mapViewOfFile3Ptr;
|
||||
|
||||
LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
|
||||
{
|
||||
if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
||||
|
@ -170,6 +183,7 @@ LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
|
|||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
Log(LogLevel::Debug, "it all returns to nothing\n");
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
@ -261,18 +275,61 @@ enum
|
|||
memstate_MappedProtected,
|
||||
};
|
||||
|
||||
|
||||
#define CHECK_ALIGNED(value) assert(((value) & (PageSize-1)) == 0)
|
||||
|
||||
bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
|
||||
{
|
||||
CHECK_ALIGNED(addr);
|
||||
CHECK_ALIGNED(offset);
|
||||
CHECK_ALIGNED(size);
|
||||
|
||||
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
||||
#ifdef __SWITCH__
|
||||
Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(),
|
||||
(u64)(MemoryBaseCodeMem + offset), size));
|
||||
return R_SUCCEEDED(r);
|
||||
#elif defined(_WIN32)
|
||||
bool r = MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, offset, size, dst) == dst;
|
||||
return r;
|
||||
uintptr_t uintptrDst = reinterpret_cast<uintptr_t>(dst);
|
||||
for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end(); it++)
|
||||
{
|
||||
if (uintptrDst >= it->Start && uintptrDst+size <= it->Start+it->Size)
|
||||
{
|
||||
//Log(LogLevel::Debug, "found mapping %llx %llx %llx %llx\n", uintptrDst, size, it->Start, it->Size);
|
||||
// we split this place holder so that we have a fitting place holder for the mapping
|
||||
if (uintptrDst != it->Start || size != it->Size)
|
||||
{
|
||||
if (!VirtualFree(dst, size, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER))
|
||||
{
|
||||
Log(LogLevel::Debug, "VirtualFree failed with %x\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VirtmemPlaceholder splitPlaceholder = *it;
|
||||
VirtmemPlaceholders.erase(it);
|
||||
if (uintptrDst > splitPlaceholder.Start)
|
||||
{
|
||||
//Log(LogLevel::Debug, "splitting on the left %llx\n", uintptrDst - splitPlaceholder.Start);
|
||||
VirtmemPlaceholders.push_back({splitPlaceholder.Start, uintptrDst - splitPlaceholder.Start});
|
||||
}
|
||||
if (uintptrDst+size < splitPlaceholder.Start+splitPlaceholder.Size)
|
||||
{
|
||||
//Log(LogLevel::Debug, "splitting on the right %llx\n", (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size));
|
||||
VirtmemPlaceholders.push_back({uintptrDst+size, (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size)});
|
||||
}
|
||||
|
||||
if (!mapViewOfFile3Ptr(MemoryFile, nullptr, dst, offset, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0))
|
||||
{
|
||||
Log(LogLevel::Debug, "MapViewOfFile3 failed with %x\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Log(LogLevel::Debug, "no mapping at all found??? %p %x %p\n", dst, size, MemoryBase);
|
||||
return false;
|
||||
#else
|
||||
return mmap(dst, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, offset) != MAP_FAILED;
|
||||
#endif
|
||||
|
@ -280,21 +337,68 @@ bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexce
|
|||
|
||||
bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
|
||||
{
|
||||
CHECK_ALIGNED(addr);
|
||||
CHECK_ALIGNED(offset);
|
||||
CHECK_ALIGNED(size);
|
||||
|
||||
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
||||
#ifdef __SWITCH__
|
||||
Result r = svcUnmapProcessMemory(dst, envGetOwnProcessHandle(),
|
||||
(u64)(MemoryBaseCodeMem + offset), size);
|
||||
return R_SUCCEEDED(r);
|
||||
#elif defined(_WIN32)
|
||||
return UnmapViewOfFile(dst);
|
||||
if (!UnmapViewOfFileEx(dst, MEM_PRESERVE_PLACEHOLDER))
|
||||
{
|
||||
Log(LogLevel::Debug, "UnmapViewOfFileEx failed %x\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t uintptrDst = reinterpret_cast<uintptr_t>(dst);
|
||||
uintptr_t coalesceStart = uintptrDst;
|
||||
size_t coalesceSize = size;
|
||||
|
||||
for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end();)
|
||||
{
|
||||
if (it->Start+it->Size == uintptrDst)
|
||||
{
|
||||
//Log(LogLevel::Debug, "Coalescing to the left\n");
|
||||
coalesceStart = it->Start;
|
||||
coalesceSize += it->Size;
|
||||
it = VirtmemPlaceholders.erase(it);
|
||||
}
|
||||
else if (it->Start == uintptrDst+size)
|
||||
{
|
||||
//Log(LogLevel::Debug, "Coalescing to the right\n");
|
||||
coalesceSize += it->Size;
|
||||
it = VirtmemPlaceholders.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
if (coalesceStart != uintptrDst || coalesceSize != size)
|
||||
{
|
||||
if (!VirtualFree(reinterpret_cast<void*>(coalesceStart), coalesceSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS))
|
||||
return false;
|
||||
|
||||
}
|
||||
VirtmemPlaceholders.push_back({coalesceStart, coalesceSize});
|
||||
//Log(LogLevel::Debug, "Adding coalesced region %llx %llx", coalesceStart, coalesceSize);
|
||||
|
||||
return true;
|
||||
#else
|
||||
return munmap(dst, size) == 0;
|
||||
return mmap(dst, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) != MAP_FAILED;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __SWITCH__
|
||||
void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept
|
||||
{
|
||||
CHECK_ALIGNED(addr);
|
||||
CHECK_ALIGNED(size);
|
||||
|
||||
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
||||
#if defined(_WIN32)
|
||||
DWORD winProtection, oldProtection;
|
||||
|
@ -305,6 +409,10 @@ void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int prot
|
|||
else
|
||||
winProtection = PAGE_READWRITE;
|
||||
bool success = VirtualProtect(dst, size, winProtection, &oldProtection);
|
||||
if (!success)
|
||||
{
|
||||
Log(LogLevel::Debug, "VirtualProtect failed with %x\n", GetLastError());
|
||||
}
|
||||
assert(success);
|
||||
#else
|
||||
int posixProt;
|
||||
|
@ -335,14 +443,14 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
|||
else
|
||||
{
|
||||
u32 segmentOffset = offset;
|
||||
u8 status = statuses[(Addr + offset) >> 12];
|
||||
while (statuses[(Addr + offset) >> 12] == status
|
||||
u8 status = statuses[(Addr + offset) >> PageShift];
|
||||
while (statuses[(Addr + offset) >> PageShift] == status
|
||||
&& offset < Size
|
||||
&& (!skipDTCM || Addr + offset != dtcmStart))
|
||||
{
|
||||
assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped);
|
||||
statuses[(Addr + offset) >> 12] = memstate_Unmapped;
|
||||
offset += 0x1000;
|
||||
assert(statuses[(Addr + offset) >> PageShift] != memstate_Unmapped);
|
||||
statuses[(Addr + offset) >> PageShift] = memstate_Unmapped;
|
||||
offset += PageSize;
|
||||
}
|
||||
|
||||
#ifdef __SWITCH__
|
||||
|
@ -358,7 +466,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
|||
}
|
||||
|
||||
#ifndef __SWITCH__
|
||||
#ifndef _WIN32
|
||||
u32 dtcmEnd = dtcmStart + dtcmSize;
|
||||
if (Num == 0
|
||||
&& dtcmEnd >= Addr
|
||||
|
@ -378,7 +485,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
|||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size);
|
||||
assert(succeded);
|
||||
|
@ -388,7 +494,7 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
|||
|
||||
void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept
|
||||
{
|
||||
offset &= ~0xFFF;
|
||||
offset &= ~(PageSize - 1);
|
||||
//printf("set code protection %d %x %d\n", region, offset, protect);
|
||||
|
||||
for (int i = 0; i < Mappings[region].Length; i++)
|
||||
|
@ -406,9 +512,9 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
|
|||
|
||||
u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7);
|
||||
|
||||
//printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> 12]);
|
||||
assert(states[effectiveAddr >> 12] == (protect ? memstate_MappedRW : memstate_MappedProtected));
|
||||
states[effectiveAddr >> 12] = protect ? memstate_MappedProtected : memstate_MappedRW;
|
||||
//printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> PageShift]);
|
||||
assert(states[effectiveAddr >> PageShift] == (protect ? memstate_MappedRW : memstate_MappedProtected));
|
||||
states[effectiveAddr >> PageShift] = protect ? memstate_MappedProtected : memstate_MappedRW;
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
bool success;
|
||||
|
@ -418,7 +524,7 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
|
|||
success = MapIntoRange(effectiveAddr, mapping.Num, OffsetsPerRegion[region] + offset, 0x1000);
|
||||
assert(success);
|
||||
#else
|
||||
SetCodeProtectionRange(effectiveAddr, 0x1000, mapping.Num, protect ? 1 : 2);
|
||||
SetCodeProtectionRange(effectiveAddr, PageSize, mapping.Num, protect ? 1 : 2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -543,11 +649,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
|||
u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1;
|
||||
u32 dtcmEnd = dtcmStart + dtcmSize;
|
||||
#ifndef __SWITCH__
|
||||
#ifndef _WIN32
|
||||
if (num == 0
|
||||
&& dtcmEnd >= mirrorStart
|
||||
&& dtcmStart < mirrorStart + mirrorSize)
|
||||
{
|
||||
if (dtcmSize < PageSize)
|
||||
{
|
||||
// we could technically mask out the DTCM by setting a hole to access permissions
|
||||
// but realistically there isn't much of a point in mapping less than 16kb of DTCM
|
||||
// so it isn't worth more complex support
|
||||
Log(LogLevel::Info, "DTCM size smaller than 16kb skipping mapping entirely");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success;
|
||||
if (dtcmStart > mirrorStart)
|
||||
{
|
||||
|
@ -562,7 +676,6 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
|||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize);
|
||||
assert(succeded);
|
||||
|
@ -579,22 +692,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
|||
{
|
||||
if (skipDTCM && mirrorStart + offset == dtcmStart)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetCodeProtectionRange(dtcmStart, dtcmSize, 0, 0);
|
||||
#endif
|
||||
offset += dtcmSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 sectionOffset = offset;
|
||||
bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]);
|
||||
bool hasCode = isExecutable && PageContainsCode(&range[offset / 512], PageSize);
|
||||
while (offset < mirrorSize
|
||||
&& (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode)
|
||||
&& (!isExecutable || PageContainsCode(&range[offset / 512], PageSize) == hasCode)
|
||||
&& (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase))
|
||||
{
|
||||
assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped);
|
||||
states[(mirrorStart + offset) >> 12] = hasCode ? memstate_MappedProtected : memstate_MappedRW;
|
||||
offset += 0x1000;
|
||||
assert(states[(mirrorStart + offset) >> PageShift] == memstate_Unmapped);
|
||||
states[(mirrorStart + offset) >> PageShift] = hasCode ? memstate_MappedProtected : memstate_MappedRW;
|
||||
offset += PageSize;
|
||||
}
|
||||
|
||||
u32 sectionSize = offset - sectionOffset;
|
||||
|
@ -624,6 +734,86 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
u32 ARMJIT_Memory::PageSize = 0;
|
||||
u32 ARMJIT_Memory::PageShift = 0;
|
||||
|
||||
bool ARMJIT_Memory::IsFastMemSupported()
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
return false;
|
||||
#else
|
||||
static bool initialised = false;
|
||||
static bool isSupported = false;
|
||||
if (!initialised)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ARMJIT_Global::Init();
|
||||
isSupported = virtualAlloc2Ptr != nullptr;
|
||||
ARMJIT_Global::DeInit();
|
||||
|
||||
PageSize = RegularPageSize;
|
||||
#else
|
||||
PageSize = sysconf(_SC_PAGESIZE);
|
||||
isSupported = PageSize == RegularPageSize || PageSize == LargePageSize;
|
||||
#endif
|
||||
PageShift = __builtin_ctz(PageSize);
|
||||
initialised = true;
|
||||
}
|
||||
return isSupported;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ARMJIT_Memory::RegisterFaultHandler()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler);
|
||||
|
||||
KernelBaseDll = LoadLibrary("KernelBase.dll");
|
||||
if (KernelBaseDll)
|
||||
{
|
||||
virtualAlloc2Ptr = reinterpret_cast<VirtualAlloc2Type>(GetProcAddress(KernelBaseDll, "VirtualAlloc2"));
|
||||
mapViewOfFile3Ptr = reinterpret_cast<MapViewOfFile3Type>(GetProcAddress(KernelBaseDll, "MapViewOfFile3"));
|
||||
}
|
||||
|
||||
if (!virtualAlloc2Ptr)
|
||||
{
|
||||
Log(LogLevel::Error, "Could not load new Windows virtual memory functions, fast memory is disabled.\n");
|
||||
}
|
||||
#else
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = nullptr;
|
||||
sa.sa_sigaction = &SigsegvHandler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGSEGV, &sa, &OldSaSegv);
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &sa, &OldSaBus);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void ARMJIT_Memory::UnregisterFaultHandler()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (ExceptionHandlerHandle)
|
||||
{
|
||||
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
|
||||
ExceptionHandlerHandle = nullptr;
|
||||
}
|
||||
|
||||
if (KernelBaseDll)
|
||||
{
|
||||
FreeLibrary(KernelBaseDll);
|
||||
KernelBaseDll = nullptr;
|
||||
}
|
||||
#else
|
||||
sigaction(SIGSEGV, &OldSaSegv, nullptr);
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &OldSaBus, nullptr);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
|
||||
{
|
||||
if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC))
|
||||
|
@ -632,7 +822,7 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
|
|||
|
||||
u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7;
|
||||
|
||||
if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped)
|
||||
if (memStatus[faultDesc.EmulatedFaultAddr >> PageShift] == memstate_Unmapped)
|
||||
rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr);
|
||||
|
||||
if (rewriteToSlowPath)
|
||||
|
@ -643,10 +833,9 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
|
|||
return false;
|
||||
}
|
||||
|
||||
const u64 AddrSpaceSize = 0x100000000;
|
||||
|
||||
ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
||||
{
|
||||
ARMJIT_Global::Init();
|
||||
#if defined(__SWITCH__)
|
||||
MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize);
|
||||
virtmemLock();
|
||||
|
@ -671,33 +860,27 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
|||
|
||||
u8* basePtr = MemoryBaseCodeMem;
|
||||
#elif defined(_WIN32)
|
||||
ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler);
|
||||
if (virtualAlloc2Ptr)
|
||||
{
|
||||
MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, MemoryTotalSize, nullptr);
|
||||
|
||||
MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MemoryTotalSize, NULL);
|
||||
MemoryBase = reinterpret_cast<u8*>(virtualAlloc2Ptr(nullptr, nullptr, VirtmemAreaSize,
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS,
|
||||
nullptr, 0));
|
||||
// split off placeholder and map base mapping
|
||||
VirtualFree(MemoryBase, MemoryTotalSize, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER);
|
||||
mapViewOfFile3Ptr(MemoryFile, nullptr, MemoryBase, 0, MemoryTotalSize, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
|
||||
|
||||
MemoryBase = (u8*)VirtualAlloc(NULL, AddrSpaceSize*4, MEM_RESERVE, PAGE_READWRITE);
|
||||
VirtualFree(MemoryBase, 0, MEM_RELEASE);
|
||||
// this is incredible hacky
|
||||
// but someone else is trying to go into our address space!
|
||||
// Windows will very likely give them virtual memory starting at the same address
|
||||
// as it is giving us now.
|
||||
// That's why we don't use this address, but instead 4gb inwards
|
||||
// I know this is terrible
|
||||
FastMem9Start = MemoryBase + AddrSpaceSize;
|
||||
FastMem7Start = MemoryBase + AddrSpaceSize*2;
|
||||
MemoryBase = MemoryBase + AddrSpaceSize*3;
|
||||
|
||||
MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, MemoryTotalSize, MemoryBase);
|
||||
VirtmemPlaceholders.push_back({reinterpret_cast<uintptr_t>(MemoryBase)+MemoryTotalSize, AddrSpaceSize*2});
|
||||
}
|
||||
else
|
||||
{
|
||||
// old Windows version
|
||||
MemoryBase = new u8[MemoryTotalSize];
|
||||
}
|
||||
#else
|
||||
// this used to be allocated with three different mmaps
|
||||
// The idea was to give the OS more freedom where to position the buffers,
|
||||
// but something was bad about this so instead we take this vmem eating monster
|
||||
// which seems to work better.
|
||||
MemoryBase = (u8*)mmap(NULL, AddrSpaceSize*4, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
munmap(MemoryBase, AddrSpaceSize*4);
|
||||
FastMem9Start = MemoryBase;
|
||||
FastMem7Start = MemoryBase + AddrSpaceSize;
|
||||
MemoryBase = MemoryBase + AddrSpaceSize*2;
|
||||
MemoryBase = (u8*)mmap(nullptr, VirtmemAreaSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
Libandroid = Platform::DynamicLibrary_Load("libandroid.so");
|
||||
|
@ -717,7 +900,7 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
|||
}
|
||||
#else
|
||||
char fastmemPidName[snprintf(NULL, 0, "/melondsfastmem%d", getpid()) + 1];
|
||||
sprintf(fastmemPidName, "/melondsfastmem%d", getpid());
|
||||
snprintf(fastmemPidName, sizeof(fastmemPidName), "/melondsfastmem%d", getpid());
|
||||
MemoryFile = shm_open(fastmemPidName, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
if (MemoryFile == -1)
|
||||
{
|
||||
|
@ -730,20 +913,10 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
|||
Log(LogLevel::Error, "Failed to allocate memory using ftruncate! (%s)", strerror(errno));
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = nullptr;
|
||||
sa.sa_sigaction = &SigsegvHandler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGSEGV, &sa, &OldSaSegv);
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &sa, &OldSaBus);
|
||||
#endif
|
||||
|
||||
mmap(MemoryBase, MemoryTotalSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, 0);
|
||||
|
||||
u8* basePtr = MemoryBase;
|
||||
#endif
|
||||
FastMem9Start = MemoryBase+MemoryTotalSize;
|
||||
FastMem7Start = static_cast<u8*>(FastMem9Start)+AddrSpaceSize;
|
||||
}
|
||||
|
||||
ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
||||
|
@ -764,34 +937,36 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
|||
free(MemoryBase);
|
||||
MemoryBase = nullptr;
|
||||
#elif defined(_WIN32)
|
||||
if (MemoryBase)
|
||||
if (virtualAlloc2Ptr)
|
||||
{
|
||||
bool viewUnmapped = UnmapViewOfFile(MemoryBase);
|
||||
assert(viewUnmapped);
|
||||
MemoryBase = nullptr;
|
||||
FastMem9Start = nullptr;
|
||||
FastMem7Start = nullptr;
|
||||
}
|
||||
if (MemoryBase)
|
||||
{
|
||||
bool viewUnmapped = UnmapViewOfFileEx(MemoryBase, MEM_PRESERVE_PLACEHOLDER);
|
||||
assert(viewUnmapped);
|
||||
bool viewCoalesced = VirtualFree(MemoryBase, VirtmemAreaSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS);
|
||||
assert(viewCoalesced);
|
||||
bool freeEverything = VirtualFree(MemoryBase, 0, MEM_RELEASE);
|
||||
assert(freeEverything);
|
||||
|
||||
if (MemoryFile)
|
||||
{
|
||||
CloseHandle(MemoryFile);
|
||||
MemoryFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
MemoryBase = nullptr;
|
||||
FastMem9Start = nullptr;
|
||||
FastMem7Start = nullptr;
|
||||
}
|
||||
|
||||
if (ExceptionHandlerHandle)
|
||||
if (MemoryFile)
|
||||
{
|
||||
CloseHandle(MemoryFile);
|
||||
MemoryFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
|
||||
ExceptionHandlerHandle = nullptr;
|
||||
delete[] MemoryBase;
|
||||
}
|
||||
#else
|
||||
sigaction(SIGSEGV, &OldSaSegv, nullptr);
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &OldSaBus, nullptr);
|
||||
#endif
|
||||
if (MemoryBase)
|
||||
{
|
||||
munmap(MemoryBase, MemoryTotalSize);
|
||||
munmap(MemoryBase, VirtmemAreaSize);
|
||||
MemoryBase = nullptr;
|
||||
FastMem9Start = nullptr;
|
||||
FastMem7Start = nullptr;
|
||||
|
@ -803,6 +978,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
|||
MemoryFile = -1;
|
||||
}
|
||||
|
||||
Log(LogLevel::Info, "unmappinged everything\n");
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (Libandroid)
|
||||
{
|
||||
|
@ -812,6 +989,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
ARMJIT_Global::DeInit();
|
||||
}
|
||||
|
||||
void ARMJIT_Memory::Reset() noexcept
|
||||
|
@ -834,17 +1013,6 @@ void ARMJIT_Memory::Reset() noexcept
|
|||
|
||||
bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
TODO: with some hacks, the smaller shared WRAM regions
|
||||
could be mapped in some occaisons as well
|
||||
*/
|
||||
if (region == memregion_DTCM
|
||||
|| region == memregion_SharedWRAM
|
||||
|| region == memregion_NewSharedWRAM_B
|
||||
|| region == memregion_NewSharedWRAM_C)
|
||||
return false;
|
||||
#endif
|
||||
return OffsetsPerRegion[region] != UINT32_MAX;
|
||||
}
|
||||
|
||||
|
@ -1432,4 +1600,4 @@ void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) co
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -23,12 +23,14 @@
|
|||
#include "MemConstants.h"
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
# include <mutex>
|
||||
# include "TinyVector.h"
|
||||
# include "ARM.h"
|
||||
# if defined(__SWITCH__)
|
||||
# include <switch.h>
|
||||
# elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
# include <vector>
|
||||
# include <windows.h>
|
||||
# else
|
||||
# include <sys/mman.h>
|
||||
# include <sys/stat.h>
|
||||
|
@ -48,23 +50,22 @@ class Compiler;
|
|||
class ARMJIT;
|
||||
#endif
|
||||
|
||||
static constexpr u32 LargePageSize = 0x4000;
|
||||
static constexpr u32 RegularPageSize = 0x1000;
|
||||
|
||||
constexpr u32 RoundUp(u32 size) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (size + 0xFFFF) & ~0xFFFF;
|
||||
#else
|
||||
return size;
|
||||
#endif
|
||||
return (size + LargePageSize - 1) & ~(LargePageSize - 1);
|
||||
}
|
||||
|
||||
const u32 MemBlockMainRAMOffset = 0;
|
||||
const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
|
||||
const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
|
||||
const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
|
||||
const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
|
||||
const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
|
||||
const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
|
||||
const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
|
||||
static constexpr u32 MemBlockMainRAMOffset = 0;
|
||||
static constexpr u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
|
||||
static constexpr u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
|
||||
static constexpr u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
|
||||
static constexpr u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
|
||||
static constexpr u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
|
||||
static constexpr u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
|
||||
static constexpr u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
|
||||
|
||||
class ARMJIT_Memory
|
||||
{
|
||||
|
@ -137,6 +138,14 @@ public:
|
|||
bool IsFastmemCompatible(int region) const noexcept;
|
||||
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept;
|
||||
bool MapAtAddress(u32 addr) noexcept;
|
||||
|
||||
static bool IsFastMemSupported();
|
||||
|
||||
static void RegisterFaultHandler();
|
||||
static void UnregisterFaultHandler();
|
||||
|
||||
static u32 PageSize;
|
||||
static u32 PageShift;
|
||||
private:
|
||||
friend class Compiler;
|
||||
struct Mapping
|
||||
|
@ -162,14 +171,22 @@ private:
|
|||
void* FastMem9Start;
|
||||
void* FastMem7Start;
|
||||
u8* MemoryBase = nullptr;
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
VirtmemReservation* FastMem9Reservation, *FastMem7Reservation;
|
||||
u8* MemoryBaseCodeMem;
|
||||
#elif defined(_WIN32)
|
||||
struct VirtmemPlaceholder
|
||||
{
|
||||
uintptr_t Start;
|
||||
size_t Size;
|
||||
};
|
||||
std::vector<VirtmemPlaceholder> VirtmemPlaceholders;
|
||||
|
||||
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
|
||||
HANDLE MemoryFile = INVALID_HANDLE_VALUE;
|
||||
LPVOID ExceptionHandlerHandle = nullptr;
|
||||
#else
|
||||
|
||||
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
|
||||
int MemoryFile = -1;
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -176,9 +176,9 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
|
|||
else
|
||||
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
|
||||
if (Num == 0)
|
||||
CALL((void*)&ARMv5JumpToTrampoline);
|
||||
ABI_CallFunction(ARMv5JumpToTrampoline);
|
||||
else
|
||||
CALL((void*)&ARMv4JumpToTrampoline);
|
||||
ABI_CallFunction(ARMv4JumpToTrampoline);
|
||||
|
||||
PopRegs(restoreCPSR, true);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,19 +21,13 @@
|
|||
#include "../ARMJIT.h"
|
||||
#include "../ARMInterpreter.h"
|
||||
#include "../NDS.h"
|
||||
#include "../ARMJIT_Global.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "../dolphin/CommonFuncs.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace Gen;
|
||||
using namespace Common;
|
||||
|
||||
|
@ -222,46 +216,21 @@ void Compiler::A_Comp_MSR()
|
|||
MOV(32, R(ABI_PARAM3), R(RCPSR));
|
||||
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
|
||||
MOV(64, R(ABI_PARAM1), R(RCPU));
|
||||
CALL((void*)&UpdateModeTrampoline);
|
||||
ABI_CallFunction(UpdateModeTrampoline);
|
||||
|
||||
PopRegs(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
We'll repurpose this .bss memory
|
||||
|
||||
*/
|
||||
u8 CodeMemory[1024 * 1024 * 32];
|
||||
|
||||
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
|
||||
{
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
ARMJIT_Global::Init();
|
||||
|
||||
u64 pageSize = (u64)sysInfo.dwPageSize;
|
||||
#else
|
||||
u64 pageSize = sysconf(_SC_PAGE_SIZE);
|
||||
#endif
|
||||
CodeMemBase = static_cast<u8*>(ARMJIT_Global::AllocateCodeMem());
|
||||
CodeMemSize = ARMJIT_Global::CodeMemorySliceSize;
|
||||
|
||||
u8* pageAligned = (u8*)(((u64)CodeMemory & ~(pageSize - 1)) + pageSize);
|
||||
u64 alignedSize = (((u64)CodeMemory + sizeof(CodeMemory)) & ~(pageSize - 1)) - (u64)pageAligned;
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD dummy;
|
||||
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
||||
#elif defined(__APPLE__)
|
||||
pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
|
||||
#else
|
||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
ResetStart = pageAligned;
|
||||
CodeMemSize = alignedSize;
|
||||
}
|
||||
ResetStart = CodeMemBase;
|
||||
|
||||
Reset();
|
||||
|
||||
|
@ -475,6 +444,13 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
|
|||
FarSize = (ResetStart + CodeMemSize) - FarStart;
|
||||
}
|
||||
|
||||
Compiler::~Compiler()
|
||||
{
|
||||
ARMJIT_Global::FreeCodeMem(CodeMemBase);
|
||||
|
||||
ARMJIT_Global::DeInit();
|
||||
}
|
||||
|
||||
void Compiler::LoadCPSR()
|
||||
{
|
||||
assert(!CPSRDirty);
|
||||
|
@ -684,7 +660,7 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
|
|||
|
||||
if (ConstantCycles)
|
||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||
JMP((u8*)&ARM_Ret, true);
|
||||
ABI_TailCall(ARM_Ret);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -846,7 +822,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
|||
|
||||
if (ConstantCycles)
|
||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||
JMP((u8*)ARM_Ret, true);
|
||||
ABI_TailCall(ARM_Ret);
|
||||
|
||||
#ifdef JIT_PROFILING_ENABLED
|
||||
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -84,6 +84,7 @@ class Compiler : public Gen::XEmitter
|
|||
{
|
||||
public:
|
||||
explicit Compiler(melonDS::NDS& nds);
|
||||
~Compiler();
|
||||
|
||||
void Reset();
|
||||
|
||||
|
@ -256,6 +257,7 @@ public:
|
|||
|
||||
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
|
||||
|
||||
u8* CodeMemBase;
|
||||
u8* ResetStart {};
|
||||
u32 CodeMemSize {};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -316,24 +316,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
{
|
||||
switch (size | NDS.ConsoleType)
|
||||
{
|
||||
case 32: CALL((void*)&SlowWrite9<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowWrite9<u16, 0>); break;
|
||||
case 8: CALL((void*)&SlowWrite9<u8, 0>); break;
|
||||
case 33: CALL((void*)&SlowWrite9<u32, 1>); break;
|
||||
case 17: CALL((void*)&SlowWrite9<u16, 1>); break;
|
||||
case 9: CALL((void*)&SlowWrite9<u8, 1>); break;
|
||||
case 32: ABI_CallFunction(SlowWrite9<u32, 0>); break;
|
||||
case 16: ABI_CallFunction(SlowWrite9<u16, 0>); break;
|
||||
case 8: ABI_CallFunction(&SlowWrite9<u8, 0>); break;
|
||||
case 33: ABI_CallFunction(&SlowWrite9<u32, 1>); break;
|
||||
case 17: ABI_CallFunction(&SlowWrite9<u16, 1>); break;
|
||||
case 9: ABI_CallFunction(&SlowWrite9<u8, 1>); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (size | NDS.ConsoleType)
|
||||
{
|
||||
case 32: CALL((void*)&SlowRead9<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowRead9<u16, 0>); break;
|
||||
case 8: CALL((void*)&SlowRead9<u8, 0>); break;
|
||||
case 33: CALL((void*)&SlowRead9<u32, 1>); break;
|
||||
case 17: CALL((void*)&SlowRead9<u16, 1>); break;
|
||||
case 9: CALL((void*)&SlowRead9<u8, 1>); break;
|
||||
case 32: ABI_CallFunction(&SlowRead9<u32, 0>); break;
|
||||
case 16: ABI_CallFunction(&SlowRead9<u16, 0>); break;
|
||||
case 8: ABI_CallFunction(&SlowRead9<u8, 0>); break;
|
||||
case 33: ABI_CallFunction(&SlowRead9<u32, 1>); break;
|
||||
case 17: ABI_CallFunction(&SlowRead9<u16, 1>); break;
|
||||
case 9: ABI_CallFunction(&SlowRead9<u8, 1>); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -347,24 +347,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
|
||||
switch (size | NDS.ConsoleType)
|
||||
{
|
||||
case 32: CALL((void*)&SlowWrite7<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowWrite7<u16, 0>); break;
|
||||
case 8: CALL((void*)&SlowWrite7<u8, 0>); break;
|
||||
case 33: CALL((void*)&SlowWrite7<u32, 1>); break;
|
||||
case 17: CALL((void*)&SlowWrite7<u16, 1>); break;
|
||||
case 9: CALL((void*)&SlowWrite7<u8, 1>); break;
|
||||
case 32: ABI_CallFunction(&SlowWrite7<u32, 0>); break;
|
||||
case 16: ABI_CallFunction(&SlowWrite7<u16, 0>); break;
|
||||
case 8: ABI_CallFunction(&SlowWrite7<u8, 0>); break;
|
||||
case 33: ABI_CallFunction(&SlowWrite7<u32, 1>); break;
|
||||
case 17: ABI_CallFunction(&SlowWrite7<u16, 1>); break;
|
||||
case 9: ABI_CallFunction(&SlowWrite7<u8, 1>); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (size | NDS.ConsoleType)
|
||||
{
|
||||
case 32: CALL((void*)&SlowRead7<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowRead7<u16, 0>); break;
|
||||
case 8: CALL((void*)&SlowRead7<u8, 0>); break;
|
||||
case 33: CALL((void*)&SlowRead7<u32, 1>); break;
|
||||
case 17: CALL((void*)&SlowRead7<u16, 1>); break;
|
||||
case 9: CALL((void*)&SlowRead7<u8, 1>); break;
|
||||
case 32: ABI_CallFunction(&SlowRead7<u32, 0>); break;
|
||||
case 16: ABI_CallFunction(&SlowRead7<u16, 0>); break;
|
||||
case 8: ABI_CallFunction(&SlowRead7<u8, 0>); break;
|
||||
case 33: ABI_CallFunction(&SlowRead7<u32, 1>); break;
|
||||
case 17: ABI_CallFunction(&SlowRead7<u16, 1>); break;
|
||||
case 9: ABI_CallFunction(&SlowRead7<u8, 1>); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -526,10 +526,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
|||
|
||||
switch (Num * 2 | NDS.ConsoleType)
|
||||
{
|
||||
case 0: CALL((void*)&SlowBlockTransfer9<false, 0>); break;
|
||||
case 1: CALL((void*)&SlowBlockTransfer9<false, 1>); break;
|
||||
case 2: CALL((void*)&SlowBlockTransfer7<false, 0>); break;
|
||||
case 3: CALL((void*)&SlowBlockTransfer7<false, 1>); break;
|
||||
case 0: ABI_CallFunction(&SlowBlockTransfer9<false, 0>); break;
|
||||
case 1: ABI_CallFunction(&SlowBlockTransfer9<false, 1>); break;
|
||||
case 2: ABI_CallFunction(&SlowBlockTransfer7<false, 0>); break;
|
||||
case 3: ABI_CallFunction(&SlowBlockTransfer7<false, 1>); break;
|
||||
}
|
||||
|
||||
PopRegs(false, false);
|
||||
|
@ -630,10 +630,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
|||
|
||||
switch (Num * 2 | NDS.ConsoleType)
|
||||
{
|
||||
case 0: CALL((void*)&SlowBlockTransfer9<true, 0>); break;
|
||||
case 1: CALL((void*)&SlowBlockTransfer9<true, 1>); break;
|
||||
case 2: CALL((void*)&SlowBlockTransfer7<true, 0>); break;
|
||||
case 3: CALL((void*)&SlowBlockTransfer7<true, 1>); break;
|
||||
case 0: ABI_CallFunction(&SlowBlockTransfer9<true, 0>); break;
|
||||
case 1: ABI_CallFunction(&SlowBlockTransfer9<true, 1>); break;
|
||||
case 2: ABI_CallFunction(&SlowBlockTransfer7<true, 0>); break;
|
||||
case 3: ABI_CallFunction(&SlowBlockTransfer7<true, 1>); break;
|
||||
}
|
||||
|
||||
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team, RSDuck
|
||||
Copyright 2016-2025 melonDS team, RSDuck
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
14
src/Args.h
14
src/Args.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -85,18 +85,6 @@ struct GDBArgs
|
|||
/// 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<NDSCart::CartCommon> 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<GBACart::CartCommon> GBAROM = nullptr;
|
||||
|
||||
/// NDS ARM9 BIOS to install.
|
||||
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
|
||||
std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
|
||||
|
|
|
@ -31,6 +31,7 @@ add_library(core STATIC
|
|||
FATStorage.cpp
|
||||
FIFO.h
|
||||
GBACart.cpp
|
||||
GBACartMotionPak.cpp
|
||||
GPU.cpp
|
||||
GPU2D.cpp
|
||||
GPU2D_Soft.cpp
|
||||
|
@ -98,8 +99,15 @@ if (ENABLE_JIT)
|
|||
|
||||
ARMJIT.cpp
|
||||
ARMJIT_Memory.cpp
|
||||
ARMJIT_Global.cpp
|
||||
|
||||
dolphin/CommonFuncs.cpp)
|
||||
|
||||
if (WIN32)
|
||||
# Required for memory mapping-related functions introduced in Windows 8
|
||||
target_compile_definitions(core PRIVATE -D_WIN32_WINNT=_WIN32_WINNT_WIN8)
|
||||
target_link_libraries(core PRIVATE onecore)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE STREQUAL x86_64)
|
||||
target_sources(core PRIVATE
|
||||
|
@ -128,6 +136,8 @@ if (ENABLE_JIT)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(core INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version")
|
||||
option(MELONDS_EMBED_BUILD_INFO "Embed detailed build info into the binary" OFF)
|
||||
set(MELONDS_GIT_BRANCH "$ENV{MELONDS_GIT_BRANCH}" CACHE STRING "The Git branch used for this build")
|
||||
|
@ -179,13 +189,14 @@ endif()
|
|||
|
||||
if (WIN32)
|
||||
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
|
||||
target_compile_definitions(core PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
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)
|
||||
target_link_libraries(core PRIVATE network)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JIT_PROFILING)
|
||||
|
|
10
src/CP15.cpp
10
src/CP15.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -44,6 +44,7 @@ void ARMv5::CP15Reset()
|
|||
CP15Control = 0x2078; // dunno
|
||||
|
||||
RNGSeed = 44203;
|
||||
TraceProcessID = 0;
|
||||
|
||||
DTCMSetting = 0;
|
||||
ITCMSetting = 0;
|
||||
|
@ -643,6 +644,10 @@ void ARMv5::CP15Write(u32 id, u32 val)
|
|||
UpdateITCMSetting();
|
||||
return;
|
||||
|
||||
case 0xD01:
|
||||
TraceProcessID = val;
|
||||
return;
|
||||
|
||||
case 0xF00:
|
||||
//printf("cache debug index register %08X\n", val);
|
||||
return;
|
||||
|
@ -760,6 +765,9 @@ u32 ARMv5::CP15Read(u32 id) const
|
|||
return DTCMSetting;
|
||||
case 0x911:
|
||||
return ITCMSetting;
|
||||
|
||||
case 0xD01:
|
||||
return TraceProcessID;
|
||||
}
|
||||
|
||||
if ((id & 0xF00) == 0xF00) // test/debug shit?
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
33
src/DSi.cpp
33
src/DSi.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -75,8 +75,6 @@ const u32 NDMAModes[] =
|
|||
DSi(
|
||||
DSiArgs {
|
||||
NDSArgs {
|
||||
nullptr,
|
||||
nullptr,
|
||||
bios_arm9_bin,
|
||||
bios_arm7_bin,
|
||||
Firmware(0),
|
||||
|
@ -117,6 +115,8 @@ DSi::DSi(DSiArgs&& args, void* userdata) noexcept :
|
|||
NWRAM_A = JIT.Memory.GetNWRAM_A();
|
||||
NWRAM_B = JIT.Memory.GetNWRAM_B();
|
||||
NWRAM_C = JIT.Memory.GetNWRAM_C();
|
||||
|
||||
SetFullBIOSBoot(args.FullBIOSBoot);
|
||||
}
|
||||
|
||||
DSi::~DSi() noexcept
|
||||
|
@ -180,6 +180,8 @@ void DSi::Reset()
|
|||
// LCD init flag
|
||||
GPU.DispStat[0] |= (1<<6);
|
||||
GPU.DispStat[1] |= (1<<6);
|
||||
|
||||
UpdateVRAMTimings();
|
||||
}
|
||||
|
||||
void DSi::Stop(Platform::StopReason reason)
|
||||
|
@ -292,12 +294,14 @@ void DSi::DoSavestateExtra(Savestate* file)
|
|||
NDMAs[i].DoSavestate(file);
|
||||
|
||||
AES.DoSavestate(file);
|
||||
CamModule.DoSavestate(file);
|
||||
DSP.DoSavestate(file);
|
||||
I2C.DoSavestate(file);
|
||||
I2S.DoSavestate(file);
|
||||
CamModule.DoSavestate(file);
|
||||
SDMMC.DoSavestate(file);
|
||||
SDIO.DoSavestate(file);
|
||||
|
||||
UpdateVRAMTimings();
|
||||
}
|
||||
|
||||
void DSi::SetCartInserted(bool inserted)
|
||||
|
@ -681,6 +685,8 @@ void DSi::SetupDirectBoot()
|
|||
ARM9.CP15Write(0x671, 0x02FFC01B);
|
||||
ARM9.CP15Write(0x910, 0x0E00000A);
|
||||
ARM9.CP15Write(0x911, 0x00000020);
|
||||
|
||||
UpdateVRAMTimings();
|
||||
}
|
||||
|
||||
void DSi::SoftReset()
|
||||
|
@ -735,10 +741,11 @@ void DSi::SoftReset()
|
|||
SCFG_RST = 0;
|
||||
DSP.SetRstLine(false);
|
||||
|
||||
|
||||
// LCD init flag
|
||||
GPU.DispStat[0] |= (1<<6);
|
||||
GPU.DispStat[1] |= (1<<6);
|
||||
|
||||
UpdateVRAMTimings();
|
||||
}
|
||||
|
||||
bool DSi::LoadNAND()
|
||||
|
@ -1270,6 +1277,20 @@ void DSi::MapNWRAMRange(u32 cpu, u32 num, u32 val)
|
|||
}
|
||||
}
|
||||
|
||||
void DSi::UpdateVRAMTimings()
|
||||
{
|
||||
if (SCFG_EXT[0] & (1<<13))
|
||||
{
|
||||
SetARM9RegionTimings(0x06000, 0x07000, Mem9_VRAM, 32, 1, 1);
|
||||
SetARM7RegionTimings(0x06000, 0x07000, Mem7_VRAM, 32, 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetARM9RegionTimings(0x06000, 0x07000, Mem9_VRAM, 16, 1, 1);
|
||||
SetARM7RegionTimings(0x06000, 0x07000, Mem7_VRAM, 16, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DSi::ApplyNewRAMSize(u32 size)
|
||||
{
|
||||
switch (size)
|
||||
|
@ -2583,6 +2604,8 @@ void DSi::ARM9IOWrite32(u32 addr, u32 val)
|
|||
//if (newram != oldram)
|
||||
// NDS::ScheduleEvent(NDS::Event_DSi_RAMSizeChange, false, 512*512*512, ApplyNewRAMSize, newram);
|
||||
Log(LogLevel::Debug, "from %08X, ARM7 %08X, %08X\n", NDS::GetPC(0), NDS::GetPC(1), ARM7.R[1]);
|
||||
|
||||
UpdateVRAMTimings();
|
||||
}
|
||||
return;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -100,6 +100,8 @@ public:
|
|||
void MapNWRAM_C(u32 num, u8 val);
|
||||
void MapNWRAMRange(u32 cpu, u32 num, u32 val);
|
||||
|
||||
void UpdateVRAMTimings();
|
||||
|
||||
u8 ARM9Read8(u32 addr) override;
|
||||
u16 ARM9Read16(u32 addr) override;
|
||||
u32 ARM9Read32(u32 addr) override;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -90,6 +90,9 @@ void DSi_AES::Reset()
|
|||
*(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72;
|
||||
*(u32*)&KeyX[1][12] = (u32)consoleid;
|
||||
|
||||
// slot 2: For 'Tad'
|
||||
std::memcpy(KeyX[2], &DSi.ARM9iBIOS[0x8B8C], 0x10);
|
||||
|
||||
// slot 3: console-unique eMMC crypto
|
||||
*(u32*)&KeyX[3][0] = (u32)consoleid;
|
||||
*(u32*)&KeyX[3][4] = (u32)consoleid ^ 0x24EE6906;
|
||||
|
@ -575,4 +578,4 @@ void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -34,14 +34,17 @@ using Platform::LogLevel;
|
|||
// namely, how long cameras take to process frames
|
||||
// camera IRQ is fired at roughly 15FPS with default config
|
||||
|
||||
const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS
|
||||
const u32 DSi_CamModule::kTransferStart = 60000;
|
||||
// camera IRQ marks camera VBlank
|
||||
// each scanline takes roughly 3173 cycles
|
||||
const u32 DSi_CamModule::kIRQInterval = 2234248; // ~15 FPS
|
||||
const u32 DSi_CamModule::kScanlineTime = 3173;
|
||||
const u32 DSi_CamModule::kTransferStart = DSi_CamModule::kIRQInterval - (DSi_CamModule::kScanlineTime * 480);
|
||||
|
||||
|
||||
DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi)
|
||||
{
|
||||
DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ));
|
||||
DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline));
|
||||
DSi.RegisterEventFuncs(Event_DSi_CamIRQ, this, {MakeEventThunk(DSi_CamModule, IRQ)});
|
||||
DSi.RegisterEventFuncs(Event_DSi_CamTransfer, this, {MakeEventThunk(DSi_CamModule, TransferScanline)});
|
||||
|
||||
Camera0 = DSi.I2C.GetOuterCamera();
|
||||
Camera1 = DSi.I2C.GetInnerCamera();
|
||||
|
@ -52,8 +55,8 @@ DSi_CamModule::~DSi_CamModule()
|
|||
Camera0 = nullptr;
|
||||
Camera1 = nullptr;
|
||||
|
||||
DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0);
|
||||
DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0);
|
||||
DSi.UnregisterEventFuncs(Event_DSi_CamIRQ);
|
||||
DSi.UnregisterEventFuncs(Event_DSi_CamTransfer);
|
||||
}
|
||||
|
||||
void DSi_CamModule::Reset()
|
||||
|
@ -64,12 +67,15 @@ void DSi_CamModule::Reset()
|
|||
CropStart = 0;
|
||||
CropEnd = 0;
|
||||
|
||||
memset(DataBuffer, 0, 512*sizeof(u32));
|
||||
BufferReadPos = 0;
|
||||
BufferWritePos = 0;
|
||||
Transferring = false;
|
||||
|
||||
memset(PixelBuffer, 0, sizeof(PixelBuffer));
|
||||
CurPixelBuffer = 0;
|
||||
BufferNumLines = 0;
|
||||
CurCamera = nullptr;
|
||||
|
||||
// TODO: ideally this should be started when a camera is active
|
||||
// instead of just being a constant thing
|
||||
DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -86,9 +92,30 @@ void DSi_CamModule::DoSavestate(Savestate* file)
|
|||
file->Var16(&ModuleCnt);
|
||||
file->Var16(&Cnt);
|
||||
|
||||
/*file->VarArray(FrameBuffer, sizeof(FrameBuffer));
|
||||
file->Var32(&TransferPos);
|
||||
file->Var32(&FrameLength);*/
|
||||
file->Var32(&CropStart);
|
||||
file->Var32(&CropEnd);
|
||||
|
||||
file->Bool32(&Transferring);
|
||||
|
||||
file->VarArray(&PixelBuffer[0].Data, 512);
|
||||
file->Var32(&PixelBuffer[0].ReadPos);
|
||||
file->Var32(&PixelBuffer[0].WritePos);
|
||||
file->VarArray(&PixelBuffer[1].Data, 512);
|
||||
file->Var32(&PixelBuffer[1].ReadPos);
|
||||
file->Var32(&PixelBuffer[1].WritePos);
|
||||
file->Var8(&CurPixelBuffer);
|
||||
|
||||
file->Var32(&BufferNumLines);
|
||||
|
||||
if (!file->Saving)
|
||||
{
|
||||
DSi_Camera* activecam = nullptr;
|
||||
|
||||
if (Camera0->IsActivated()) activecam = Camera0;
|
||||
else if (Camera1->IsActivated()) activecam = Camera1;
|
||||
|
||||
CurCamera = activecam;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,14 +135,8 @@ void DSi_CamModule::IRQ(u32 param)
|
|||
if (Cnt & (1<<11))
|
||||
DSi.SetIRQ(0, IRQ_DSi_Camera);
|
||||
|
||||
if (Cnt & (1<<15))
|
||||
{
|
||||
BufferReadPos = 0;
|
||||
BufferWritePos = 0;
|
||||
BufferNumLines = 0;
|
||||
CurCamera = activecam;
|
||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0);
|
||||
}
|
||||
CurCamera = activecam;
|
||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0);
|
||||
}
|
||||
|
||||
DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0);
|
||||
|
@ -123,17 +144,35 @@ void DSi_CamModule::IRQ(u32 param)
|
|||
|
||||
void DSi_CamModule::TransferScanline(u32 line)
|
||||
{
|
||||
u32* dstbuf = &DataBuffer[BufferWritePos];
|
||||
int maxlen = 512 - BufferWritePos;
|
||||
if (Cnt & (1<<4))
|
||||
{
|
||||
Transferring = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (line == 0)
|
||||
{
|
||||
if (!(Cnt & (1<<15)))
|
||||
return;
|
||||
|
||||
BufferNumLines = 0;
|
||||
Transferring = true;
|
||||
}
|
||||
|
||||
sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer];
|
||||
u32* dstbuf = &buffer->Data[buffer->WritePos];
|
||||
int maxlen = 512 - buffer->WritePos;
|
||||
|
||||
u32 tmpbuf[512];
|
||||
int datalen = CurCamera->TransferScanline(tmpbuf, 512);
|
||||
int lines_next;
|
||||
int datalen = CurCamera->TransferScanline(tmpbuf, 512, lines_next);
|
||||
u32 numscan;
|
||||
|
||||
// TODO: must be tweaked such that each block has enough time to transfer
|
||||
u32 delay = datalen*4 + 16;
|
||||
u32 delay = lines_next * kScanlineTime;
|
||||
|
||||
int copystart = 0;
|
||||
int copylen = datalen;
|
||||
bool line_last = false;
|
||||
|
||||
if (Cnt & (1<<14))
|
||||
{
|
||||
|
@ -143,10 +182,8 @@ void DSi_CamModule::TransferScanline(u32 line)
|
|||
int yend = (CropEnd >> 16) & 0x1FF;
|
||||
if (line < ystart || line > yend)
|
||||
{
|
||||
if (!CurCamera->TransferDone())
|
||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
|
||||
|
||||
return;
|
||||
if (line == yend+1) line_last = true;
|
||||
goto skip_line;
|
||||
}
|
||||
|
||||
int xstart = (CropStart >> 1) & 0x1FF;
|
||||
|
@ -164,7 +201,6 @@ void DSi_CamModule::TransferScanline(u32 line)
|
|||
if (copylen > maxlen)
|
||||
{
|
||||
copylen = maxlen;
|
||||
Cnt |= (1<<4);
|
||||
}
|
||||
|
||||
if (Cnt & (1<<13))
|
||||
|
@ -206,27 +242,70 @@ void DSi_CamModule::TransferScanline(u32 line)
|
|||
memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32));
|
||||
}
|
||||
|
||||
u32 numscan = Cnt & 0x000F;
|
||||
buffer->WritePos += copylen;
|
||||
if (buffer->WritePos > 512) buffer->WritePos = 512;
|
||||
|
||||
numscan = Cnt & 0x000F;
|
||||
if (BufferNumLines >= numscan)
|
||||
{
|
||||
BufferReadPos = 0; // checkme
|
||||
BufferWritePos = 0;
|
||||
BufferNumLines = 0;
|
||||
DSi.CheckNDMAs(0, 0x0B);
|
||||
SwapPixelBuffers();
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferWritePos += copylen;
|
||||
if (BufferWritePos > 512) BufferWritePos = 512;
|
||||
BufferNumLines++;
|
||||
}
|
||||
|
||||
if (CurCamera->TransferDone())
|
||||
skip_line:
|
||||
bool done = CurCamera->TransferDone();
|
||||
if (done || line_last)
|
||||
{
|
||||
// when the frame is finished, transfer any remaining data if needed
|
||||
// (if the frame height isn't a multiple of the DMA interval)
|
||||
if (BufferNumLines > 0)
|
||||
{
|
||||
BufferNumLines = 0;
|
||||
SwapPixelBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
if (done)
|
||||
{
|
||||
Transferring = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
|
||||
}
|
||||
|
||||
void DSi_CamModule::SwapPixelBuffers()
|
||||
{
|
||||
// pixel buffers are swapped every time a buffer is filled (ie. when the DMA interval is reached)
|
||||
// the swap fails if the other buffer isn't empty
|
||||
|
||||
sPixelBuffer* otherbuf = &PixelBuffer[CurPixelBuffer ^ 1];
|
||||
if (otherbuf->ReadPos < otherbuf->WritePos)
|
||||
{
|
||||
// overrun
|
||||
Cnt |= (1<<4);
|
||||
Transferring = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PixelBuffer[CurPixelBuffer].ReadPos = 0;
|
||||
otherbuf->WritePos = 0;
|
||||
CurPixelBuffer ^= 1;
|
||||
DSi.CheckNDMAs(0, 0x0B);
|
||||
}
|
||||
}
|
||||
|
||||
bool DSi_CamModule::IsTransferring()
|
||||
{
|
||||
if (Cnt & (1<<15)) return true;
|
||||
if (Transferring) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
u8 DSi_CamModule::Read8(u32 addr)
|
||||
{
|
||||
|
@ -241,7 +320,7 @@ u16 DSi_CamModule::Read16(u32 addr)
|
|||
switch (addr)
|
||||
{
|
||||
case 0x04004200: return ModuleCnt;
|
||||
case 0x04004202: return Cnt;
|
||||
case 0x04004202: return Cnt | (Transferring ? (1<<15) : 0);
|
||||
}
|
||||
|
||||
Log(LogLevel::Debug, "unknown DSi cam read16 %08X\n", addr);
|
||||
|
@ -254,14 +333,14 @@ u32 DSi_CamModule::Read32(u32 addr)
|
|||
{
|
||||
case 0x04004204:
|
||||
{
|
||||
u32 ret = DataBuffer[BufferReadPos];
|
||||
if (Cnt & (1<<15))
|
||||
{
|
||||
if (BufferReadPos < 511)
|
||||
BufferReadPos++;
|
||||
// CHECKME!!!!
|
||||
// also presumably we should set bit4 in Cnt if there's no new data to be read
|
||||
}
|
||||
sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer ^ 1];
|
||||
u32 ret;
|
||||
if (buffer->ReadPos < buffer->WritePos)
|
||||
ret = buffer->Data[buffer->ReadPos++];
|
||||
else if (buffer->ReadPos > 0)
|
||||
ret = buffer->Data[buffer->ReadPos - 1];
|
||||
else
|
||||
ret = buffer->Data[0];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -296,6 +375,7 @@ void DSi_CamModule::Write16(u32 addr, u16 val)
|
|||
// CHECKME
|
||||
|
||||
Cnt = 0;
|
||||
Transferring = false;
|
||||
}
|
||||
|
||||
if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5)))
|
||||
|
@ -307,12 +387,9 @@ void DSi_CamModule::Write16(u32 addr, u16 val)
|
|||
|
||||
case 0x04004202:
|
||||
{
|
||||
// TODO: during a transfer, clearing bit15 does not reflect immediately
|
||||
// maybe it needs to finish the trasnfer or atleast the current block
|
||||
|
||||
// checkme
|
||||
u16 oldmask;
|
||||
if (Cnt & 0x8000)
|
||||
if (IsTransferring())
|
||||
{
|
||||
val &= 0x8F20;
|
||||
oldmask = 0x601F;
|
||||
|
@ -327,32 +404,26 @@ void DSi_CamModule::Write16(u32 addr, u16 val)
|
|||
if (val & (1<<5))
|
||||
{
|
||||
Cnt &= ~(1<<4);
|
||||
BufferReadPos = 0;
|
||||
BufferWritePos = 0;
|
||||
}
|
||||
|
||||
if ((val & (1<<15)) && !(Cnt & (1<<15)))
|
||||
{
|
||||
// start transfer
|
||||
//DSi::CheckNDMAs(0, 0x0B);
|
||||
memset(PixelBuffer, 0, sizeof(PixelBuffer));
|
||||
CurPixelBuffer = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case 0x04004210:
|
||||
if (Cnt & (1<<15)) return;
|
||||
if (IsTransferring()) return;
|
||||
CropStart = (CropStart & 0x01FF0000) | (val & 0x03FE);
|
||||
return;
|
||||
case 0x04004212:
|
||||
if (Cnt & (1<<15)) return;
|
||||
if (IsTransferring()) return;
|
||||
CropStart = (CropStart & 0x03FE) | ((val & 0x01FF) << 16);
|
||||
return;
|
||||
case 0x04004214:
|
||||
if (Cnt & (1<<15)) return;
|
||||
if (IsTransferring()) return;
|
||||
CropEnd = (CropEnd & 0x01FF0000) | (val & 0x03FE);
|
||||
return;
|
||||
case 0x04004216:
|
||||
if (Cnt & (1<<15)) return;
|
||||
if (IsTransferring()) return;
|
||||
CropEnd = (CropEnd & 0x03FE) | ((val & 0x01FF) << 16);
|
||||
return;
|
||||
}
|
||||
|
@ -365,11 +436,11 @@ void DSi_CamModule::Write32(u32 addr, u32 val)
|
|||
switch (addr)
|
||||
{
|
||||
case 0x04004210:
|
||||
if (Cnt & (1<<15)) return;
|
||||
if (IsTransferring()) return;
|
||||
CropStart = val & 0x01FF03FE;
|
||||
return;
|
||||
case 0x04004214:
|
||||
if (Cnt & (1<<15)) return;
|
||||
if (IsTransferring()) return;
|
||||
CropEnd = val & 0x01FF03FE;
|
||||
return;
|
||||
}
|
||||
|
@ -429,6 +500,7 @@ void DSi_Camera::Reset()
|
|||
// default state is preview mode (checkme)
|
||||
MCURegs[0x2104] = 3;
|
||||
|
||||
InternalY = 0;
|
||||
TransferY = 0;
|
||||
memset(FrameBuffer, 0, (640*480/2)*sizeof(u32));
|
||||
}
|
||||
|
@ -449,6 +521,7 @@ bool DSi_Camera::IsActivated() const
|
|||
|
||||
void DSi_Camera::StartTransfer()
|
||||
{
|
||||
InternalY = 0;
|
||||
TransferY = 0;
|
||||
|
||||
u8 state = MCURegs[0x2104];
|
||||
|
@ -482,9 +555,11 @@ bool DSi_Camera::TransferDone() const
|
|||
return TransferY >= FrameHeight;
|
||||
}
|
||||
|
||||
int DSi_Camera::TransferScanline(u32* buffer, int maxlen)
|
||||
int DSi_Camera::TransferScanline(u32* buffer, int maxlen, int& nlines)
|
||||
{
|
||||
if (TransferY >= FrameHeight)
|
||||
nlines = 0;
|
||||
|
||||
if ((TransferY >= FrameHeight) || (InternalY >= 480))
|
||||
return 0;
|
||||
|
||||
if (FrameWidth > 640 || FrameHeight > 480 ||
|
||||
|
@ -500,7 +575,7 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen)
|
|||
// TODO: non-YUV pixel formats and all
|
||||
|
||||
int retlen = FrameWidth >> 1;
|
||||
int sy = (TransferY * 480) / FrameHeight;
|
||||
int sy = InternalY;
|
||||
if (FrameReadMode & (1<<1))
|
||||
sy = 479 - sy;
|
||||
|
||||
|
@ -529,7 +604,15 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen)
|
|||
}
|
||||
}
|
||||
|
||||
TransferY++;
|
||||
// determine how many scanlines we're skipping until the next scanline
|
||||
int oldy = TransferY;
|
||||
do
|
||||
{
|
||||
InternalY++;
|
||||
TransferY = (InternalY * FrameHeight) / 480;
|
||||
nlines++;
|
||||
}
|
||||
while ((TransferY == oldy) && (InternalY < 480));
|
||||
|
||||
return retlen;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -44,7 +44,7 @@ public:
|
|||
bool TransferDone() const;
|
||||
|
||||
// lengths in words
|
||||
int TransferScanline(u32* buffer, int maxlen);
|
||||
int TransferScanline(u32* buffer, int maxlen, int& nlines);
|
||||
|
||||
void Acquire() override;
|
||||
u8 Read(bool last) override;
|
||||
|
@ -77,6 +77,7 @@ private:
|
|||
|
||||
u16 FrameWidth, FrameHeight;
|
||||
u16 FrameReadMode, FrameFormat;
|
||||
int InternalY;
|
||||
int TransferY;
|
||||
u32 FrameBuffer[640*480/2]; // YUYV framebuffer, two pixels per word
|
||||
};
|
||||
|
@ -117,14 +118,26 @@ private:
|
|||
|
||||
u32 CropStart, CropEnd;
|
||||
|
||||
// pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are
|
||||
u32 DataBuffer[512];
|
||||
u32 BufferReadPos, BufferWritePos;
|
||||
bool Transferring;
|
||||
|
||||
// pixel data buffers hold a maximum of 512 words, regardless of how long scanlines are
|
||||
typedef struct
|
||||
{
|
||||
u32 Data[512];
|
||||
u32 ReadPos, WritePos;
|
||||
|
||||
} sPixelBuffer;
|
||||
sPixelBuffer PixelBuffer[2];
|
||||
u8 CurPixelBuffer;
|
||||
u32 BufferNumLines;
|
||||
DSi_Camera* CurCamera;
|
||||
|
||||
static const u32 kIRQInterval;
|
||||
static const u32 kScanlineTime;
|
||||
static const u32 kTransferStart;
|
||||
|
||||
void SwapPixelBuffers();
|
||||
bool IsTransferring();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ void DSi_DSP::AudioCb(std::array<s16, 2> frame)
|
|||
|
||||
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
||||
{
|
||||
DSi.RegisterEventFunc(Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32));
|
||||
DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
|
||||
|
||||
TeakraCore = new Teakra::Teakra();
|
||||
SCFG_RST = false;
|
||||
|
@ -156,7 +156,7 @@ DSi_DSP::~DSi_DSP()
|
|||
//PDATAWriteFifo = NULL;
|
||||
TeakraCore = NULL;
|
||||
|
||||
DSi.UnregisterEventFunc(Event_DSi_DSP, 0);
|
||||
DSi.UnregisterEventFuncs(Event_DSi_DSP);
|
||||
}
|
||||
|
||||
void DSi_DSP::Reset()
|
||||
|
@ -321,7 +321,7 @@ void DSi_DSP::PDataDMAFetch()
|
|||
}
|
||||
void DSi_DSP::PDataDMAStart()
|
||||
{
|
||||
switch ((DSP_PSTS & (3<<2)) >> 2)
|
||||
switch ((DSP_PCFG & (3<<2)) >> 2)
|
||||
{
|
||||
case 0: PDataDMALen = 1; break;
|
||||
case 1: PDataDMALen = 8; break;
|
||||
|
@ -346,7 +346,7 @@ void DSi_DSP::PDataDMACancel()
|
|||
}
|
||||
u16 DSi_DSP::PDataDMAReadMMIO()
|
||||
{
|
||||
u16 ret;
|
||||
u16 ret = 0; // TODO: is this actually 0, or just open bus?
|
||||
|
||||
if (!PDATAReadFifo.IsEmpty())
|
||||
ret = PDATAReadFifo.Read();
|
||||
|
@ -360,15 +360,9 @@ u16 DSi_DSP::PDataDMAReadMMIO()
|
|||
|
||||
for (int i = 0; i < left; ++i)
|
||||
PDataDMAFetch();
|
||||
|
||||
ret = PDATAReadFifo.Read();
|
||||
}
|
||||
else
|
||||
{
|
||||
// ah, crap
|
||||
ret = 0; // TODO: is this actually 0, or just open bus?
|
||||
}
|
||||
|
||||
// TODO only trigger IRQ if enabled!!
|
||||
if (!PDATAReadFifo.IsEmpty() || PDATAReadFifo.IsFull())
|
||||
DSi.SetIRQ(0, IRQ_DSi_DSP);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -189,20 +189,18 @@ void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr)
|
|||
u8 iv[16];
|
||||
memcpy(iv, FATIV.data(), sizeof(iv));
|
||||
|
||||
u32 res;
|
||||
res = iv[15] + (ctr & 0xFF);
|
||||
iv[15] = (res & 0xFF);
|
||||
res = iv[14] + ((ctr >> 8) & 0xFF) + (res >> 8);
|
||||
iv[14] = (res & 0xFF);
|
||||
res = iv[13] + ((ctr >> 16) & 0xFF) + (res >> 8);
|
||||
iv[13] = (res & 0xFF);
|
||||
res = iv[12] + (ctr >> 24) + (res >> 8);
|
||||
iv[12] = (res & 0xFF);
|
||||
iv[11] += (res >> 8);
|
||||
for (int i = 10; i >= 0; i--)
|
||||
{
|
||||
if (iv[i+1] == 0) iv[i]++;
|
||||
else break;
|
||||
u8 ctr_value[16] = {0};
|
||||
ctr_value[15] = ctr & 0xFF;
|
||||
ctr_value[14] = (ctr >> 8) & 0xFF;
|
||||
ctr_value[13] = (ctr >> 16) & 0xFF;
|
||||
ctr_value[12] = (ctr >> 24) & 0xFF;
|
||||
|
||||
unsigned carry = 0;
|
||||
for (unsigned i = 0; i < 16; i ++) {
|
||||
unsigned j = 15-i;
|
||||
unsigned x = iv[j] + ctr_value[j] + carry;
|
||||
carry = x >= 0x100;
|
||||
iv[j] = x;
|
||||
}
|
||||
|
||||
AES_init_ctx_iv(ctx, FATKey.data(), iv);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -132,7 +132,6 @@ void DSi_NDMA::WriteCnt(u32 val)
|
|||
|
||||
// TODO: unsupported start modes:
|
||||
// * timers (00-03)
|
||||
// * camera (ARM9 0B)
|
||||
// * microphone (ARM7 0C)
|
||||
// * NDS-wifi?? (ARM7 07, likely not working)
|
||||
|
||||
|
@ -270,11 +269,18 @@ void DSi_NDMA::Run9()
|
|||
|
||||
if ((StartMode & 0x1F) == 0x10) // CHECKME
|
||||
{
|
||||
// no repeat
|
||||
Cnt &= ~(1<<31);
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
else if (!(Cnt & (1<<29)))
|
||||
else if (Cnt & (1<<29))
|
||||
{
|
||||
// repeat infinitely
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
else
|
||||
{
|
||||
// repeat until total count is reached
|
||||
if (TotalRemCount == 0)
|
||||
{
|
||||
Cnt &= ~(1<<31);
|
||||
|
@ -359,11 +365,18 @@ void DSi_NDMA::Run7()
|
|||
|
||||
if ((StartMode & 0x1F) == 0x10) // CHECKME
|
||||
{
|
||||
// no repeat
|
||||
Cnt &= ~(1<<31);
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
else if (!(Cnt & (1<<29)))
|
||||
else if (Cnt & (1<<29))
|
||||
{
|
||||
// repeat infinitely
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
else
|
||||
{
|
||||
// repeat until total count is reached
|
||||
if (TotalRemCount == 0)
|
||||
{
|
||||
Cnt &= ~(1<<31);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -31,7 +31,7 @@ using Platform::Log;
|
|||
using Platform::LogLevel;
|
||||
|
||||
|
||||
const u8 CIS0[256] =
|
||||
u8 CIS0[256] =
|
||||
{
|
||||
0x01, 0x03, 0xD9, 0x01, 0xFF,
|
||||
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
|
||||
|
@ -70,7 +70,7 @@ const u8 CIS0[256] =
|
|||
0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const u8 CIS1[256] =
|
||||
u8 CIS1[256] =
|
||||
{
|
||||
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
|
||||
0x21, 0x02, 0x0C, 0x00,
|
||||
|
@ -134,7 +134,7 @@ DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) :
|
|||
},
|
||||
DSi(dsi)
|
||||
{
|
||||
DSi.RegisterEventFunc(Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer));
|
||||
DSi.RegisterEventFuncs(Event_DSi_NWifi, this, {MakeEventThunk(DSi_NWifi, MSTimer)});
|
||||
|
||||
// this seems to control whether the firmware upload is done
|
||||
EEPROMReady = 0;
|
||||
|
@ -144,7 +144,7 @@ DSi_NWifi::~DSi_NWifi()
|
|||
{
|
||||
DSi.CancelEvent(Event_DSi_NWifi);
|
||||
|
||||
DSi.UnregisterEventFunc(Event_DSi_NWifi, 0);
|
||||
DSi.UnregisterEventFuncs(Event_DSi_NWifi);
|
||||
}
|
||||
|
||||
void DSi_NWifi::Reset()
|
||||
|
@ -201,6 +201,9 @@ void DSi_NWifi::Reset()
|
|||
break;
|
||||
}
|
||||
|
||||
CIS0[9] = ChipID >= 0x0D000000;
|
||||
CIS1[4] = CIS0[9];
|
||||
|
||||
memset(EEPROM, 0, 0x400);
|
||||
|
||||
*(u32*)&EEPROM[0x000] = 0x300;
|
||||
|
@ -227,6 +230,8 @@ void DSi_NWifi::Reset()
|
|||
BeaconTimer = 0x10A2220ULL;
|
||||
ConnectionStatus = 0;
|
||||
|
||||
SendBSSInfo = true;
|
||||
|
||||
DSi.CancelEvent(Event_DSi_NWifi);
|
||||
}
|
||||
|
||||
|
@ -1001,7 +1006,7 @@ void DSi_NWifi::WMI_Command()
|
|||
}
|
||||
|
||||
// checkme
|
||||
ScanTimer = scantime*5;
|
||||
ScanTimer = scantime*8;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1036,6 +1041,7 @@ void DSi_NWifi::WMI_Command()
|
|||
|
||||
// TODO: store it somewhere
|
||||
Log(LogLevel::Debug, "WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid);
|
||||
SendBSSInfo = flags == 0 || strcmp(ssid, WifiAP::APName) == 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1405,6 +1411,11 @@ void DSi_NWifi::SendWMIAck(u8 ep)
|
|||
|
||||
void DSi_NWifi::SendWMIBSSInfo(u8 type, u8* data, u32 len)
|
||||
{
|
||||
if (!SendBSSInfo) {
|
||||
Log(LogLevel::Info, "NWifi: melonAP filtered, not sending WMI BSSINFO event\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Mailbox[8].CanFit(6+len+2+16))
|
||||
{
|
||||
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -147,6 +147,8 @@ private:
|
|||
u32 ConnectionStatus;
|
||||
|
||||
u8 LANBuffer[2048];
|
||||
|
||||
bool SendBSSInfo;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -64,10 +64,9 @@ enum
|
|||
|
||||
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
|
||||
{
|
||||
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
|
||||
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
|
||||
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
|
||||
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
|
||||
DSi.RegisterEventFuncs(Event_DSi_SDMMCTransfer, this,
|
||||
{MakeEventThunk(DSi_SDHost, FinishTX),
|
||||
MakeEventThunk(DSi_SDHost, FinishRX)});
|
||||
|
||||
Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr;
|
||||
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
|
||||
|
@ -77,10 +76,9 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optio
|
|||
// 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));
|
||||
DSi.RegisterEventFuncs(Event_DSi_SDIOTransfer, this,
|
||||
{MakeEventThunk(DSi_SDHost, FinishTX),
|
||||
MakeEventThunk(DSi_SDHost, FinishRX)});
|
||||
|
||||
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
|
||||
Ports[1] = nullptr;
|
||||
|
@ -88,10 +86,7 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
|
|||
|
||||
DSi_SDHost::~DSi_SDHost()
|
||||
{
|
||||
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
||||
Transfer_TX);
|
||||
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
||||
Transfer_RX);
|
||||
DSi.UnregisterEventFuncs(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer);
|
||||
|
||||
// unique_ptr's destructor will clean up the ports
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
134
src/GBACart.cpp
134
src/GBACart.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -242,8 +242,6 @@ u16 CartGame::ROMRead(u32 addr) const
|
|||
case 0xC8: return GPIO.control;
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CHECKME: does ROM mirror?
|
||||
|
@ -539,6 +537,57 @@ CartGameSolarSensor::CartGameSolarSensor(std::unique_ptr<u8[]>&& rom, u32 len, s
|
|||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata) noexcept
|
||||
{
|
||||
return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata);
|
||||
}
|
||||
|
||||
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata) noexcept
|
||||
{
|
||||
return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata);
|
||||
}
|
||||
|
||||
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata) noexcept
|
||||
{
|
||||
if (!gamecode)
|
||||
return nullptr;
|
||||
|
||||
if (strnlen(gamecode, sizeof(GBAHeader::GameCode)) > sizeof(GBAHeader::GameCode))
|
||||
return nullptr;
|
||||
|
||||
bool solarsensor = false;
|
||||
for (const char* i : SOLAR_SENSOR_GAMECODES)
|
||||
{
|
||||
if (strcmp(gamecode, i) == 0) {
|
||||
solarsensor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!solarsensor)
|
||||
return nullptr;
|
||||
|
||||
// just 256 bytes; we don't need a whole ROM!
|
||||
constexpr size_t FAKE_BOKTAI_ROM_LENGTH = 0x100;
|
||||
std::unique_ptr<u8[]> rom = std::make_unique<u8[]>(FAKE_BOKTAI_ROM_LENGTH);
|
||||
|
||||
// create a fake ROM
|
||||
GBAHeader& header = *reinterpret_cast<GBAHeader*>(rom.get());
|
||||
memcpy(header.Title, BOKTAI_STUB_TITLE, strnlen(BOKTAI_STUB_TITLE, sizeof(header.Title)));
|
||||
memcpy(header.GameCode, gamecode, strnlen(gamecode, sizeof(header.GameCode)));
|
||||
header.FixedValue = 0x96;
|
||||
if (logo)
|
||||
{
|
||||
memcpy(header.NintendoLogo, logo, sizeof(header.NintendoLogo));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(header.NintendoLogo, 0xFF, sizeof(header.NintendoLogo));
|
||||
}
|
||||
|
||||
return std::make_unique<CartGameSolarSensor>(std::move(rom), FAKE_BOKTAI_ROM_LENGTH, nullptr, 0, userdata);
|
||||
}
|
||||
|
||||
const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183};
|
||||
|
||||
void CartGameSolarSensor::Reset()
|
||||
|
@ -724,6 +773,27 @@ void CartRumblePak::ROMWrite(u32 addr, u16 val)
|
|||
}
|
||||
}
|
||||
|
||||
CartGuitarGrip::CartGuitarGrip(void* userdata) :
|
||||
CartCommon(GuitarGrip),
|
||||
UserData(userdata)
|
||||
{
|
||||
}
|
||||
|
||||
CartGuitarGrip::~CartGuitarGrip() = default;
|
||||
|
||||
u16 CartGuitarGrip::ROMRead(u32 addr) const
|
||||
{
|
||||
return 0xF9FF;
|
||||
}
|
||||
|
||||
u8 CartGuitarGrip::SRAMRead(u32 addr)
|
||||
{
|
||||
return ~((Platform::Addon_KeyDown(Platform::KeyGuitarGripGreen, UserData) ? 0x40 : 0)
|
||||
| (Platform::Addon_KeyDown(Platform::KeyGuitarGripRed, UserData) ? 0x20 : 0)
|
||||
| (Platform::Addon_KeyDown(Platform::KeyGuitarGripYellow, UserData) ? 0x10 : 0)
|
||||
| (Platform::Addon_KeyDown(Platform::KeyGuitarGripBlue, UserData) ? 0x08 : 0));
|
||||
}
|
||||
|
||||
GBACartSlot::GBACartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& cart) noexcept : NDS(nds), Cart(std::move(cart))
|
||||
{
|
||||
}
|
||||
|
@ -832,6 +902,47 @@ std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen
|
|||
return cart;
|
||||
}
|
||||
|
||||
std::unique_ptr<CartCommon> LoadAddon(int type, void* userdata)
|
||||
{
|
||||
std::unique_ptr<CartCommon> cart;
|
||||
switch (type)
|
||||
{
|
||||
case GBAAddon_RAMExpansion:
|
||||
cart = std::make_unique<CartRAMExpansion>();
|
||||
break;
|
||||
case GBAAddon_RumblePak:
|
||||
cart = std::make_unique<CartRumblePak>(userdata);
|
||||
break;
|
||||
case GBAAddon_SolarSensorBoktai1:
|
||||
// US Boktai 1
|
||||
cart = CreateFakeSolarSensorROM("U3IE", nullptr, userdata);
|
||||
break;
|
||||
case GBAAddon_SolarSensorBoktai2:
|
||||
// US Boktai 2
|
||||
cart = CreateFakeSolarSensorROM("U32E", nullptr, userdata);
|
||||
break;
|
||||
case GBAAddon_SolarSensorBoktai3:
|
||||
// JP Boktai 3
|
||||
cart = CreateFakeSolarSensorROM("U33J", nullptr, userdata);
|
||||
break;
|
||||
case GBAAddon_MotionPakHomebrew:
|
||||
cart = std::make_unique<CartMotionPakHomebrew>(userdata);
|
||||
break;
|
||||
case GBAAddon_MotionPakRetail:
|
||||
cart = std::make_unique<CartMotionPakRetail>(userdata);
|
||||
break;
|
||||
case GBAAddon_GuitarGrip:
|
||||
cart = std::make_unique<CartGuitarGrip>(userdata);
|
||||
break;
|
||||
default:
|
||||
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cart->Reset();
|
||||
return cart;
|
||||
}
|
||||
|
||||
void GBACartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
|
||||
{
|
||||
Cart = std::move(cart);
|
||||
|
@ -864,23 +975,6 @@ void GBACartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
void GBACartSlot::LoadAddon(void* userdata, int type) noexcept
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GBAAddon_RAMExpansion:
|
||||
Cart = std::make_unique<CartRAMExpansion>();
|
||||
break;
|
||||
case GBAAddon_RumblePak:
|
||||
Cart = std::make_unique<CartRumblePak>(userdata);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CartCommon> GBACartSlot::EjectCart() noexcept
|
||||
{
|
||||
return std::move(Cart);
|
||||
|
|
104
src/GBACart.h
104
src/GBACart.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -33,8 +33,30 @@ enum CartType
|
|||
GameSolarSensor = 0x102,
|
||||
RAMExpansion = 0x201,
|
||||
RumblePak = 0x202,
|
||||
MotionPakHomebrew = 0x203,
|
||||
MotionPakRetail = 0x204,
|
||||
GuitarGrip = 0x205,
|
||||
};
|
||||
|
||||
// See https://problemkaputt.de/gbatek.htm#gbacartridgeheader for details
|
||||
struct GBAHeader
|
||||
{
|
||||
u32 EntryPoint;
|
||||
u8 NintendoLogo[156]; // must be valid
|
||||
char Title[12];
|
||||
char GameCode[4];
|
||||
char MakerCode[2];
|
||||
u8 FixedValue; // must be 0x96
|
||||
u8 MainUnitCode;
|
||||
u8 DeviceType;
|
||||
u8 Reserved0[7];
|
||||
u8 SoftwareVersion;
|
||||
u8 ComplementCheck;
|
||||
u8 Reserved1[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(GBAHeader) == 192, "GBAHeader should be 192 bytes");
|
||||
|
||||
// CartCommon -- base code shared by all cart types
|
||||
class CartCommon
|
||||
{
|
||||
|
@ -91,6 +113,8 @@ public:
|
|||
|
||||
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
|
||||
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
|
||||
[[nodiscard]] const GBAHeader& GetHeader() const noexcept { return *reinterpret_cast<const GBAHeader*>(ROM.get()); }
|
||||
[[nodiscard]] GBAHeader& GetHeader() noexcept { return *reinterpret_cast<GBAHeader*>(ROM.get()); }
|
||||
|
||||
u8* GetSaveMemory() const override;
|
||||
u32 GetSaveMemoryLength() const override;
|
||||
|
@ -211,11 +235,68 @@ private:
|
|||
u16 RumbleState = 0;
|
||||
};
|
||||
|
||||
// CartGuitarGrip -- DS Guitar Grip (used in various NDS games)
|
||||
class CartGuitarGrip : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartGuitarGrip(void* userdata);
|
||||
~CartGuitarGrip() override;
|
||||
|
||||
u16 ROMRead(u32 addr) const override;
|
||||
u8 SRAMRead(u32 addr) override;
|
||||
|
||||
private:
|
||||
void* UserData;
|
||||
};
|
||||
|
||||
// CartMotionPakHomebrew -- DS Motion Pak (Homebrew)
|
||||
class CartMotionPakHomebrew : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartMotionPakHomebrew(void* userdata);
|
||||
~CartMotionPakHomebrew() override;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
u16 ROMRead(u32 addr) const override;
|
||||
u8 SRAMRead(u32 addr) override;
|
||||
|
||||
private:
|
||||
void* UserData;
|
||||
u16 ShiftVal = 0;
|
||||
};
|
||||
|
||||
// CartMotionPakRetail -- DS Motion Pack (Retail)
|
||||
class CartMotionPakRetail : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartMotionPakRetail(void* userdata);
|
||||
~CartMotionPakRetail() override;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
u16 ROMRead(u32 addr) const override;
|
||||
u8 SRAMRead(u32 addr) override;
|
||||
|
||||
private:
|
||||
void* UserData;
|
||||
u8 Value;
|
||||
u8 Step = 16;
|
||||
};
|
||||
|
||||
// possible inputs for GBA carts that might accept user input
|
||||
enum
|
||||
{
|
||||
Input_SolarSensorDown = 0,
|
||||
Input_SolarSensorUp,
|
||||
Input_GuitarGripGreen,
|
||||
Input_GuitarGripRed,
|
||||
Input_GuitarGripYellow,
|
||||
Input_GuitarGripBlue,
|
||||
};
|
||||
|
||||
class GBACartSlot
|
||||
|
@ -241,8 +322,6 @@ public:
|
|||
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
|
||||
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
|
||||
|
||||
void LoadAddon(void* userdata, int type) noexcept;
|
||||
|
||||
/// @return The cart that was in the cart slot if any,
|
||||
/// or \c nullptr if the cart slot was empty.
|
||||
std::unique_ptr<CartCommon> EjectCart() noexcept;
|
||||
|
@ -309,6 +388,25 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, const u8* sr
|
|||
/// or \c nullptr if there was an error.
|
||||
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen, void* userdata = nullptr);
|
||||
|
||||
std::unique_ptr<CartCommon> LoadAddon(int type, void* userdata);
|
||||
|
||||
/// Creates a solar sensor-enabled GBA cart without needing a real Boktai ROM.
|
||||
/// This enables the solar sensor to be used in supported games.
|
||||
/// Will not contain any SRAM.
|
||||
/// @param gamecode
|
||||
/// @param logo The Nintendo logo data embedded in the headers for GBA ROMs and NDS ROMs.
|
||||
/// Required for the cart to be recognized as a valid GBA cart.
|
||||
/// Overloads that accept cart objects directly exist as well.
|
||||
/// If not provided, then it will have to be patched with equivalent data
|
||||
/// from a real ROM (NDS or GBA) before booting the emulator.
|
||||
/// @param userdata Optional user data to associate with the cart.
|
||||
/// @return A CartGameSolarSensor if the ROM was created successfully,
|
||||
/// or nullptr if any argument is wrong (e.g. an incorrect game code).
|
||||
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata = nullptr) noexcept;
|
||||
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata = nullptr) noexcept;
|
||||
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata = nullptr) noexcept;
|
||||
|
||||
constexpr const char* BOKTAI_STUB_TITLE = "BOKTAI STUB";
|
||||
}
|
||||
|
||||
#endif // GBACART_H
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
Copyright 2016-2024 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/.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "NDS.h"
|
||||
#include "GBACart.h"
|
||||
#include "Platform.h"
|
||||
#include <algorithm>
|
||||
#include "math.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace GBACart
|
||||
{
|
||||
|
||||
CartMotionPakHomebrew::CartMotionPakHomebrew(void* userdata) :
|
||||
CartCommon(MotionPakHomebrew),
|
||||
UserData(userdata)
|
||||
{
|
||||
}
|
||||
|
||||
CartMotionPakHomebrew::~CartMotionPakHomebrew() = default;
|
||||
|
||||
void CartMotionPakHomebrew::Reset()
|
||||
{
|
||||
ShiftVal = 0;
|
||||
}
|
||||
|
||||
void CartMotionPakHomebrew::DoSavestate(Savestate* file)
|
||||
{
|
||||
CartCommon::DoSavestate(file);
|
||||
file->Var16(&ShiftVal);
|
||||
}
|
||||
|
||||
u16 CartMotionPakHomebrew::ROMRead(u32 addr) const
|
||||
{
|
||||
// CHECKME: Does this apply to the homebrew cart as well?
|
||||
return 0xFCFF;
|
||||
}
|
||||
|
||||
static int AccelerationToMotionPak(float accel)
|
||||
{
|
||||
const float GRAVITY_M_S2 = 9.80665f;
|
||||
|
||||
return std::clamp(
|
||||
(int) ((accel / (5 * GRAVITY_M_S2) + 0.5) * 4096),
|
||||
0, 4095
|
||||
);
|
||||
}
|
||||
|
||||
static int AccelerationToMotionPakRetail(float accel)
|
||||
{
|
||||
const float GRAVITY_M_S2 = 9.80665f;
|
||||
|
||||
return std::clamp(
|
||||
(int) ((accel / (5 * GRAVITY_M_S2) + 0.5) * 256),
|
||||
0, 254
|
||||
);
|
||||
}
|
||||
|
||||
static int RotationToMotionPak(float rot)
|
||||
{
|
||||
const float DEGREES_PER_RAD = 180 / M_PI;
|
||||
const float COUNTS_PER_DEG_PER_SEC = 0.825;
|
||||
const int CENTER = 1680;
|
||||
|
||||
return std::clamp(
|
||||
(int) ((rot * DEGREES_PER_RAD * COUNTS_PER_DEG_PER_SEC) + CENTER + 0.5),
|
||||
0, 4095
|
||||
);
|
||||
}
|
||||
|
||||
u8 CartMotionPakHomebrew::SRAMRead(u32 addr)
|
||||
{
|
||||
// CHECKME: SRAM address mask
|
||||
addr &= 0xFFFF;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
// Read next byte
|
||||
break;
|
||||
case 2:
|
||||
// Read X acceleration
|
||||
ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationX, UserData)) << 4;
|
||||
// CHECKME: First byte returned when reading acceleration/rotation
|
||||
return 0;
|
||||
case 4:
|
||||
// Read Y acceleration
|
||||
ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationY, UserData)) << 4;
|
||||
return 0;
|
||||
case 6:
|
||||
// Read Z acceleration
|
||||
ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationZ, UserData)) << 4;
|
||||
return 0;
|
||||
case 8:
|
||||
// Read Z rotation
|
||||
// CHECKME: This is a guess, compare with real hardware
|
||||
ShiftVal = RotationToMotionPak(-Platform::Addon_MotionQuery(Platform::MotionRotationZ, UserData)) << 4;
|
||||
return 0;
|
||||
case 10:
|
||||
// Identify cart
|
||||
ShiftVal = 0xF00F;
|
||||
return 0;
|
||||
case 12:
|
||||
case 14:
|
||||
case 16:
|
||||
case 18:
|
||||
// Read/enable analog inputs
|
||||
//
|
||||
// These are not connected by defualt and require do-it-yourself cart
|
||||
// modification, so there is no reason to emulate them.
|
||||
ShiftVal = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Read high byte from the emulated shift register
|
||||
u8 val = ShiftVal >> 8;
|
||||
ShiftVal <<= 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
CartMotionPakRetail::CartMotionPakRetail(void* userdata) :
|
||||
CartCommon(MotionPakRetail),
|
||||
UserData(userdata)
|
||||
{
|
||||
}
|
||||
|
||||
CartMotionPakRetail::~CartMotionPakRetail() = default;
|
||||
|
||||
void CartMotionPakRetail::Reset()
|
||||
{
|
||||
Value = 0;
|
||||
Step = 16;
|
||||
}
|
||||
|
||||
void CartMotionPakRetail::DoSavestate(Savestate* file)
|
||||
{
|
||||
CartCommon::DoSavestate(file);
|
||||
file->Var8(&Value);
|
||||
file->Var8(&Step);
|
||||
}
|
||||
|
||||
u16 CartMotionPakRetail::ROMRead(u32 addr) const
|
||||
{
|
||||
// A9-A8 is pulled low on a real Motion Pack.
|
||||
return 0xFCFF;
|
||||
}
|
||||
|
||||
u8 CartMotionPakRetail::SRAMRead(u32 addr)
|
||||
{
|
||||
switch (Step)
|
||||
{
|
||||
case 0: // Synchronization - read 0xFF
|
||||
Value = 0xFF;
|
||||
break;
|
||||
case 4: // X acceleration
|
||||
Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationX, UserData));
|
||||
break;
|
||||
case 8: // Y acceleration
|
||||
Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationY, UserData));
|
||||
break;
|
||||
case 12: // Z acceleration
|
||||
Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationZ, UserData));
|
||||
break;
|
||||
case 16: // Synchronization - read 0b00
|
||||
Step = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int shift = 6 - ((Step & 3) * 2);
|
||||
Step++;
|
||||
return (Value >> shift) & 0x03;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
19
src/GPU.cpp
19
src/GPU.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -70,10 +70,13 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d, std::uniqu
|
|||
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>()),
|
||||
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this))
|
||||
{
|
||||
NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank));
|
||||
NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline));
|
||||
NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame));
|
||||
NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO));
|
||||
NDS.RegisterEventFuncs(Event_LCD, this,
|
||||
{
|
||||
MakeEventThunk(GPU, StartHBlank),
|
||||
MakeEventThunk(GPU, StartScanline),
|
||||
MakeEventThunk(GPU, FinishFrame)
|
||||
});
|
||||
NDS.RegisterEventFuncs(Event_DisplayFIFO, this, {MakeEventThunk(GPU, DisplayFIFO)});
|
||||
|
||||
InitFramebuffers();
|
||||
}
|
||||
|
@ -82,10 +85,8 @@ GPU::~GPU() noexcept
|
|||
{
|
||||
// All unique_ptr fields are automatically cleaned up
|
||||
|
||||
NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank);
|
||||
NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline);
|
||||
NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame);
|
||||
NDS.UnregisterEventFunc(Event_DisplayFIFO, 0);
|
||||
NDS.UnregisterEventFuncs(Event_LCD);
|
||||
NDS.UnregisterEventFuncs(Event_DisplayFIFO);
|
||||
}
|
||||
|
||||
void GPU::ResetVRAMCache() noexcept
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2024 melonDS team
|
||||
Copyright 2016-2025 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -914,6 +914,9 @@ void SoftRenderer::DrawBG_3D()
|
|||
template<bool mosaic, SoftRenderer::DrawPixel drawPixel>
|
||||
void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum)
|
||||
{
|
||||
// workaround for backgrounds missing on aarch64 with lto build
|
||||
asm volatile ("" : : : "memory");
|
||||
|
||||
u16 bgcnt = CurUnit->BGCnt[bgnum];
|
||||
|
||||
u32 tilesetaddr, tilemapaddr;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue