Compare commits

..

No commits in common. "master" and "0.9.1" have entirely different histories.

715 changed files with 33973 additions and 191784 deletions

18
.gitattributes vendored
View File

@ -1,18 +0,0 @@
# Vendored Dependencies
src/frontend/glad/** linguist-vendored
src/frontend/qt_sdl/gif-h/** linguist-vendored
src/frontend/qt_sdl/toml/** linguist-vendored
src/net/libslirp/** linguist-vendored
src/net/pcap/** linguist-vendored
src/sha1/** linguist-vendored
src/teakra/** linguist-vendored
src/tiny-AES-c/** linguist-vendored
src/xxhash/** linguist-vendored
# A handful of custom files embedded in the vendored dependencies
## Ad-hoc CMakeLists.txt for melonDS
src/net/libslirp/src/CMakeLists.txt -linguist-vendored
## glib stub
src/net/libslirp/src/glib/** -linguist-vendored

3
.github/FUNDING.yml vendored
View File

@ -1,2 +1 @@
patreon: Arisotura
custom: ["https://paypal.me/Arisotura", "http://melonds.kuribo64.net/donate.php"]
patreon: staplebutter

View File

@ -1,94 +1,39 @@
name: macOS
name: CMake Build (macOS x86-64)
on:
push:
branches:
- master
- 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"
BUILD_TYPE: Release
jobs:
build-macos:
strategy:
matrix:
arch: [x86_64, arm64]
build:
name: ${{ matrix.arch }}
runs-on: macos-14
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install dependencies for package building
run: |
brew install autoconf automake autoconf-archive libtool python-setuptools
- name: Set up CMake
uses: lukka/get-cmake@latest
- name: Set up vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
- name: Build
uses: lukka/run-cmake@v10
with:
configurePreset: release-mac-${{ matrix.arch }}
buildPreset: release-mac-${{ matrix.arch }}
configurePresetAdditionalArgs: "['-DMELONDS_EMBED_BUILD_INFO=ON']"
- name: Compress app bundle
shell: bash
run: |
cd build/release-mac-${{ matrix.arch }}
zip -r -y ../../macOS-${{ matrix.arch }}.zip melonDS.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: macOS-${{ matrix.arch }}
path: macOS-${{ matrix.arch }}.zip
retention-days: 1
runs-on: macos-latest
universal-binary:
name: Universal binary
needs: [build-macos]
runs-on: macos-13
continue-on-error: true
steps:
- name: Download x86_64
uses: actions/download-artifact@v4
with:
name: macOS-x86_64
path: x86_64
- name: Download arm64
uses: actions/download-artifact@v4
with:
name: macOS-arm64
path: arm64
- name: Combine app bundles
shell: bash
run: |
unzip x86_64/*.zip -d x86_64
unzip arm64/*.zip -d arm64
lipo {x86_64,arm64}/melonDS.app/Contents/MacOS/melonDS -create -output melonDS
cp -a arm64/melonDS.app melonDS.app
cp melonDS melonDS.app/Contents/MacOS/melonDS
codesign -s - --deep melonDS.app
zip -r -y macOS-universal.zip melonDS.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: macOS-universal
path: macOS-universal.zip
# - name: Clean up architecture-specific artifacts
# uses: geekyeggo/delete-artifact@v4
# with:
# failOnError: false
# name: |
# macOS-x86_64
# macOS-arm64
- uses: actions/checkout@v1
- name: Install dependencies
working-directory: ${{runner.workspace}}
run: |
brew install cmake sdl2 qt5 libslirp
- name: Create build environment
run: mkdir ${{runner.workspace}}/build
- name: Configure
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
- name: Make
working-directory: ${{runner.workspace}}/build
run: |
make -j$(sysctl -n hw.ncpu)
mkdir dist
cp -r melonDS.app dist
- uses: actions/upload-artifact@v1
with:
name: melonDS.app
path: ${{runner.workspace}}/build/dist

View File

@ -0,0 +1,57 @@
name: CMake Build (Ubuntu aarch64)
on:
push:
branches:
- master
pull_request:
branches:
- master
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- name: Upgrade system
shell: bash
working-directory: ${{runner.workspace}}
run: | #Fix grub installation error - https://github.com/actions/virtual-environments/issues/1605
sudo apt update
sudo apt-get install grub-efi
sudo update-grub
sudo apt full-upgrade
- name: Install dependencies
shell: bash
working-directory: ${{runner.workspace}}
run: |
sudo rm -f /etc/apt/sources.list.d/kubernetes.list
sudo dpkg --add-architecture arm64
sudo 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"
sudo rm /etc/apt/sources.list
sudo mv /etc/apt/sources.list{.new,}
sudo apt update
sudo apt install aptitude
sudo aptitude install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu libsdl2-dev:arm64 qtbase5-dev:arm64 libslirp-dev:arm64
- name: Create build environment
run: mkdir ${{runner.workspace}}/build
- name: Configure
shell: bash
working-directory: ${{runner.workspace}}/build
run: |
CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Make
shell: bash
working-directory: ${{runner.workspace}}/build
run: |
make -j$(nproc --all) \
&& mkdir dist \
&& cp melonDS dist
- uses: actions/upload-artifact@v1
with:
name: melonDS
path: ${{runner.workspace}}/build/dist

View File

@ -1,63 +1,37 @@
name: Ubuntu
name: CMake Build (Ubuntu x86-64)
on:
push:
branches:
- master
- ci/*
pull_request:
branches:
- master
env:
MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions
MELONDS_VERSION_SUFFIX: " RC"
jobs:
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 }}
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
name: Check out sources
- uses: actions/checkout@v1
- name: Install dependencies
run: |
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
sudo apt update
sudo apt install --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
sudo apt install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default libslirp0 libslirp-dev --allow-downgrades
- name: Create build environment
run: mkdir ${{runner.workspace}}/build
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON
- name: Build
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE
- name: Make
working-directory: ${{runner.workspace}}/build
run: |
cmake --build build
DESTDIR=AppDir cmake --install build
- uses: actions/upload-artifact@v4
make -j$(nproc --all)
mkdir dist
cp melonDS dist
- uses: actions/upload-artifact@v1
with:
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-${{ 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-${{ matrix.arch.name }}.AppImage --appdir AppDir --plugin qt --output appimage
- uses: actions/upload-artifact@v4
with:
name: melonDS-appimage-${{ matrix.arch.name }}
path: melonDS*.AppImage
name: melonDS
path: ${{runner.workspace}}/build/dist

View File

@ -1,45 +1,43 @@
name: Windows
name: CMake Build (Windows x86-64)
on:
push:
branches:
- master
- 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"
BUILD_TYPE: Release
jobs:
build:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Set up MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: ucrt64
update: true
pacboy: gcc:p cmake:p ninja:p make:p
- name: Set up vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
- uses: actions/checkout@v1
- name: Install MSYS2
working-directory: ${{runner.workspace}}
run: | # Fetch MSYS2 build from XQEmu. Official distribution causes a CI failure due to permission errors.
choco install msys2
C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Syuq --noconfirm"
- name: Install dependencies
run: C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Sq --noconfirm git make mingw-w64-x86_64-{cmake,mesa,SDL2,qt5-static,libslirp,toolchain}"
- name: Create build environment
run: |
New-Item -ItemType directory -Path ${{runner.workspace}}\melonDS\build
Copy-Item -Path ${{runner.workspace}}\melonDS -Destination C:\tools\msys64\home\runneradmin -Recurse
- name: Configure
run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON
- name: Build
run: cmake --build --preset=release-mingw-x86_64
- uses: actions/upload-artifact@v4
run: |
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
&& cd melonDS/build && cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC=ON -DQT5_STATIC_DIR=C:/tools/msys64/mingw64/qt5-static"
- name: Make
run: |
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
&& cd melonDS/build && make -j$(nproc --all)"
- uses: actions/upload-artifact@v1
with:
name: melonDS-windows-x86_64
path: .\build\release-mingw-x86_64\melonDS.exe
name: melonDS
path: C:\tools\msys64\home\runneradmin\melonDS\build\melonDS.exe

12
.gitignore vendored
View File

@ -1,4 +1,4 @@
build*/
build
bin
obj
*.depend
@ -6,18 +6,10 @@ obj
*.o
melon_grc.c
melon_grc.h
melon.rc
cmake-build*
cmake-build
cmake-build-debug
compile_commands.json
.idea
.cache
*.exe
.DS_Store
.vs
.vscode
CMakeFiles
CMakeCache.txt

View File

@ -1,81 +0,0 @@
# 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.

View File

@ -1,44 +1,28 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.13)
cmake_policy(VERSION 3.15)
include(CheckSymbolExists)
include(CheckLibraryExists)
cmake_policy(VERSION 3.13)
if (POLICY CMP0076)
cmake_policy(SET CMP0076 NEW)
endif()
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
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)
include(ConfigureVcpkg)
endif()
project(melonDS
VERSION 1.0
DESCRIPTION "DS emulator, sorta"
HOMEPAGE_URL "https://melonds.kuribo64.net"
LANGUAGES C CXX)
include(CheckSymbolExists)
include(CheckLibraryExists)
include(CMakeDependentOption)
include(CheckIPOSupported)
include(SetupCCache)
include(Sanitizers)
project(melonDS CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
check_library_exists(m pow "" LIBM)
if(LIBM)
link_libraries(m)
endif()
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
function(detect_architecture symbol arch)
@ -62,40 +46,59 @@ detect_architecture("__i386__" x86)
detect_architecture("__arm__" ARM)
detect_architecture("__aarch64__" ARM64)
cmake_dependent_option(ENABLE_JIT "Enable JIT recompiler" ON
"ARCHITECTURE STREQUAL x86_64 OR ARCHITECTURE STREQUAL ARM64" OFF)
cmake_dependent_option(ENABLE_JIT_PROFILING "Enable JIT profiling with VTune" OFF "ENABLE_JIT" OFF)
if (ARCHITECTURE STREQUAL x86_64 OR ARCHITECTURE STREQUAL ARM64)
option(ENABLE_JIT "Enable x64 JIT recompiler" ON)
endif()
if (ENABLE_JIT)
add_definitions(-DJIT_ENABLED)
endif()
if (CMAKE_BUILD_TYPE STREQUAL Release)
option(ENABLE_LTO "Enable link-time optimization" ON)
else()
option(ENABLE_LTO "Enable link-time optimization" OFF)
endif()
option(ENABLE_OGLRENDERER "Enable OpenGL renderer" ON)
check_ipo_supported(RESULT IPO_SUPPORTED)
cmake_dependent_option(ENABLE_LTO_RELEASE "Enable link-time optimizations for release builds" ON "IPO_SUPPORTED" OFF)
cmake_dependent_option(ENABLE_LTO "Enable link-time optimizations" OFF "IPO_SUPPORTED" OFF)
if (ENABLE_LTO_RELEASE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
if (ENABLE_OGLRENDERER)
add_definitions(-DOGLRENDERER_ENABLED)
endif()
if (ENABLE_LTO)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
if (CMAKE_BUILD_TYPE STREQUAL Debug)
add_compile_options(-Og)
endif()
if (NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
if (CMAKE_BUILD_TYPE STREQUAL Release)
add_compile_options(-O3)
add_link_options(-s)
endif()
if (WIN32)
option(BUILD_STATIC "Statically link dependencies" OFF)
option(BUILD_STATIC "Statically link dependencies" OFF)
endif()
if (BUILD_STATIC AND WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
if (ENABLE_LTO)
if (WIN32)
add_compile_options(-flto)
add_link_options(-flto)
else()
add_compile_options(-flto -fPIC)
add_link_options(-flto -fuse-linker-plugin -pie)
endif()
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
option(ENABLE_GDBSTUB "Enable GDB stub" ON)
if (ENABLE_GDBSTUB)
add_definitions(-DGDBSTUB_ENABLED)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_AR "gcc-ar")
set(CMAKE_RANLIB "gcc-ranlib")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
find_program(LLD NAMES ld.lld ld64.lld lld-link)
if (NOT LLD STREQUAL "LLD-NOTFOUND")
add_link_options(-fuse-ld=lld)
endif()
set(CMAKE_AR "llvm-ar")
set(CMAKE_RANLIB "llvm-ranlib")
endif()
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
@ -103,5 +106,5 @@ option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
add_subdirectory(src)
if (BUILD_QT_SDL)
add_subdirectory(src/frontend/qt_sdl)
endif()
add_subdirectory(src/frontend/qt_sdl)
endif()

View File

@ -1,105 +0,0 @@
{
"version": 6,
"configurePresets": [
{
"name": "release",
"displayName": "Release",
"description": "Default release build configuration.",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/release"
},
{
"inherits": "release",
"name": "release-vcpkg",
"displayName": "Release (vcpkg)",
"description": "Release build with packages from vcpkg.",
"cacheVariables": {
"USE_VCPKG": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "release-mingw-x86_64",
"inherits": "release-vcpkg",
"displayName": "Windows MinGW release (x86_64)",
"binaryDir": "${sourceDir}/build/release-mingw-x86_64",
"generator": "Ninja",
"cacheVariables": {
"BUILD_STATIC": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "release-mac-x86_64",
"inherits": "release-vcpkg",
"displayName": "macOS release (x86_64)",
"binaryDir": "${sourceDir}/build/release-mac-x86_64",
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "x86_64" }
},
{
"name": "release-mac-arm64",
"inherits": "release-vcpkg",
"displayName": "macOS release (arm64)",
"binaryDir": "${sourceDir}/build/release-mac-arm64",
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" }
}
],
"buildPresets": [
{
"name": "release",
"configurePreset": "release"
},
{
"name": "release-vcpkg",
"configurePreset": "release-vcpkg"
},
{
"name": "release-mingw-x86_64",
"configurePreset": "release-mingw-x86_64"
},
{
"name": "release-mac-x86_64",
"configurePreset": "release-mac-x86_64"
},
{
"name": "release-mac-arm64",
"configurePreset": "release-mac-arm64"
}
],
"workflowPresets": [
{
"name": "release",
"displayName": "Release",
"steps": [
{ "type": "configure", "name": "release" },
{ "type": "build", "name": "release" }
]
},
{
"name": "release-vcpkg",
"displayName": "Release (vcpkg)",
"steps": [
{ "type": "configure", "name": "release-vcpkg" },
{ "type": "build", "name": "release-vcpkg" }
]
},
{
"name": "release-mac-x86_64",
"steps": [
{ "type": "configure", "name": "release-mac-x86_64" },
{ "type": "build", "name": "release-mac-x86_64" }
]
},
{
"name": "release-mac-arm64",
"steps": [
{ "type": "configure", "name": "release-mac-arm64" },
{ "type": "build", "name": "release-mac-arm64" }
]
}
]
}

View File

@ -1,150 +0,0 @@
# Contributor guide for melonDS
Please follow a style as documented here. Note that this guide was not always enforced, so some parts of the code violate it.
Files should use 4 spaces for indentation.
```c++
// for single line comments prefer C++ style
/*
for multiline comments
both C style comments
*/
// as well as
// C++ style comments are possible
// when including headers from the C standard library use their C names (so don't use cstdio/cstring, ...)
#include <stdio.h>
// namespaces in PascalCase
namespace Component
{ // for all constructs curly braces go onto the following line
// the content of namespaces should not be indented
int GlobalVariable; // in PascalCase
// function names should use PascalCase, parameters camelCase:
void Test(int someParam)
{
int variable = someParam * 2; // local variables in camelCase
// you can slightly vary the spacing around operators:
int variable2 = someParam*2 + 1;
// but avoid something like this: someParam* 2+ 3
for (int i = 0; i < variable; i++) // always a space between if/for/while and the braces
{
// not using curly braces is allowed
// if the body of the if/for/while is simple:
if ((i % 2) == 0)
printf("%d\n", i); // no space between the function name and the braces
}
}
// defines should always be in CAPITALISED_SNAKE_CASE
#ifdef SOME_CONFIGURATION
// the content of #ifdef sections should not be indented
// the only exception being otherwise hard to read nested sections of #ifdef/#if/#defines
// as e.g. in ARMJIT_Memory.cpp
// prefer #ifdef/#ifndef, only use if defined(...) for complex expressions like this:
#if defined(THIS) || defined(THAT)
// ...
#endif
class MyClass // PascalCase
{
public: // access specfications are not indented
void Test(int param) // for methods the same rules apply as for functions
{
}
private:
int MemberVariable; // PascalCase, no prefix
};
#endif
#endif
enum
{
// enums should always have a common prefix in camelCase
// separated by an underscore with the item name
// which has to be in PascalCase
enumPrefix_FirstElement,
enumPrefix_SecondElement,
enumPrefix_ThirdElement,
enumPrefix_FourthElement,
};
}
```
Some additional notes:
* Keep the definition and initialisation of local variables in one place and keep the scope of local variables as small as possible.
**That means avoid code like this**:
```cpp
void ColorConvert(u32* dst, u16* vram)
{
u16 color;
u8 r, g, b;
int i;
for (i = 0; i < 256; i++)
{
color = vram[i];
r = (color & 0x001F) << 1;
g = (color & 0x03E0) >> 4;
b = (color & 0x7C00) >> 9;
dst[i] = r | (g << 8) | (b << 16);
}
}
```
**Do this instead:**
```cpp
void ColorConvert(u32* dst, u16* vram)
{
for (int i = 0; i < 256; i++)
{
u16 color = vram[i];
u8 r = (color & 0x001F) << 1;
u8 g = (color & 0x03E0) >> 4;
u8 b = (color & 0x7C00) >> 9;
dst[i] = r | (g << 8) | (b << 16);
}
}
```
* For integer types preferably use the explictly typed ones. We have short aliases for them defined in types.h (for unsigned types: `u8`, `u16`, `u32`, `u16`. For signed `s8`, `s16`, `s32`, `s64`). In some situations like loop variables, using `int` is possible as well.
* Don't overdo object oriented programming. Always try to use a simpler construct first, only use a polymorphic class if a namespace with functions in it doesn't cut it.
* In doubt put a namespace around your part of the code.
* C style strings (and the associated functions from the C standard library) are used in most places. We are thinking about changing this, as C strings are a bit of a hassle to deal with, but for the time being this is what we use.
* Only the C standard IO is used (so use `printf`, `fopen`, … Do not use `std::cout`/`std::ostream`, …).
* Complex C++ containers can be used (`std::vector`, `std::list`, `std::map`, …). `std::array` is usually not used, unless necessary so that the container can be used with other C++ constructs (e.g. `<algorithm>`). Only use them if a C array doesn't cut it.
* File names should be in PascalCase
* If a header file is called MyHeader.h it should be guarded with an ifdef like this:
```cpp
#ifndef MYHEADER_H
#define MYHEADER_H
// ...
#endif
```
* And at last, if you have any questions, visit us on IRC (see the readme)!

100
README.md
View File

@ -1,15 +1,10 @@
<p align="center"><img src="https://raw.githubusercontent.com/melonDS-emu/melonDS/master/res/icon/melon_128x128.png"></p>
<p align="center"><img src="https://raw.githubusercontent.com/StapleButter/melonDS/master/icon/melon_128x128.png"></p>
<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: 0.9.1"><img src="https://img.shields.io/badge/release-0.9.1-%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>
<br>
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml/badge.svg" /></a>
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml/badge.svg" /></a>
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml/badge.svg" /></a>
</p>
DS emulator, sorta
@ -18,7 +13,12 @@ The goal is to do things right and fast, akin to blargSNES (but hopefully better
## How to use
Firmware boot (not direct boot) requires a BIOS/firmware dump from an original DS or DS Lite.
melonDS requires BIOS/firmware copies from a DS. Files required:
* bios7.bin, 16KB: ARM7 BIOS
* bios9.bin, 4KB: ARM9 BIOS
* firmware.bin, 128/256/512KB: firmware
Firmware boot requires a firmware dump from an original DS or DS Lite.
DS firmwares dumped from a DSi or 3DS aren't bootable and only contain configuration data, thus they are only suitable when booting games directly.
### Possible firmware sizes
@ -32,20 +32,86 @@ 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
See [BUILD.md](./BUILD.md) for build instructions.
### Linux:
* Install dependencies:
```sh
sudo apt-get install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtdeclarative5-dev libslirp-dev
```
* Compile:
```sh
mkdir -p build
cd build
cmake ..
make -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
#### Dynamic builds (with DLLs)
4. Install dependencies: `pacman -S git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5,libslirp}`
5. Run the following commands
```bash
git clone https://github.com/Arisotura/melonDS.git
cd melonDS
mkdir build
cd build
cmake .. -G "MSYS Makefiles"
make -j$(nproc --all)
../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)
4. Install dependencies: `pacman -S git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-static,libslirp}`
5. Run the following commands
```bash
git clone https://github.com/Arisotura/melonDS.git
cd melonDS
mkdir build
cd build
cmake .. -G 'MSYS Makefiles' -DBUILD_STATIC=ON -DQT5_STATIC_DIR=/mingw64/qt5-static
make -j$(nproc --all)
mkdir dist && cp melonDS.exe dist
```
If everything went well, melonDS should now be in the `dist` folder.
### macOS:
1. Install the [Homebrew Package Manager](https://brew.sh)
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt5 libslirp`
3. Compile:
```zsh
git clone https://github.com/Arisotura/melonDS.git
cd melonDS
mkdir build && cd build
cmake .. -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
make -j$(sysctl -n hw.ncpu)
mkdir dist && cp -r melonDS.app dist
```
If everything went well, melonDS.app should now be in the `dist` folder.
## TODO LIST
* better DSi emulation
* better OpenGL rendering
* netplay
* DSi emulation
* the impossible quest of pixel-perfect 3D graphics
* improve libui and the emulator UI
* support for rendering screens to separate windows
* emulating some fancy addons
* other non-core shit (debugger, graphics viewers, etc)
* other non-core shit (debugger, graphics viewers, cheat crapo, etc)
### TODO LIST FOR LATER (low priority)
### TODO LIST FOR LATER
* better wifi
* maybe emulate flashcarts or other fancy hardware
* big-endian compatibility (Wii, etc)
* LCD refresh time (used by some games for blending effects)
* any feature you can eventually ask for that isn't outright stupid
@ -57,14 +123,10 @@ See [BUILD.md](./BUILD.md) for build instructions.
* limittox for the icon
* All of you comrades who have been testing melonDS, reporting issues, suggesting shit, etc
## Licenses
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)
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.
### External
* Images used in the Input Config Dialog - see `src/frontend/qt_sdl/InputConfig/resources/LICENSE.md`

View File

@ -1,115 +0,0 @@
include(FetchContent)
set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg")
set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository")
if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
if (APPLE) # this doesn't work on non-macOS
file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE)
endif()
FetchContent_Declare(vcpkg
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
GIT_TAG 2ad004460f5db4d3b66f62f5799ff66c265c4b5d
EXCLUDE_FROM_ALL
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
FetchContent_MakeAvailable(vcpkg)
endif()
set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets")
option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
# Duplicated here because it needs to be set before project()
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)
option(USE_SYSTEM_LIBSLIRP "Use system libslirp instead of the bundled version" ON)
endif()
if (NOT USE_QT6)
list(APPEND VCPKG_MANIFEST_FEATURES qt5)
set(VCPKG_MANIFEST_NO_DEFAULT_FEATURES ON)
endif()
if (CMAKE_OSX_ARCHITECTURES MATCHES ";")
message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.")
endif()
if (USE_RECOMMENDED_TRIPLETS)
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE _HOST_PROCESSOR
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(_CAN_TARGET_AS_HOST OFF)
if (APPLE)
if (NOT CMAKE_OSX_ARCHITECTURES)
if (_HOST_PROCESSOR STREQUAL arm64)
set(CMAKE_OSX_ARCHITECTURES arm64)
else()
set(CMAKE_OSX_ARCHITECTURES x86_64)
endif()
endif()
if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
set(_WANTED_TRIPLET arm64-osx-11-release)
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
else()
set(_WANTED_TRIPLET x64-osx-1015-release)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
endif()
elseif(WIN32)
# TODO Windows arm64 if possible
set(_CAN_TARGET_AS_HOST ON)
set(_WANTED_TRIPLET x64-mingw-static-release)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
# Can't really detect cross compiling here.
set(_CAN_TARGET_AS_HOST ON)
if (_HOST_PROCESSOR STREQUAL x86_64)
set(_WANTED_TRIPLET x64-linux-release)
elseif(_HOST_PROCESSOR STREQUAL "aarch64")
set(_WANTED_TRIPLET arm64-linux-release)
endif()
endif()
# Don't override it if the user set something else
if (NOT VCPKG_TARGET_TRIPLET)
set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}")
else()
set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}")
endif()
if (APPLE)
if (_HOST_PROCESSOR MATCHES arm64)
if (_WANTED_TRIPLET MATCHES "^arm64-osx-")
set(_CAN_TARGET_AS_HOST ON)
elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release")
# Use the default triplet for when building for arm64
# because we're probably making a universal build
set(VCPKG_HOST_TRIPLET arm64-osx-11-release)
endif()
else()
if (_WANTED_TRIPLET MATCHES "^x64-osx-")
set(_CAN_TARGET_AS_HOST ON)
elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release")
set(VCPKG_HOST_TRIPLET x64-osx-1015-release)
endif()
endif()
endif()
# If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries.
# In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant.
if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET)
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON)
else()
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF)
endif()
if (VCPKG_TARGET_AS_HOST)
set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg")
endif()
endif()
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")

View File

@ -1,9 +0,0 @@
if (CMAKE_C_COMPILER_ID STREQUAL GNU)
set(CMAKE_C_FLAGS_DEBUG_INIT "-g -Og")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og")
endif()
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}")
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}")

View File

@ -1,297 +0,0 @@
#.rst:
# ECMFindModuleHelpers
# --------------------
#
# Helper macros for find modules: ecm_find_package_version_check(),
# ecm_find_package_parse_components() and
# ecm_find_package_handle_library_components().
#
# ::
#
# ecm_find_package_version_check(<name>)
#
# Prints warnings if the CMake version or the project's required CMake version
# is older than that required by extra-cmake-modules.
#
# ::
#
# ecm_find_package_parse_components(<name>
# RESULT_VAR <variable>
# KNOWN_COMPONENTS <component1> [<component2> [...]]
# [SKIP_DEPENDENCY_HANDLING])
#
# This macro will populate <variable> with a list of components found in
# <name>_FIND_COMPONENTS, after checking that all those components are in the
# list of KNOWN_COMPONENTS; if there are any unknown components, it will print
# an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
# return().
#
# The order of components in <variable> is guaranteed to match the order they
# are listed in the KNOWN_COMPONENTS argument.
#
# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable
# <name>_<component>_component_deps will be checked for dependent components.
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
# dependencies will also be added to <variable>.
#
# ::
#
# ecm_find_package_handle_library_components(<name>
# COMPONENTS <component> [<component> [...]]
# [SKIP_DEPENDENCY_HANDLING])
# [SKIP_PKG_CONFIG])
#
# Creates an imported library target for each component. The operation of this
# macro depends on the presence of a number of CMake variables.
#
# The <name>_<component>_lib variable should contain the name of this library,
# and <name>_<component>_header variable should contain the name of a header
# file associated with it (whatever relative path is normally passed to
# '#include'). <name>_<component>_header_subdir variable can be used to specify
# which subdirectory of the include path the headers will be found in.
# ecm_find_package_components() will then search for the library
# and include directory (creating appropriate cache variables) and create an
# imported library target named <name>::<component>.
#
# Additional variables can be used to provide additional information:
#
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
# pkg-config is found, the pkg-config module given by
# <name>_<component>_pkg_config will be searched for and used to help locate the
# library and header file. It will also be used to set
# <name>_<component>_VERSION.
#
# Note that if version information is found via pkg-config,
# <name>_<component>_FIND_VERSION can be set to require a particular version
# for each component.
#
# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property
# of the imported target for <component> will be set to contain the imported
# targets for the components listed in <name>_<component>_component_deps.
# <component>_FOUND will also be set to false if any of the compoments in
# <name>_<component>_component_deps are not found. This requires the components
# in <name>_<component>_component_deps to be listed before <component> in the
# COMPONENTS argument.
#
# The following variables will be set:
#
# ``<name>_TARGETS``
# the imported targets
# ``<name>_LIBRARIES``
# the found libraries
# ``<name>_INCLUDE_DIRS``
# the combined required include directories for the components
# ``<name>_DEFINITIONS``
# the "other" CFLAGS provided by pkg-config, if any
# ``<name>_VERSION``
# the value of ``<name>_<component>_VERSION`` for the first component that
# has this variable set (note that components are searched for in the order
# they are passed to the macro), although if it is already set, it will not
# be altered
#
# Note that these variables are never cleared, so if
# ecm_find_package_handle_library_components() is called multiple times with
# different components (typically because of multiple find_package() calls) then
# ``<name>_TARGETS``, for example, will contain all the targets found in any
# call (although no duplicates).
#
# Since pre-1.0.0.
#=============================================================================
# Copyright 2014 Alex Merry <alex.merry@kde.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include(CMakeParseArguments)
macro(ecm_find_package_version_check module_name)
if(CMAKE_VERSION VERSION_LESS 2.8.12)
message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
endif()
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake")
endif()
endmacro()
macro(ecm_find_package_parse_components module_name)
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
set(ecm_fppc_oneValueArgs RESULT_VAR)
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
if(ECM_FPPC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPPC_RESULT_VAR)
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
endif()
if(${module_name}_FIND_COMPONENTS)
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
# Make sure deps are included
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
if("${ecm_fppc_index}" STREQUAL "-1")
if(NOT ${module_name}_FIND_QUIETLY)
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
endif()
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
endif()
endforeach()
endforeach()
else()
message(STATUS "Skipping dependency handling for ${module_name}")
endif()
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
# This makes sure components are listed in the same order as
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
set(${ECM_FPPC_RESULT_VAR})
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
endif()
endforeach()
# if there are any left, they are unknown components
if(ecm_fppc_requestedComps)
set(ecm_fppc_msgType STATUS)
if(${module_name}_FIND_REQUIRED)
set(ecm_fppc_msgType FATAL_ERROR)
endif()
if(NOT ${module_name}_FIND_QUIETLY)
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
endif()
return()
endif()
else()
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
endif()
endmacro()
macro(ecm_find_package_handle_library_components module_name)
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
set(ecm_fpwc_oneValueArgs)
set(ecm_fpwc_multiValueArgs COMPONENTS)
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
if(ECM_FPWC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPWC_COMPONENTS)
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
endif()
include(FindPackageHandleStandardArgs)
find_package(PkgConfig)
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
set(ecm_fpwc_dep_vars)
set(ecm_fpwc_dep_targets)
if(NOT SKIP_DEPENDENCY_HANDLING)
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
endforeach()
endif()
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
${${module_name}_${ecm_fpwc_comp}_pkg_config})
endif()
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
)
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
)
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
if(NOT ${module_name}_VERSION)
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
endif()
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
FOUND_VAR
${module_name}_${ecm_fpwc_comp}_FOUND
REQUIRED_VARS
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
${ecm_fpwc_dep_vars}
VERSION_VAR
${module_name}_${ecm_fpwc_comp}_VERSION
)
mark_as_advanced(
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
)
if(${module_name}_${ecm_fpwc_comp}_FOUND)
list(APPEND ${module_name}_LIBRARIES
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
list(APPEND ${module_name}_INCLUDE_DIRS
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
set(${module_name}_DEFINITIONS
${${module_name}_DEFINITIONS}
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
)
endif()
list(APPEND ${module_name}_TARGETS
"${module_name}::${ecm_fpwc_comp}")
endif()
endforeach()
if(${module_name}_LIBRARIES)
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
endif()
if(${module_name}_INCLUDE_DIRS)
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
endif()
if(${module_name}_DEFINITIONS)
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
endif()
if(${module_name}_TARGETS)
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
endif()
endmacro()

View File

@ -1 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpers.cmake)

View File

@ -1,172 +0,0 @@
#.rst:
# FindEGL
# -------
#
# Try to find EGL.
#
# This will define the following variables:
#
# ``EGL_FOUND``
# True if (the requested version of) EGL is available
# ``EGL_VERSION``
# The version of EGL; note that this is the API version defined in the
# headers, rather than the version of the implementation (eg: Mesa)
# ``EGL_LIBRARIES``
# This can be passed to target_link_libraries() instead of the ``EGL::EGL``
# target
# ``EGL_INCLUDE_DIRS``
# This should be passed to target_include_directories() if the target is not
# used for linking
# ``EGL_DEFINITIONS``
# This should be passed to target_compile_options() if the target is not
# used for linking
#
# If ``EGL_FOUND`` is TRUE, it will also define the following imported target:
#
# ``EGL::EGL``
# The EGL library
#
# In general we recommend using the imported target, as it is easier to use.
# Bear in mind, however, that if the target is in the link interface of an
# exported library, it must be made available by the package config file.
#
# Since pre-1.0.0.
#=============================================================================
# Copyright 2014 Alex Merry <alex.merry@kde.org>
# Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#=============================================================================
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
include(CheckCXXSourceCompiles)
include(CMakePushCheckState)
ecm_find_package_version_check(EGL)
# Use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
pkg_check_modules(PKG_EGL QUIET egl)
set(EGL_DEFINITIONS ${PKG_EGL_CFLAGS_OTHER})
find_path(EGL_INCLUDE_DIR
NAMES
EGL/egl.h
HINTS
${PKG_EGL_INCLUDE_DIRS}
)
find_library(EGL_LIBRARY
NAMES
EGL
HINTS
${PKG_EGL_LIBRARY_DIRS}
)
# NB: We do *not* use the version information from pkg-config, as that
# is the implementation version (eg: the Mesa version)
if(EGL_INCLUDE_DIR)
# egl.h has defines of the form EGL_VERSION_x_y for each supported
# version; so the header for EGL 1.1 will define EGL_VERSION_1_0 and
# EGL_VERSION_1_1. Finding the highest supported version involves
# finding all these defines and selecting the highest numbered.
file(READ "${EGL_INCLUDE_DIR}/EGL/egl.h" _EGL_header_contents)
string(REGEX MATCHALL
"[ \t]EGL_VERSION_[0-9_]+"
_EGL_version_lines
"${_EGL_header_contents}"
)
unset(_EGL_header_contents)
foreach(_EGL_version_line ${_EGL_version_lines})
string(REGEX REPLACE
"[ \t]EGL_VERSION_([0-9_]+)"
"\\1"
_version_candidate
"${_EGL_version_line}"
)
string(REPLACE "_" "." _version_candidate "${_version_candidate}")
if(NOT DEFINED EGL_VERSION OR EGL_VERSION VERSION_LESS _version_candidate)
set(EGL_VERSION "${_version_candidate}")
endif()
endforeach()
unset(_EGL_version_lines)
endif()
cmake_push_check_state(RESET)
list(APPEND CMAKE_REQUIRED_LIBRARIES "${EGL_LIBRARY}")
list(APPEND CMAKE_REQUIRED_INCLUDES "${EGL_INCLUDE_DIR}")
check_cxx_source_compiles("
#include <EGL/egl.h>
int main(int argc, char *argv[]) {
EGLint x = 0; EGLDisplay dpy = 0; EGLContext ctx = 0;
eglDestroyContext(dpy, ctx);
}" HAVE_EGL)
cmake_pop_check_state()
set(required_vars EGL_INCLUDE_DIR HAVE_EGL)
if(NOT EMSCRIPTEN)
list(APPEND required_vars EGL_LIBRARY)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(EGL
FOUND_VAR
EGL_FOUND
REQUIRED_VARS
${required_vars}
VERSION_VAR
EGL_VERSION
)
if(EGL_FOUND AND NOT TARGET EGL::EGL)
if (EMSCRIPTEN)
add_library(EGL::EGL INTERFACE IMPORTED)
# Nothing further to be done, system include paths have headers and linkage is implicit.
else()
add_library(EGL::EGL UNKNOWN IMPORTED)
set_target_properties(EGL::EGL PROPERTIES
IMPORTED_LOCATION "${EGL_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${EGL_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${EGL_INCLUDE_DIR}"
)
endif()
endif()
mark_as_advanced(EGL_LIBRARY EGL_INCLUDE_DIR HAVE_EGL)
# compatibility variables
set(EGL_LIBRARIES ${EGL_LIBRARY})
set(EGL_INCLUDE_DIRS ${EGL_INCLUDE_DIR})
set(EGL_VERSION_STRING ${EGL_VERSION})
include(FeatureSummary)
set_package_properties(EGL PROPERTIES
URL "https://www.khronos.org/egl/"
DESCRIPTION "A platform-agnostic mechanism for creating rendering surfaces for use with other graphics libraries, such as OpenGL|ES and OpenVG."
)

View File

@ -1,48 +0,0 @@
# - Try to find enet
# Once done this will define
#
# ENET_FOUND - system has enet
# ENET_INCLUDE_DIRS - the enet include directory
# ENET_LIBRARIES - the libraries needed to use enet
#
# $ENETDIR is an environment variable used for finding enet.
#
# Borrowed from The Mana World
# http://themanaworld.org/
#
# Several changes and additions by Fabian 'x3n' Landau
# Lots of simplifications by Adrian Friedli
# > www.orxonox.net <
FIND_PATH(ENET_INCLUDE_DIRS enet/enet.h
PATHS
$ENV{ENETDIR}
/usr/local
/usr
PATH_SUFFIXES include
)
FIND_LIBRARY(ENET_LIBRARY
NAMES enet
PATHS
$ENV{ENETDIR}
/usr/local
/usr
PATH_SUFFIXES lib
)
# handle the QUIETLY and REQUIRED arguments and set ENET_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ENet DEFAULT_MSG ENET_LIBRARY ENET_INCLUDE_DIRS)
IF (ENET_FOUND)
IF(WIN32)
SET(WINDOWS_ENET_DEPENDENCIES "ws2_32;winmm")
SET(ENET_LIBRARIES ${ENET_LIBRARY} ${WINDOWS_ENET_DEPENDENCIES})
ELSE(WIN32)
SET(ENET_LIBRARIES ${ENET_LIBRARY})
ENDIF(WIN32)
ENDIF (ENET_FOUND)
MARK_AS_ADVANCED(ENET_LIBRARY ENET_LIBRARIES ENET_INCLUDE_DIRS)

View File

@ -1,10 +0,0 @@
find_path(VTUNE_PATH "")
set(VTUNE_INCLUDE_DIR "${VTUNE_PATH}/include")
if (WIN32)
set(VTUNE_LIBRARY "${VTUNE_PATH}/lib64/jitprofiling.lib")
else()
set(VTUNE_LIBRARY "${VTUNE_PATH}/lib64/jitprofiling.a")
endif()

View File

@ -1,35 +0,0 @@
# The entire codebase quite reasonably does things like #include <SDL2/SDL.h> or <epoxy/gl.h>
# CMake apparently doesn't think you should be doing this, so just includes $PREFIX/include/packagename for a given
# package as include directories when using `target_link_libraries` with an imported target, this hacky function fixes
# that up so includes can keep working as they always did but we can still use fancy imported targets.
# This is stupid.
function(fix_interface_includes)
foreach (target ${ARGN})
set(NEW_DIRS)
get_target_property(DIRS "${target}" INTERFACE_INCLUDE_DIRECTORIES)
if (NOT DIRS)
continue()
endif()
foreach (DIR ${DIRS})
get_filename_component(PARENT_DIR "${DIR}" DIRECTORY)
if (PARENT_DIR MATCHES "include$")
list(APPEND NEW_DIRS "${PARENT_DIR}")
endif()
# HACK
# The libarchive pkg-config file in MSYS2 seems to include a UNIX-style path for its
# include directory and CMake doesn't like that.
if (WIN32 AND MINGW AND target STREQUAL PkgConfig::LibArchive)
list(FILTER DIRS EXCLUDE REGEX "^/[^.]+64/.*")
endif()
endforeach()
list(APPEND DIRS ${NEW_DIRS})
set_target_properties("${target}" PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${DIRS}")
endforeach()
endfunction()

View File

@ -1,8 +0,0 @@
set(SANITIZE "" CACHE STRING "Sanitizers to enable.")
string(REGEX MATCHALL "[^,]+" ENABLED_SANITIZERS "${SANITIZE}")
foreach(SANITIZER ${ENABLED_SANITIZERS})
add_compile_options("-fsanitize=${SANITIZER}")
add_link_options("-fsanitize=${SANITIZER}")
endforeach()

View File

@ -1,19 +0,0 @@
include(FindPackageMessage)
find_program(CCACHE "ccache")
cmake_dependent_option(USE_CCACHE "Use CCache to speed up repeated builds." ON CCACHE OFF)
if (NOT CCACHE OR NOT USE_CCACHE)
return()
endif()
# Fedora, and probably also Red Hat-based distros in general, use CCache by default if it's installed on the system.
# We'll try to detect this here, and exit if that's the case.
# Trying to launch ccache with ccache as we'd otherwise do seems to cause build issues.
if (CMAKE_C_COMPILER MATCHES "ccache" OR CMAKE_CXX_COMPILER MATCHES "ccache")
return()
endif()
find_package_message(CCache "Using CCache to speed up compilation" "${USE_CCACHE}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE}")

View File

@ -1,12 +0,0 @@
# Toolchain file for building with Homebrew's LLVM on macOS
# This is useful on 10.14 where std::filesystem is not supported.
set(CMAKE_C_COMPILER /usr/local/opt/llvm/bin/clang)
set(CMAKE_CXX_COMPILER /usr/local/opt/llvm/bin/clang++)
add_link_options(-L/usr/local/opt/llvm/lib)
# LLVM in Homebrew is built with latest Xcode which has a newer linker than
# what is bundled in the default install of Xcode Command Line Tools, so we
# override it to prevent it passing flags not supported by the system's ld.
add_link_options(-mlinker-version=450)

View File

@ -1,12 +0,0 @@
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_CMAKE_SYSTEM_VERSION 11.0)
set(VCPKG_OSX_ARCHITECTURES arm64)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0)
set(VCPKG_C_FLAGS -mmacosx-version-min=11.0)
set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0)

View File

@ -1,7 +0,0 @@
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_ENV_PASSTHROUGH PATH)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_CMAKE_SYSTEM_NAME MinGW)

View File

@ -1,12 +0,0 @@
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_CMAKE_SYSTEM_VERSION 10.15)
set(VCPKG_OSX_ARCHITECTURES x86_64)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)
set(VCPKG_C_FLAGS -mmacosx-version-min=10.15)
set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15)

View File

@ -1,61 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1739020877,
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

109
flake.nix
View File

@ -1,109 +0,0 @@
{
description = "Nintendo DS emulator";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
inherit (pkgs.lib) cmakeBool optionals makeLibraryPath;
inherit (pkgs.stdenv) isLinux isDarwin;
revision = with self; if sourceInfo?dirtyRev
then sourceInfo.dirtyRev
else sourceInfo.rev;
shortRevision = with self; if sourceInfo?dirtyShortRev
then sourceInfo.dirtyShortRev
else sourceInfo.shortRev;
melonDS = pkgs.stdenv.mkDerivation {
pname = "melonDS";
version = "1.0-${shortRevision}";
src = ./.;
nativeBuildInputs = with pkgs; [
cmake
ninja
pkg-config
qt6.wrapQtAppsHook
];
buildInputs = (with pkgs; [
qt6.qtbase
qt6.qtmultimedia
SDL2
zstd
libarchive
libGL
libslirp
enet
]) ++ optionals (!isDarwin) (with pkgs; [
kdePackages.extra-cmake-modules
qt6.qtwayland
wayland
]);
cmakeFlags = [
(cmakeBool "USE_QT6" true)
(cmakeBool "USE_SYSTEM_LIBSLIRP" true)
(cmakeBool "MELONDS_EMBED_BUILD_INFO" true)
];
env.MELONDS_GIT_HASH = revision;
env.MELONDS_GIT_BRANCH = "(unknown)";
env.MELONDS_BUILD_PROVIDER = "Nix";
qtWrapperArgs = optionals isLinux [
"--prefix LD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap pkgs.wayland ]}"
] ++ optionals isDarwin [
"--prefix DYLD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap ]}"
];
passthru = {
exePath = if isDarwin then
"/Applications/melonDS.app/Contents/MacOS/melonDS"
else "/bin/melonDS";
};
};
in {
packages.default = melonDS;
apps.default = flake-utils.lib.mkApp {
drv = self.packages.${system}.default;
};
devShells = {
default = pkgs.mkShell {
inputsFrom = [ self.packages.${system}.default ];
packages = with pkgs; [
qt6.qttools
];
};
# Shell for building static melonDS release builds with vcpkg
# Use mkShellNoCC to ensure Nix's gcc/clang and stdlib isn't used
vcpkg = pkgs.mkShellNoCC {
packages = with pkgs; [
autoconf
autoconf-archive
automake
cmake
cups.dev # Needed by qtbase despite not enabling print support
git
iconv.dev
libtool
ninja
pkg-config
python3
];
# Undo the SDK setup done by nixpkgs so we can use AppleClang
shellHook = ''
unset DEVELOPER_DIR SDKROOT MACOSX_DEPLOYMENT_TARGET
'';
};
};
}
);
}

View File

@ -0,0 +1,29 @@
---
app-id: net.kuribo64.melonDS
runtime: org.freedesktop.Platform
runtime-version: '18.08'
sdk: org.freedesktop.Sdk
command: melonDS
finish-args:
- "--share=ipc"
- "--socket=x11"
- "--socket=pulseaudio"
- "--share=network"
- "--device=all"
- "--filesystem=home"
modules:
- name: libpcap
sources:
- type: archive
url: http://www.tcpdump.org/release/libpcap-1.9.0.tar.gz
sha256: 2edb88808e5913fdaa8e9c1fcaf272e19b2485338742b5074b9fe44d68f37019
- name: melonds
buildsystem: cmake-ninja
sources:
- type: git
url: https://github.com/Arisotura/melonDS.git
branch: master
post-install:
- "desktop-file-install --dir=/app/share/applications net.kuribo64.melonDS.desktop"
- "install -D icon/melon_256x256.png /app/share/icons/hicolor/256x256/apps/net.kuribo64.melonDS.png"

View File

@ -1,17 +0,0 @@
TC_PREFIX = /home/exophase/pandora-dev
PREFIX = $(TC_PREFIX)/arm-2011.03
AS = $(PREFIX)/bin/arm-none-linux-gnueabi-gcc
OBJCOPY = $(PREFIX)/bin/arm-none-linux-gnueabi-objcopy
BIN_ARM7 = drastic_bios_arm7
BIN_ARM9 = drastic_bios_arm9
all:
$(AS) bios_common.S -DBIOS_ARM7 -march=armv4 -c -Wa,-asl=$(BIN_ARM7).list -o $(BIN_ARM7).o
$(AS) bios_common.S -DBIOS_ARM9 -march=armv5 -c -Wa,-asl=$(BIN_ARM9).list -o $(BIN_ARM9).o
$(OBJCOPY) -O binary $(BIN_ARM7).o $(BIN_ARM7).bin
$(OBJCOPY) -O binary $(BIN_ARM9).o $(BIN_ARM9).bin
clean:
rm -f $(BIN_ARM7).bin $(BIN_ARM7).o $(BIN_ARM9).bin $(BIN_ARM9).o

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@ -1,36 +0,0 @@
Custom NDS ARM7/ARM9 BIOS replacement
Copyright (c) 2013, Gilead Kutnick
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1) Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2) Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-- Info --
This archive contains source code and assembly for a custom BIOS replacement
for the Nintendo DS system. This code is in no way affiliated with Nintendo
and is not derived from Nintendo's BIOS implementation but has been implemented
using publically available documentation.
It can be assembled using the included Makefile along with a proper ARM gcc
toolchain. Change the first four lines to point to the proper toolchain of your
choice.

BIN
icon/melon_128x128.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
icon/melon_16x16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

BIN
icon/melon_256x256.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
icon/melon_32x32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
icon/melon_48x48.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
icon/melon_64x64.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

6
melon.qrc Normal file
View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file alias="melon-icon">icon/melon_32x32.png</file>
</qresource>
</RCC>

View File

@ -2,12 +2,12 @@
#define VFT_APP 0x00000001L
//this will set your .exe icon
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "${CMAKE_SOURCE_DIR}/res/melon.ico"
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "melon.ico"
//include version information in .exe, modify these values to match your needs
1 VERSIONINFO
FILEVERSION ${MELON_RC_VERSION}
PRODUCTVERSION ${MELON_RC_VERSION}
FILEVERSION 0,9,1,0
PRODUCTVERSION 0,9,1,0
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
@ -15,14 +15,14 @@ FILETYPE VFT_APP
BLOCK "040904E4"
{
VALUE "CompanyName", "Melon Factory of Kuribo64"
VALUE "FileVersion", "${melonDS_VERSION}"
VALUE "FileDescription", "melonDS emulator"
VALUE "FileVersion", "0.9.1"
VALUE "FileDescription", "DS emulator, sorta. also 1st quality melon."
VALUE "InternalName", "SDnolem"
VALUE "LegalCopyright", "2016-2023 melonDS team"
VALUE "LegalCopyright", "2016-2020 Arisotura & co."
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "melonDS.exe"
VALUE "OriginalFilename", "zafkflzdasd.exe"
VALUE "ProductName", "melonDS"
VALUE "ProductVersion", "${melonDS_VERSION}"
VALUE "ProductVersion", "0.9.1"
}
}
BLOCK "VarFileInfo"

BIN
melonDS.icns Normal file

Binary file not shown.

24
melonDS.plist Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>melonDS</string>
<key>CFBundleIconFile</key>
<string>melonDS.icns</string>
<key>CFBundleIdentifier</key>
<string>net.kuribo64.melonDS</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>0.9</string>
<key>CFBundleShortVersionString</key>
<string>0.9</string>
<key>NSHumanReadableCopyright</key>
<string>Licensed under GPLv3</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

11
melon_grc.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/kuriboland/melonDS">
<file preprocess="to-pixdata">icon/melon_16x16.png</file>
<file preprocess="to-pixdata">icon/melon_32x32.png</file>
<file preprocess="to-pixdata">icon/melon_48x48.png</file>
<file preprocess="to-pixdata">icon/melon_64x64.png</file>
<file preprocess="to-pixdata">icon/melon_128x128.png</file>
<file preprocess="to-pixdata">icon/melon_256x256.png</file>
</gresource>
</gresources>

View File

@ -2,7 +2,7 @@
Name=melonDS
GenericName=Nintendo DS Emulator
Comment=A fast and accurate Nintendo DS emulator.
Exec=melonDS %f
Exec=melonDS
Type=Application
Categories=Game;Emulator;
Terminal=false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

View File

@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>melonDS</string>
<key>CFBundleIconFile</key>
<string>melon.icns</string>
<key>CFBundleIdentifier</key>
<string>net.kuribo64.melonDS</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>${melonDS_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${melonDS_VERSION}</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>NSHumanReadableCopyright</key>
<string>Licensed under GPLv3</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSMicrophoneUsageDescription</key>
<string>We need microphone access so you can use the emulated DS microphone</string>
<key>NSCameraUsageDescription</key>
<string>Camera access is needed for emulation of the DSi's cameras</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Nintendo DS ROM</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>nds</string>
<string>srl</string>
<string>dsi</string>
<string>ids</string>
<string>nds.zst</string>
<string>srl.zst</string>
<string>dsi.zst</string>
<string>ids.zst</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>Game Boy Advance ROM</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>gba</string>
<string>agb</string>
<string>gba.zst</string>
<string>agb.zst</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>Archive containing ROM</string>
<key>CFBundleTypeExtensions</key>
<array>
<key>zip</key>
<key>7z</key>
<key>rar</key>
<key>tar</key>
<key>tar.gz</key>
<key>tgz</key>
<key>tar.xz</key>
<key>txz</key>
<key>tar.bz2</key>
<key>tbz2</key>
<key>tar.lz4</key>
<key>tlz4</key>
<key>tar.zst</key>
<key>tzst</key>
<key>tar.Z</key>
<key>taz</key>
<key>tar.lz</key>
<key>tar.lzma</key>
<key>tlz</key>
<key>tar.lrz</key>
<key>tlrz</key>
<key>tar.lzo</key>
<key>tzo</key>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>
</dict>
</plist>

View File

@ -1,7 +0,0 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file alias="melon-icon">icon/melon_256x256.png</file>
<file alias="melon-logo">melon384.png</file>
</qresource>
</RCC>

View File

@ -1,80 +0,0 @@
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
<path fill="#5c913b" fill-rule="evenodd" d="M357.62 154.38c56.821-56.821 85.232-85.232 118.463-94.604a132.32 132.32 0 0 1 71.834 0c33.231 9.372 61.642 37.783 118.463 94.604l203.24 203.24c56.821 56.821 85.232 85.232 94.604 118.463a132.32 132.32 0 0 1 0 71.834c-9.372 33.231-37.783 61.642-94.604 118.463L666.38 869.62c-56.821 56.821-85.232 85.232-118.463 94.604a132.319 132.319 0 0 1-71.834 0c-33.231-9.372-61.642-37.783-118.463-94.604L154.38 666.38c-56.821-56.821-85.232-85.232-94.604-118.463a132.318 132.318 0 0 1 0-71.834c9.372-33.231 37.783-61.642 94.604-118.463Z"/>
<filter id="a" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="18.378"/>
<feOffset dy="7.351" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".4"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#ffe8b6" fill-rule="evenodd" filter="url(#a)" d="M383.23 209.478c47.35-47.35 71.026-71.026 98.72-78.836a110.266 110.266 0 0 1 59.86 0c27.694 7.81 51.369 31.486 98.72 78.836L814.66 383.61c47.351 47.351 71.027 71.026 78.837 98.72a110.265 110.265 0 0 1 0 59.86c-7.81 27.694-31.486 51.37-78.837 98.72L640.53 815.042c-47.351 47.35-71.027 71.026-98.72 78.836a110.265 110.265 0 0 1-59.86 0c-27.694-7.81-51.37-31.485-98.72-78.836L209.098 640.91c-47.35-47.35-71.026-71.026-78.836-98.72a110.266 110.266 0 0 1 0-59.86c7.81-27.694 31.485-51.369 78.836-98.72Z"/>
<filter id="b" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="18.378"/>
<feOffset dy="7.351" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".4"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#dd2e44" fill-rule="evenodd" filter="url(#b)" d="M404.822 239.527c39.46-39.46 59.189-59.189 82.266-65.697a91.889 91.889 0 0 1 49.885 0c23.077 6.508 42.807 26.238 82.266 65.697l165.295 165.295c39.459 39.46 59.188 59.189 65.697 82.266a91.888 91.888 0 0 1 0 49.884c-6.509 23.078-26.238 42.808-65.697 82.266L619.239 784.534c-39.46 39.459-59.189 59.188-82.266 65.697a91.888 91.888 0 0 1-49.885 0c-23.077-6.509-42.807-26.238-82.266-65.697L239.527 619.239c-39.46-39.46-59.189-59.189-65.697-82.267a91.888 91.888 0 0 1 0-49.884c6.508-23.077 26.238-42.807 65.697-82.266Z"/>
<filter id="c" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".104"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#ff554d" fill-rule="evenodd" filter="url(#c)" d="M405.568 606.767s-14.487 11.68-31.187 15.594c-9.553 2.238-24.422 1.568-31.188-5.198l-103.96-103.96c-6.766-6.766-7.436-21.635-5.198-31.187 3.914-16.7 15.594-31.188 15.594-31.188l197.523-197.523s14.488-11.68 31.188-15.594c9.553-2.239 24.422-1.568 31.188 5.198l103.96 103.96c6.765 6.765 7.436 21.634 5.197 31.187-3.913 16.7-15.594 31.188-15.594 31.188Z"/>
<filter id="d" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".104"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#ff554d" fill-rule="evenodd" filter="url(#d)" d="M574.579 779.453s-14.488 11.68-31.188 15.594c-9.553 2.238-24.422 1.568-31.188-5.198l-103.96-103.96c-6.765-6.766-7.436-21.635-5.197-31.187 3.914-16.7 15.594-31.188 15.594-31.188L616.163 425.99s14.488-11.68 31.187-15.594c9.553-2.239 24.422-1.568 31.188 5.198l103.96 103.96c6.766 6.765 7.436 21.634 5.198 31.187-3.914 16.7-15.594 31.188-15.594 31.188Z"/>
<filter id="e" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".35"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#31352e" fill-rule="evenodd" filter="url(#e)" d="M414.022 532.978s-8.315 6.704-17.9 8.95c-5.483 1.285-14.016.9-17.9-2.983l-59.667-59.667c-3.883-3.884-4.268-12.417-2.983-17.9 2.246-9.585 8.95-17.9 8.95-17.9L437.89 330.11s8.315-6.704 17.9-8.95c5.482-1.285 14.016-.9 17.9 2.984l59.666 59.666c3.884 3.884 4.269 12.418 2.984 17.9-2.246 9.585-8.95 17.9-8.95 17.9Z"/>
<filter id="f" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".35"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#31352e" fill-rule="evenodd" filter="url(#f)" d="M677.203 544.885c7.177 7.176 18.813 7.176 25.99 0l19.954-19.955c7.177-7.176 7.177-18.813 0-25.99l-19.954-19.954c-7.177-7.176-18.813-7.176-25.99 0l-19.954 19.955c-7.177 7.176-7.177 18.813 0 25.99Z"/>
<filter id="g" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".35"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#31352e" fill-rule="evenodd" filter="url(#g)" d="M489.752 728.66c7.177 7.177 18.813 7.177 25.99 0l19.954-19.954c7.177-7.177 7.177-18.813 0-25.99l-19.954-19.954c-7.177-7.176-18.813-7.176-25.99 0l-19.954 19.955c-7.177 7.176-7.177 18.812 0 25.99Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -21,43 +21,32 @@
#include "ARCodeFile.h"
#include "Platform.h"
namespace melonDS
{
using namespace Platform;
// TODO: import codes from other sources (usrcheat.dat, ...)
// TODO: more user-friendly error reporting
ARCodeFile::ARCodeFile(const std::string& filename)
ARCodeFile::ARCodeFile(const char* filename)
{
Filename = filename;
memset(Filename, 0, sizeof(Filename));
strncpy(Filename, filename, 1023);
Error = false;
Categories.clear();
if (!Load())
Error = true;
}
std::vector<ARCode> ARCodeFile::GetCodes() const noexcept
ARCodeFile::~ARCodeFile()
{
if (Error)
return {};
std::vector<ARCode> codes;
for (const ARCodeCat& cat : Categories)
{
for (const ARCode& code : cat.Codes)
{
codes.push_back(code);
}
}
return codes;
Categories.clear();
}
bool ARCodeFile::Load()
{
FileHandle* f = OpenFile(Filename, FileMode::ReadText);
FILE* f = Platform::OpenFile(Filename, "r");
if (!f) return true;
Categories.clear();
@ -69,11 +58,9 @@ bool ARCodeFile::Load()
ARCode curcode;
char linebuf[1024];
while (!IsEndOfFile(f))
while (!feof(f))
{
if (!FileReadLine(linebuf, 1024, f))
break;
fgets(linebuf, 1024, f);
linebuf[1023] = '\0';
char* start = linebuf;
@ -91,8 +78,8 @@ bool ARCodeFile::Load()
if (ret < 1)
{
Log(LogLevel::Error, "AR: malformed CAT line: %s\n", start);
CloseFile(f);
printf("AR: malformed CAT line: %s\n", start);
fclose(f);
return false;
}
@ -102,7 +89,7 @@ bool ARCodeFile::Load()
if (isincat) Categories.push_back(curcat);
isincat = true;
curcat.Name = catname;
memcpy(curcat.Name, catname, 128);
curcat.Codes.clear();
}
else if (!strncasecmp(start, "CODE", 4))
@ -114,24 +101,24 @@ bool ARCodeFile::Load()
if (ret < 2)
{
Log(LogLevel::Error, "AR: malformed CODE line: %s\n", start);
CloseFile(f);
printf("AR: malformed CODE line: %s\n", start);
fclose(f);
return false;
}
if (!isincat)
{
Log(LogLevel::Error, "AR: encountered CODE line with no category started\n");
CloseFile(f);
printf("AR: encountered CODE line with no category started\n");
fclose(f);
return false;
}
if (isincode) curcat.Codes.push_back(curcode);
isincode = true;
curcode.Name = codename;
memcpy(curcode.Name, codename, 128);
curcode.Enabled = enable!=0;
curcode.Code.clear();
curcode.CodeLen = 0;
}
else
{
@ -140,58 +127,65 @@ bool ARCodeFile::Load()
if (ret < 2)
{
Log(LogLevel::Error, "AR: malformed data line: %s\n", start);
CloseFile(f);
printf("AR: malformed data line: %s\n", start);
fclose(f);
return false;
}
if (!isincode)
{
Log(LogLevel::Error, "AR: encountered data line with no code started\n");
CloseFile(f);
printf("AR: encountered data line with no code started\n");
fclose(f);
return false;
}
curcode.Code.push_back(c0);
curcode.Code.push_back(c1);
if (curcode.CodeLen >= 2*64)
{
printf("AR: code too long!\n");
fclose(f);
return false;
}
u32 idx = curcode.CodeLen;
curcode.Code[idx+0] = c0;
curcode.Code[idx+1] = c1;
curcode.CodeLen += 2;
}
}
if (isincode) curcat.Codes.push_back(curcode);
if (isincat) Categories.push_back(curcat);
CloseFile(f);
fclose(f);
return true;
}
bool ARCodeFile::Save()
{
FileHandle* f = Platform::OpenFile(Filename, FileMode::WriteText);
FILE* f = Platform::OpenFile(Filename, "w");
if (!f) return false;
for (ARCodeCatList::iterator it = Categories.begin(); it != Categories.end(); it++)
{
ARCodeCat& cat = *it;
if (it != Categories.begin()) FileWriteFormatted(f, "\n");
FileWriteFormatted(f, "CAT %s\n\n", cat.Name.c_str());
if (it != Categories.begin()) fprintf(f, "\n");
fprintf(f, "CAT %s\n\n", cat.Name);
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
{
ARCode& code = *jt;
FileWriteFormatted(f, "CODE %d %s\n", code.Enabled, code.Name.c_str());
fprintf(f, "CODE %d %s\n", code.Enabled, code.Name);
for (size_t i = 0; i < code.Code.size(); i+=2)
for (u32 i = 0; i < code.CodeLen; i+=2)
{
FileWriteFormatted(f, "%08X %08X\n", code.Code[i], code.Code[i + 1]);
fprintf(f, "%08X %08X\n", code.Code[i], code.Code[i+1]);
}
FileWriteFormatted(f, "\n");
fprintf(f, "\n");
}
}
CloseFile(f);
fclose(f);
return true;
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -19,27 +19,27 @@
#ifndef ARCODEFILE_H
#define ARCODEFILE_H
#include <string>
#include <list>
#include <vector>
#include "types.h"
namespace melonDS
typedef struct
{
struct ARCode
{
std::string Name;
char Name[128];
bool Enabled;
std::vector<u32> Code;
};
u32 CodeLen;
u32 Code[2*64];
} ARCode;
typedef std::list<ARCode> ARCodeList;
struct ARCodeCat
typedef struct
{
std::string Name;
char Name[128];
ARCodeList Codes;
};
} ARCodeCat;
typedef std::list<ARCodeCat> ARCodeCatList;
@ -47,21 +47,18 @@ typedef std::list<ARCodeCat> ARCodeCatList;
class ARCodeFile
{
public:
ARCodeFile(const std::string& filename);
~ARCodeFile() noexcept = default;
ARCodeFile(const char* filename);
~ARCodeFile();
[[nodiscard]] std::vector<ARCode> GetCodes() const noexcept;
bool Error = false;
bool Error;
bool Load();
bool Save();
ARCodeCatList Categories {};
ARCodeCatList Categories;
private:
std::string Filename;
char Filename[1024];
};
}
#endif // ARCODEFILE_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -21,34 +21,80 @@
#include "NDS.h"
#include "DSi.h"
#include "AREngine.h"
#include "Platform.h"
namespace melonDS
namespace AREngine
{
using Platform::Log;
using Platform::LogLevel;
// AR code file - frontend is responsible for managing this
ARCodeFile* CodeFile;
AREngine::AREngine(melonDS::NDS& nds) : NDS(nds)
u8 (*BusRead8)(u32 addr);
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite8)(u32 addr, u8 val);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
bool Init()
{
CodeFile = nullptr;
return true;
}
void DeInit()
{
}
void Reset()
{
CodeFile = nullptr;
if (NDS::ConsoleType == 1)
{
BusRead8 = DSi::ARM7Read8;
BusRead16 = DSi::ARM7Read16;
BusRead32 = DSi::ARM7Read32;
BusWrite8 = DSi::ARM7Write8;
BusWrite16 = DSi::ARM7Write16;
BusWrite32 = DSi::ARM7Write32;
}
else
{
BusRead8 = NDS::ARM7Read8;
BusRead16 = NDS::ARM7Read16;
BusRead32 = NDS::ARM7Read32;
BusWrite8 = NDS::ARM7Write8;
BusWrite16 = NDS::ARM7Write16;
BusWrite32 = NDS::ARM7Write32;
}
}
void SetCodeFile(ARCodeFile* file)
{
CodeFile = file;
}
#define case16(x) \
case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \
case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
void AREngine::RunCheat(const ARCode& arcode)
void RunCheat(ARCode& arcode)
{
const u32* code = &arcode.Code[0];
u32* code = &arcode.Code[0];
u32 offset = 0;
u32 datareg = 0;
u32 cond = 1;
u32 condstack = 0;
const u32* loopstart = code;
u32* loopstart = code;
u32 loopcount = 0;
u32 loopcond = 1;
u32 loopcondstack = 0;
@ -58,7 +104,7 @@ void AREngine::RunCheat(const ARCode& arcode)
for (;;)
{
if (code >= &arcode.Code[arcode.Code.size()])
if (code >= &arcode.Code[arcode.CodeLen])
break;
u32 a = *code++;
@ -83,15 +129,15 @@ void AREngine::RunCheat(const ARCode& arcode)
switch (op)
{
case16(0x00): // 32-bit write
NDS.ARM7Write32((a & 0x0FFFFFFF) + offset, b);
BusWrite32((a & 0x0FFFFFFF) + offset, b);
break;
case16(0x10): // 16-bit write
NDS.ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
BusWrite16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
break;
case16(0x20): // 8-bit write
NDS.ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF);
BusWrite8((a & 0x0FFFFFFF) + offset, b & 0xFF);
break;
case16(0x30): // IF b > u32[a]
@ -99,9 +145,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(a & 0x0FFFFFFF);
cond = (b > chk) ? 1:0;
}
@ -112,9 +156,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(a & 0x0FFFFFFF);
cond = (b < chk) ? 1:0;
}
@ -125,9 +167,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(a & 0x0FFFFFFF);
cond = (b == chk) ? 1:0;
}
@ -138,9 +178,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(a & 0x0FFFFFFF);
cond = (b != chk) ? 1:0;
}
@ -151,9 +189,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
@ -166,9 +202,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
@ -181,9 +215,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
@ -196,9 +228,7 @@ void AREngine::RunCheat(const ARCode& arcode)
condstack <<= 1;
condstack |= cond;
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(a & 0x0FFFFFFF);
u16 chk = ~(b >> 16);
chk &= val;
@ -207,7 +237,7 @@ void AREngine::RunCheat(const ARCode& arcode)
break;
case16(0xB0): // offset = u32[a + offset]
offset = NDS.ARM7Read32((a & 0x0FFFFFFF) + offset);
offset = BusRead32((a & 0x0FFFFFFF) + offset);
break;
case 0xC0: // FOR 0..b
@ -222,7 +252,7 @@ void AREngine::RunCheat(const ARCode& arcode)
// in practice could be used for a self-modifying AR code
// could be implemented with some hackery, but, does anything even
// use it??
Log(LogLevel::Error, "AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
printf("AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
return;
case 0xC5: // count++ / IF (count & b.l) == b.h
@ -244,7 +274,7 @@ void AREngine::RunCheat(const ARCode& arcode)
break;
case 0xC6: // u32[b] = offset
NDS.ARM7Write32(b, offset);
BusWrite32(b, offset);
break;
case 0xD0: // ENDIF
@ -293,30 +323,30 @@ void AREngine::RunCheat(const ARCode& arcode)
break;
case 0xD6: // u32[b+offset] = datareg / offset += 4
NDS.ARM7Write32(b + offset, datareg);
BusWrite32(b + offset, datareg);
offset += 4;
break;
case 0xD7: // u16[b+offset] = datareg / offset += 2
NDS.ARM7Write16(b + offset, datareg & 0xFFFF);
BusWrite16(b + offset, datareg & 0xFFFF);
offset += 2;
break;
case 0xD8: // u8[b+offset] = datareg / offset += 1
NDS.ARM7Write8(b + offset, datareg & 0xFF);
BusWrite8(b + offset, datareg & 0xFF);
offset += 1;
break;
case 0xD9: // datareg = u32[b+offset]
datareg = NDS.ARM7Read32(b + offset);
datareg = BusRead32(b + offset);
break;
case 0xDA: // datareg = u16[b+offset]
datareg = NDS.ARM7Read16(b + offset);
datareg = BusRead16(b + offset);
break;
case 0xDB: // datareg = u8[b+offset]
datareg = NDS.ARM7Read8(b + offset);
datareg = BusRead8(b + offset);
break;
case 0xDC: // offset += b
@ -331,8 +361,8 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 bytesleft = b;
while (bytesleft >= 8)
{
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
BusWrite32(dstaddr, *code++); dstaddr += 4;
BusWrite32(dstaddr, *code++); dstaddr += 4;
bytesleft -= 8;
}
if (bytesleft > 0)
@ -341,13 +371,13 @@ void AREngine::RunCheat(const ARCode& arcode)
code += 2;
if (bytesleft >= 4)
{
NDS.ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
BusWrite32(dstaddr, *(u32*)leftover); dstaddr += 4;
leftover += 4;
bytesleft -= 4;
}
while (bytesleft > 0)
{
NDS.ARM7Write8(dstaddr, *leftover++); dstaddr++;
BusWrite8(dstaddr, *leftover++); dstaddr++;
bytesleft--;
}
}
@ -363,14 +393,14 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 bytesleft = b;
while (bytesleft >= 4)
{
NDS.ARM7Write32(dstaddr, NDS.ARM7Read32(srcaddr));
BusWrite32(dstaddr, BusRead32(srcaddr));
srcaddr += 4;
dstaddr += 4;
bytesleft -= 4;
}
while (bytesleft > 0)
{
NDS.ARM7Write8(dstaddr, NDS.ARM7Read8(srcaddr));
BusWrite8(dstaddr, BusRead8(srcaddr));
srcaddr++;
dstaddr++;
bytesleft--;
@ -379,20 +409,28 @@ void AREngine::RunCheat(const ARCode& arcode)
break;
default:
Log(LogLevel::Warn, "!! bad AR opcode %08X %08X\n", a, b);
printf("!! bad AR opcode %08X %08X\n", a, b);
return;
}
}
}
void AREngine::RunCheats()
void RunCheats()
{
if (Cheats.empty()) return;
if (!CodeFile) return;
for (const ARCode& code : Cheats)
for (ARCodeCatList::iterator i = CodeFile->Categories.begin(); i != CodeFile->Categories.end(); i++)
{
if (code.Enabled)
RunCheat(code);
ARCodeCat& cat = *i;
for (ARCodeList::iterator j = cat.Codes.begin(); j != cat.Codes.end(); j++)
{
ARCode& code = *j;
if (code.Enabled)
RunCheat(code);
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -19,25 +19,19 @@
#ifndef ARENGINE_H
#define ARENGINE_H
#include <vector>
#include "ARCodeFile.h"
namespace melonDS
namespace AREngine
{
class NDS;
class AREngine
{
public:
AREngine(melonDS::NDS& nds);
std::vector<ARCode> Cheats {};
private:
friend class ARM;
void RunCheats();
void RunCheat(const ARCode& arcode);
bool Init();
void DeInit();
void Reset();
melonDS::NDS& NDS;
};
void SetCodeFile(ARCodeFile* file);
void RunCheats();
}
#endif // ARENGINE_H

File diff suppressed because it is too large Load Diff

332
src/ARM.h
View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -20,18 +20,10 @@
#define ARM_H
#include <algorithm>
#include <optional>
#include "types.h"
#include "MemRegion.h"
#include "MemConstants.h"
#include "NDS.h"
#ifdef GDBSTUB_ENABLED
#include "debug/GdbStub.h"
#endif
namespace melonDS
{
inline u32 ROR(u32 x, u32 n)
{
return (x >> (n&0x1F)) | (x << ((32-n)&0x1F));
@ -43,33 +35,15 @@ enum
RWFlags_ForceUser = (1<<21),
};
enum class CPUExecuteMode : u32
{
Interpreter,
InterpreterGDB,
#ifdef JIT_ENABLED
JIT
#endif
};
struct GDBArgs;
class ARMJIT;
class GPU;
class ARMJIT_Memory;
class NDS;
class Savestate;
const u32 ITCMPhysicalSize = 0x8000;
const u32 DTCMPhysicalSize = 0x4000;
class ARM
#ifdef GDBSTUB_ENABLED
: public Gdb::StubCallbacks
#endif
{
public:
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
ARM(u32 num);
virtual ~ARM(); // destroy shit
void SetGdbArgs(std::optional<GDBArgs> gdb);
virtual void Reset();
virtual void DoSavestate(Savestate* file);
@ -85,9 +59,12 @@ public:
Halted = halt;
}
void NocashPrint(u32 addr) noexcept;
virtual void Execute() = 0;
#ifdef JIT_ENABLED
virtual void ExecuteJIT() = 0;
#endif
bool CheckCondition(u32 code) const
bool CheckCondition(u32 code)
{
if (code == 0xE) return true;
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
@ -116,19 +93,7 @@ public:
if (v) CPSR |= 0x10000000;
}
inline bool ModeIs(u32 mode) const
{
u32 cm = CPSR & 0x1f;
mode &= 0x1f;
if (mode == cm) return true;
if (mode == 0x17) return cm >= 0x14 && cm <= 0x17; // abt
if (mode == 0x1b) return cm >= 0x18 && cm <= 0x1b; // und
return false;
}
void UpdateMode(u32 oldmode, u32 newmode, bool phony = false);
void UpdateMode(u32 oldmode, u32 newmode);
void TriggerIRQ();
@ -149,7 +114,6 @@ public:
virtual void AddCycles_CDI() = 0;
virtual void AddCycles_CD() = 0;
void CheckGdbIncoming();
u32 Num;
@ -183,101 +147,75 @@ public:
u32 ExceptionBase;
MemRegion CodeMem;
NDS::MemRegion CodeMem;
#ifdef JIT_ENABLED
u32 FastBlockLookupStart, FastBlockLookupSize;
u64* FastBlockLookup;
#endif
static const u32 ConditionTable[16];
#ifdef GDBSTUB_ENABLED
Gdb::GdbStub GdbStub;
#endif
melonDS::NDS& NDS;
protected:
virtual u8 BusRead8(u32 addr) = 0;
virtual u16 BusRead16(u32 addr) = 0;
virtual u32 BusRead32(u32 addr) = 0;
virtual void BusWrite8(u32 addr, u8 val) = 0;
virtual void BusWrite16(u32 addr, u16 val) = 0;
virtual void BusWrite32(u32 addr, u32 val) = 0;
#ifdef GDBSTUB_ENABLED
bool IsSingleStep;
bool BreakReq;
bool BreakOnStartup;
u16 Port;
public:
int GetCPU() const override { return Num ? 7 : 9; }
u32 ReadReg(Gdb::Register reg) override;
void WriteReg(Gdb::Register reg, u32 v) override;
u32 ReadMem(u32 addr, int size) override;
void WriteMem(u32 addr, int size, u32 v) override;
void ResetGdb() override;
int RemoteCmd(const u8* cmd, size_t len) override;
static u32 ConditionTable[16];
protected:
#endif
void GdbCheckA();
void GdbCheckB();
void GdbCheckC();
u8 (*BusRead8)(u32 addr);
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite8)(u32 addr, u8 val);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
};
class ARMv5 : public ARM
{
public:
ARMv5(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
ARMv5();
~ARMv5();
void Reset() override;
void Reset();
void DoSavestate(Savestate* file) override;
void DoSavestate(Savestate* file);
void UpdateRegionTimings(u32 addrstart, u32 addrend);
void FillPipeline() override;
void FillPipeline();
void JumpTo(u32 addr, bool restorecpsr = false) override;
void JumpTo(u32 addr, bool restorecpsr = false);
void PrefetchAbort();
void DataAbort();
template <CPUExecuteMode mode>
void Execute();
#ifdef JIT_ENABLED
void ExecuteJIT();
#endif
// all code accesses are forced nonseq 32bit
u32 CodeRead32(u32 addr, bool branch);
void DataRead8(u32 addr, u32* val) override;
void DataRead16(u32 addr, u32* val) override;
void DataRead32(u32 addr, u32* val) override;
void DataRead32S(u32 addr, u32* val) override;
void DataWrite8(u32 addr, u8 val) override;
void DataWrite16(u32 addr, u16 val) override;
void DataWrite32(u32 addr, u32 val) override;
void DataWrite32S(u32 addr, u32 val) override;
void DataRead8(u32 addr, u32* val);
void DataRead16(u32 addr, u32* val);
void DataRead32(u32 addr, u32* val);
void DataRead32S(u32 addr, u32* val);
void DataWrite8(u32 addr, u8 val);
void DataWrite16(u32 addr, u16 val);
void DataWrite32(u32 addr, u32 val);
void DataWrite32S(u32 addr, u32 val);
void AddCycles_C() override
void AddCycles_C()
{
// code only. always nonseq 32-bit for ARM9.
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
Cycles += numC;
}
void AddCycles_CI(s32 numI) override
void AddCycles_CI(s32 numI)
{
// code+internal
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
Cycles += numC + numI;
}
void AddCycles_CDI() override
void AddCycles_CDI()
{
// LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
// TODO: ITCM data fetches shouldn't be parallelized, they say
@ -290,7 +228,7 @@ public:
// Cycles += numC + numD;
}
void AddCycles_CD() override
void AddCycles_CD()
{
// TODO: ITCM data fetches shouldn't be parallelized, they say
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
@ -302,7 +240,7 @@ public:
// Cycles += numC + numD;
}
void GetCodeMemRegion(u32 addr, MemRegion* region);
void GetCodeMemRegion(u32 addr, NDS::MemRegion* region);
void CP15Reset();
void CP15DoSavestate(Savestate* file);
@ -320,19 +258,18 @@ public:
void ICacheInvalidateAll();
void CP15Write(u32 id, u32 val);
u32 CP15Read(u32 id) const;
u32 CP15Read(u32 id);
u32 CP15Control;
u32 RNGSeed;
u32 TraceProcessID;
u32 DTCMSetting, ITCMSetting;
// for aarch64 JIT they need to go up here
// to be addressable by a 12-bit immediate
u32 ITCMSize;
u32 DTCMBase, DTCMMask;
u32 DTCMBase, DTCMSize;
s32 RegionCodeCycles;
u8 ITCM[ITCMPhysicalSize];
@ -356,41 +293,31 @@ public:
u8 PU_UserMap[0x100000];
// games operate under system mode, generally
//#define PU_Map PU_PrivMap
u8* PU_Map;
#define PU_Map PU_PrivMap
// code/16N/32N/32S
u8 MemTimings[0x100000][4];
u8* CurICacheLine;
bool (*GetMemRegion)(u32 addr, bool write, MemRegion* region);
#ifdef GDBSTUB_ENABLED
u32 ReadMem(u32 addr, int size) override;
void WriteMem(u32 addr, int size, u32 v) override;
#endif
protected:
u8 BusRead8(u32 addr) override;
u16 BusRead16(u32 addr) override;
u32 BusRead32(u32 addr) override;
void BusWrite8(u32 addr, u8 val) override;
void BusWrite16(u32 addr, u16 val) override;
void BusWrite32(u32 addr, u32 val) override;
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
};
class ARMv4 : public ARM
{
public:
ARMv4(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
ARMv4();
void FillPipeline() override;
void Reset();
void JumpTo(u32 addr, bool restorecpsr = false) override;
void FillPipeline();
void JumpTo(u32 addr, bool restorecpsr = false);
template <CPUExecuteMode mode>
void Execute();
#ifdef JIT_ENABLED
void ExecuteJIT();
#endif
u16 CodeRead16(u32 addr)
{
@ -402,25 +329,134 @@ public:
return BusRead32(addr);
}
void DataRead8(u32 addr, u32* val) override;
void DataRead16(u32 addr, u32* val) override;
void DataRead32(u32 addr, u32* val) override;
void DataRead32S(u32 addr, u32* val) override;
void DataWrite8(u32 addr, u8 val) override;
void DataWrite16(u32 addr, u16 val) override;
void DataWrite32(u32 addr, u32 val) override;
void DataWrite32S(u32 addr, u32 val) override;
void AddCycles_C() override;
void AddCycles_CI(s32 num) override;
void AddCycles_CDI() override;
void AddCycles_CD() override;
protected:
u8 BusRead8(u32 addr) override;
u16 BusRead16(u32 addr) override;
u32 BusRead32(u32 addr) override;
void BusWrite8(u32 addr, u8 val) override;
void BusWrite16(u32 addr, u16 val) override;
void BusWrite32(u32 addr, u32 val) override;
void DataRead8(u32 addr, u32* val)
{
*val = BusRead8(addr);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataRead16(u32 addr, u32* val)
{
addr &= ~1;
*val = BusRead16(addr);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataRead32(u32 addr, u32* val)
{
addr &= ~3;
*val = BusRead32(addr);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
}
void DataRead32S(u32 addr, u32* val)
{
addr &= ~3;
*val = BusRead32(addr);
DataCycles += NDS::ARM7MemTimings[addr >> 15][3];
}
void DataWrite8(u32 addr, u8 val)
{
BusWrite8(addr, val);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataWrite16(u32 addr, u16 val)
{
addr &= ~1;
BusWrite16(addr, val);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataWrite32(u32 addr, u32 val)
{
addr &= ~3;
BusWrite32(addr, val);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
}
void DataWrite32S(u32 addr, u32 val)
{
addr &= ~3;
BusWrite32(addr, val);
DataCycles += NDS::ARM7MemTimings[addr >> 15][3];
}
void AddCycles_C()
{
// code only. this code fetch is sequential.
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
}
void AddCycles_CI(s32 num)
{
// code+internal. results in a nonseq code fetch.
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
}
void AddCycles_CDI()
{
// LDR/LDM cycles.
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
s32 numD = DataCycles;
if ((DataRegion >> 24) == 0x02) // mainRAM
{
if (CodeRegion == 0x02)
Cycles += numC + numD;
else
{
numC++;
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
}
else if (CodeRegion == 0x02)
{
numD++;
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
else
{
Cycles += numC + numD + 1;
}
}
void AddCycles_CD()
{
// TODO: max gain should be 5c when writing to mainRAM
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
s32 numD = DataCycles;
if ((DataRegion >> 24) == 0x02)
{
if (CodeRegion == 0x02)
Cycles += numC + numD;
else
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
else if (CodeRegion == 0x02)
{
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
else
{
Cycles += numC + numD;
}
}
};
namespace ARMInterpreter
@ -430,5 +466,13 @@ void A_UNK(ARM* cpu);
void T_UNK(ARM* cpu);
}
namespace NDS
{
extern ARMv5* ARM9;
extern ARMv4* ARM7;
}
#endif // ARM_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -22,24 +22,15 @@
#include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_Branch.h"
#include "ARMInterpreter_LoadStore.h"
#include "Platform.h"
#ifdef GDBSTUB_ENABLED
#include "debug/GdbStub.h"
#endif
namespace melonDS::ARMInterpreter
namespace ARMInterpreter
{
using Platform::Log;
using Platform::LogLevel;
void A_UNK(ARM* cpu)
{
Log(LogLevel::Warn, "undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
#ifdef GDBSTUB_ENABLED
cpu->GdbStub.Enter(cpu->GdbStub.IsConnected(), Gdb::TgtStatus::FaultInsn, cpu->R[15]-8);
#endif
printf("undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
//for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
//NDS::Halt();
u32 oldcpsr = cpu->CPSR;
@ -54,10 +45,7 @@ void A_UNK(ARM* cpu)
void T_UNK(ARM* cpu)
{
Log(LogLevel::Warn, "undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
#ifdef GDBSTUB_ENABLED
cpu->GdbStub.Enter(cpu->GdbStub.IsConnected(), Gdb::TgtStatus::FaultInsn, cpu->R[15]-4);
#endif
printf("undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
//NDS::Halt();
u32 oldcpsr = cpu->CPSR;
cpu->CPSR &= ~0xBF;
@ -81,17 +69,9 @@ void A_MSR_IMM(ARM* cpu)
case 0x11: psr = &cpu->R_FIQ[7]; break;
case 0x12: psr = &cpu->R_IRQ[2]; break;
case 0x13: psr = &cpu->R_SVC[2]; break;
case 0x14:
case 0x15:
case 0x16:
case 0x17: psr = &cpu->R_ABT[2]; break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: psr = &cpu->R_UND[2]; break;
default:
cpu->AddCycles_C();
return;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
}
}
else
@ -112,9 +92,6 @@ void A_MSR_IMM(ARM* cpu)
u32 val = ROR((cpu->CurInstr & 0xFF), ((cpu->CurInstr >> 7) & 0x1E));
// bit4 is forced to 1
val |= 0x00000010;
*psr &= ~mask;
*psr |= (val & mask);
@ -134,17 +111,9 @@ void A_MSR_REG(ARM* cpu)
case 0x11: psr = &cpu->R_FIQ[7]; break;
case 0x12: psr = &cpu->R_IRQ[2]; break;
case 0x13: psr = &cpu->R_SVC[2]; break;
case 0x14:
case 0x15:
case 0x16:
case 0x17: psr = &cpu->R_ABT[2]; break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: psr = &cpu->R_UND[2]; break;
default:
cpu->AddCycles_C();
return;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
}
}
else
@ -165,9 +134,6 @@ void A_MSR_REG(ARM* cpu)
u32 val = cpu->R[cpu->CurInstr & 0xF];
// bit4 is forced to 1
val |= 0x00000010;
*psr &= ~mask;
*psr |= (val & mask);
@ -187,15 +153,9 @@ void A_MRS(ARM* cpu)
case 0x11: psr = cpu->R_FIQ[7]; break;
case 0x12: psr = cpu->R_IRQ[2]; break;
case 0x13: psr = cpu->R_SVC[2]; break;
case 0x14:
case 0x15:
case 0x16:
case 0x17: psr = cpu->R_ABT[2]; break;
case 0x18:
case 0x19:
case 0x1A:
case 0x1B: psr = cpu->R_UND[2]; break;
default: psr = cpu->CPSR; break;
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
}
}
else
@ -208,9 +168,6 @@ void A_MRS(ARM* cpu)
void A_MCR(ARM* cpu)
{
if ((cpu->CPSR & 0x1F) == 0x10)
return A_UNK(cpu);
u32 cp = (cpu->CurInstr >> 8) & 0xF;
//u32 op = (cpu->CurInstr >> 21) & 0x7;
u32 cn = (cpu->CurInstr >> 16) & 0xF;
@ -223,11 +180,11 @@ void A_MCR(ARM* cpu)
}
else if (cpu->Num==1 && cp==14)
{
Log(LogLevel::Debug, "MCR p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
printf("MCR p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
}
else
{
Log(LogLevel::Warn, "bad MCR opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
printf("bad MCR opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
return A_UNK(cpu); // TODO: check what kind of exception it really is
}
@ -236,9 +193,6 @@ void A_MCR(ARM* cpu)
void A_MRC(ARM* cpu)
{
if ((cpu->CPSR & 0x1F) == 0x10)
return A_UNK(cpu);
u32 cp = (cpu->CurInstr >> 8) & 0xF;
//u32 op = (cpu->CurInstr >> 21) & 0x7;
u32 cn = (cpu->CurInstr >> 16) & 0xF;
@ -251,11 +205,11 @@ void A_MRC(ARM* cpu)
}
else if (cpu->Num==1 && cp==14)
{
Log(LogLevel::Debug, "MRC p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
printf("MRC p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
}
else
{
Log(LogLevel::Warn, "bad MRC opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
printf("bad MRC opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
return A_UNK(cpu); // TODO: check what kind of exception it really is
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -22,8 +22,6 @@
#include "types.h"
#include "ARM.h"
namespace melonDS
{
namespace ARMInterpreter
{
@ -43,5 +41,4 @@ void A_BLX_IMM(ARM* cpu); // I'm a special one look at me
}
}
#endif // ARMINTERPRETER_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -18,46 +18,18 @@
#include <stdio.h>
#include "ARM.h"
#include "NDS.h"
namespace melonDS::ARMInterpreter
#define CARRY_ADD(a, b) ((0xFFFFFFFF-a) < b)
#define CARRY_SUB(a, b) (a >= b)
#define OVERFLOW_ADD(a, b, res) ((!(((a) ^ (b)) & 0x80000000)) && (((a) ^ (res)) & 0x80000000))
#define OVERFLOW_SUB(a, b, res) ((((a) ^ (b)) & 0x80000000) && (((a) ^ (res)) & 0x80000000))
namespace ARMInterpreter
{
inline bool CarryAdd(u32 a, u32 b)
{
return (0xFFFFFFFF-a) < b;
}
inline bool CarrySub(u32 a, u32 b)
{
return a >= b;
}
inline bool OverflowAdd(u32 a, u32 b)
{
u32 res = a + b;
return (!((a ^ b) & 0x80000000)) && ((a ^ res) & 0x80000000);
}
inline bool OverflowSub(u32 a, u32 b)
{
u32 res = a - b;
return ((a ^ b) & 0x80000000) && ((a ^ res) & 0x80000000);
}
inline bool OverflowAdc(u32 a, u32 b, u32 carry)
{
s64 fullResult = (s64)(s32)a + (s32)b + carry;
u32 res = a + b + carry;
return (s32)res != fullResult;
}
inline bool OverflowSbc(u32 a, u32 b, u32 carry)
{
s64 fullResult = (s64)(s32)a - (s32)b - carry;
u32 res = a - b - carry;
return (s32)res != fullResult;
}
#define LSL_IMM(x, s) \
x <<= s;
@ -318,7 +290,7 @@ void A_##x##_REG_ROR_REG(ARM* cpu) \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -349,7 +321,7 @@ A_IMPLEMENT_ALU_OP(AND,_S)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -380,7 +352,7 @@ A_IMPLEMENT_ALU_OP(EOR,_S)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -392,8 +364,8 @@ A_IMPLEMENT_ALU_OP(EOR,_S)
u32 res = a - b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarrySub(a, b), \
OverflowSub(a, b)); \
CARRY_SUB(a, b), \
OVERFLOW_SUB(a, b, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -413,7 +385,7 @@ A_IMPLEMENT_ALU_OP(SUB,)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -425,8 +397,8 @@ A_IMPLEMENT_ALU_OP(SUB,)
u32 res = b - a; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarrySub(b, a), \
OverflowSub(b, a)); \
CARRY_SUB(b, a), \
OVERFLOW_SUB(b, a, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -446,7 +418,7 @@ A_IMPLEMENT_ALU_OP(RSB,)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -458,8 +430,8 @@ A_IMPLEMENT_ALU_OP(RSB,)
u32 res = a + b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarryAdd(a, b), \
OverflowAdd(a, b)); \
CARRY_ADD(a, b), \
OVERFLOW_ADD(a, b, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -479,7 +451,7 @@ A_IMPLEMENT_ALU_OP(ADD,)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -493,8 +465,8 @@ A_IMPLEMENT_ALU_OP(ADD,)
u32 res = res_tmp + carry; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarryAdd(a, b) | CarryAdd(res_tmp, carry), \
OverflowAdc(a, b, carry)); \
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry), \
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -514,7 +486,7 @@ A_IMPLEMENT_ALU_OP(ADC,)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -528,8 +500,8 @@ A_IMPLEMENT_ALU_OP(ADC,)
u32 res = res_tmp - carry; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarrySub(a, b) & CarrySub(res_tmp, carry), \
OverflowSbc(a, b, carry)); \
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry), \
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -549,7 +521,7 @@ A_IMPLEMENT_ALU_OP(SBC,)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -563,8 +535,8 @@ A_IMPLEMENT_ALU_OP(SBC,)
u32 res = res_tmp - carry; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarrySub(b, a) & CarrySub(res_tmp, carry), \
OverflowSbc(b, a, carry)); \
CARRY_SUB(b, a) & CARRY_SUB(res_tmp, carry), \
OVERFLOW_SUB(b, a, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -603,8 +575,8 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S)
u32 res = a - b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarrySub(a, b), \
OverflowSub(a, b)); \
CARRY_SUB(a, b), \
OVERFLOW_SUB(a, b, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(CMP,)
@ -615,8 +587,8 @@ A_IMPLEMENT_ALU_TEST(CMP,)
u32 res = a + b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CarryAdd(a, b), \
OverflowAdd(a, b)); \
CARRY_ADD(a, b), \
OVERFLOW_ADD(a, b, res)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(CMN,)
@ -628,7 +600,7 @@ A_IMPLEMENT_ALU_TEST(CMN,)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -657,7 +629,7 @@ A_IMPLEMENT_ALU_OP(ORR,_S)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(b & ~1); \
cpu->JumpTo(b); \
} \
else \
{ \
@ -689,11 +661,8 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu)
(cpu->NextInstr[0] & 0xFF000000) == 0xEA000000 && // branch
(cpu->NextInstr[1] & 0xFFFF) == 0x6464)
{
// GBATek says the two bytes after the 2nd ID are _reserved_ for flags
// but since they serve no purpose ATTOW, we can skip them
u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags
// TODO: Pass flags to NocashPrint
cpu->NDS.NocashPrint(cpu->Num, addr);
u32 addr = cpu->R[15] + 2;
NDS::NocashPrint(cpu->Num, addr);
}
}
@ -704,7 +673,7 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res & ~1); \
cpu->JumpTo(res); \
} \
else \
{ \
@ -734,7 +703,7 @@ A_IMPLEMENT_ALU_OP(BIC,_S)
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(b & ~1); \
cpu->JumpTo(b); \
} \
else \
{ \
@ -961,7 +930,7 @@ void A_SMLAxy(ARM* cpu)
u32 res = res_mul + rn;
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
if (OverflowAdd(res_mul, rn))
if (OVERFLOW_ADD(res_mul, rn, res))
cpu->CPSR |= 0x08000000;
cpu->AddCycles_C(); // TODO: interlock??
@ -982,7 +951,7 @@ void A_SMLAWy(ARM* cpu)
u32 res = res_mul + rn;
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
if (OverflowAdd(res_mul, rn))
if (OVERFLOW_ADD(res_mul, rn, res))
cpu->CPSR |= 0x08000000;
cpu->AddCycles_C(); // TODO: interlock??
@ -1079,7 +1048,7 @@ void A_QADD(ARM* cpu)
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 res = rm + rn;
if (OverflowAdd(rm, rn))
if (OVERFLOW_ADD(rm, rn, res))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1097,7 +1066,7 @@ void A_QSUB(ARM* cpu)
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 res = rm - rn;
if (OverflowSub(rm, rn))
if (OVERFLOW_SUB(rm, rn, res))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1114,7 +1083,7 @@ void A_QDADD(ARM* cpu)
u32 rm = cpu->R[cpu->CurInstr & 0xF];
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
if (OverflowAdd(rn, rn))
if (OVERFLOW_ADD(rn, rn, rn<<1))
{
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
cpu->CPSR |= 0x08000000; // CHECKME
@ -1123,7 +1092,7 @@ void A_QDADD(ARM* cpu)
rn <<= 1;
u32 res = rm + rn;
if (OverflowAdd(rm, rn))
if (OVERFLOW_ADD(rm, rn, res))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1140,7 +1109,7 @@ void A_QDSUB(ARM* cpu)
u32 rm = cpu->R[cpu->CurInstr & 0xF];
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
if (OverflowAdd(rn, rn))
if (OVERFLOW_ADD(rn, rn, rn<<1))
{
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
cpu->CPSR |= 0x08000000; // CHECKME
@ -1149,7 +1118,7 @@ void A_QDSUB(ARM* cpu)
rn <<= 1;
u32 res = rm - rn;
if (OverflowSub(rm, rn))
if (OVERFLOW_SUB(rm, rn, res))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1206,8 +1175,8 @@ void T_ADD_REG_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarryAdd(a, b),
OverflowAdd(a, b));
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
cpu->AddCycles_C();
}
@ -1219,8 +1188,8 @@ void T_SUB_REG_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(a, b),
OverflowSub(a, b));
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
cpu->AddCycles_C();
}
@ -1232,8 +1201,8 @@ void T_ADD_IMM_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarryAdd(a, b),
OverflowAdd(a, b));
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
cpu->AddCycles_C();
}
@ -1245,8 +1214,8 @@ void T_SUB_IMM_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(a, b),
OverflowSub(a, b));
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
cpu->AddCycles_C();
}
@ -1266,8 +1235,8 @@ void T_CMP_IMM(ARM* cpu)
u32 res = a - b;
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(a, b),
OverflowSub(a, b));
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
cpu->AddCycles_C();
}
@ -1279,8 +1248,8 @@ void T_ADD_IMM(ARM* cpu)
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarryAdd(a, b),
OverflowAdd(a, b));
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
cpu->AddCycles_C();
}
@ -1292,8 +1261,8 @@ void T_SUB_IMM(ARM* cpu)
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(a, b),
OverflowSub(a, b));
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
cpu->AddCycles_C();
}
@ -1363,8 +1332,8 @@ void T_ADC_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarryAdd(a, b) | CarryAdd(res_tmp, carry),
OverflowAdc(a, b, carry));
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry),
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res));
cpu->AddCycles_C();
}
@ -1378,8 +1347,8 @@ void T_SBC_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(a, b) & CarrySub(res_tmp, carry),
OverflowSbc(a, b, carry));
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry),
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res));
cpu->AddCycles_C();
}
@ -1411,8 +1380,8 @@ void T_NEG_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(0, b),
OverflowSub(0, b));
CARRY_SUB(0, b),
OVERFLOW_SUB(0, b, res));
cpu->AddCycles_C();
}
@ -1423,8 +1392,8 @@ void T_CMP_REG(ARM* cpu)
u32 res = a - b;
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(a, b),
OverflowSub(a, b));
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
cpu->AddCycles_C();
}
@ -1435,8 +1404,8 @@ void T_CMN_REG(ARM* cpu)
u32 res = a + b;
cpu->SetNZCV(res & 0x80000000,
!res,
CarryAdd(a, b),
OverflowAdd(a, b));
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
cpu->AddCycles_C();
}
@ -1532,8 +1501,8 @@ void T_CMP_HIREG(ARM* cpu)
cpu->SetNZCV(res & 0x80000000,
!res,
CarrySub(a, b),
OverflowSub(a, b));
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
cpu->AddCycles_C();
}
@ -1558,11 +1527,8 @@ void T_MOV_HIREG(ARM* cpu)
(cpu->NextInstr[0] & 0xF800) == 0xE000 && // branch
(cpu->NextInstr[1] & 0xFFFF) == 0x6464)
{
// GBATek says the two bytes after the 2nd ID are _reserved_ for flags
// but since they serve no purpose ATTOW, we can skip them
u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags
// TODO: Pass flags to NocashPrint
cpu->NDS.NocashPrint(cpu->Num, addr);
u32 addr = cpu->R[15] + 2;
NDS::NocashPrint(cpu->Num, addr);
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -19,8 +19,6 @@
#ifndef ARMINTERPRETER_ALU_H
#define ARMINTERPRETER_ALU_H
namespace melonDS
{
namespace ARMInterpreter
{
@ -136,5 +134,4 @@ void T_ADD_SP(ARM* cpu);
}
}
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -16,13 +16,12 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include "ARM.h"
#include "Platform.h"
namespace melonDS::ARMInterpreter
namespace ARMInterpreter
{
using Platform::Log;
using Platform::LogLevel;
void A_B(ARM* cpu)
@ -80,7 +79,7 @@ void T_BLX_REG(ARM* cpu)
{
if (cpu->Num==1)
{
Log(LogLevel::Warn, "!! THUMB BLX_REG ON ARM7\n");
printf("!! THUMB BLX_REG ON ARM7\n");
return;
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -19,8 +19,6 @@
#ifndef ARMINTERPRETER_BRANCH_H
#define ARMINTERPRETER_BRANCH_H
namespace melonDS
{
namespace ARMInterpreter
{
@ -38,5 +36,4 @@ void T_BL_LONG_2(ARM* cpu);
}
}
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -20,7 +20,7 @@
#include "ARM.h"
namespace melonDS::ARMInterpreter
namespace ARMInterpreter
{
@ -62,20 +62,14 @@ namespace melonDS::ARMInterpreter
#define A_STR \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
storeval += 4; \
cpu->DataWrite32(offset, storeval); \
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->AddCycles_CD();
// TODO: user mode (bit21)
#define A_STR_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
storeval += 4; \
cpu->DataWrite32(addr, storeval); \
cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->AddCycles_CD();
@ -416,7 +410,7 @@ void A_LDM(ARM* cpu)
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10, true);
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
for (int i = 0; i < 15; i++)
{
@ -430,9 +424,9 @@ void A_LDM(ARM* cpu)
}
}
u32 pc = 0;
if (cpu->CurInstr & (1<<15))
{
u32 pc;
if (preinc) base += 4;
if (first) cpu->DataRead32 (base, &pc);
else cpu->DataRead32S(base, &pc);
@ -440,8 +434,13 @@ void A_LDM(ARM* cpu)
if (cpu->Num == 1)
pc &= ~0x1;
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
if (cpu->CurInstr & (1<<21))
{
// post writeback
@ -461,12 +460,6 @@ void A_LDM(ARM* cpu)
cpu->R[baseid] = wbbase;
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
if (cpu->CurInstr & (1<<15))
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
cpu->AddCycles_CDI();
}
@ -501,7 +494,7 @@ void A_STM(ARM* cpu)
else if (mode != 0x10 && mode != 0x1F)
isbanked = (baseid >= 13 && baseid < 15);
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10, true);
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
}
for (u32 i = 0; i < 16; i++)
@ -527,7 +520,7 @@ void A_STM(ARM* cpu)
}
if (cpu->CurInstr & (1<<22))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21)))
cpu->R[baseid] = base;

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.
@ -19,7 +19,7 @@
#ifndef ARMINTERPRETER_LOADSTORE_H
#define ARMINTERPRETER_LOADSTORE_H
namespace melonDS::ARMInterpreter
namespace ARMInterpreter
{
#define A_PROTO_WB_LDRSTR(x) \

View File

@ -1,23 +1,5 @@
/*
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 "ARMJIT.h"
#include "ARMJIT_Memory.h"
#include <string.h>
#include <assert.h>
#include <unordered_map>
@ -25,12 +7,11 @@
#define XXH_STATIC_LINKING_ONLY
#include "xxhash/xxhash.h"
#include "Platform.h"
#include "Config.h"
#include "ARMJIT_Internal.h"
#include "ARMJIT_Memory.h"
#include "ARMJIT_Compiler.h"
#include "ARMJIT_Global.h"
#include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_LoadStore.h"
@ -43,52 +24,130 @@
#include "SPU.h"
#include "Wifi.h"
#include "NDSCart.h"
#include "Platform.h"
#include "ARMJIT_x64/ARMJIT_Offsets.h"
static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset);
static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset);
static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset);
namespace melonDS
namespace ARMJIT
{
using Platform::Log;
using Platform::LogLevel;
static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset, "");
static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset, "");
static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset, "");
#define JIT_DEBUGPRINT(msg, ...)
//#define JIT_DEBUGPRINT(msg, ...) Platform::Log(Platform::LogLevel::Debug, msg, ## __VA_ARGS__)
//#define JIT_DEBUGPRINT(msg, ...) printf(msg, ## __VA_ARGS__)
Compiler* JITCompiler;
std::unordered_map<u32, JitBlock*> JitBlocks9;
std::unordered_map<u32, JitBlock*> JitBlocks7;
std::unordered_map<u32, JitBlock*> RestoreCandidates;
TinyVector<u32> InvalidLiterals;
AddressRange CodeIndexITCM[ITCMPhysicalSize / 512];
AddressRange CodeIndexMainRAM[NDS::MainRAMMaxSize / 512];
AddressRange CodeIndexSWRAM[NDS::SharedWRAMSize / 512];
AddressRange CodeIndexVRAM[0x100000 / 512];
AddressRange CodeIndexARM9BIOS[sizeof(NDS::ARM9BIOS) / 512];
AddressRange CodeIndexARM7BIOS[sizeof(NDS::ARM7BIOS) / 512];
AddressRange CodeIndexARM7WRAM[NDS::ARM7WRAMSize / 512];
AddressRange CodeIndexARM7WVRAM[0x40000 / 512];
AddressRange CodeIndexBIOS9DSi[0x10000 / 512];
AddressRange CodeIndexBIOS7DSi[0x10000 / 512];
AddressRange CodeIndexNWRAM_A[DSi::NWRAMSize / 512];
AddressRange CodeIndexNWRAM_B[DSi::NWRAMSize / 512];
AddressRange CodeIndexNWRAM_C[DSi::NWRAMSize / 512];
u64 FastBlockLookupITCM[ITCMPhysicalSize / 2];
u64 FastBlockLookupMainRAM[NDS::MainRAMMaxSize / 2];
u64 FastBlockLookupSWRAM[NDS::SharedWRAMSize / 2];
u64 FastBlockLookupVRAM[0x100000 / 2];
u64 FastBlockLookupARM9BIOS[sizeof(NDS::ARM9BIOS) / 2];
u64 FastBlockLookupARM7BIOS[sizeof(NDS::ARM7BIOS) / 2];
u64 FastBlockLookupARM7WRAM[NDS::ARM7WRAMSize / 2];
u64 FastBlockLookupARM7WVRAM[0x40000 / 2];
u64 FastBlockLookupBIOS9DSi[0x10000 / 2];
u64 FastBlockLookupBIOS7DSi[0x10000 / 2];
u64 FastBlockLookupNWRAM_A[DSi::NWRAMSize / 2];
u64 FastBlockLookupNWRAM_B[DSi::NWRAMSize / 2];
u64 FastBlockLookupNWRAM_C[DSi::NWRAMSize / 2];
const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] =
{
0,
ITCMPhysicalSize,
0,
ARM9BIOSSize,
MainRAMMaxSize,
SharedWRAMSize,
sizeof(NDS::ARM9BIOS),
NDS::MainRAMMaxSize,
NDS::SharedWRAMSize,
0,
0x100000,
ARM7BIOSSize,
ARM7WRAMSize,
sizeof(NDS::ARM7BIOS),
NDS::ARM7WRAMSize,
0,
0,
0x40000,
0x10000,
0x10000,
NWRAMSize,
NWRAMSize,
NWRAMSize,
DSi::NWRAMSize,
DSi::NWRAMSize,
DSi::NWRAMSize,
};
u32 ARMJIT::LocaliseCodeAddress(u32 num, u32 addr) const noexcept
AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
CodeIndexITCM,
NULL,
CodeIndexARM9BIOS,
CodeIndexMainRAM,
CodeIndexSWRAM,
NULL,
CodeIndexVRAM,
CodeIndexARM7BIOS,
CodeIndexARM7WRAM,
NULL,
NULL,
CodeIndexARM7WVRAM,
CodeIndexBIOS9DSi,
CodeIndexBIOS7DSi,
CodeIndexNWRAM_A,
CodeIndexNWRAM_B,
CodeIndexNWRAM_C
};
u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
FastBlockLookupITCM,
NULL,
FastBlockLookupARM9BIOS,
FastBlockLookupMainRAM,
FastBlockLookupSWRAM,
NULL,
FastBlockLookupVRAM,
FastBlockLookupARM7BIOS,
FastBlockLookupARM7WRAM,
NULL,
NULL,
FastBlockLookupARM7WVRAM,
FastBlockLookupBIOS9DSi,
FastBlockLookupBIOS7DSi,
FastBlockLookupNWRAM_A,
FastBlockLookupNWRAM_B,
FastBlockLookupNWRAM_C
};
u32 LocaliseCodeAddress(u32 num, u32 addr)
{
int region = num == 0
? Memory.ClassifyAddress9(addr)
: Memory.ClassifyAddress7(addr);
? ARMJIT_Memory::ClassifyAddress9(addr)
: ARMJIT_Memory::ClassifyAddress7(addr);
if (CodeMemRegions[region])
return Memory.LocaliseAddress(region, num, addr);
return ARMJIT_Memory::LocaliseAddress(region, num, addr);
return 0;
}
@ -101,34 +160,14 @@ T SlowRead9(u32 addr, ARMv5* cpu)
T val;
if (addr < cpu->ITCMSize)
val = *(T*)&cpu->ITCM[addr & 0x7FFF];
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
val = *(T*)&cpu->DTCM[addr & 0x3FFF];
else if (addr >= cpu->DTCMBase && addr < (cpu->DTCMBase + cpu->DTCMSize))
val = *(T*)&cpu->DTCM[(addr - cpu->DTCMBase) & 0x3FFF];
else if (std::is_same<T, u32>::value)
val = NDS::Current->ARM9Read32(addr);
val = (ConsoleType == 0 ? NDS::ARM9Read32 : DSi::ARM9Read32)(addr);
else if (std::is_same<T, u16>::value)
val = NDS::Current->ARM9Read16(addr);
val = (ConsoleType == 0 ? NDS::ARM9Read16 : DSi::ARM9Read16)(addr);
else
val = NDS::Current->ARM9Read8(addr);
if (std::is_same<T, u32>::value)
return ROR(val, offset << 3);
else
return val;
}
template <typename T, int ConsoleType>
T SlowRead7(u32 addr)
{
u32 offset = addr & 0x3;
addr &= ~(sizeof(T) - 1);
T val;
if (std::is_same<T, u32>::value)
val = NDS::Current->ARM7Read32(addr);
else if (std::is_same<T, u16>::value)
val = NDS::Current->ARM7Read16(addr);
else
val = NDS::Current->ARM7Read8(addr);
val = (ConsoleType == 0 ? NDS::ARM9Read8 : DSi::ARM9Read8)(addr);
if (std::is_same<T, u32>::value)
return ROR(val, offset << 3);
@ -143,45 +182,65 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
if (addr < cpu->ITCMSize)
{
cpu->NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
*(T*)&cpu->ITCM[addr & 0x7FFF] = val;
}
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
else if (addr >= cpu->DTCMBase && addr < (cpu->DTCMBase + cpu->DTCMSize))
{
*(T*)&cpu->DTCM[addr & 0x3FFF] = val;
*(T*)&cpu->DTCM[(addr - cpu->DTCMBase) & 0x3FFF] = val;
}
else if (std::is_same<T, u32>::value)
{
NDS::Current->ARM9Write32(addr, val);
(ConsoleType == 0 ? NDS::ARM9Write32 : DSi::ARM9Write32)(addr, val);
}
else if (std::is_same<T, u16>::value)
{
NDS::Current->ARM9Write16(addr, val);
(ConsoleType == 0 ? NDS::ARM9Write16 : DSi::ARM9Write16)(addr, val);
}
else
{
NDS::Current->ARM9Write8(addr, val);
(ConsoleType == 0 ? NDS::ARM9Write8 : DSi::ARM9Write8)(addr, val);
}
}
template <typename T, int ConsoleType>
T SlowRead7(u32 addr)
{
u32 offset = addr & 0x3;
addr &= ~(sizeof(T) - 1);
T val;
if (std::is_same<T, u32>::value)
val = (ConsoleType == 0 ? NDS::ARM7Read32 : DSi::ARM7Read32)(addr);
else if (std::is_same<T, u16>::value)
val = (ConsoleType == 0 ? NDS::ARM7Read16 : DSi::ARM7Read16)(addr);
else
val = (ConsoleType == 0 ? NDS::ARM7Read8 : DSi::ARM7Read8)(addr);
if (std::is_same<T, u32>::value)
return ROR(val, offset << 3);
else
return val;
}
template <typename T, int ConsoleType>
void SlowWrite7(u32 addr, u32 val)
{
addr &= ~(sizeof(T) - 1);
if (std::is_same<T, u32>::value)
NDS::Current->ARM7Write32(addr, val);
(ConsoleType == 0 ? NDS::ARM7Write32 : DSi::ARM7Write32)(addr, val);
else if (std::is_same<T, u16>::value)
NDS::Current->ARM7Write16(addr, val);
(ConsoleType == 0 ? NDS::ARM7Write16 : DSi::ARM7Write16)(addr, val);
else
NDS::Current->ARM7Write8(addr, val);
(ConsoleType == 0 ? NDS::ARM7Write8 : DSi::ARM7Write8)(addr, val);
}
template <bool Write, int ConsoleType>
void SlowBlockTransfer9(u32 addr, u64* data, u32 num, ARMv5* cpu)
{
addr &= ~0x3;
for (u32 i = 0; i < num; i++)
for (int i = 0; i < num; i++)
{
if (Write)
SlowWrite9<u32, ConsoleType>(addr, cpu, data[i]);
@ -195,7 +254,7 @@ template <bool Write, int ConsoleType>
void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
{
addr &= ~0x3;
for (u32 i = 0; i < num; i++)
for (int i = 0; i < num; i++)
{
if (Write)
SlowWrite7<u32, ConsoleType>(addr, data[i]);
@ -230,18 +289,26 @@ void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
INSTANTIATE_SLOWMEM(0)
INSTANTIATE_SLOWMEM(1)
ARMJIT::~ARMJIT() noexcept
void Init()
{
JitEnableWrite();
ResetBlockCache();
JITCompiler = new Compiler();
ARMJIT_Memory::Init();
}
void ARMJIT::Reset() noexcept
void DeInit()
{
ResetBlockCache();
ARMJIT_Memory::DeInit();
delete JITCompiler;
}
void Reset()
{
JitEnableWrite();
ResetBlockCache();
Memory.Reset();
ARMJIT_Memory::Reset();
}
void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags)
@ -289,7 +356,7 @@ bool DecodeLiteral(bool thumb, const FetchedInstr& instr, u32& addr)
return false;
}
bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink, u32 lr, bool& link,
bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink, u32 lr, bool& link,
u32& linkAddr, u32& targetAddr)
{
if (thumb)
@ -332,7 +399,7 @@ bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink
linkAddr = instr.Addr + 4;
cond = instr.Cond();
if (instr.Info.Kind == ARMInstrInfo::ak_BL
if (instr.Info.Kind == ARMInstrInfo::ak_BL
|| instr.Info.Kind == ARMInstrInfo::ak_B)
{
s32 offset = (s32)(instr.Instr << 8) >> 6;
@ -373,7 +440,7 @@ bool IsIdleLoop(bool thumb, FetchedInstr* instrs, int instrsCount)
u16 dstRegs = instrs[i].Info.DstRegs & ~(1 << 15);
regsDisallowedToWrite |= srcRegs & ~regsWrittenTo;
if (dstRegs & regsDisallowedToWrite)
return false;
regsWrittenTo |= dstRegs;
@ -463,22 +530,12 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
F(LDRB_IMM), F(STRH_IMM), F(LDRH_IMM), F(STR_SPREL), F(LDR_SPREL),
F(PUSH), F(POP), F(LDMIA), F(STMIA),
F(BCOND), F(BX), F(BLX_REG), F(B), F(BL_LONG_1), F(BL_LONG_2),
F(UNK), F(SVC),
F(UNK), F(SVC),
T_BL_LONG // BL_LONG psudo opcode
};
#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
void RetireJitBlock(JitBlock* block)
{
auto it = RestoreCandidates.find(block->InstrHash);
if (it != RestoreCandidates.end())
@ -492,53 +549,21 @@ 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
|| LiteralOptimizations != args.LiteralOptimizations
|| BranchOptimizations != args.BranchOptimizations
|| FastMemory != args.FastMemory)
ResetBlockCache();
MaxBlockSize = args.MaxBlockSize;
LiteralOptimizations = args.LiteralOptimizations;
BranchOptimizations = args.BranchOptimizations;
FastMemory = args.FastMemory;
}
void ARMJIT::SetMaxBlockSize(int size) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(size), LiteralOptimizations, LiteralOptimizations, FastMemory});
}
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), enabled, BranchOptimizations, FastMemory});
}
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, enabled, FastMemory});
}
void ARMJIT::SetFastMemory(bool enabled) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled});
}
void ARMJIT::CompileBlock(ARM* cpu) noexcept
void CompileBlock(ARM* cpu)
{
bool thumb = cpu->CPSR & 0x20;
if (Config::JIT_MaxBlockSize < 1)
Config::JIT_MaxBlockSize = 1;
if (Config::JIT_MaxBlockSize > 32)
Config::JIT_MaxBlockSize = 32;
u32 blockAddr = cpu->R[15] - (thumb ? 2 : 4);
u32 localAddr = LocaliseCodeAddress(cpu->Num, blockAddr);
if (!localAddr)
{
Log(LogLevel::Warn, "trying to compile non executable code? %x\n", blockAddr);
printf("trying to compile non executable code? %x\n", blockAddr);
}
auto& map = cpu->Num == 0 ? JitBlocks9 : JitBlocks7;
@ -556,34 +581,29 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
u64* entry = &FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2];
*entry = ((u64)blockAddr | cpu->Num) << 32;
*entry |= JITCompiler.SubEntryOffset(existingBlockIt->second->EntryPoint);
*entry |= JITCompiler->SubEntryOffset(existingBlockIt->second->EntryPoint);
return;
}
// some memory has been remapped
RetireJitBlock(existingBlockIt->second);
RetireJitBlock(existingBlockIt->second);
map.erase(existingBlockIt);
}
FetchedInstr instrs[MaxBlockSize];
FetchedInstr instrs[Config::JIT_MaxBlockSize];
int i = 0;
u32 r15 = cpu->R[15];
u32 addressRanges[MaxBlockSize];
u32 addressMasks[MaxBlockSize];
memset(addressMasks, 0, MaxBlockSize * sizeof(u32));
u32 addressRanges[Config::JIT_MaxBlockSize];
u32 addressMasks[Config::JIT_MaxBlockSize];
memset(addressMasks, 0, Config::JIT_MaxBlockSize * sizeof(u32));
u32 numAddressRanges = 0;
u32 numLiterals = 0;
u32 literalLoadAddrs[MaxBlockSize];
u32 literalLoadAddrs[Config::JIT_MaxBlockSize];
// they are going to be hashed
u32 literalValues[MaxBlockSize];
u32 instrValues[MaxBlockSize];
// due to instruction merging i might not reflect the amount of actual instructions
u32 numInstrs = 0;
u32 writeAddrs[MaxBlockSize];
u32 numWriteAddrs = 0, writeAddrsTranslated = 0;
u32 literalValues[Config::JIT_MaxBlockSize];
u32 instrValues[Config::JIT_MaxBlockSize];
cpu->FillPipeline();
u32 nextInstr[2] = {cpu->NextInstr[0], cpu->NextInstr[1]};
@ -595,8 +615,6 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
u32 lr;
bool hasLink = false;
bool hasMemoryInstr = false;
do
{
r15 += thumb ? 2 : 4;
@ -605,13 +623,13 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
instrs[i].SetFlags = 0;
instrs[i].Instr = nextInstr[0];
nextInstr[0] = nextInstr[1];
instrs[i].Addr = nextInstrAddr[0];
nextInstrAddr[0] = nextInstrAddr[1];
nextInstrAddr[1] = r15;
JIT_DEBUGPRINT("instr %08x %x\n", instrs[i].Instr & (thumb ? 0xFFFF : ~0), instrs[i].Addr);
instrValues[numInstrs++] = instrs[i].Instr;
instrValues[i] = instrs[i].Instr;
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, instrs[i].Addr);
assert(translatedAddr >> 27);
@ -619,7 +637,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
if (i == 0 || translatedAddrRounded != addressRanges[numAddressRanges - 1])
{
bool returning = false;
for (u32 j = 0; j < numAddressRanges; j++)
for (int j = 0; j < numAddressRanges; j++)
{
if (addressRanges[j] == translatedAddrRounded)
{
@ -657,11 +675,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
nextInstr[1] = cpuv4->CodeRead32(r15);
instrs[i].CodeCycles = cpu->CodeCycles;
}
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr, LiteralOptimizations);
hasMemoryInstr |= thumb
? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA)
: (instrs[i].Info.Kind >= ARMInstrInfo::ak_STR_REG_LSL && instrs[i].Info.Kind <= ARMInstrInfo::ak_STM);
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr);
cpu->R[15] = r15;
cpu->CurInstr = instrs[i].Instr;
@ -701,47 +715,41 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
instrs[i].DataRegion = cpu->DataRegion;
u32 literalAddr;
if (LiteralOptimizations
if (Config::JIT_LiteralOptimisations
&& instrs[i].Info.SpecialKind == ARMInstrInfo::special_LoadLiteral
&& DecodeLiteral(thumb, instrs[i], literalAddr))
{
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, literalAddr);
if (!translatedAddr)
{
Log(LogLevel::Warn,"literal in non executable memory?\n");
printf("literal in non executable memory?\n");
}
if (InvalidLiterals.Find(translatedAddr) == -1)
{
u32 translatedAddrRounded = translatedAddr & ~0x1FF;
u32 translatedAddrRounded = translatedAddr & ~0x1FF;
u32 j = 0;
for (; j < numAddressRanges; j++)
if (addressRanges[j] == translatedAddrRounded)
break;
if (j == numAddressRanges)
addressRanges[numAddressRanges++] = translatedAddrRounded;
addressMasks[j] |= 1 << ((translatedAddr & 0x1FF) / 16);
JIT_DEBUGPRINT("literal loading %08x %08x %08x %08x\n", literalAddr, translatedAddr, addressMasks[j], addressRanges[j]);
cpu->DataRead32(literalAddr, &literalValues[numLiterals]);
literalLoadAddrs[numLiterals++] = translatedAddr;
}
u32 j = 0;
for (; j < numAddressRanges; j++)
if (addressRanges[j] == translatedAddrRounded)
break;
if (j == numAddressRanges)
addressRanges[numAddressRanges++] = translatedAddrRounded;
addressMasks[j] |= 1 << ((translatedAddr & 0x1FF) / 16);
JIT_DEBUGPRINT("literal loading %08x %08x %08x %08x\n", literalAddr, translatedAddr, addressMasks[j], addressRanges[j]);
cpu->DataRead32(literalAddr, &literalValues[numLiterals]);
literalLoadAddrs[numLiterals++] = translatedAddr;
}
else if (instrs[i].Info.SpecialKind == ARMInstrInfo::special_WriteMem)
writeAddrs[numWriteAddrs++] = instrs[i].DataRegion;
else if (thumb && instrs[i].Info.Kind == ARMInstrInfo::tk_BL_LONG_2 && i > 0
if (thumb && instrs[i].Info.Kind == ARMInstrInfo::tk_BL_LONG_2 && i > 0
&& instrs[i - 1].Info.Kind == ARMInstrInfo::tk_BL_LONG_1)
{
instrs[i - 1].Info.Kind = ARMInstrInfo::tk_BL_LONG;
instrs[i - 1].Instr = (instrs[i - 1].Instr & 0xFFFF) | (instrs[i].Instr << 16);
instrs[i - 1].Info.DstRegs = 0xC000;
instrs[i - 1].Info.SrcRegs = 0;
instrs[i - 1].Info.EndBlock = true;
i--;
instrs[i].Info.Kind = ARMInstrInfo::tk_BL_LONG;
instrs[i].Instr = (instrs[i].Instr & 0xFFFF) | (instrs[i + 1].Instr << 16);
instrs[i].Info.DstRegs = 0xC000;
instrs[i].Info.SrcRegs = 0;
instrs[i].Info.EndBlock = true;
JIT_DEBUGPRINT("merged BL\n");
}
if (instrs[i].Info.Branches() && BranchOptimizations
&& instrs[i].Info.Kind != (thumb ? ARMInstrInfo::tk_SVC : ARMInstrInfo::ak_SVC))
if (instrs[i].Info.Branches() && Config::JIT_BranchOptimisations)
{
bool hasBranched = cpu->R[15] != r15;
@ -759,7 +767,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
{
for (int j = 0; j < i; j++)
{
if (instrs[j].Addr == target)
if (instrs[i].Addr == target)
{
isBackJump = true;
break;
@ -777,14 +785,14 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
JIT_DEBUGPRINT("found %s idle loop %d in block %08x\n", thumb ? "thumb" : "arm", cpu->Num, blockAddr);
}
}
else if (hasBranched && !isBackJump && i + 1 < MaxBlockSize)
else if (hasBranched && !isBackJump && i + 1 < Config::JIT_MaxBlockSize)
{
if (link)
{
lr = linkAddr;
hasLink = true;
}
r15 = target + (thumb ? 2 : 4);
assert(r15 == cpu->R[15]);
@ -805,9 +813,8 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
}
}
if (!hasBranched && cond < 0xE && i + 1 < MaxBlockSize)
if (!hasBranched && cond < 0xE && i + 1 < Config::JIT_MaxBlockSize)
{
JIT_DEBUGPRINT("block lengthened by untaken branch\n");
instrs[i].Info.EndBlock = false;
instrs[i].BranchFlags |= branch_FollowCondNotTaken;
}
@ -815,34 +822,14 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
i++;
bool canCompile = JITCompiler.CanCompile(thumb, instrs[i - 1].Info.Kind);
bool canCompile = JITCompiler->CanCompile(thumb, instrs[i - 1].Info.Kind);
bool secondaryFlagReadCond = !canCompile || (instrs[i - 1].BranchFlags & (branch_FollowCondTaken | branch_FollowCondNotTaken));
if (instrs[i - 1].Info.ReadFlags != 0 || secondaryFlagReadCond)
FloodFillSetFlags(instrs, i - 2, !secondaryFlagReadCond ? instrs[i - 1].Info.ReadFlags : 0xF);
} while(!instrs[i - 1].Info.EndBlock && i < MaxBlockSize && !cpu->Halted && (!cpu->IRQ || (cpu->CPSR & 0x80)));
if (numLiterals)
{
for (u32 j = 0; j < numWriteAddrs; j++)
{
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, writeAddrs[j]);
if (translatedAddr)
{
for (u32 k = 0; k < numLiterals; k++)
{
if (literalLoadAddrs[k] == translatedAddr)
{
if (InvalidLiterals.Find(translatedAddr) == -1)
InvalidLiterals.Add(translatedAddr);
break;
}
}
}
}
}
} while(!instrs[i - 1].Info.EndBlock && i < Config::JIT_MaxBlockSize && !cpu->Halted && (!cpu->IRQ || (cpu->CPSR & 0x80)));
u32 literalHash = (u32)XXH3_64bits(literalValues, numLiterals * 4);
u32 instrHash = (u32)XXH3_64bits(instrValues, numInstrs * 4);
u32 instrHash = (u32)XXH3_64bits(instrValues, i * 4);
auto prevBlockIt = RestoreCandidates.find(instrHash);
JitBlock* prevBlock = NULL;
@ -856,7 +843,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
if (mayRestore && prevBlock->NumAddresses == numAddressRanges)
{
for (u32 j = 0; j < numAddressRanges; j++)
for (int j = 0; j < numAddressRanges; j++)
{
if (prevBlock->AddressRanges()[j] != addressRanges[j]
|| prevBlock->AddressMasks()[j] != addressMasks[j])
@ -883,9 +870,9 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
block = new JitBlock(cpu->Num, i, numAddressRanges, numLiterals);
block->LiteralHash = literalHash;
block->InstrHash = instrHash;
for (u32 j = 0; j < numAddressRanges; j++)
for (int j = 0; j < numAddressRanges; j++)
block->AddressRanges()[j] = addressRanges[j];
for (u32 j = 0; j < numAddressRanges; j++)
for (int j = 0; j < numAddressRanges; j++)
block->AddressMasks()[j] = addressMasks[j];
for (int j = 0; j < numLiterals; j++)
block->Literals()[j] = literalLoadAddrs[j];
@ -895,9 +882,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
FloodFillSetFlags(instrs, i - 1, 0xF);
JitEnableWrite();
block->EntryPoint = JITCompiler.CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr);
JitEnableExecute();
block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i);
JIT_DEBUGPRINT("block start %p\n", block->EntryPoint);
}
@ -908,7 +893,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
}
assert((localAddr & 1) == 0);
for (u32 j = 0; j < numAddressRanges; j++)
for (int j = 0; j < numAddressRanges; j++)
{
assert(addressRanges[j] == block->AddressRanges()[j]);
assert(addressMasks[j] == block->AddressMasks()[j]);
@ -916,8 +901,8 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
if (!PageContainsCode(&region[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
if (!PageContainsCode(&region[(addressRanges[j] & 0x7FFF000) / 512]))
ARMJIT_Memory::SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
AddressRange* range = &region[(addressRanges[j] & 0x7FFFFFF) / 512];
range->Code |= addressMasks[j];
@ -931,10 +916,10 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
u64* entry = &FastBlockLookupRegions[(localAddr >> 27)][(localAddr & 0x7FFFFFF) / 2];
*entry = ((u64)blockAddr | cpu->Num) << 32;
*entry |= JITCompiler.SubEntryOffset(block->EntryPoint);
*entry |= JITCompiler->SubEntryOffset(block->EntryPoint);
}
void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
void InvalidateByAddr(u32 localAddr)
{
JIT_DEBUGPRINT("invalidating by addr %x\n", localAddr);
@ -969,9 +954,9 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
range->Blocks.Remove(i);
if (range->Blocks.Length == 0
&& !PageContainsCode(&region[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
&& !PageContainsCode(&region[(localAddr & 0x7FFF000) / 512]))
{
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
ARMJIT_Memory::SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
}
bool literalInvalidation = false;
@ -980,7 +965,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
u32 addr = block->Literals()[j];
if (addr == localAddr)
{
if (InvalidLiterals.Find(localAddr) == -1)
if (InvalidLiterals.Find(localAddr) != -1)
{
InvalidLiterals.Add(localAddr);
JIT_DEBUGPRINT("found invalid literal %d\n", InvalidLiterals.Length);
@ -1003,8 +988,8 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
if (otherRange->Blocks.Length == 0)
{
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512]))
ARMJIT_Memory::SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
otherRange->Code = 0;
}
@ -1028,64 +1013,49 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
}
}
void ARMJIT::CheckAndInvalidateITCM() noexcept
void CheckAndInvalidateITCM()
{
for (u32 i = 0; i < ITCMPhysicalSize; i+=512)
for (u32 i = 0; i < ITCMPhysicalSize; i+=16)
{
if (CodeIndexITCM[i / 512].Code)
if (CodeIndexITCM[i / 512].Code & (1 << ((i & 0x1FF) / 16)))
{
// maybe using bitscan would be better here?
// The thing is that in densely populated sets
// The old fashioned way can actually be faster
for (u32 j = 0; j < 512; j += 16)
{
if (CodeIndexITCM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_ITCM << 27));
}
InvalidateByAddr(i | (ARMJIT_Memory::memregion_ITCM << 27));
}
}
}
void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept
template <u32 num, int region>
void CheckAndInvalidate(u32 addr)
{
u32 start = bank == 1 ? 0x20000 : 0;
for (u32 i = start; i < start+0x20000; i+=512)
{
if (CodeIndexARM7WVRAM[i / 512].Code)
{
for (u32 j = 0; j < 512; j += 16)
{
if (CodeIndexARM7WVRAM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_VWRAM << 27));
}
}
}
u32 localAddr = ARMJIT_Memory::LocaliseAddress(region, num, addr);
if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16)))
InvalidateByAddr(localAddr);
}
JitBlockEntry ARMJIT::LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr)
{
u64* entry = &entries[offset / 2];
if (*entry >> 32 == (addr | num))
return JITCompiler.AddEntryOffset((u32)*entry);
return JITCompiler->AddEntryOffset((u32)*entry);
return NULL;
}
void ARMJIT::blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry)
{
u32 localAddr = LocaliseCodeAddress(num, blockAddr);
assert(JITCompiler.AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry);
assert(JITCompiler->AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry);
}
bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size)
{
// amazingly ignoring the DTCM is the proper behaviour for code fetches
int region = num == 0
? Memory.ClassifyAddress9(blockAddr)
: Memory.ClassifyAddress7(blockAddr);
? ARMJIT_Memory::ClassifyAddress9(blockAddr)
: ARMJIT_Memory::ClassifyAddress7(blockAddr);
u32 memoryOffset;
if (FastBlockLookupRegions[region]
&& Memory.GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size))
&& ARMJIT_Memory::GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size))
{
//printf("setup exec region %d %d %08x %08x %x %x\n", num, region, blockAddr, start, size, memoryOffset);
entry = FastBlockLookupRegions[region] + memoryOffset / 2;
@ -1094,28 +1064,28 @@ bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& sta
return false;
}
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept;
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32);
void ARMJIT::ResetBlockCache() noexcept
void ResetBlockCache()
{
Log(LogLevel::Debug, "Resetting JIT block cache...\n");
printf("Resetting JIT block cache...\n");
// could be replace through a function which only resets
// the permissions but we're too lazy
Memory.Reset();
ARMJIT_Memory::Reset();
InvalidLiterals.Clear();
for (int i = 0; i < ARMJIT_Memory::memregions_Count; i++)
@ -1153,23 +1123,7 @@ void ARMJIT::ResetBlockCache() noexcept
JitBlocks9.clear();
JitBlocks7.clear();
JITCompiler.Reset();
}
void ARMJIT::JitEnableWrite() noexcept
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))
pthread_jit_write_protect_np(false);
#endif
}
void ARMJIT::JitEnableExecute() noexcept
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))
pthread_jit_write_protect_np(true);
#endif
JITCompiler->Reset();
}
}

View File

@ -1,206 +1,37 @@
/*
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/.
*/
#ifndef ARMJIT_H
#define ARMJIT_H
#include <algorithm>
#include <optional>
#include <memory>
#include "types.h"
#include "MemConstants.h"
#include "Args.h"
#include "ARMJIT_Memory.h"
#ifdef JIT_ENABLED
#include "JitBlock.h"
#include "ARM.h"
#include "ARM_InstrInfo.h"
#if defined(__APPLE__) && defined(__aarch64__)
#include <pthread.h>
#endif
#include "ARMJIT_Compiler.h"
namespace melonDS
namespace ARMJIT
{
class ARM;
class JitBlock;
class ARMJIT
{
public:
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept;
~ARMJIT() noexcept;
void InvalidateByAddr(u32) noexcept;
void CheckAndInvalidateWVRAM(int) noexcept;
void CheckAndInvalidateITCM() noexcept;
void Reset() noexcept;
void JitEnableWrite() noexcept;
void JitEnableExecute() noexcept;
void CompileBlock(ARM* cpu) noexcept;
void ResetBlockCache() noexcept;
typedef void (*JitBlockEntry)();
template <u32 num, int region>
void CheckAndInvalidate(u32 addr) noexcept
{
u32 localAddr = Memory.LocaliseAddress(region, num, addr);
if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16)))
InvalidateByAddr(localAddr);
}
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept;
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept;
u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept;
void Init();
void DeInit();
ARMJIT_Memory Memory;
private:
int MaxBlockSize {};
bool LiteralOptimizations = false;
bool BranchOptimizations = false;
bool FastMemory = false;
void Reset();
public:
melonDS::NDS& NDS;
TinyVector<u32> InvalidLiterals {};
friend class ARMJIT_Memory;
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept;
void RetireJitBlock(JitBlock* block) noexcept;
void CheckAndInvalidateITCM();
int GetMaxBlockSize() const noexcept { return MaxBlockSize; }
bool LiteralOptimizationsEnabled() const noexcept { return LiteralOptimizations; }
bool BranchOptimizationsEnabled() const noexcept { return BranchOptimizations; }
bool FastMemoryEnabled() const noexcept { return FastMemory; }
void InvalidateByAddr(u32 pseudoPhysical);
void SetJITArgs(JITArgs args) noexcept;
void SetMaxBlockSize(int size) noexcept;
void SetLiteralOptimizations(bool enabled) noexcept;
void SetBranchOptimizations(bool enabled) noexcept;
void SetFastMemory(bool enabled) noexcept;
template <u32 num, int region>
void CheckAndInvalidate(u32 addr);
Compiler JITCompiler;
std::unordered_map<u32, JitBlock*> JitBlocks9 {};
std::unordered_map<u32, JitBlock*> JitBlocks7 {};
void CompileBlock(ARM* cpu);
std::unordered_map<u32, JitBlock*> RestoreCandidates {};
void ResetBlockCache();
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr);
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size);
AddressRange CodeIndexITCM[ITCMPhysicalSize / 512] {};
AddressRange CodeIndexMainRAM[MainRAMMaxSize / 512] {};
AddressRange CodeIndexSWRAM[SharedWRAMSize / 512] {};
AddressRange CodeIndexVRAM[0x100000 / 512] {};
AddressRange CodeIndexARM9BIOS[ARM9BIOSSize / 512] {};
AddressRange CodeIndexARM7BIOS[ARM7BIOSSize / 512] {};
AddressRange CodeIndexARM7WRAM[ARM7WRAMSize / 512] {};
AddressRange CodeIndexARM7WVRAM[0x40000 / 512] {};
AddressRange CodeIndexBIOS9DSi[0x10000 / 512] {};
AddressRange CodeIndexBIOS7DSi[0x10000 / 512] {};
AddressRange CodeIndexNWRAM_A[NWRAMSize / 512] {};
AddressRange CodeIndexNWRAM_B[NWRAMSize / 512] {};
AddressRange CodeIndexNWRAM_C[NWRAMSize / 512] {};
u64 FastBlockLookupITCM[ITCMPhysicalSize / 2] {};
u64 FastBlockLookupMainRAM[MainRAMMaxSize / 2] {};
u64 FastBlockLookupSWRAM[SharedWRAMSize / 2] {};
u64 FastBlockLookupVRAM[0x100000 / 2] {};
u64 FastBlockLookupARM9BIOS[ARM9BIOSSize / 2] {};
u64 FastBlockLookupARM7BIOS[ARM7BIOSSize / 2] {};
u64 FastBlockLookupARM7WRAM[ARM7WRAMSize / 2] {};
u64 FastBlockLookupARM7WVRAM[0x40000 / 2] {};
u64 FastBlockLookupBIOS9DSi[0x10000 / 2] {};
u64 FastBlockLookupBIOS7DSi[0x10000 / 2] {};
u64 FastBlockLookupNWRAM_A[NWRAMSize / 2] {};
u64 FastBlockLookupNWRAM_B[NWRAMSize / 2] {};
u64 FastBlockLookupNWRAM_C[NWRAMSize / 2] {};
AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
CodeIndexITCM,
NULL,
CodeIndexARM9BIOS,
CodeIndexMainRAM,
CodeIndexSWRAM,
NULL,
CodeIndexVRAM,
CodeIndexARM7BIOS,
CodeIndexARM7WRAM,
NULL,
NULL,
CodeIndexARM7WVRAM,
CodeIndexBIOS9DSi,
CodeIndexBIOS7DSi,
CodeIndexNWRAM_A,
CodeIndexNWRAM_B,
CodeIndexNWRAM_C
};
u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
FastBlockLookupITCM,
NULL,
FastBlockLookupARM9BIOS,
FastBlockLookupMainRAM,
FastBlockLookupSWRAM,
NULL,
FastBlockLookupVRAM,
FastBlockLookupARM7BIOS,
FastBlockLookupARM7WRAM,
NULL,
NULL,
FastBlockLookupARM7WVRAM,
FastBlockLookupBIOS9DSi,
FastBlockLookupBIOS7DSi,
FastBlockLookupNWRAM_A,
FastBlockLookupNWRAM_B,
FastBlockLookupNWRAM_C
};
};
}
// Defined in assembly
extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry);
#else
namespace melonDS
{
class ARM;
// This version is a stub; the methods all do nothing,
// but there's still a Memory member.
class ARMJIT
{
public:
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs>) noexcept : Memory(nds) {}
~ARMJIT() noexcept {}
void InvalidateByAddr(u32) noexcept {}
void CheckAndInvalidateWVRAM(int) noexcept {}
void CheckAndInvalidateITCM() noexcept {}
void Reset() noexcept {}
void JitEnableWrite() noexcept {}
void JitEnableExecute() noexcept {}
void CompileBlock(ARM*) noexcept {}
void ResetBlockCache() noexcept {}
template <u32, int>
void CheckAndInvalidate(u32 addr) noexcept {}
ARMJIT_Memory Memory;
};
}
#endif // JIT_ENABLED
#endif // ARMJIT_H
extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry);
#endif

View File

@ -1,26 +1,8 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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 "ARMJIT_Compiler.h"
using namespace Arm64Gen;
namespace melonDS
namespace ARMJIT
{
void Compiler::Comp_RegShiftReg(int op, bool S, Op2& op2, ARM64Reg rs)
@ -324,29 +306,16 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
UBFX(W2, RCPSR, 29, 1);
if (S)
{
CVInGPR = true;
ADDS(W1, rn, W2);
CSET(W2, CC_CS);
CSET(W3, CC_VS);
if (op2.IsImm)
{
CVInGPR = true;
ADDS(W1, rn, W2);
CSET(W2, CC_CS);
CSET(W3, CC_VS);
if (op2.IsImm)
ADDSI2R(rd, W1, op2.Imm, W0);
else
ADDS(rd, W1, op2.Reg.Rm, op2.ToArithOption());
CSINC(W2, W2, WZR, CC_CC);
CSINC(W3, W3, WZR, CC_VC);
}
ADDSI2R(rd, W1, op2.Imm, W0);
else
{
if (op2.Reg.ShiftAmount > 0)
{
MOV(W0, op2.Reg.Rm, op2.ToArithOption());
op2 = Op2(W0, ST_LSL, 0);
}
CMP(W2, 1);
ADCS(rd, rn, op2.Reg.Rm);
}
ADDS(rd, W1, op2.Reg.Rm, op2.ToArithOption());
CSINC(W2, W2, WZR, CC_CC);
CSINC(W3, W3, WZR, CC_VC);
}
else
{
@ -359,38 +328,25 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
break;
case 0x6: // SBC
UBFX(W2, RCPSR, 29, 1);
if (S && !op2.IsImm)
// W1 = -op2 - 1
if (op2.IsImm)
MOVI2R(W1, ~op2.Imm);
else
ORN(W1, WZR, op2.Reg.Rm, op2.ToArithOption());
if (S)
{
if (op2.Reg.ShiftAmount > 0)
{
MOV(W0, op2.Reg.Rm, op2.ToArithOption());
op2 = Op2(W0, ST_LSL, 0);
}
CMP(W2, 1);
SBCS(rd, rn, op2.Reg.Rm);
CVInGPR = true;
ADDS(W1, W2, W1);
CSET(W2, CC_CS);
CSET(W3, CC_VS);
ADDS(rd, rn, W1);
CSINC(W2, W2, WZR, CC_CC);
CSINC(W3, W3, WZR, CC_VC);
}
else
{
// W1 = -op2 - 1
if (op2.IsImm)
MOVI2R(W1, ~op2.Imm);
else
ORN(W1, WZR, op2.Reg.Rm, op2.ToArithOption());
if (S)
{
CVInGPR = true;
ADDS(W1, W2, W1);
CSET(W2, CC_CS);
CSET(W3, CC_VS);
ADDS(rd, rn, W1);
CSINC(W2, W2, WZR, CC_CC);
CSINC(W3, W3, WZR, CC_VC);
}
else
{
ADD(W1, W2, W1);
ADD(rd, rn, W1);
}
ADD(W1, W2, W1);
ADD(rd, rn, W1);
}
break;
case 0x7: // RSC
@ -480,7 +436,7 @@ void Compiler::A_Comp_GetOp2(bool S, Op2& op2)
Comp_AddCycles_C();
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift);
if (S && shift && (CurInstr.SetFlags & 0x2))
{
@ -559,7 +515,21 @@ void Compiler::A_Comp_ALUMovOp()
}
else
{
MOV(rd, op2.Reg.Rm, op2.ToArithOption());
// ORR with shifted operand has cycles latency
if (op2.Reg.ShiftAmount > 0)
{
switch (op2.Reg.ShiftType)
{
case ST_LSL: LSL(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
case ST_LSR: LSR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
case ST_ASR: ASR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
case ST_ROR: ROR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
}
}
else
{
MOV(rd, op2.Reg.Rm, op2.ToArithOption());
}
}
}

View File

@ -1,34 +1,15 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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 "ARMJIT_Compiler.h"
#include "../NDS.h"
using namespace Arm64Gen;
// hack
const int kCodeCacheTiming = 3;
namespace melonDS
namespace ARMJIT
{
template <typename T>
void JumpToTrampoline(T* cpu, u32 addr, bool changeCPSR)
void jumpToTrampoline(T* cpu, u32 addr, bool changeCPSR)
{
cpu->JumpTo(addr, changeCPSR);
}
@ -83,7 +64,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);
cpu9->CodeRead32(addr-2, true) >> 16;
cycles += cpu9->CodeCycles;
cpu9->CodeRead32(addr+2, false);
cycles += CurCPU->CodeCycles;
@ -133,7 +114,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1];
cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1];
CurCPU->R[15] = compileTimePC;
}
@ -145,7 +126,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3];
cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3];
CurCPU->R[15] = compileTimePC;
}
@ -236,7 +217,7 @@ void* Compiler::Gen_JumpTo7(int kind)
LSR(W1, W0, 15);
STR(INDEX_UNSIGNED, W1, RCPU, offsetof(ARM, CodeCycles));
MOVP2R(X2, NDS.ARM7MemTimings);
MOVP2R(X2, NDS::ARM7MemTimings);
LDR(W3, X2, ArithOption(W1, true));
FixupBranch switchToThumb;
@ -302,7 +283,7 @@ void Compiler::Comp_JumpTo(Arm64Gen::ARM64Reg addr, bool switchThumb, bool resto
bool cpsrDirty = CPSRDirty;
SaveCPSR();
SaveCycles();
PushRegs(restoreCPSR, true);
PushRegs(restoreCPSR);
if (switchThumb)
MOV(W1, addr);
@ -316,11 +297,11 @@ void Compiler::Comp_JumpTo(Arm64Gen::ARM64Reg addr, bool switchThumb, bool resto
MOV(X0, RCPU);
MOVI2R(W2, restoreCPSR);
if (Num == 0)
QuickCallFunction(X3, JumpToTrampoline<ARMv5>);
QuickCallFunction(X3, jumpToTrampoline<ARMv5>);
else
QuickCallFunction(X3, JumpToTrampoline<ARMv4>);
QuickCallFunction(X3, jumpToTrampoline<ARMv4>);
PopRegs(restoreCPSR, true);
PopRegs(restoreCPSR);
LoadCycles();
LoadCPSR();
if (CurInstr.Cond() < 0xE)
@ -389,7 +370,7 @@ void Compiler::T_Comp_BranchXchangeReg()
{
if (Num == 1)
{
Log(LogLevel::Warn, "BLX unsupported on ARM7!!!\n");
printf("BLX unsupported on ARM7!!!\n");
return;
}
MOV(W0, MapReg(CurInstr.A_Reg(3)));
@ -437,4 +418,4 @@ void Compiler::T_Comp_BL_Merged()
Comp_JumpTo(target);
}
}
}

View File

@ -1,28 +1,17 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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 "ARMJIT_Compiler.h"
#include "../ARMJIT_Internal.h"
#include "../ARMInterpreter.h"
#include "../ARMJIT.h"
#include "../NDS.h"
#include "../ARMJIT_Global.h"
#include "../Config.h"
#ifdef __SWITCH__
#include <switch.h>
extern char __start__;
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
#include <stdlib.h>
@ -30,7 +19,7 @@ using namespace Arm64Gen;
extern "C" void ARM_Ret();
namespace melonDS
namespace ARMJIT
{
/*
@ -47,14 +36,14 @@ namespace melonDS
template <>
const ARM64Reg RegisterCache<Compiler, ARM64Reg>::NativeRegAllocOrder[] =
{
W19, W20, W21, W22, W23, W24, W25,
W8, W9, W10, W11, W12, W13, W14, W15
};
{W19, W20, W21, W22, W23, W24, W25, W26};
template <>
const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 8;
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()
{
@ -92,7 +81,7 @@ void Compiler::A_Comp_MSR()
if (CurInstr.Instr & (1 << 25))
{
val = W0;
MOVI2R(val, melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
MOVI2R(val, ::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
}
else
{
@ -153,67 +142,52 @@ void Compiler::A_Comp_MSR()
MOV(W2, RCPSR);
MOV(X0, RCPU);
PushRegs(true, true);
QuickCallFunction(X3, UpdateModeTrampoline);
PopRegs(true, true);
PushRegs(true);
QuickCallFunction(X3, (void*)&UpdateModeTrampoline);
PopRegs(true);
}
}
}
void Compiler::PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload)
void Compiler::PushRegs(bool saveHiRegs)
{
BitSet32 loadedRegs(RegCache.LoadedRegs);
if (saveHiRegs)
{
BitSet32 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
for (int reg : hiRegsLoaded)
if (Thumb || CurInstr.Cond() == 0xE)
{
if (Thumb || CurInstr.Cond() == 0xE)
BitSet16 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
for (int reg : hiRegsLoaded)
RegCache.UnloadRegister(reg);
else
SaveReg(reg, RegCache.Mapping[reg]);
// prevent saving the register twice
loadedRegs[reg] = false;
}
}
for (int reg : loadedRegs)
{
if (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs))))
else
{
if ((Thumb || CurInstr.Cond() == 0xE) && !((1 << reg) & (CurInstr.Info.DstRegs|CurInstr.Info.SrcRegs)) && allowUnload)
RegCache.UnloadRegister(reg);
else
BitSet16 hiRegsDirty(RegCache.LoadedRegs & 0x7F00);
for (int reg : hiRegsDirty)
SaveReg(reg, RegCache.Mapping[reg]);
}
}
}
void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
void Compiler::PopRegs(bool saveHiRegs)
{
BitSet32 loadedRegs(RegCache.LoadedRegs);
for (int reg : loadedRegs)
if (saveHiRegs)
{
if ((saveHiRegs && reg >= 8 && reg < 15)
|| (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs)))))
{
BitSet16 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
for (int reg : hiRegsLoaded)
LoadReg(reg, RegCache.Mapping[reg]);
}
}
}
Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
Compiler::Compiler()
{
#ifdef __SWITCH__
JitRWBase = aligned_alloc(0x1000, JitMemSize);
JitRXStart = (u8*)&__start__ - JitMemSize - 0x1000;
virtmemLock();
JitRWStart = virtmemFindAslr(JitMemSize, 0x1000);
JitRWStart = virtmemReserve(JitMemSize);
MemoryInfo info = {0};
u32 pageInfo = {0};
int i = 0;
@ -226,7 +200,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
break;
if (i++ > 8)
{
Log(LogLevel::Error, "couldn't find unmapped place for jit memory\n");
printf("couldn't find unmapped place for jit memory\n");
JitRXStart = NULL;
}
}
@ -240,18 +214,16 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
succeded = R_SUCCEEDED(svcMapProcessMemory(JitRWStart, envGetOwnProcessHandle(), (u64)JitRXStart, JitMemSize));
assert(succeded);
virtmemUnlock();
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
JitMemMainSize = JitMemSize;
#else
ARMJIT_Global::Init();
u64 pageSize = sysconf(_SC_PAGE_SIZE);
u8* pageAligned = (u8*)(((u64)JitMem & ~(pageSize - 1)) + pageSize);
u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
CodeMemBase = ARMJIT_Global::AllocateCodeMem();
nds.JIT.JitEnableWrite();
SetCodeBase(reinterpret_cast<u8*>(CodeMemBase), reinterpret_cast<u8*>(CodeMemBase));
JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize;
SetCodeBase(pageAligned, pageAligned);
JitMemMainSize = alignedSize;
#endif
SetCodePtr(0);
@ -262,7 +234,6 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
}
/*
W4 - whether the register was written to
W5 - mode
W1 - reg num
W3 - in/out value of reg
@ -352,11 +323,9 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
{
for (int size = 0; size < 3; size++)
{
for (int reg = 0; reg < 32; reg++)
for (int reg = 0; reg < 8; reg++)
{
if (!(reg == W4 || (reg >= W8 && reg <= W15) || (reg >= W19 && reg <= W25)))
continue;
ARM64Reg rdMapped = (ARM64Reg)reg;
ARM64Reg rdMapped = (ARM64Reg)(W19 + reg);
PatchedStoreFuncs[consoleType][num][size][reg] = GetRXPtr();
if (num == 0)
{
@ -367,7 +336,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
{
MOV(W1, rdMapped);
}
ABI_PushRegisters(BitSet32({30}) | CallerSavedPushRegs);
ABI_PushRegisters({30});
if (consoleType == 0)
{
switch ((8 << size) | num)
@ -393,7 +362,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
}
}
ABI_PopRegisters(BitSet32({30}) | CallerSavedPushRegs);
ABI_PopRegisters({30});
RET();
for (int signextend = 0; signextend < 2; signextend++)
@ -401,7 +370,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetRXPtr();
if (num == 0)
MOV(X1, RCPU);
ABI_PushRegisters(BitSet32({30}) | CallerSavedPushRegs);
ABI_PushRegisters({30});
if (consoleType == 0)
{
switch ((8 << size) | num)
@ -426,7 +395,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
case 9: QuickCallFunction(X3, SlowRead7<u8, 1>); break;
}
}
ABI_PopRegisters(BitSet32({30}) | CallerSavedPushRegs);
ABI_PopRegisters({30});
if (size == 32)
MOV(rdMapped, W0);
else if (signextend)
@ -457,14 +426,12 @@ Compiler::~Compiler()
{
bool succeded = R_SUCCEEDED(svcUnmapProcessMemory(JitRWStart, envGetOwnProcessHandle(), (u64)JitRXStart, JitMemSize));
assert(succeded);
virtmemFree(JitRWStart, JitMemSize);
succeded = R_SUCCEEDED(svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)JitRXStart, (u64)JitRWBase, JitMemSize));
assert(succeded);
free(JitRWBase);
}
#endif
ARMJIT_Global::FreeCodeMem(CodeMemBase);
ARMJIT_Global::DeInit();
}
void Compiler::LoadCycles()
@ -482,12 +449,12 @@ void Compiler::LoadReg(int reg, ARM64Reg nativeReg)
if (reg == 15)
MOVI2R(nativeReg, R15);
else
LDR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R) + reg*4);
LDR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R[reg]));
}
void Compiler::SaveReg(int reg, ARM64Reg nativeReg)
{
STR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R) + reg*4);
STR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R[reg]));
}
void Compiler::LoadCPSR()
@ -667,23 +634,22 @@ void Compiler::Comp_BranchSpecialBehaviour(bool taken)
{
RegCache.PrepareExit();
if (ConstantCycles)
ADD(RCycles, RCycles, ConstantCycles);
ADD(RCycles, RCycles, ConstantCycles);
QuickTailCall(X0, ARM_Ret);
}
}
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemInstr)
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount)
{
if (JitMemMainSize - GetCodeOffset() < 1024 * 16)
{
Log(LogLevel::Debug, "JIT near memory full, resetting...\n");
NDS.JIT.ResetBlockCache();
printf("JIT near memory full, resetting...\n");
ResetBlockCache();
}
if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8)
{
Log(LogLevel::Debug, "JIT far memory full, resetting...\n");
NDS.JIT.ResetBlockCache();
printf("JIT far memory full, resetting...\n");
ResetBlockCache();
}
JitBlockEntry res = (JitBlockEntry)GetRXPtr();
@ -695,9 +661,6 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
RegCache = RegisterCache<Compiler, ARM64Reg>(this, instrs, instrsCount, true);
CPSRDirty = false;
if (hasMemInstr)
MOVP2R(RMemBase, Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start);
for (int i = 0; i < instrsCount; i++)
{
CurInstr = instrs[i];
@ -746,9 +709,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
QuickCallFunction(X1, InterpretTHUMB[CurInstr.Info.Kind]);
}
else
{
(this->*comp)();
}
}
else
{
@ -764,12 +725,10 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
}
}
else if (cond == 0xF)
{
Comp_AddCycles_C();
}
else
{
IrregularCycles = comp == NULL;
IrregularCycles = false;
FixupBranch skipExecute;
if (cond < 0xE)
@ -794,17 +753,14 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
FixupBranch skipNop = B();
SetJumpTarget(skipExecute);
if (IrregularCycles)
Comp_AddCycles_C(true);
Comp_AddCycles_C();
Comp_BranchSpecialBehaviour(false);
SetJumpTarget(skipNop);
}
else
{
SetJumpTarget(skipExecute);
}
}
}
@ -819,8 +775,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
RegCache.Flush();
if (ConstantCycles)
ADD(RCycles, RCycles, ConstantCycles);
ADD(RCycles, RCycles, ConstantCycles);
QuickTailCall(X0, ARM_Ret);
FlushIcache();
@ -844,7 +799,7 @@ void Compiler::Reset()
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
{
s32 cycles = Num ?
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
if (forceNonConstant)
@ -858,7 +813,7 @@ void Compiler::Comp_AddCycles_CI(u32 numI)
IrregularCycles = true;
s32 cycles = (Num ?
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + numI;
if (Thumb || CurInstr.Cond() == 0xE)
@ -872,7 +827,7 @@ void Compiler::Comp_AddCycles_CI(u32 c, ARM64Reg numI, ArithOption shift)
IrregularCycles = true;
s32 cycles = (Num ?
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + c;
ADD(RCycles, RCycles, cycles);
@ -892,7 +847,7 @@ void Compiler::Comp_AddCycles_CDI()
s32 cycles;
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
@ -937,7 +892,7 @@ void Compiler::Comp_AddCycles_CD()
}
else
{
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 24) == 0x02)

View File

@ -1,27 +1,8 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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_A64_COMPILER_H
#define ARMJIT_A64_COMPILER_H
#if defined(JIT_ENABLED) && defined(__aarch64__)
#include "../ARM.h"
#include "../ARMJIT.h"
#include "../dolphin/Arm64Emitter.h"
@ -30,10 +11,9 @@
#include <unordered_map>
namespace melonDS
namespace ARMJIT
{
class ARMJIT;
const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26;
const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27;
const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28;
const Arm64Gen::ARM64Reg RCPU = Arm64Gen::X29;
@ -69,7 +49,7 @@ struct Op2
bool IsSimpleReg()
{ return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; }
bool ImmFits12Bit()
{ return IsImm && ((Imm & 0xFFF) == Imm); }
{ return IsImm && (Imm & 0xFFF == Imm); }
bool IsZero()
{ return IsImm && !Imm; }
@ -98,11 +78,11 @@ class Compiler : public Arm64Gen::ARM64XEmitter
public:
typedef void (Compiler::*CompileFunc)();
explicit Compiler(melonDS::NDS& nds);
~Compiler() override;
Compiler();
~Compiler();
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
void PushRegs(bool saveHiRegs);
void PopRegs(bool saveHiRegs);
Arm64Gen::ARM64Reg MapReg(int reg)
{
@ -110,11 +90,11 @@ public:
return RegCache.Mapping[reg];
}
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemInstr);
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount);
bool CanCompile(bool thumb, u16 kind);
bool FlagsNZNeeded() const
bool FlagsNZNeeded()
{
return CurInstr.SetFlags & 0xC;
}
@ -186,7 +166,7 @@ public:
void T_Comp_BL_LONG_2();
void T_Comp_BL_Merged();
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode);
void Comp_Mul_Mla(bool S, bool mla, Arm64Gen::ARM64Reg rd, Arm64Gen::ARM64Reg rm, Arm64Gen::ARM64Reg rs, Arm64Gen::ARM64Reg rn);
@ -234,7 +214,7 @@ public:
return (u8*)entry - GetRXBase();
}
bool IsJITFault(const u8* pc);
bool IsJITFault(u8* pc);
u8* RewriteMemAccess(u8* pc);
void SwapCodeRegion()
@ -244,7 +224,6 @@ public:
OtherCodeRegion = offset;
}
melonDS::NDS& NDS;
ptrdiff_t OtherCodeRegion;
bool Exit;
@ -262,8 +241,17 @@ public:
u32 JitMemSecondarySize;
u32 JitMemMainSize;
void* ReadBanked, *WriteBanked;
void* JumpToFuncs9[3];
void* JumpToFuncs7[3];
std::unordered_map<ptrdiff_t, LoadStorePatch> LoadStorePatches;
// [Console Type][Num][Size][Sign Extend][Output register]
void* PatchedLoadFuncs[2][2][3][2][8];
void* PatchedStoreFuncs[2][2][3][8];
RegisterCache<Compiler, Arm64Gen::ARM64Reg> RegCache;
bool CPSRDirty = false;
@ -275,20 +263,8 @@ public:
void* JitRWStart;
void* JitRXStart;
#endif
void* CodeMemBase;
void* ReadBanked, *WriteBanked;
void* JumpToFuncs9[3];
void* JumpToFuncs7[3];
// [Console Type][Num][Size][Sign Extend][Output register]
void* PatchedLoadFuncs[2][2][3][2][32];
void* PatchedStoreFuncs[2][2][3][32];
};
}
#endif
#endif

View File

@ -1,21 +1,3 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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 "../ARMJIT_x64/ARMJIT_Offsets.h"
.text
@ -94,8 +76,3 @@ ARM_RestoreContext:
mov sp, x17
br x18
#if !defined(__APPLE__) && !defined(__WIN32__)
.section .note.GNU-stack,"",@progbits
#endif

View File

@ -1,34 +1,15 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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 "ARMJIT_Compiler.h"
#include "../ARMJIT.h"
#include "../Config.h"
#include "../ARMJIT_Memory.h"
#include "../NDS.h"
using namespace Arm64Gen;
namespace melonDS
namespace ARMJIT
{
bool Compiler::IsJITFault(const u8* pc)
bool Compiler::IsJITFault(u8* pc)
{
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
}
@ -57,17 +38,18 @@ u8* Compiler::RewriteMemAccess(u8* pc)
return pc + (ptrdiff_t)patch.PatchOffset;
}
Log(LogLevel::Error, "this is a JIT bug! %08x\n", __builtin_bswap32(*(u32*)pc));
printf("this is a JIT bug! %08x\n", __builtin_bswap32(*(u32*)pc));
abort();
}
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
{
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
u32 localAddr = LocaliseCodeAddress(Num, addr);
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
if (invalidLiteralIdx != -1)
{
InvalidLiterals.Remove(invalidLiteralIdx);
return false;
}
@ -80,7 +62,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
if (size == 32)
{
CurCPU->DataRead32(addr & ~0x3, &val);
val = melonDS::ROR(val, (addr & 0x3) << 3);
val = ::ROR(val, (addr & 0x3) << 3);
}
else if (size == 16)
{
@ -112,7 +94,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (size == 16)
addressMask = ~1;
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (Config::JIT_LiteralOptimisations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -134,12 +116,6 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
rnMapped = W3;
}
if (flags & memop_Store && flags & (memop_Post|memop_Writeback) && rd == rn)
{
MOV(W4, rdMapped);
rdMapped = W4;
}
ARM64Reg finalAddr = W0;
if (flags & memop_Post)
{
@ -147,7 +123,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(W0, rnMapped);
}
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
bool addrIsStatic = Config::JIT_LiteralOptimisations
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -186,18 +162,20 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(rnMapped, W0);
u32 expectedTarget = Num == 0
? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
? ARMJIT_Memory::ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
if (Config::JIT_FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
{
ptrdiff_t memopStart = GetCodeOffset();
LoadStorePatch patch;
assert((rdMapped >= W8 && rdMapped <= W15) || (rdMapped >= W19 && rdMapped <= W25) || rdMapped == W4);
patch.PatchFunc = flags & memop_Store
? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped]
: PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped];
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped - W19]
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped - W19];
assert(rdMapped - W19 >= 0 && rdMapped - W19 < 8);
MOVP2R(X7, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
// take a chance at fastmem
if (size > 8)
@ -206,11 +184,11 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
ptrdiff_t loadStorePosition = GetCodeOffset();
if (flags & memop_Store)
{
STRGeneric(size, rdMapped, size > 8 ? X1 : X0, RMemBase);
STRGeneric(size, rdMapped, size > 8 ? X1 : X0, X7);
}
else
{
LDRGeneric(size, flags & memop_SignExtend, rdMapped, size > 8 ? X1 : X0, RMemBase);
LDRGeneric(size, flags & memop_SignExtend, rdMapped, size > 8 ? X1 : X0, X7);
if (size == 32 && !addrIsStatic)
{
UBFIZ(W0, W0, 3, 2);
@ -226,9 +204,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
{
void* func = NULL;
if (addrIsStatic)
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
PushRegs(false, false);
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
if (func)
{
@ -236,8 +212,6 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(W1, rdMapped);
QuickCallFunction(X2, (void (*)())func);
PopRegs(false, false);
if (!(flags & memop_Store))
{
if (size == 32)
@ -264,7 +238,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (flags & memop_Store)
{
MOV(W2, rdMapped);
switch (size | NDS.ConsoleType)
switch (size | NDS::ConsoleType)
{
case 32: QuickCallFunction(X3, SlowWrite9<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowWrite9<u32, 1>); break;
@ -276,7 +250,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
}
else
{
switch (size | NDS.ConsoleType)
switch (size | NDS::ConsoleType)
{
case 32: QuickCallFunction(X3, SlowRead9<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowRead9<u32, 1>); break;
@ -292,7 +266,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (flags & memop_Store)
{
MOV(W1, rdMapped);
switch (size | NDS.ConsoleType)
switch (size | NDS::ConsoleType)
{
case 32: QuickCallFunction(X3, SlowWrite7<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowWrite7<u32, 1>); break;
@ -304,7 +278,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
}
else
{
switch (size | NDS.ConsoleType)
switch (size | NDS::ConsoleType)
{
case 32: QuickCallFunction(X3, SlowRead7<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowRead7<u32, 1>); break;
@ -316,8 +290,6 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
}
}
PopRegs(false, false);
if (!(flags & memop_Store))
{
if (size == 32)
@ -333,7 +305,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (CurInstr.Info.Branches())
{
if (size < 32)
Log(LogLevel::Debug, "LDR size < 32 branching?\n");
printf("LDR size < 32 branching?\n");
Comp_JumpTo(rdMapped, Num == 0, false);
}
}
@ -453,7 +425,7 @@ void Compiler::T_Comp_LoadPCRel()
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
u32 addr = (R15 & ~0x2) + offset;
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!Config::JIT_LiteralOptimisations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@ -465,7 +437,7 @@ void Compiler::T_Comp_MemSPRel()
Comp_MemAccess(CurInstr.T_Reg(8), 13, Op2(offset), 32, load ? 0 : memop_Store);
}
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn)
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode)
{
IrregularCycles = true;
@ -474,8 +446,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (regsCount == 0)
return 0; // actually not the right behaviour TODO: fix me
int firstReg = *regs.begin();
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << firstReg) && !(firstReg == rn && skipLoadingRn))
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << *regs.begin()))
{
int flags = 0;
if (store)
@ -484,7 +455,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
flags |= memop_SubtractOffset;
Op2 offset = preinc ? Op2(4) : Op2(0);
Comp_MemAccess(firstReg, rn, offset, 32, flags);
Comp_MemAccess(*regs.begin(), rn, offset, 32, flags);
return decrement ? -4 : 4;
}
@ -495,32 +466,40 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
Comp_AddCycles_CDI();
int expectedTarget = Num == 0
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
bool compileFastPath = Config::JIT_FastMemory
&& store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
if (decrement)
{
s32 offset = decrement
? -regsCount * 4 + (preinc ? 0 : 4)
: (preinc ? 4 : 0);
s32 offset = -regsCount * 4 + (preinc ? 0 : 4);
if (offset)
{
ADDI2R(W0, MapReg(rn), offset);
else if (compileFastPath)
ANDI2R(W0, MapReg(rn), ~3);
ANDI2R(W0, W0, ~3);
}
else
MOV(W0, MapReg(rn));
{
ANDI2R(W0, MapReg(rn), ~3);
}
}
else
{
ANDI2R(W0, MapReg(rn), ~3);
if (preinc)
ADD(W0, W0, 4);
}
u8* patchFunc;
if (compileFastPath)
{
ptrdiff_t fastPathStart = GetCodeOffset();
ptrdiff_t loadStoreOffsets[8];
ptrdiff_t loadStoreOffsets[16];
ADD(X1, RMemBase, X0);
MOVP2R(X1, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
ADD(X1, X1, X0);
u32 offset = 0;
BitSet16::Iterator it = regs.begin();
@ -540,16 +519,12 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
loadStoreOffsets[i++] = GetCodeOffset();
if (store)
{
STR(INDEX_UNSIGNED, first, X1, offset);
}
else if (!(reg == rn && skipLoadingRn))
{
else
LDR(INDEX_UNSIGNED, first, X1, offset);
if (!(RegCache.LoadedRegs & (1 << reg)))
SaveReg(reg, first);
}
if (!(RegCache.LoadedRegs & (1 << reg)) && !store)
SaveReg(reg, first);
offset += 4;
}
@ -563,38 +538,25 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
ARM64Reg first = W3, second = W4;
if (RegCache.LoadedRegs & (1 << reg))
{
if (!(reg == rn && skipLoadingRn))
first = MapReg(reg);
}
first = MapReg(reg);
else if (store)
{
LoadReg(reg, first);
}
if (RegCache.LoadedRegs & (1 << nextReg))
{
if (!(nextReg == rn && skipLoadingRn))
second = MapReg(nextReg);
}
second = MapReg(nextReg);
else if (store)
{
LoadReg(nextReg, second);
}
loadStoreOffsets[i++] = GetCodeOffset();
if (store)
{
STP(INDEX_SIGNED, first, second, X1, offset);
}
else
{
LDP(INDEX_SIGNED, first, second, X1, offset);
if (!(RegCache.LoadedRegs & (1 << reg)))
SaveReg(reg, first);
if (!(RegCache.LoadedRegs & (1 << nextReg)))
SaveReg(nextReg, second);
}
if (!(RegCache.LoadedRegs & (1 << reg)) && !store)
SaveReg(reg, first);
if (!(RegCache.LoadedRegs & (1 << nextReg)) && !store)
SaveReg(nextReg, second);
offset += 8;
}
@ -604,8 +566,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
SwapCodeRegion();
patchFunc = (u8*)GetRXPtr();
patch.PatchFunc = patchFunc;
u32 numLoadStores = i;
for (i = 0; i < numLoadStores; i++)
for (i = 0; i < regsCount; i++)
{
patch.PatchOffset = fastPathStart - loadStoreOffsets[i];
LoadStorePatches[loadStoreOffsets[i]] = patch;
@ -673,15 +634,13 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
}
}
PushRegs(false, false, !compileFastPath);
ADD(X1, SP, 0);
MOVI2R(W2, regsCount);
if (Num == 0)
{
MOV(X3, RCPU);
switch ((u32)store * 2 | NDS.ConsoleType)
switch ((u32)store * 2 | NDS::ConsoleType)
{
case 0: QuickCallFunction(X4, SlowBlockTransfer9<false, 0>); break;
case 1: QuickCallFunction(X4, SlowBlockTransfer9<false, 1>); break;
@ -691,7 +650,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
}
else
{
switch ((u32)store * 2 | NDS.ConsoleType)
switch ((u32)store * 2 | NDS::ConsoleType)
{
case 0: QuickCallFunction(X4, SlowBlockTransfer7<false, 0>); break;
case 1: QuickCallFunction(X4, SlowBlockTransfer7<false, 1>); break;
@ -700,8 +659,6 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
}
}
PopRegs(false, false);
if (!store)
{
if (usermode && !regs[15] && (regs & BitSet16(0x7f00)))
@ -720,23 +677,20 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
LDR(INDEX_UNSIGNED, W3, SP, i * 8);
MOVI2R(W1, reg - 8);
BL(WriteBanked);
if (!(reg == rn && skipLoadingRn))
{
FixupBranch alreadyWritten = CBNZ(W4);
if (RegCache.LoadedRegs & (1 << reg))
MOV(MapReg(reg), W3);
else
SaveReg(reg, W3);
SetJumpTarget(alreadyWritten);
}
FixupBranch alreadyWritten = CBNZ(W4);
if (RegCache.LoadedRegs & (1 << reg))
MOV(MapReg(reg), W3);
else
SaveReg(reg, W3);
SetJumpTarget(alreadyWritten);
}
else if (!usermode && nextReg != regs.end())
{
ARM64Reg first = W3, second = W4;
if (RegCache.LoadedRegs & (1 << reg) && !(reg == rn && skipLoadingRn))
if (RegCache.LoadedRegs & (1 << reg))
first = MapReg(reg);
if (RegCache.LoadedRegs & (1 << *nextReg) && !(*nextReg == rn && skipLoadingRn))
if (RegCache.LoadedRegs & (1 << *nextReg))
second = MapReg(*nextReg);
LDP(INDEX_SIGNED, EncodeRegTo64(first), EncodeRegTo64(second), SP, i * 8);
@ -751,11 +705,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
}
else if (RegCache.LoadedRegs & (1 << reg))
{
if (!(reg == rn && skipLoadingRn))
{
ARM64Reg mapped = MapReg(reg);
LDR(INDEX_UNSIGNED, mapped, SP, i * 8);
}
ARM64Reg mapped = MapReg(reg);
LDR(INDEX_UNSIGNED, mapped, SP, i * 8);
}
else
{
@ -799,13 +750,13 @@ void Compiler::A_Comp_LDM_STM()
ARM64Reg rn = MapReg(CurInstr.A_Reg(16));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode);
if (load && writeback && regs[CurInstr.A_Reg(16)])
writeback = Num == 0
&& (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode, load && writeback);
if (writeback && offset)
? (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1))
: false;
if (writeback)
{
if (offset > 0)
ADD(rn, rn, offset);
@ -827,15 +778,12 @@ void Compiler::T_Comp_PUSH_POP()
}
ARM64Reg sp = MapReg(13);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false, false);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false);
if (offset)
{
if (offset > 0)
if (offset > 0)
ADD(sp, sp, offset);
else
SUB(sp, sp, -offset);
}
}
void Compiler::T_Comp_LDMIA_STMIA()
@ -844,12 +792,10 @@ void Compiler::T_Comp_LDMIA_STMIA()
ARM64Reg rb = MapReg(CurInstr.T_Reg(8));
bool load = CurInstr.Instr & (1 << 11);
u32 regsCount = regs.Count();
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false);
bool writeback = !load || !regs[CurInstr.T_Reg(8)];
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false, load && writeback);
if (writeback && offset)
if (!load || !regs[CurInstr.T_Reg(8)])
{
if (offset > 0)
ADD(rb, rb, offset);

View File

@ -1,26 +1,6 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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_COMPILER_H
#define ARMJIT_COMPILER_H
#ifdef JIT_ENABLED
#if defined(__x86_64__)
#include "ARMJIT_x64/ARMJIT_Compiler.h"
#elif defined(__aarch64__)
@ -29,6 +9,9 @@
#error "The current target platform doesn't have a JIT backend"
#endif
#endif
namespace ARMJIT
{
extern Compiler* JITCompiler;
}
#endif

View File

@ -1,126 +0,0 @@
#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();
}
}
}
}

View File

@ -1,21 +1,3 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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_INTERNAL_H
#define ARMJIT_INTERNAL_H
@ -24,17 +6,13 @@
#include <string.h>
#include <assert.h>
#include "ARM_InstrInfo.h"
#include "JitBlock.h"
#include "TinyVector.h"
namespace melonDS
{
class ARM;
class ARMv5;
#include "ARMJIT.h"
#include "ARMJIT_Memory.h"
// here lands everything which doesn't fit into ARMJIT.h
// where it would be included by pretty much everything
namespace ARMJIT
{
enum
{
@ -73,6 +51,139 @@ struct FetchedInstr
ARMInstrInfo::Info Info;
};
/*
TinyVector
- because reinventing the wheel is the best!
- meant to be used very often, with not so many elements
max 1 << 16 elements
- doesn't allocate while no elements are inserted
- not stl confirmant of course
- probably only works with POD types
- remove operations don't preserve order, but O(1)!
*/
template <typename T>
struct __attribute__((packed)) TinyVector
{
T* Data = NULL;
u16 Capacity = 0;
u16 Length = 0;
~TinyVector()
{
delete[] Data;
}
void MakeCapacity(u32 capacity)
{
assert(capacity <= UINT16_MAX);
assert(capacity > Capacity);
T* newMem = new T[capacity];
if (Data != NULL)
memcpy(newMem, Data, sizeof(T) * Length);
T* oldData = Data;
Data = newMem;
if (oldData != NULL)
delete[] oldData;
Capacity = capacity;
}
void SetLength(u16 length)
{
if (Capacity < length)
MakeCapacity(length);
Length = length;
}
void Clear()
{
Length = 0;
}
void Add(T element)
{
assert(Length + 1 <= UINT16_MAX);
if (Length + 1 > Capacity)
MakeCapacity(((Capacity + 4) * 3) / 2);
Data[Length++] = element;
}
void Remove(int index)
{
assert(Length > 0);
assert(index >= 0 && index < Length);
Length--;
Data[index] = Data[Length];
/*for (int i = index; i < Length; i++)
Data[i] = Data[i + 1];*/
}
int Find(T needle)
{
for (int i = 0; i < Length; i++)
{
if (Data[i] == needle)
return i;
}
return -1;
}
bool RemoveByValue(T needle)
{
for (int i = 0; i < Length; i++)
{
if (Data[i] == needle)
{
Remove(i);
return true;
}
}
return false;
}
T& operator[](int index)
{
assert(index >= 0 && index < Length);
return Data[index];
}
};
class JitBlock
{
public:
JitBlock(u32 num, u32 literalHash, u32 numAddresses, u32 numLiterals)
{
Num = num;
NumAddresses = numAddresses;
NumLiterals = numLiterals;
Data.SetLength(numAddresses * 2 + numLiterals);
}
u32 StartAddr;
u32 StartAddrLocal;
u32 InstrHash, LiteralHash;
u8 Num;
u16 NumAddresses;
u16 NumLiterals;
JitBlockEntry EntryPoint;
u32* AddressRanges()
{ return &Data[0]; }
u32* AddressMasks()
{ return &Data[NumAddresses]; }
u32* Literals()
{ return &Data[NumAddresses * 2]; }
private:
TinyVector<u32> Data;
};
// size should be 16 bytes because I'm to lazy to use mul and whatnot
struct __attribute__((packed)) AddressRange
{
@ -85,9 +196,13 @@ typedef void (*InterpreterFunc)(ARM* cpu);
extern InterpreterFunc InterpretARM[];
extern InterpreterFunc InterpretTHUMB[];
inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
extern TinyVector<u32> InvalidLiterals;
extern AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count];
inline bool PageContainsCode(AddressRange* range)
{
for (int i = 0; i < pageSize / 512; i++)
for (int i = 0; i < 8; i++)
{
if (range[i].Blocks.Length > 0)
return true;
@ -95,6 +210,11 @@ inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
return false;
}
u32 LocaliseCodeAddress(u32 num, u32 addr);
template <u32 Num>
void LinkBlock(ARM* cpu, u32 codeOffset);
template <typename T, int ConsoleType> T SlowRead9(u32 addr, ARMv5* cpu);
template <typename T, int ConsoleType> void SlowWrite9(u32 addr, ARMv5* cpu, u32 val);
template <typename T, int ConsoleType> T SlowRead7(u32 addr);

File diff suppressed because it is too large Load Diff

View File

@ -1,244 +1,63 @@
/*
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/.
*/
#ifndef ARMJIT_MEMORY
#define ARMJIT_MEMORY
#include "types.h"
#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>
# else
# include <sys/mman.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <signal.h>
# endif
#else
# include <array>
#endif
#include "ARM.h"
namespace melonDS
namespace ARMJIT_Memory
{
#ifdef JIT_ENABLED
namespace Platform { struct DynamicLibrary; }
class Compiler;
class ARMJIT;
#endif
static constexpr u32 LargePageSize = 0x4000;
static constexpr u32 RegularPageSize = 0x1000;
extern void* FastMem9Start;
extern void* FastMem7Start;
constexpr u32 RoundUp(u32 size) noexcept
void Init();
void DeInit();
void Reset();
enum
{
return (size + LargePageSize - 1) & ~(LargePageSize - 1);
}
memregion_Other = 0,
memregion_ITCM,
memregion_DTCM,
memregion_BIOS9,
memregion_MainRAM,
memregion_SharedWRAM,
memregion_IO9,
memregion_VRAM,
memregion_BIOS7,
memregion_WRAM7,
memregion_IO7,
memregion_Wifi,
memregion_VWRAM,
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);
// DSi
memregion_BIOS9DSi,
memregion_BIOS7DSi,
memregion_NewSharedWRAM_A,
memregion_NewSharedWRAM_B,
memregion_NewSharedWRAM_C,
class ARMJIT_Memory
{
public:
enum
{
memregion_Other = 0,
memregion_ITCM,
memregion_DTCM,
memregion_BIOS9,
memregion_MainRAM,
memregion_SharedWRAM,
memregion_IO9,
memregion_VRAM,
memregion_BIOS7,
memregion_WRAM7,
memregion_IO7,
memregion_Wifi,
memregion_VWRAM,
// DSi
memregion_BIOS9DSi,
memregion_BIOS7DSi,
memregion_NewSharedWRAM_A,
memregion_NewSharedWRAM_B,
memregion_NewSharedWRAM_C,
memregions_Count
};
#ifdef JIT_ENABLED
public:
explicit ARMJIT_Memory(melonDS::NDS& nds);
~ARMJIT_Memory() noexcept;
ARMJIT_Memory(const ARMJIT_Memory&) = delete;
ARMJIT_Memory(ARMJIT_Memory&&) = delete;
ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete;
ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete;
void Reset() noexcept;
void RemapDTCM(u32 newBase, u32 newSize) noexcept;
void RemapSWRAM() noexcept;
void RemapNWRAM(int num) noexcept;
void SetCodeProtection(int region, u32 offset, bool protect) noexcept;
[[nodiscard]] u8* GetMainRAM() noexcept { return MemoryBase + MemBlockMainRAMOffset; }
[[nodiscard]] const u8* GetMainRAM() const noexcept { return MemoryBase + MemBlockMainRAMOffset; }
[[nodiscard]] u8* GetSharedWRAM() noexcept { return MemoryBase + MemBlockSWRAMOffset; }
[[nodiscard]] const u8* GetSharedWRAM() const noexcept { return MemoryBase + MemBlockSWRAMOffset; }
[[nodiscard]] u8* GetARM7WRAM() noexcept { return MemoryBase + MemBlockARM7WRAMOffset; }
[[nodiscard]] const u8* GetARM7WRAM() const noexcept { return MemoryBase + MemBlockARM7WRAMOffset; }
[[nodiscard]] u8* GetARM9DTCM() noexcept { return MemoryBase + MemBlockDTCMOffset; }
[[nodiscard]] const u8* GetARM9DTCM() const noexcept { return MemoryBase + MemBlockDTCMOffset; }
[[nodiscard]] u8* GetNWRAM_A() noexcept { return MemoryBase + MemBlockNWRAM_AOffset; }
[[nodiscard]] const u8* GetNWRAM_A() const noexcept { return MemoryBase + MemBlockNWRAM_AOffset; }
[[nodiscard]] u8* GetNWRAM_B() noexcept { return MemoryBase + MemBlockNWRAM_BOffset; }
[[nodiscard]] const u8* GetNWRAM_B() const noexcept { return MemoryBase + MemBlockNWRAM_BOffset; }
[[nodiscard]] u8* GetNWRAM_C() noexcept { return MemoryBase + MemBlockNWRAM_COffset; }
[[nodiscard]] const u8* GetNWRAM_C() const noexcept { return MemoryBase + MemBlockNWRAM_COffset; }
int ClassifyAddress9(u32 addr) const noexcept;
int ClassifyAddress7(u32 addr) const noexcept;
bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) const noexcept;
u32 LocaliseAddress(int region, u32 num, u32 addr) const noexcept;
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
{
u32 Addr;
u32 Size, LocalOffset;
u32 Num;
void Unmap(int region, NDS& nds) noexcept;
};
struct FaultDescription
{
u32 EmulatedFaultAddr;
u8* FaultPC;
};
static bool FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds);
bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept;
bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept;
void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept;
melonDS::NDS& NDS;
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;
#else
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
int MemoryFile = -1;
#endif
#ifdef ANDROID
Platform::DynamicLibrary* Libandroid = nullptr;
#endif
u8 MappingStatus9[1 << (32-12)] {};
u8 MappingStatus7[1 << (32-12)] {};
TinyVector<Mapping> Mappings[memregions_Count] {};
#else
public:
explicit ARMJIT_Memory(melonDS::NDS&) {};
~ARMJIT_Memory() = default;
ARMJIT_Memory(const ARMJIT_Memory&) = delete;
ARMJIT_Memory(ARMJIT_Memory&&) = delete;
ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete;
ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete;
void Reset() noexcept {}
void RemapDTCM(u32 newBase, u32 newSize) noexcept {}
void RemapSWRAM() noexcept {}
void RemapNWRAM(int num) noexcept {}
void SetCodeProtection(int region, u32 offset, bool protect) noexcept {}
[[nodiscard]] u8* GetMainRAM() noexcept { return MainRAM.data(); }
[[nodiscard]] const u8* GetMainRAM() const noexcept { return MainRAM.data(); }
[[nodiscard]] u8* GetSharedWRAM() noexcept { return SharedWRAM.data(); }
[[nodiscard]] const u8* GetSharedWRAM() const noexcept { return SharedWRAM.data(); }
[[nodiscard]] u8* GetARM7WRAM() noexcept { return ARM7WRAM.data(); }
[[nodiscard]] const u8* GetARM7WRAM() const noexcept { return ARM7WRAM.data(); }
[[nodiscard]] u8* GetARM9DTCM() noexcept { return DTCM.data(); }
[[nodiscard]] const u8* GetARM9DTCM() const noexcept { return DTCM.data(); }
[[nodiscard]] u8* GetNWRAM_A() noexcept { return NWRAM_A.data(); }
[[nodiscard]] const u8* GetNWRAM_A() const noexcept { return NWRAM_A.data(); }
[[nodiscard]] u8* GetNWRAM_B() noexcept { return NWRAM_B.data(); }
[[nodiscard]] const u8* GetNWRAM_B() const noexcept { return NWRAM_B.data(); }
[[nodiscard]] u8* GetNWRAM_C() noexcept { return NWRAM_C.data(); }
[[nodiscard]] const u8* GetNWRAM_C() const noexcept { return NWRAM_C.data(); }
private:
std::array<u8, MainRAMMaxSize> MainRAM {};
std::array<u8, ARM7WRAMSize> ARM7WRAM {};
std::array<u8, SharedWRAMSize> SharedWRAM {};
std::array<u8, DTCMPhysicalSize> DTCM {};
std::array<u8, NWRAMSize> NWRAM_A {};
std::array<u8, NWRAMSize> NWRAM_B {};
std::array<u8, NWRAMSize> NWRAM_C {};
#endif
memregions_Count
};
int ClassifyAddress9(u32 addr);
int ClassifyAddress7(u32 addr);
bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize);
u32 LocaliseAddress(int region, u32 num, u32 addr);
bool IsFastmemCompatible(int region);
void RemapDTCM(u32 newBase, u32 newSize);
void RemapSWRAM();
void RemapNWRAM(int num);
void SetCodeProtection(int region, u32 offset, bool protect);
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size);
}
#endif
#endif

View File

@ -1,38 +1,15 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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_REGCACHE_H
#define ARMJIT_REGCACHE_H
#include "ARMJIT_Internal.h"
#include "Platform.h"
#include "ARMJIT.h"
// TODO: replace this in the future
#include "dolphin/BitSet.h"
#include <assert.h>
namespace melonDS
namespace ARMJIT
{
using Platform::Log;
using Platform::LogLevel;
using namespace Common;
// Imported inside the namespace so that other headers aren't polluted
template <typename T, typename Reg>
class RegisterCache
@ -46,7 +23,7 @@ public:
{
for (int i = 0; i < 16; i++)
Mapping[i] = (Reg)-1;
PCAllocatableAsSrc = ~(pcAllocatableAsSrc
? 0
: (1 << 15));
@ -84,7 +61,7 @@ public:
}
}
Log(LogLevel::Error, "this is a JIT bug! LoadRegister failed\n");
printf("this is a JIT bug! LoadRegister failed\n");
abort();
}
@ -99,7 +76,7 @@ public:
LiteralsLoaded &= ~(1 << reg);
}
bool IsLiteral(int reg) const
bool IsLiteral(int reg)
{
return LiteralsLoaded & (1 << reg);
}

View File

@ -1,27 +1,8 @@
/*
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 "ARMJIT_Compiler.h"
#include "../ARM.h"
using namespace Gen;
namespace melonDS
namespace ARMJIT
{
// uses RSCRATCH3
@ -129,7 +110,7 @@ OpArg Compiler::A_Comp_GetALUOp2(bool S, bool& carryUsed)
Comp_AddCycles_C();
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift);
carryUsed = false;
if (S && shift)
@ -228,7 +209,7 @@ void Compiler::A_Comp_Arith()
Comp_ArithTriOp(&Compiler::AND, rd, rn, op2, carryUsed, sFlag|opSymmetric|opInvertOp2);
break;
default:
Log(LogLevel::Error, "this is a JIT bug! %04x\n", op);
printf("this is a JIT bug! %04x\n", op);
abort();
}
@ -365,7 +346,7 @@ void Compiler::A_Comp_Mul_Long()
{
BSR(32, RSCRATCH, R(RSCRATCH3));
}
SHR(32, R(RSCRATCH), Imm8(3));
SetJumpTarget(zeroBSR); // fortunately that's even right
Comp_AddCycles_CI(RSCRATCH, 2);
@ -618,7 +599,7 @@ void Compiler::T_Comp_AddSub_()
int op = (CurInstr.Instr >> 9) & 0x3;
OpArg rn = op >= 2 ? Imm32((CurInstr.Instr >> 6) & 0x7) : MapReg(CurInstr.T_Reg(6));
Comp_AddCycles_C();
// special case for thumb mov being alias to add rd, rn, #0

View File

@ -1,28 +1,8 @@
/*
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 "ARMJIT_Compiler.h"
#include "../ARM.h"
#include "../NDS.h"
using namespace Gen;
namespace melonDS
namespace ARMJIT
{
template <typename T>
@ -32,7 +12,7 @@ int squeezePointer(T* ptr)
assert((T*)((u64)truncated) == ptr);
return truncated;
}
void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
{
// we can simplify constant branches by a lot
@ -121,7 +101,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1];
cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1];
CurCPU->R[15] = compileTimePC;
}
@ -133,7 +113,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3];
cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3];
CurCPU->R[15] = compileTimePC;
}
@ -167,7 +147,7 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
bool cpsrDirty = CPSRDirty;
SaveCPSR();
PushRegs(restoreCPSR, true);
PushRegs(restoreCPSR);
MOV(64, R(ABI_PARAM1), R(RCPU));
MOV(32, R(ABI_PARAM2), R(addr));
@ -176,11 +156,11 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
else
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
if (Num == 0)
ABI_CallFunction(ARMv5JumpToTrampoline);
CALL((void*)&ARMv5JumpToTrampoline);
else
ABI_CallFunction(ARMv4JumpToTrampoline);
CALL((void*)&ARMv4JumpToTrampoline);
PopRegs(restoreCPSR, true);
PopRegs(restoreCPSR);
LoadCPSR();
// in case this instruction is skipped
@ -249,7 +229,7 @@ void Compiler::T_Comp_BranchXchangeReg()
{
if (Num == 1)
{
Log(LogLevel::Warn, "BLX unsupported on ARM7!!!\n");
printf("BLX unsupported on ARM7!!!\n");
return;
}
MOV(32, R(RSCRATCH), MapReg(CurInstr.A_Reg(3)));
@ -295,7 +275,7 @@ void Compiler::T_Comp_BL_Merged()
target |= 1;
MOV(32, MapReg(14), Imm32((R15 - 2) | 1));
Comp_JumpTo(target);
}

View File

@ -1,39 +1,24 @@
/*
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 "ARMJIT_Compiler.h"
#include "../ARMJIT.h"
#include "../ARMInterpreter.h"
#include "../NDS.h"
#include "../ARMJIT_Global.h"
#include "../Config.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;
extern "C" void ARM_Ret();
namespace melonDS
namespace ARMJIT
{
template <>
const X64Reg RegisterCache<Compiler, X64Reg>::NativeRegAllocOrder[] =
@ -61,7 +46,7 @@ const BitSet32 CallerSavedPushRegs({R10, R11});
const BitSet32 CallerSavedPushRegs({R9, R10, R11});
#endif
void Compiler::PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload)
void Compiler::PushRegs(bool saveHiRegs)
{
BitSet32 loadedRegs(RegCache.LoadedRegs);
@ -80,26 +65,17 @@ void Compiler::PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnl
}
for (int reg : loadedRegs)
{
if (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs))))
{
if ((Thumb || CurInstr.Cond() == 0xE) && !((1 << reg) & (CurInstr.Info.DstRegs|CurInstr.Info.SrcRegs)) && allowUnload)
RegCache.UnloadRegister(reg);
else
SaveReg(reg, RegCache.Mapping[reg]);
}
}
if (BitSet32(1 << RegCache.Mapping[reg]) & ABI_ALL_CALLER_SAVED)
SaveReg(reg, RegCache.Mapping[reg]);
}
void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
void Compiler::PopRegs(bool saveHiRegs)
{
BitSet32 loadedRegs(RegCache.LoadedRegs);
for (int reg : loadedRegs)
{
if ((saveHiRegs && reg >= 8 && reg < 15)
|| (CallerSavedPushRegs[RegCache.Mapping[reg]]
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs)))))
|| BitSet32(1 << RegCache.Mapping[reg]) & ABI_ALL_CALLER_SAVED)
{
LoadReg(reg, RegCache.Mapping[reg]);
}
@ -122,9 +98,7 @@ void Compiler::A_Comp_MRS()
MOV(32, rd, R(RSCRATCH3));
}
else
{
MOV(32, rd, R(RCPSR));
}
}
void UpdateModeTrampoline(ARM* arm, u32 oldmode, u32 newmode)
@ -137,7 +111,7 @@ void Compiler::A_Comp_MSR()
Comp_AddCycles_C();
OpArg val = CurInstr.Instr & (1 << 25)
? Imm32(melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
? Imm32(::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
: MapReg(CurInstr.A_Reg(0));
u32 mask = 0;
@ -211,26 +185,51 @@ void Compiler::A_Comp_MSR()
AND(32, R(RSCRATCH2), val);
OR(32, R(RCPSR), R(RSCRATCH2));
PushRegs(true, true);
PushRegs(true);
MOV(32, R(ABI_PARAM3), R(RCPSR));
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
MOV(64, R(ABI_PARAM1), R(RCPU));
ABI_CallFunction(UpdateModeTrampoline);
CALL((void*)&UpdateModeTrampoline);
PopRegs(true, true);
PopRegs(true);
}
}
}
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
/*
We'll repurpose this .bss memory
*/
u8 CodeMemory[1024 * 1024 * 32];
Compiler::Compiler()
{
ARMJIT_Global::Init();
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
CodeMemBase = static_cast<u8*>(ARMJIT_Global::AllocateCodeMem());
CodeMemSize = ARMJIT_Global::CodeMemorySliceSize;
u64 pageSize = (u64)sysInfo.dwPageSize;
#else
u64 pageSize = sysconf(_SC_PAGE_SIZE);
#endif
ResetStart = CodeMemBase;
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;
}
Reset();
@ -270,10 +269,6 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
SetJumpTarget(und);
MOV(32, R(RSCRATCH3), MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)));
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("ReadBanked", ReadBanked);
#endif
}
{
// RSCRATCH mode
@ -317,10 +312,6 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
MOV(32, MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)), R(RSCRATCH3));
CLC();
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("WriteBanked", WriteBanked);
#endif
}
for (int consoleType = 0; consoleType < 2; consoleType++)
@ -381,10 +372,6 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
ABI_PopRegistersAndAdjustStack(CallerSavedPushRegs, 8);
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("FastMemStorePatch%d_%d_%d", PatchedStoreFuncs[consoleType][num][size][reg], num, size, reg);
#endif
for (int signextend = 0; signextend < 2; signextend++)
{
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetWritableCodePtr();
@ -423,10 +410,6 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
else
MOVZX(32, 8 << size, rdMapped, R(RSCRATCH));
RET();
#ifdef JIT_PROFILING_ENABLED
CreateMethod("FastMemLoadPatch%d_%d_%d_%d", PatchedLoadFuncs[consoleType][num][size][signextend][reg], num, size, reg, signextend);
#endif
}
}
}
@ -444,13 +427,6 @@ 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);
@ -471,14 +447,14 @@ void Compiler::SaveCPSR(bool flagClean)
void Compiler::LoadReg(int reg, X64Reg nativeReg)
{
if (reg != 15)
MOV(32, R(nativeReg), MDisp(RCPU, offsetof(ARM, R) + reg*4));
MOV(32, R(nativeReg), MDisp(RCPU, offsetof(ARM, R[reg])));
else
MOV(32, R(nativeReg), Imm32(R15));
}
void Compiler::SaveReg(int reg, X64Reg nativeReg)
{
MOV(32, MDisp(RCPU, offsetof(ARM, R) + reg*4), R(nativeReg));
MOV(32, MDisp(RCPU, offsetof(ARM, R[reg])), R(nativeReg));
}
// invalidates RSCRATCH and RSCRATCH3
@ -616,18 +592,18 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
// LDR/STR sp rel
F(T_Comp_MemSPRel), F(T_Comp_MemSPRel),
// PUSH/POP
F(T_Comp_PUSH_POP), F(T_Comp_PUSH_POP),
F(T_Comp_PUSH_POP), F(T_Comp_PUSH_POP),
// LDMIA, STMIA
F(T_Comp_LDMIA_STMIA), F(T_Comp_LDMIA_STMIA),
F(T_Comp_LDMIA_STMIA), F(T_Comp_LDMIA_STMIA),
// Branch
F(T_Comp_BCOND), F(T_Comp_BranchXchangeReg), F(T_Comp_BranchXchangeReg), F(T_Comp_B), F(T_Comp_BL_LONG_1), F(T_Comp_BL_LONG_2),
F(T_Comp_BCOND), F(T_Comp_BranchXchangeReg), F(T_Comp_BranchXchangeReg), F(T_Comp_B), F(T_Comp_BL_LONG_1), F(T_Comp_BL_LONG_2),
// Unk, SVC
NULL, NULL,
F(T_Comp_BL_Merged)
};
#undef F
bool Compiler::CanCompile(bool thumb, u16 kind) const
bool Compiler::CanCompile(bool thumb, u16 kind)
{
return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL;
}
@ -643,7 +619,7 @@ void Compiler::Reset()
LoadStorePatches.clear();
}
bool Compiler::IsJITFault(const u8* addr)
bool Compiler::IsJITFault(u8* addr)
{
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
}
@ -658,45 +634,22 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
{
RegCache.PrepareExit();
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
ABI_TailCall(ARM_Ret);
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)&ARM_Ret, true);
}
}
#ifdef JIT_PROFILING_ENABLED
void Compiler::CreateMethod(const char* namefmt, void* start, ...)
{
if (iJIT_IsProfilingActive())
{
va_list args;
va_start(args, start);
char name[64];
vsprintf(name, namefmt, args);
va_end(args);
iJIT_Method_Load method = {0};
method.method_id = iJIT_GetNewMethodID();
method.method_name = name;
method.method_load_address = start;
method.method_size = GetWritableCodePtr() - (u8*)start;
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&method);
}
}
#endif
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemoryInstr)
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount)
{
if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess...
{
Log(LogLevel::Debug, "near reset\n");
NDS.JIT.ResetBlockCache();
printf("near reset\n");
ResetBlockCache();
}
if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess...
{
Log(LogLevel::Debug, "far reset\n");
NDS.JIT.ResetBlockCache();
printf("far reset\n");
ResetBlockCache();
}
ConstantCycles = 0;
@ -750,9 +703,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
ABI_CallFunction(InterpretTHUMB[CurInstr.Info.Kind]);
}
else
{
(this->*comp)();
}
}
else
{
@ -773,7 +724,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
}
else
{
IrregularCycles = comp == NULL;
IrregularCycles = false;
FixupBranch skipExecute;
if (cond < 0xE)
@ -786,9 +737,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
ABI_CallFunction(InterpretARM[CurInstr.Info.Kind]);
}
else
{
(this->*comp)();
}
Comp_SpecialBranchBehaviour(true);
@ -806,11 +755,9 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
SetJumpTarget(skipFailed);
}
else
{
SetJumpTarget(skipExecute);
}
}
}
}
@ -820,13 +767,8 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
RegCache.Flush();
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
ABI_TailCall(ARM_Ret);
#ifdef JIT_PROFILING_ENABLED
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
#endif
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)ARM_Ret, true);
/*FILE* codeout = fopen("codeout", "a");
fprintf(codeout, "beginning block argargarg__ %x!!!", instrs[0].Addr);
@ -840,7 +782,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
{
s32 cycles = Num ?
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
if ((!Thumb && CurInstr.Cond() < 0xE) || forceNonConstant)
@ -852,7 +794,7 @@ void Compiler::Comp_AddCycles_C(bool forceNonConstant)
void Compiler::Comp_AddCycles_CI(u32 i)
{
s32 cycles = (Num ?
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + i;
if (!Thumb && CurInstr.Cond() < 0xE)
@ -864,9 +806,9 @@ void Compiler::Comp_AddCycles_CI(u32 i)
void Compiler::Comp_AddCycles_CI(Gen::X64Reg i, int add)
{
s32 cycles = Num ?
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
if (!Thumb && CurInstr.Cond() < 0xE)
{
LEA(32, RSCRATCH, MDisp(i, add + cycles));
@ -889,7 +831,7 @@ void Compiler::Comp_AddCycles_CDI()
s32 cycles;
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
@ -911,7 +853,7 @@ void Compiler::Comp_AddCycles_CDI()
{
cycles = numC + numD + 1;
}
if (!Thumb && CurInstr.Cond() < 0xE)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm8(cycles));
else
@ -934,7 +876,7 @@ void Compiler::Comp_AddCycles_CD()
}
else
{
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 4) == 0x02)

View File

@ -1,43 +1,17 @@
/*
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/.
*/
#ifndef ARMJIT_X64_COMPILER_H
#define ARMJIT_X64_COMPILER_H
#if defined(JIT_ENABLED) && defined(__x86_64__)
#include "../dolphin/x64Emitter.h"
#include "../ARMJIT.h"
#include "../ARMJIT_Internal.h"
#include "../ARMJIT_RegisterCache.h"
#ifdef JIT_PROFILING_ENABLED
#include <jitprofiling.h>
#endif
#include <unordered_map>
namespace melonDS
namespace ARMJIT
{
class ARMJIT;
class ARMJIT_Memory;
class NDS;
const Gen::X64Reg RCPU = Gen::RBP;
const Gen::X64Reg RCPSR = Gen::R15;
@ -83,17 +57,16 @@ struct Op2
class Compiler : public Gen::XEmitter
{
public:
explicit Compiler(melonDS::NDS& nds);
~Compiler();
Compiler();
void Reset();
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemoryInstr);
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount);
void LoadReg(int reg, Gen::X64Reg nativeReg);
void SaveReg(int reg, Gen::X64Reg nativeReg);
bool CanCompile(bool thumb, u16 kind) const;
bool CanCompile(bool thumb, u16 kind);
typedef void (Compiler::*CompileFunc)();
@ -126,7 +99,7 @@ public:
void A_Comp_Mul_Long();
void A_Comp_CLZ();
void A_Comp_MemWB();
void A_Comp_MemHalf();
void A_Comp_LDM_STM();
@ -172,10 +145,10 @@ public:
memop_SubtractOffset = 1 << 4
};
void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags);
s32 Comp_MemAccessBlock(int rn, Common::BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode);
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
void Comp_ArithTriOpReverse(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
@ -201,8 +174,8 @@ public:
Gen::FixupBranch CheckCondition(u32 cond);
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
void PushRegs(bool saveHiRegs);
void PopRegs(bool saveHiRegs);
Gen::OpArg MapReg(int reg)
{
@ -235,55 +208,48 @@ public:
SetCodePtr(FarCode);
}
bool IsJITFault(const u8* addr);
bool IsJITFault(u8* addr);
u8* RewriteMemAccess(u8* pc);
#ifdef JIT_PROFILING_ENABLED
void CreateMethod(const char* namefmt, void* start, ...);
#endif
u8* FarCode;
u8* NearCode;
u32 FarSize;
u32 NearSize;
melonDS::NDS& NDS;
u8* FarCode {};
u8* NearCode {};
u32 FarSize {};
u32 NearSize {};
u8* NearStart;
u8* FarStart;
u8* NearStart {};
u8* FarStart {};
void* PatchedStoreFuncs[2][2][3][16];
void* PatchedLoadFuncs[2][2][3][2][16];
void* PatchedStoreFuncs[2][2][3][16] {};
void* PatchedLoadFuncs[2][2][3][2][16] {};
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches;
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
u8* ResetStart;
u32 CodeMemSize;
u8* CodeMemBase;
u8* ResetStart {};
u32 CodeMemSize {};
bool Exit;
bool IrregularCycles;
bool Exit {};
bool IrregularCycles {};
void* ReadBanked {};
void* WriteBanked {};
void* ReadBanked;
void* WriteBanked;
bool CPSRDirty = false;
FetchedInstr CurInstr {};
FetchedInstr CurInstr;
RegisterCache<Compiler, Gen::X64Reg> RegCache {};
RegisterCache<Compiler, Gen::X64Reg> RegCache;
bool Thumb {};
u32 Num {};
u32 R15 {};
u32 CodeRegion {};
bool Thumb;
u32 Num;
u32 R15;
u32 CodeRegion;
u32 ConstantCycles {};
u32 ConstantCycles;
ARM* CurCPU {};
ARM* CurCPU;
};
}
#endif
#endif

View File

@ -1,23 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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 "../ARM.h"
using namespace melonDS;
int main(int argc, char* argv[])
{
FILE* f = fopen("ARMJIT_Offsets.h", "w");

View File

@ -1,21 +1,3 @@
/*
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/.
*/
.intel_syntax noprefix
#include "ARMJIT_Offsets.h"
@ -104,8 +86,3 @@ ARM_Ret:
#endif
ret
#if !defined(__APPLE__) && !defined(WIN64)
.section .note.GNU-stack,"",@progbits
#endif

View File

@ -1,28 +1,10 @@
/*
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 "ARMJIT_Compiler.h"
#include "../ARMJIT.h"
#include "../NDS.h"
#include "../Config.h"
using namespace Gen;
namespace melonDS
namespace ARMJIT
{
template <typename T>
@ -53,7 +35,7 @@ u8* Compiler::RewriteMemAccess(u8* pc)
return pc + (ptrdiff_t)patch.Offset;
}
Log(LogLevel::Error, "this is a JIT bug %sx\n", pc);
printf("this is a JIT bug %llx\n", pc);
abort();
}
@ -69,11 +51,12 @@ u8* Compiler::RewriteMemAccess(u8* pc)
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
{
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
u32 localAddr = LocaliseCodeAddress(Num, addr);
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
if (invalidLiteralIdx != -1)
{
InvalidLiterals.Remove(invalidLiteralIdx);
return false;
}
@ -86,7 +69,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
if (size == 32)
{
CurCPU->DataRead32(addr & ~0x3, &val);
val = melonDS::ROR(val, (addr & 0x3) << 3);
val = ::ROR(val, (addr & 0x3) << 3);
}
else if (size == 16)
{
@ -106,7 +89,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
if (Thumb || CurInstr.Cond() == 0xE)
RegCache.PutLiteral(rd, val);
return true;
}
@ -119,10 +102,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (size == 16)
addressMask = ~1;
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (Config::JIT_LiteralOptimisations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
if (Comp_MemLoadLiteral(size, flags & memop_SignExtend, rd, addr))
return;
}
@ -136,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
Comp_AddCycles_CDI();
}
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
bool addrIsStatic = Config::JIT_LiteralOptimisations
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -147,12 +130,6 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (Thumb && rn == 15)
rnMapped = Imm32(R15 & ~0x2);
if (flags & memop_Store && flags & (memop_Post|memop_Writeback) && rd == rn)
{
MOV(32, R(RSCRATCH4), rdMapped);
rdMapped = R(RSCRATCH4);
}
X64Reg finalAddr = RSCRATCH3;
if (flags & memop_Post)
{
@ -172,7 +149,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (!(flags & memop_SubtractOffset) && rm.IsSimpleReg() && rnMapped.IsSimpleReg()
&& op2.Reg.Op == 0 && op2.Reg.Amount > 0 && op2.Reg.Amount <= 3)
{
LEA(32, finalAddr,
LEA(32, finalAddr,
MComplex(rnMapped.GetSimpleReg(), rm.GetSimpleReg(), 1 << op2.Reg.Amount, 0));
}
else
@ -197,10 +174,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
MOV(32, rnMapped, R(finalAddr));
u32 expectedTarget = Num == 0
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
if (Config::JIT_FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
{
if (rdMapped.IsImm())
{
@ -213,12 +190,12 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
assert(rdMapped.GetSimpleReg() >= 0 && rdMapped.GetSimpleReg() < 16);
patch.PatchFunc = flags & memop_Store
? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()]
: PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()];
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()]
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()];
assert(patch.PatchFunc != NULL);
MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
X64Reg maskedAddr = RSCRATCH3;
if (size > 8)
@ -265,11 +242,11 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
}
else
{
PushRegs(false, false);
PushRegs(false);
void* func = NULL;
if (addrIsStatic)
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
if (func)
{
@ -282,7 +259,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
ABI_CallFunction((void (*)())func);
PopRegs(false, false);
PopRegs(false);
if (!(flags & memop_Store))
{
@ -305,35 +282,33 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
{
if (Num == 0)
{
// on Windows param 3 is R8 which is also scratch 4 which can be used for rd
if (flags & memop_Store)
MOV(32, R(ABI_PARAM3), rdMapped);
MOV(64, R(ABI_PARAM2), R(RCPU));
if (ABI_PARAM1 != RSCRATCH3)
MOV(32, R(ABI_PARAM1), R(RSCRATCH3));
if (flags & memop_Store)
{
switch (size | NDS.ConsoleType)
MOV(32, R(ABI_PARAM3), rdMapped);
switch (size | NDS::ConsoleType)
{
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;
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;
}
}
else
{
switch (size | NDS.ConsoleType)
switch (size | NDS::ConsoleType)
{
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;
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;
}
}
}
@ -345,32 +320,32 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
{
MOV(32, R(ABI_PARAM2), rdMapped);
switch (size | NDS.ConsoleType)
switch (size | NDS::ConsoleType)
{
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;
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;
}
}
else
{
switch (size | NDS.ConsoleType)
switch (size | NDS::ConsoleType)
{
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;
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;
}
}
}
PopRegs(false, false);
PopRegs(false);
if (!(flags & memop_Store))
{
if (flags & memop_SignExtend)
@ -384,7 +359,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (!(flags & memop_Store) && rd == 15)
{
if (size < 32)
Log(LogLevel::Debug, "!!! LDR <32 bit PC %08X %x\n", R15, CurInstr.Instr);
printf("!!! LDR <32 bit PC %08X %x\n", R15, CurInstr.Instr);
{
if (Num == 1)
{
@ -398,15 +373,14 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
}
}
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn)
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode)
{
int regsCount = regs.Count();
if (regsCount == 0)
return 0; // actually not the right behaviour TODO: fix me
int firstReg = *regs.begin();
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << firstReg) && !(firstReg == rn && skipLoadingRn))
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << *regs.begin()))
{
int flags = 0;
if (store)
@ -415,7 +389,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
flags |= memop_SubtractOffset;
Op2 offset = preinc ? Op2(4) : Op2(0);
Comp_MemAccess(firstReg, rn, offset, 32, flags);
Comp_MemAccess(*regs.begin(), rn, offset, 32, flags);
return decrement ? -4 : 4;
}
@ -423,16 +397,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
s32 offset = (regsCount * 4) * (decrement ? -1 : 1);
int expectedTarget = Num == 0
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
if (!store)
Comp_AddCycles_CDI();
else
Comp_AddCycles_CD();
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
bool compileFastPath = Config::JIT_FastMemory
&& !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
// we need to make sure that the stack stays aligned to 16 bytes
#ifdef _WIN32
@ -455,7 +429,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
u8* fastPathStart = GetWritableCodePtr();
u8* loadStoreAddr[16];
MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
ADD(64, R(RSCRATCH2), R(RSCRATCH4));
u32 offset = 0;
@ -482,10 +456,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
{
if (RegCache.LoadedRegs & (1 << reg))
{
if (!(reg == rn && skipLoadingRn))
MOV(32, MapReg(reg), mem);
else
MOV(32, R(RSCRATCH), mem); // just touch the memory
MOV(32, MapReg(reg), mem);
}
else
{
@ -511,7 +482,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (!store)
{
PushRegs(false, false, !compileFastPath);
PushRegs(false);
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
@ -524,15 +495,15 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (Num == 0)
MOV(64, R(ABI_PARAM4), R(RCPU));
switch (Num * 2 | NDS.ConsoleType)
switch (Num * 2 | NDS::ConsoleType)
{
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;
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;
}
PopRegs(false, false);
PopRegs(false);
if (allocOffset)
ADD(64, R(RSP), Imm8(allocOffset));
@ -551,15 +522,12 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
MOV(32, R(RSCRATCH2), Imm32(reg - 8));
POP(RSCRATCH3);
CALL(WriteBanked);
if (!(reg == rn && skipLoadingRn))
{
FixupBranch sucessfulWritten = J_CC(CC_NC);
if (RegCache.LoadedRegs & (1 << reg))
MOV(32, R(RegCache.Mapping[reg]), R(RSCRATCH3));
else
SaveReg(reg, RSCRATCH3);
SetJumpTarget(sucessfulWritten);
}
FixupBranch sucessfulWritten = J_CC(CC_NC);
if (RegCache.LoadedRegs & (1 << reg))
MOV(32, R(RegCache.Mapping[reg]), R(RSCRATCH3));
else
SaveReg(reg, RSCRATCH3);
SetJumpTarget(sucessfulWritten);
}
else if (!(RegCache.LoadedRegs & (1 << reg)))
{
@ -568,10 +536,6 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
POP(RSCRATCH);
SaveReg(reg, RSCRATCH);
}
else if (reg == rn && skipLoadingRn)
{
ADD(64, R(RSP), Imm8(8));
}
else
{
POP(MapReg(reg).GetSimpleReg());
@ -616,29 +580,29 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (allocOffset)
SUB(64, R(RSP), Imm8(allocOffset));
PushRegs(false, false, !compileFastPath);
PushRegs(false);
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
if (allocOffset)
LEA(64, ABI_PARAM2, MDisp(RSP, allocOffset));
else
MOV(64, R(ABI_PARAM2), R(RSP));
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
if (Num == 0)
MOV(64, R(ABI_PARAM4), R(RCPU));
switch (Num * 2 | NDS.ConsoleType)
switch (Num * 2 | NDS::ConsoleType)
{
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;
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;
}
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
PopRegs(false, false);
PopRegs(false);
}
if (compileFastPath)
@ -668,7 +632,7 @@ void Compiler::A_Comp_MemWB()
bool load = CurInstr.Instr & (1 << 20);
bool byte = CurInstr.Instr & (1 << 22);
int size = byte ? 8 : 32;
int flags = 0;
if (!load)
flags |= memop_Store;
@ -742,7 +706,7 @@ void Compiler::T_Comp_MemReg()
bool load = op & 0x2;
bool byte = op & 0x1;
Comp_MemAccess(CurInstr.T_Reg(0), CurInstr.T_Reg(3), Op2(CurInstr.T_Reg(6), 0, 0),
Comp_MemAccess(CurInstr.T_Reg(0), CurInstr.T_Reg(3), Op2(CurInstr.T_Reg(6), 0, 0),
byte ? 8 : 32, load ? 0 : memop_Store);
}
@ -758,14 +722,14 @@ void Compiler::A_Comp_LDM_STM()
OpArg rn = MapReg(CurInstr.A_Reg(16));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode);
if (load && writeback && regs[CurInstr.A_Reg(16)])
writeback = Num == 0
&& (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1));
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode, load && writeback);
if (writeback && offset)
ADD(32, rn, Imm32(offset));
? (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1))
: false;
if (writeback)
ADD(32, rn, offset >= INT8_MIN && offset < INT8_MAX ? Imm8(offset) : Imm32(offset));
}
void Compiler::T_Comp_MemImm()
@ -809,7 +773,7 @@ void Compiler::T_Comp_LoadPCRel()
{
u32 offset = (CurInstr.Instr & 0xFF) << 2;
u32 addr = (R15 & ~0x2) + offset;
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!Config::JIT_LiteralOptimisations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@ -835,10 +799,9 @@ void Compiler::T_Comp_PUSH_POP()
}
OpArg sp = MapReg(13);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false, false);
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false);
if (offset)
ADD(32, sp, Imm8(offset)); // offset will be always be in range since PUSH accesses 9 regs max
ADD(32, sp, Imm8(offset)); // offset will be always be in range since PUSH accesses 9 regs max
}
void Compiler::T_Comp_LDMIA_STMIA()
@ -847,11 +810,9 @@ void Compiler::T_Comp_LDMIA_STMIA()
OpArg rb = MapReg(CurInstr.T_Reg(8));
bool load = CurInstr.Instr & (1 << 11);
bool writeback = !load || !regs[CurInstr.T_Reg(8)];
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false);
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false, load && writeback);
if (writeback && offset)
if (!load || !regs[CurInstr.T_Reg(8)])
ADD(32, rb, Imm8(offset));
}

View File

@ -1,21 +1,3 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
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/.
*/
#define ARM_CPSR_offset 0x64
#define ARM_Cycles_offset 0xc
#define ARM_StopExecution_offset 0x10

View File

@ -1,28 +1,10 @@
/*
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 "ARM_InstrInfo.h"
#include <stdio.h>
#include "ARMJIT.h"
#include "Config.h"
namespace melonDS::ARMInstrInfo
namespace ARMInstrInfo
{
#define ak(x) ((x) << 23)
@ -230,7 +212,7 @@ enum {
T_SetMaybeC = 1 << 17,
T_ReadC = 1 << 18,
T_SetC = 1 << 19,
T_WriteMem = 1 << 20,
T_LoadMem = 1 << 21,
};
@ -315,7 +297,7 @@ const u32 T_SVC = T_BranchAlways | T_WriteR14 | tk(tk_SVC);
#include "ARM_InstrTable.h"
#undef INSTRFUNC_PROTO
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
Info Decode(bool thumb, u32 num, u32 instr)
{
const u8 FlagsReadPerCond[7] = {
flag_Z,
@ -345,7 +327,7 @@ Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
res.DstRegs |= 1 << (instr & 0x7);
if (data & T_Write8)
res.DstRegs |= 1 << ((instr >> 8) & 0x7);
if (data & T_ReadHi0)
res.SrcRegs |= 1 << ((instr & 0x7) | ((instr >> 4) & 0x8));
if (data & T_ReadHi3)
@ -381,12 +363,12 @@ Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
if (data & T_WriteMem)
res.SpecialKind = special_WriteMem;
if (data & T_LoadMem)
{
if (res.Kind == tk_LDR_PCREL)
{
if (!literaloptimizations)
if (!Config::JIT_LiteralOptimisations)
res.SrcRegs |= 1 << 15;
res.SpecialKind = special_LoadLiteral;
}
@ -471,18 +453,18 @@ Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
res.SrcRegs |= 1 << ((instr >> 8) & 0xF);
if (data & A_Read12)
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
if (data & A_Write12)
res.DstRegs |= 1 << ((instr >> 12) & 0xF);
if (data & A_Write16)
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
if (data & A_MemWriteback && instr & (1 << 21))
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
if (data & A_BranchAlways)
res.DstRegs |= 1 << 15;
if (data & A_Read12Double)
{
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
@ -526,21 +508,16 @@ Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
if (data & A_LoadMem)
{
if (res.SrcRegs == (1 << 15))
res.SpecialKind = special_LoadLiteral;
res.SpecialKind = special_LoadLiteral;
else
res.SpecialKind = special_LoadMem;
}
if (res.Kind == ak_LDM)
{
u16 set = (instr & 0xFFFF);
res.NotStrictlyNeeded |= set & ~(res.SrcRegs|res.DstRegs|(1<<15));
res.DstRegs |= set;
// when the instruction is executed not in usermode a banked register in memory will be written to
// but the unbanked register will still be allocated, so it is expected to carry the proper value
// thus it is a source register
if (instr & (1<<22))
res.SrcRegs |= set & 0x7F00;
}
if (res.Kind == ak_STM)
{

View File

@ -1,27 +1,9 @@
/*
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/.
*/
#ifndef ARMINSTRINFO_H
#define ARMINSTRINFO_H
#include "types.h"
namespace melonDS::ARMInstrInfo
namespace ARMInstrInfo
{
// Instruction kinds, for faster dispatch
@ -222,7 +204,7 @@ enum
tk_POP,
tk_LDMIA,
tk_STMIA,
tk_BCOND,
tk_BX,
tk_BLX_REG,
@ -274,8 +256,8 @@ struct Info
}
};
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations);
Info Decode(bool thumb, u32 num, u32 instr);
}
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2020 Arisotura
This file is part of melonDS.

Some files were not shown because too many files have changed in this diff Show More