Compare commits

..

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

553 changed files with 36204 additions and 97819 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

View File

@ -0,0 +1,73 @@
name: CMake Build (macOS Universal)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
prepare:
runs-on: [self-hosted, macOS, ARM64]
steps:
- name: Clean workspace
run: rm -rf ${{runner.workspace}}/build
- uses: actions/checkout@v3
build-arm64:
needs: prepare
runs-on: [self-hosted, macOS, ARM64]
env:
homebrew_prefix: /opt/homebrew
steps:
- name: Create build directory
run: mkdir -p ${{runner.workspace}}/build/arm64
- name: Configure
working-directory: ${{runner.workspace}}/build/arm64
run: arch -arm64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
- name: Make
working-directory: ${{runner.workspace}}/build/arm64
run: arch -arm64 make -j$(sysctl -n hw.logicalcpu)
build-x86_64:
needs: prepare
runs-on: [self-hosted, macOS, ARM64]
env:
homebrew_prefix: /usr/local
steps:
- name: Create build directory
run: mkdir -p ${{runner.workspace}}/build/x86_64
- name: Configure
working-directory: ${{runner.workspace}}/build/x86_64
run: arch -x86_64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
- name: Make
working-directory: ${{runner.workspace}}/build/x86_64
run: arch -x86_64 make -j$(sysctl -n hw.logicalcpu)
universal-binary:
needs: [build-arm64, build-x86_64]
runs-on: [self-hosted, macOS, ARM64]
steps:
- name: Merge binaries
run: $GITHUB_WORKSPACE/tools/mac-universal.py ${{runner.workspace}}/build/arm64/melonDS.app ${{runner.workspace}}/build/x86_64/melonDS.app ${{runner.workspace}}/build/universal/melonDS.app
- name: Create DMG
run: hdiutil create -fs HFS+ -volname melonDS -srcfolder ${{runner.workspace}}/build/universal/melonDS.app -ov -format UDBZ ${{runner.workspace}}/build/universal/melonDS.dmg
- uses: actions/upload-artifact@v3
with:
name: macOS-universal
path: ${{runner.workspace}}/build/universal/melonDS.dmg

View File

@ -1,94 +0,0 @@
name: macOS
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"
jobs:
build-macos:
strategy:
matrix:
arch: [x86_64, arm64]
name: ${{ matrix.arch }}
runs-on: macos-14
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install dependencies for package building
run: |
brew install autoconf automake autoconf-archive libtool python-setuptools
- name: Set up CMake
uses: lukka/get-cmake@latest
- name: Set up vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ 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
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

View File

@ -0,0 +1,50 @@
name: CMake Build (Ubuntu aarch64)
on:
push:
branches:
- master
pull_request:
branches:
- master
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-20.04
container: ubuntu:20.04
steps:
- name: Prepare system
shell: bash
run: |
apt update
apt -y full-upgrade
apt -y install git
- name: Check out source
uses: actions/checkout@v1
- name: Install dependencies
shell: bash
run: |
dpkg --add-architecture arm64
sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
rm /etc/apt/sources.list
mv /etc/apt/sources.list{.new,}
apt update
DEBIAN_FRONTEND=noninteractive apt install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu {libsdl2,qtbase5,qtbase5-private,qtmultimedia5,libslirp,libarchive}-dev:arm64 cmake extra-cmake-modules dpkg-dev
- name: Configure
shell: bash
run: |
CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -B build
- name: Make
shell: bash
run: |
cmake --build build -j$(nproc --all)
mkdir dist
cp build/melonDS dist
- uses: actions/upload-artifact@v1
with:
name: melonDS-ubuntu-aarch64
path: dist

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 extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-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-ubuntu-x86_64
path: ${{runner.workspace}}/build/dist

View File

@ -1,45 +1,47 @@
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
- uses: actions/checkout@v1
- 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 }}
msystem: MINGW64
update: true
- name: Install dependencies
run: pacman -Sq --noconfirm git make pkgconf mingw-w64-x86_64-{cmake,mesa,SDL2,qt5-static,libslirp,libarchive,libepoxy,toolchain}
- name: Create build environment
working-directory: ${{runner.workspace}}
run: mkdir build
- 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
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=C:/tools/msys64/mingw64/qt5-static
- name: Make
working-directory: ${{runner.workspace}}/build
run: make -j$(nproc --all)
- uses: actions/upload-artifact@v1
with:
name: melonDS-windows-x86_64
path: .\build\release-mingw-x86_64\melonDS.exe
path: ${{runner.workspace}}\build\melonDS.exe

9
.gitignore vendored
View File

@ -1,4 +1,4 @@
build*/
build
bin
obj
*.depend
@ -7,7 +7,7 @@ obj
melon_grc.c
melon_grc.h
melon.rc
cmake-build*
cmake-build
cmake-build-debug
compile_commands.json
.idea
@ -16,8 +16,3 @@ compile_commands.json
*.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,4 +1,4 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.15)
cmake_policy(VERSION 3.15)
if (POLICY CMP0076)
@ -7,17 +7,10 @@ endif()
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
if (USE_VCPKG)
include(ConfigureVcpkg)
endif()
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
project(melonDS
VERSION 1.0
VERSION 0.9.5
DESCRIPTION "DS emulator, sorta"
HOMEPAGE_URL "https://melonds.kuribo64.net"
LANGUAGES C CXX)
@ -27,8 +20,7 @@ include(CheckLibraryExists)
include(CMakeDependentOption)
include(CheckIPOSupported)
include(SetupCCache)
include(Sanitizers)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
@ -36,6 +28,8 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_compile_definitions(MELONDS_VERSION="${melonDS_VERSION}")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
@ -79,6 +73,11 @@ if (ENABLE_LTO)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
if (NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
endif()
@ -93,9 +92,11 @@ endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
option(ENABLE_GDBSTUB "Enable GDB stub" ON)
if (ENABLE_GDBSTUB)
add_definitions(-DGDBSTUB_ENABLED)
find_program(CCACHE "ccache")
if (CCACHE)
message(STATUS "Using CCache to speed up compilation")
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
endif()
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)

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

@ -5,11 +5,12 @@
<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="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>
<a href="https://github.com/melonDS-emu/melonDS/actions?query=workflow%3A%22CMake+Build+%28Windows+x86-64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/melonDS-emu/melonDS/CMake%20Build%20(Windows%20x86-64)?label=Windows%20x86-64&logo=GitHub"></img></a>
<a href="https://github.com/melonDS-emu/melonDS/actions?query=workflow%3A%22CMake+Build+%28Ubuntu+x86-64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/melonDS-emu/melonDS/CMake%20Build%20(Ubuntu%20x86-64)?label=Linux%20x86-64&logo=GitHub"></img></a>
<a href="https://github.com/melonDS-emu/melonDS/actions?query=workflow%3A%22CMake+Build+%28Ubuntu+aarch64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/melonDS-emu/melonDS/CMake%20Build%20(Ubuntu%20aarch64)?label=Linux%20ARM64&logo=GitHub"></img></a>
<a href="https://dev.azure.com/melonDS/melonDS/_build?definitionId=1&repositoryFilter=1&branchFilter=2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2"><img src="https://img.shields.io/azure-devops/build/melonDS/7c9c08a1-669f-42a4-bef4-a6c74eadf723/1/master?label=macOS%20x86-64&logo=Azure%20Pipelines"></img></a>
<a href="https://dev.azure.com/melonDS/melonDS/_build?definitionId=2&_a=summary&repositoryFilter=1&branchFilter=2%2C2%2C2%2C2%2C2"><img src="https://img.shields.io/azure-devops/build/melonDS/7c9c08a1-669f-42a4-bef4-a6c74eadf723/2/master?label=macOS%20ARM64&logo=Azure%20Pipelines"></img></a>
</p>
DS emulator, sorta
@ -32,7 +33,75 @@ 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
1. Install dependencies:
* Ubuntu 22.04: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libslirp-dev libarchive-dev`
* Older Ubuntu: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp-dev libarchive-dev`
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia libslirp libarchive`
3. Download the melonDS repository and prepare:
```bash
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
3. Compile:
```bash
cmake -B build
cmake --build build -j$(nproc --all)
```
### Windows
1. Install [MSYS2](https://www.msys2.org/)
2. Open the **MSYS2 MinGW 64-bit** terminal
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to
4. Install git to clone the repository
```bash
pacman -S git
```
5. Download the melonDS repository and prepare:
```bash
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
#### Dynamic builds (with DLLs)
5. Install dependencies: `pacman -S make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-base,qt5-svg,qt5-multimedia,libslirp,libarchive}`
6. Compile:
```bash
cmake -B build -G "MSYS Makefiles"
cmake --build build -j$(nproc --all)
cd build
../tools/msys-dist.sh
```
If everything went well, melonDS and the libraries it needs should now be in the `dist` folder.
#### Static builds (without DLLs, standalone executable)
5. Install dependencies: `pacman -S make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-static,libslirp,libarchive}`
6. Compile:
```bash
cmake -B build -G 'MSYS Makefiles' -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static
cmake --build build -j$(nproc --all)
```
If everything went well, melonDS should now be in the `build` folder.
### macOS
1. Install the [Homebrew Package Manager](https://brew.sh)
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libslirp libarchive`
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)" -DUSE_QT6=ON
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-bundle.rb melonDS.app` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
## TODO LIST

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,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 +1,5 @@
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()
include_directories("${VTUNE_PATH}/include")
link_directories("${VTUNE_PATH}/lib64")

View File

@ -19,13 +19,6 @@ function(fix_interface_includes)
if (PARENT_DIR MATCHES "include$")
list(APPEND NEW_DIRS "${PARENT_DIR}")
endif()
# HACK
# The libarchive pkg-config file in MSYS2 seems to include a UNIX-style path for its
# include directory and CMake doesn't like that.
if (WIN32 AND MINGW AND target STREQUAL PkgConfig::LibArchive)
list(FILTER DIRS EXCLUDE REGEX "^/[^.]+64/.*")
endif()
endforeach()
list(APPEND DIRS ${NEW_DIRS})

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 @@
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

@ -517,9 +517,6 @@ swi_get_crc16:
mov const_0x1E, #0x1E
adr crc_table_ptr, crc_table
bic crc_value, crc_value, #0xFF000000
bic crc_value, crc_value, #0x00FF0000
movs crc_length, crc_length, lsr #1
beq 1f

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

View File

@ -16,8 +16,6 @@
<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>
@ -31,71 +29,14 @@
<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

@ -2,6 +2,5 @@
<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

@ -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 "res/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 ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
@ -18,9 +18,9 @@ FILETYPE VFT_APP
VALUE "FileVersion", "${melonDS_VERSION}"
VALUE "FileDescription", "melonDS emulator"
VALUE "InternalName", "SDnolem"
VALUE "LegalCopyright", "2016-2023 melonDS team"
VALUE "LegalCopyright", "2016-2022 melonDS team"
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "melonDS.exe"
VALUE "OriginalFilename", "zafkflzdasd.exe"
VALUE "ProductName", "melonDS"
VALUE "ProductVersion", "${melonDS_VERSION}"
}
@ -31,4 +31,4 @@ FILETYPE VFT_APP
}
}
1 24 "xp.manifest"
1 24 "res/xp.manifest"

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

@ -4,7 +4,7 @@
manifestVersion="1.0">
<assemblyIdentity
name="Exe.Apps.Project"
processorArchitecture="${WIN32_ARCHITECTURE}"
processorArchitecture="amd64"
version="1.0.0.0"
type="win32"/>
<description>Project</description>
@ -14,7 +14,7 @@
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="${WIN32_ARCHITECTURE}"
processorArchitecture="amd64"
publicKeyToken="6595b64144ccf1df"
language="*"
/>

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,43 +21,31 @@
#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(std::string filename)
{
Filename = filename;
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,9 +57,9 @@ bool ARCodeFile::Load()
ARCode curcode;
char linebuf[1024];
while (!IsEndOfFile(f))
while (!feof(f))
{
if (!FileReadLine(linebuf, 1024, f))
if (fgets(linebuf, 1024, f) == nullptr)
break;
linebuf[1023] = '\0';
@ -91,8 +79,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;
}
@ -114,15 +102,15 @@ 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;
}
@ -131,7 +119,7 @@ bool ARCodeFile::Load()
curcode.Name = codename;
curcode.Enabled = enable!=0;
curcode.Code.clear();
curcode.CodeLen = 0;
}
else
{
@ -140,58 +128,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, "\r\n");
fprintf(f, "CAT %s\r\n\r\n", cat.Name.c_str());
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\r\n", code.Enabled, code.Name.c_str());
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\r\n", code.Code[i], code.Code[i+1]);
}
FileWriteFormatted(f, "\n");
fprintf(f, "\r\n");
}
}
CloseFile(f);
fclose(f);
return true;
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,16 +21,15 @@
#include <string>
#include <list>
#include <vector>
#include "types.h"
namespace melonDS
{
struct ARCode
{
std::string Name;
bool Enabled;
std::vector<u32> Code;
u32 CodeLen;
u32 Code[2*64];
};
typedef std::list<ARCode> ARCodeList;
@ -47,21 +46,18 @@ typedef std::list<ARCodeCat> ARCodeCatList;
class ARCodeFile
{
public:
ARCodeFile(const std::string& filename);
~ARCodeFile() noexcept = default;
ARCodeFile(std::string 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;
};
}
#endif // ARCODEFILE_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,34 +21,83 @@
#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()
{
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;
}
}
ARCodeFile* GetCodeFile()
{
return CodeFile;
}
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 +107,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 +132,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]
@ -101,7 +150,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(addr);
cond = (b > chk) ? 1:0;
}
@ -114,7 +163,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(addr);
cond = (b < chk) ? 1:0;
}
@ -127,7 +176,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(addr);
cond = (b == chk) ? 1:0;
}
@ -140,7 +189,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = NDS.ARM7Read32(addr);
u32 chk = BusRead32(addr);
cond = (b != chk) ? 1:0;
}
@ -153,7 +202,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -168,7 +217,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -183,7 +232,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -198,7 +247,7 @@ void AREngine::RunCheat(const ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = NDS.ARM7Read16(addr);
u16 val = BusRead16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -207,7 +256,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 +271,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 +293,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 +342,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 +380,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 +390,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 +412,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 +428,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-2022 melonDS team
This file is part of melonDS.
@ -19,25 +19,20 @@
#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;
};
ARCodeFile* GetCodeFile();
void SetCodeFile(ARCodeFile* file);
void RunCheats();
}
#endif // ARENGINE_H

File diff suppressed because it is too large Load Diff

325
src/ARM.h
View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
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,18 +93,6 @@ 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 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,12 +258,11 @@ 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;
@ -364,33 +301,24 @@ public:
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 +330,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 +467,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-2022 melonDS team
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;
@ -223,11 +211,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
}
@ -251,11 +239,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-2022 melonDS team
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-2022 melonDS team
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;
@ -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) \
{ \
@ -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) \
{ \
@ -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) \
{ \
@ -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) \
{ \
@ -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) \
{ \
@ -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,)
@ -693,7 +665,7 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu)
// 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);
NDS::NocashPrint(cpu->Num, addr);
}
}
@ -961,7 +933,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 +954,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 +1051,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 +1069,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 +1086,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 +1095,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 +1112,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 +1121,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 +1178,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 +1191,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 +1204,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 +1217,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 +1238,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 +1251,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 +1264,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 +1335,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 +1350,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 +1383,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 +1395,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 +1407,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 +1504,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();
}
@ -1562,7 +1534,7 @@ void T_MOV_HIREG(ARM* cpu)
// 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);
NDS::NocashPrint(cpu->Num, addr);
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
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-2022 melonDS team
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-2022 melonDS team
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-2022 melonDS team
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();
@ -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, true);
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();
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
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,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -17,7 +17,7 @@
*/
#include "ARMJIT.h"
#include "ARMJIT_Memory.h"
#include <string.h>
#include <assert.h>
#include <unordered_map>
@ -30,7 +30,6 @@
#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 +42,136 @@
#include "SPU.h"
#include "Wifi.h"
#include "NDSCart.h"
#include "Platform.h"
#include "ARMJIT_x64/ARMJIT_Offsets.h"
namespace melonDS
{
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, "");
namespace ARMJIT
{
#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;
int MaxBlockSize;
bool LiteralOptimizations;
bool BranchOptimizations;
bool FastMemory;
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;
}
@ -104,31 +187,11 @@ T SlowRead9(u32 addr, ARMv5* cpu)
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
val = *(T*)&cpu->DTCM[addr & 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,7 +206,7 @@ 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)
@ -152,29 +215,49 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 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>
@ -230,18 +313,38 @@ 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()
{
JitEnableWrite();
ResetBlockCache();
ARMJIT_Memory::DeInit();
Memory.Reset();
delete JITCompiler;
}
void Reset()
{
MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize);
LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations);
BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations);
FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory);
if (MaxBlockSize < 1)
MaxBlockSize = 1;
if (MaxBlockSize > 32)
MaxBlockSize = 32;
JitEnableWrite();
ResetBlockCache();
ARMJIT_Memory::Reset();
}
void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags)
@ -468,17 +571,7 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
};
#undef F
ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept :
NDS(nds),
Memory(nds),
JITCompiler(nds),
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
FastMemory((jit.has_value() ? jit->FastMemory : false) && ARMJIT_Memory::IsFastMemSupported())
{}
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
void RetireJitBlock(JitBlock* block)
{
auto it = RestoreCandidates.find(block->InstrHash);
if (it != RestoreCandidates.end())
@ -492,44 +585,7 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
}
}
void ARMJIT::SetJITArgs(JITArgs args) noexcept
{
args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported();
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
if (MaxBlockSize != args.MaxBlockSize
|| 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;
@ -538,7 +594,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
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,7 +612,7 @@ 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;
}
@ -657,7 +713,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);
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr);
hasMemoryInstr |= thumb
? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA)
@ -708,7 +764,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
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)
{
@ -759,7 +815,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;
@ -815,7 +871,7 @@ 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);
@ -896,7 +952,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
FloodFillSetFlags(instrs, i - 1, 0xF);
JitEnableWrite();
block->EntryPoint = JITCompiler.CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr);
block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr);
JitEnableExecute();
JIT_DEBUGPRINT("block start %p\n", block->EntryPoint);
@ -916,8 +972,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 +987,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 +1025,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;
@ -1003,8 +1059,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,7 +1084,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
}
}
void ARMJIT::CheckAndInvalidateITCM() noexcept
void CheckAndInvalidateITCM()
{
for (u32 i = 0; i < ITCMPhysicalSize; i+=512)
{
@ -1046,7 +1102,7 @@ void ARMJIT::CheckAndInvalidateITCM() noexcept
}
}
void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept
void CheckAndInvalidateWVRAM(int bank)
{
u32 start = bank == 1 ? 0x20000 : 0;
for (u32 i = start; i < start+0x20000; i+=512)
@ -1062,30 +1118,38 @@ void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept
}
}
JitBlockEntry ARMJIT::LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept
template <u32 num, int region>
void CheckAndInvalidate(u32 addr)
{
u32 localAddr = ARMJIT_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)
{
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 +1158,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,10 +1217,10 @@ void ARMJIT::ResetBlockCache() noexcept
JitBlocks9.clear();
JitBlocks7.clear();
JITCompiler.Reset();
JITCompiler->Reset();
}
void ARMJIT::JitEnableWrite() noexcept
void JitEnableWrite()
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))
@ -1164,7 +1228,7 @@ void ARMJIT::JitEnableWrite() noexcept
#endif
}
void ARMJIT::JitEnableExecute() noexcept
void JitEnableExecute()
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -19,188 +19,49 @@
#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;
extern int MaxBlockSize;
extern bool LiteralOptimizations;
extern bool BranchOptimizations;
extern bool FastMemory;
ARMJIT_Memory Memory;
private:
int MaxBlockSize {};
bool LiteralOptimizations = false;
bool BranchOptimizations = false;
bool FastMemory = false;
void Init();
void DeInit();
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 Reset();
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 CheckAndInvalidateITCM();
void CheckAndInvalidateWVRAM(int bank);
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;
void InvalidateByAddr(u32 pseudoPhysical);
Compiler JITCompiler;
std::unordered_map<u32, JitBlock*> JitBlocks9 {};
std::unordered_map<u32, JitBlock*> JitBlocks7 {};
template <u32 num, int region>
void CheckAndInvalidate(u32 addr);
std::unordered_map<u32, JitBlock*> RestoreCandidates {};
void CompileBlock(ARM* cpu);
void ResetBlockCache();
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] {};
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr);
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size);
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
};
};
void JitEnableWrite();
void JitEnableExecute();
}
// 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,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -20,7 +20,7 @@
using namespace Arm64Gen;
namespace melonDS
namespace ARMJIT
{
void Compiler::Comp_RegShiftReg(int op, bool S, Op2& op2, ARM64Reg rs)
@ -480,7 +480,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))
{

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -17,14 +17,13 @@
*/
#include "ARMJIT_Compiler.h"
#include "../NDS.h"
using namespace Arm64Gen;
// hack
const int kCodeCacheTiming = 3;
namespace melonDS
namespace ARMJIT
{
template <typename T>
@ -83,7 +82,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 +132,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 +144,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 +235,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;
@ -389,7 +388,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 +436,4 @@ void Compiler::T_Comp_BL_Merged()
Comp_JumpTo(target);
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -20,9 +20,15 @@
#include "../ARMJIT_Internal.h"
#include "../ARMInterpreter.h"
#include "../ARMJIT.h"
#include "../NDS.h"
#include "../ARMJIT_Global.h"
#ifdef __SWITCH__
#include <switch.h>
extern char __start__;
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
#include <stdlib.h>
@ -30,7 +36,7 @@ using namespace Arm64Gen;
extern "C" void ARM_Ret();
namespace melonDS
namespace ARMJIT
{
/*
@ -56,6 +62,11 @@ const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
const int JitMemSize = 16 * 1024 * 1024;
#ifndef __SWITCH__
u8 JitMem[JitMemSize];
#endif
void Compiler::MovePC()
{
ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4);
@ -92,7 +103,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
{
@ -206,7 +217,7 @@ void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
}
}
Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
Compiler::Compiler()
{
#ifdef __SWITCH__
JitRWBase = aligned_alloc(0x1000, JitMemSize);
@ -226,7 +237,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;
}
}
@ -245,13 +256,18 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
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;
#ifdef __APPLE__
pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
JitEnableWrite();
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
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);
@ -462,9 +478,6 @@ Compiler::~Compiler()
free(JitRWBase);
}
#endif
ARMJIT_Global::FreeCodeMem(CodeMemBase);
ARMJIT_Global::DeInit();
}
void Compiler::LoadCycles()
@ -677,13 +690,13 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
{
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();
@ -696,7 +709,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
CPSRDirty = false;
if (hasMemInstr)
MOVP2R(RMemBase, Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start);
MOVP2R(RMemBase, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
for (int i = 0; i < instrsCount; i++)
{
@ -844,7 +857,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 +871,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 +885,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 +905,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 +950,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,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -19,9 +19,8 @@
#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,9 +29,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;
@ -69,7 +68,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,8 +97,8 @@ 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);
@ -114,7 +113,7 @@ public:
bool CanCompile(bool thumb, u16 kind);
bool FlagsNZNeeded() const
bool FlagsNZNeeded()
{
return CurInstr.SetFlags & 0xC;
}
@ -234,7 +233,7 @@ public:
return (u8*)entry - GetRXBase();
}
bool IsJITFault(const u8* pc);
bool IsJITFault(u8* pc);
u8* RewriteMemAccess(u8* pc);
void SwapCodeRegion()
@ -244,7 +243,6 @@ public:
OtherCodeRegion = offset;
}
melonDS::NDS& NDS;
ptrdiff_t OtherCodeRegion;
bool Exit;
@ -275,7 +273,6 @@ public:
void* JitRWStart;
void* JitRXStart;
#endif
void* CodeMemBase;
void* ReadBanked, *WriteBanked;
@ -290,5 +287,3 @@ public:
}
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -94,8 +94,3 @@ ARM_RestoreContext:
mov sp, x17
br x18
#if !defined(__APPLE__) && !defined(__WIN32__)
.section .note.GNU-stack,"",@progbits
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -21,14 +21,13 @@
#include "../ARMJIT.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,15 +56,15 @@ 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)
{
return false;
@ -80,7 +79,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 +111,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 (ARMJIT::LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -147,7 +146,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 = ARMJIT::LiteralOptimizations
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -186,18 +185,18 @@ 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 (ARMJIT::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]
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped];
// take a chance at fastmem
if (size > 8)
@ -226,7 +225,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);
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
PushRegs(false, false);
@ -264,7 +263,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 +275,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 +291,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 +303,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;
@ -333,7 +332,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 +452,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 (!ARMJIT::LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@ -495,11 +494,11 @@ 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 = ARMJIT::FastMemory
&& store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
{
s32 offset = decrement
@ -681,7 +680,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
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 +690,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;

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -19,8 +19,6 @@
#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 +27,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,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -24,17 +24,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 +69,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 +214,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 +228,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,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -20,225 +20,62 @@
#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

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -19,20 +19,16 @@
#ifndef ARMJIT_REGCACHE_H
#define ARMJIT_REGCACHE_H
#include "ARMJIT.h"
#include "ARMJIT_Internal.h"
#include "Platform.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
@ -84,7 +80,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 +95,7 @@ public:
LiteralsLoaded &= ~(1 << reg);
}
bool IsLiteral(int reg) const
bool IsLiteral(int reg)
{
return LiteralsLoaded & (1 << reg);
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -17,11 +17,10 @@
*/
#include "ARMJIT_Compiler.h"
#include "../ARM.h"
using namespace Gen;
namespace melonDS
namespace ARMJIT
{
// uses RSCRATCH3
@ -129,7 +128,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 +227,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();
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -17,12 +17,10 @@
*/
#include "ARMJIT_Compiler.h"
#include "../ARM.h"
#include "../NDS.h"
using namespace Gen;
namespace melonDS
namespace ARMJIT
{
template <typename T>
@ -121,7 +119,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 +131,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;
}
@ -176,9 +174,9 @@ 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);
@ -249,7 +247,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)));

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -18,22 +18,25 @@
#include "ARMJIT_Compiler.h"
#include "../ARMJIT.h"
#include "../ARMInterpreter.h"
#include "../NDS.h"
#include "../ARMJIT_Global.h"
#include <assert.h>
#include <stdarg.h>
#include "../dolphin/CommonFuncs.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
using namespace Gen;
using namespace Common;
extern "C" void ARM_Ret();
namespace melonDS
namespace ARMJIT
{
template <>
const X64Reg RegisterCache<Compiler, X64Reg>::NativeRegAllocOrder[] =
@ -137,7 +140,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;
@ -216,21 +219,46 @@ void Compiler::A_Comp_MSR()
MOV(32, R(ABI_PARAM3), R(RCPSR));
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
MOV(64, R(ABI_PARAM1), R(RCPU));
ABI_CallFunction(UpdateModeTrampoline);
CALL((void*)&UpdateModeTrampoline);
PopRegs(true, 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();
@ -444,13 +472,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);
@ -627,7 +648,7 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
};
#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 +664,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;
}
@ -660,7 +681,7 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
ABI_TailCall(ARM_Ret);
JMP((u8*)&ARM_Ret, true);
}
}
@ -690,13 +711,13 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
{
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;
@ -822,7 +843,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
ABI_TailCall(ARM_Ret);
JMP((u8*)ARM_Ret, true);
#ifdef JIT_PROFILING_ENABLED
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
@ -840,7 +861,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 +873,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,7 +885,7 @@ 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)
@ -889,7 +910,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
@ -934,7 +955,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,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -19,10 +19,9 @@
#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"
@ -32,12 +31,9 @@
#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,8 +79,7 @@ struct Op2
class Compiler : public Gen::XEmitter
{
public:
explicit Compiler(melonDS::NDS& nds);
~Compiler();
Compiler();
void Reset();
@ -93,7 +88,7 @@ public:
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)();
@ -172,7 +167,7 @@ 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 skipLoadingRn);
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
@ -235,7 +230,7 @@ public:
SetCodePtr(FarCode);
}
bool IsJITFault(const u8* addr);
bool IsJITFault(u8* addr);
u8* RewriteMemAccess(u8* pc);
@ -243,47 +238,44 @@ public:
void CreateMethod(const char* namefmt, void* start, ...);
#endif
melonDS::NDS& NDS;
u8* FarCode {};
u8* NearCode {};
u32 FarSize {};
u32 NearSize {};
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* CodeMemBase;
u8* ResetStart {};
u32 CodeMemSize {};
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,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team, RSDuck
Copyright 2016-2022 melonDS team, RSDuck
This file is part of melonDS.
@ -17,7 +17,7 @@
*/
#include "../ARM.h"
using namespace melonDS;
int main(int argc, char* argv[])
{
FILE* f = fopen("ARMJIT_Offsets.h", "w");

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -104,8 +104,3 @@ ARM_Ret:
#endif
ret
#if !defined(__APPLE__) && !defined(WIN64)
.section .note.GNU-stack,"",@progbits
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -17,12 +17,10 @@
*/
#include "ARMJIT_Compiler.h"
#include "../ARMJIT.h"
#include "../NDS.h"
using namespace Gen;
namespace melonDS
namespace ARMJIT
{
template <typename T>
@ -53,7 +51,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 %sx\n", pc);
abort();
}
@ -69,9 +67,9 @@ 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)
{
return false;
@ -86,7 +84,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)
{
@ -119,7 +117,7 @@ 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 (LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -136,7 +134,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 = LiteralOptimizations
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -197,10 +195,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 (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
{
if (rdMapped.IsImm())
{
@ -213,12 +211,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)
@ -269,7 +267,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
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)
{
@ -314,26 +312,26 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
MOV(32, R(ABI_PARAM1), R(RSCRATCH3));
if (flags & memop_Store)
{
switch (size | NDS.ConsoleType)
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,26 +343,26 @@ 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;
}
}
}
@ -384,7 +382,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)
{
@ -423,16 +421,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 = 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 +453,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;
@ -524,12 +522,12 @@ 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);
@ -628,12 +626,12 @@ 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<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));
@ -809,7 +807,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 (!LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}

View File

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

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -22,7 +22,7 @@
#include "ARMJIT.h"
namespace melonDS::ARMInstrInfo
namespace ARMInstrInfo
{
#define ak(x) ((x) << 23)
@ -315,7 +315,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,
@ -386,7 +386,7 @@ Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
{
if (res.Kind == tk_LDR_PCREL)
{
if (!literaloptimizations)
if (!ARMJIT::LiteralOptimizations)
res.SrcRegs |= 1 << 15;
res.SpecialKind = special_LoadLiteral;
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,7 +21,7 @@
#include "types.h"
namespace melonDS::ARMInstrInfo
namespace ARMInstrInfo
{
// Instruction kinds, for faster dispatch
@ -274,7 +274,7 @@ struct Info
}
};
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations);
Info Decode(bool thumb, u32 num, u32 instr);
}

View File

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

View File

@ -1,140 +0,0 @@
/*
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 MELONDS_ARGS_H
#define MELONDS_ARGS_H
#include <array>
#include <optional>
#include <memory>
#include "NDSCart.h"
#include "GBACart.h"
#include "types.h"
#include "MemConstants.h"
#include "DSi_NAND.h"
#include "FATStorage.h"
#include "FreeBIOS.h"
#include "GPU3D_Soft.h"
#include "SPI_Firmware.h"
#include "SPU.h"
namespace melonDS
{
namespace NDSCart { class CartCommon; }
namespace GBACart { class CartCommon; }
template<size_t N>
constexpr std::array<u8, N> BrokenBIOS = []() constexpr {
std::array<u8, N> broken {};
for (int i = 0; i < 16; i++)
{
broken[i*4+0] = 0xE7;
broken[i*4+1] = 0xFF;
broken[i*4+2] = 0xDE;
broken[i*4+3] = 0xFF;
}
return broken;
}();
/// Arguments that configure the JIT.
/// Ignored in builds that don't have the JIT included.
struct JITArgs
{
unsigned MaxBlockSize = 32;
bool LiteralOptimizations = true;
bool BranchOptimizations = true;
/// Ignored in builds that have fast memory excluded
/// (even if the JIT is otherwise available).
/// Enabled by default, but frontends should disable this when debugging
/// so the constants segfaults don't hinder debugging.
bool FastMemory = true;
};
using ARM9BIOSImage = std::array<u8, ARM9BIOSSize>;
using ARM7BIOSImage = std::array<u8, ARM7BIOSSize>;
using DSiBIOSImage = std::array<u8, DSiBIOSSize>;
struct GDBArgs
{
u16 PortARM7 = 0;
u16 PortARM9 = 0;
bool ARM7BreakOnStartup = false;
bool ARM9BreakOnStartup = false;
};
/// Arguments to pass into the NDS constructor.
/// New fields here should have default values if possible.
struct NDSArgs
{
/// NDS ARM9 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
/// NDS ARM7 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::unique_ptr<ARM7BIOSImage> ARM7BIOS = std::make_unique<ARM7BIOSImage>(bios_arm7_bin);
/// Firmware image to install.
/// Defaults to generated NDS firmware.
/// Generated firmware is not compatible with DSi mode.
melonDS::Firmware Firmware {0};
/// How the JIT should be configured when initializing.
/// Defaults to enabled, with default settings.
/// To disable the JIT, set this to std::nullopt.
/// Ignored in builds that don't have the JIT included.
std::optional<JITArgs> JIT = JITArgs();
AudioBitDepth BitDepth = AudioBitDepth::Auto;
AudioInterpolation Interpolation = AudioInterpolation::None;
/// How the GDB stub should be handled.
/// Defaults to disabled.
/// Ignored in builds that don't have the GDB stub included.
std::optional<GDBArgs> GDB = std::nullopt;
/// The 3D renderer to initialize the DS with.
/// Defaults to the software renderer.
/// Can be changed later at any time.
std::unique_ptr<melonDS::Renderer3D> Renderer3D = std::make_unique<SoftRenderer>();
};
/// Arguments to pass into the DSi constructor.
/// New fields here should have default values if possible.
/// Contains no virtual methods, so there's no vtable.
struct DSiArgs final : public NDSArgs
{
std::unique_ptr<DSiBIOSImage> ARM9iBIOS = std::make_unique<DSiBIOSImage>(BrokenBIOS<DSiBIOSSize>);
std::unique_ptr<DSiBIOSImage> ARM7iBIOS = std::make_unique<DSiBIOSImage>(BrokenBIOS<DSiBIOSSize>);
/// NAND image to install.
/// Required, there is no default value.
DSi_NAND::NANDImage NANDImage;
/// SD card to install.
/// Defaults to std::nullopt, which means no SD card.
std::optional<FATStorage> DSiSDCard;
bool FullBIOSBoot = false;
};
}
#endif //MELONDS_ARGS_H

View File

@ -15,7 +15,6 @@ add_library(core STATIC
CRC32.cpp
DMA.cpp
DMA_Timings.h
DMA_Timings.cpp
DSi.cpp
DSi_AES.cpp
DSi_Camera.cpp
@ -26,7 +25,6 @@ add_library(core STATIC
DSi_NWifi.cpp
DSi_SD.cpp
DSi_SPI_TSC.cpp
FATIO.cpp
FATStorage.cpp
FIFO.h
GBACart.cpp
@ -35,28 +33,22 @@ add_library(core STATIC
GPU2D_Soft.cpp
GPU3D.cpp
GPU3D_Soft.cpp
GPU3D_Texcache.cpp
GPU3D_Texcache.h
melonDLDI.h
NDS.cpp
NDSCart.cpp
NDSCartR4.cpp
Platform.h
ROMList.h
ROMList.cpp
FreeBIOS.h
FreeBIOS.cpp
RTC.cpp
Savestate.cpp
SPI.cpp
SPI_Firmware.cpp
SPU.cpp
types.h
Utils.cpp
Utils.h
version.h
Wifi.cpp
WifiAP.cpp
fatfs/diskio.c
fatfs/ff.c
fatfs/ffsystem.c
fatfs/ffunicode.c
@ -66,23 +58,11 @@ add_library(core STATIC
tiny-AES-c/aes.c
xxhash/xxhash.c)
if (ENABLE_GDBSTUB)
message(NOTICE "Enabling GDB stub")
target_sources(core PRIVATE
debug/GdbStub.cpp
debug/GdbProto.cpp
debug/GdbCmds.cpp
)
endif()
if (ENABLE_OGLRENDERER)
target_sources(core PRIVATE
GPU_OpenGL.cpp
GPU_OpenGL_shaders.h
GPU3D_OpenGL.cpp
GPU3D_Compute.cpp
GPU3D_TexcacheOpenGL.cpp
GPU3D_TexcacheOpenGL.h
GPU3D_OpenGL_shaders.h
OpenGLSupport.cpp)
@ -97,15 +77,8 @@ if (ENABLE_JIT)
ARMJIT.cpp
ARMJIT_Memory.cpp
ARMJIT_Global.cpp
dolphin/CommonFuncs.cpp)
if (WIN32)
# Required for memory mapping-related functions introduced in Windows 8
target_compile_definitions(core PRIVATE -D_WIN32_WINNT=_WIN32_WINNT_WIN8)
target_link_libraries(core PRIVATE onecore)
endif()
if (ARCHITECTURE STREQUAL x86_64)
target_sources(core PRIVATE
@ -134,42 +107,11 @@ if (ENABLE_JIT)
endif()
endif()
target_include_directories(core INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version")
option(MELONDS_EMBED_BUILD_INFO "Embed detailed build info into the binary" OFF)
set(MELONDS_GIT_BRANCH "$ENV{MELONDS_GIT_BRANCH}" CACHE STRING "The Git branch used for this build")
set(MELONDS_GIT_HASH "$ENV{MELONDS_GIT_HASH}" CACHE STRING "The hash of the Git commit")
set(MELONDS_BUILD_PROVIDER "$ENV{MELONDS_BUILD_PROVIDER}" CACHE STRING "The name of the provider of this build")
if (MELONDS_EMBED_BUILD_INFO)
target_compile_definitions(core PUBLIC MELONDS_EMBED_BUILD_INFO)
if (NOT MELONDS_GIT_BRANCH OR NOT MELONDS_GIT_HASH OR NOT MELONDS_BUILD_PROVIDER)
message(FATAL_ERROR "When embedding build information, all fields must be filled out. See src/CMakeLists.txt.")
endif()
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h")
target_include_directories(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(teakra EXCLUDE_FROM_ALL)
# Workaround for building teakra with -O0 on Windows either failing or hanging forever
target_compile_options(teakra PRIVATE "$<$<CONFIG:DEBUG>:-Og>")
target_link_libraries(core PRIVATE teakra)
if (NOT MSVC)
# MSVC has its own compiler flag syntax; if we ever support it,
# be sure to add equivalent flags here.
target_compile_options(core PUBLIC -fwrapv)
target_compile_options(core PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>")
# These warnings are excessive, and are only triggered in the ARMJIT code
# (which is fundamentally non-portable, so this is fine)
endif()
find_library(m MATH_LIBRARY)
if (MATH_LIBRARY)
@ -180,35 +122,17 @@ if (ENABLE_JIT)
target_compile_definitions(core PUBLIC JIT_ENABLED)
if (ENABLE_JIT_PROFILING)
include(../cmake/FindVTune.cmake)
include(cmake/FindVTune.cmake)
add_definitions(-DJIT_PROFILING_ENABLED)
endif()
endif()
if (WIN32)
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
target_compile_definitions(core PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX)
elseif(NOT APPLE AND NOT HAIKU)
check_library_exists(rt shm_open "" NEED_LIBRT)
if (NEED_LIBRT)
target_link_libraries(core PRIVATE rt)
endif()
elseif(HAIKU)
target_link_libraries(core PRIVATE network)
target_link_libraries(core PRIVATE ole32 comctl32 ws2_32)
elseif(NOT APPLE)
target_link_libraries(core PRIVATE rt)
endif()
if (ENABLE_JIT_PROFILING)
target_include_directories(core PRIVATE "${VTUNE_INCLUDE_DIR}")
target_link_libraries(core PRIVATE "${VTUNE_LIBRARY}")
target_link_libraries(core PRIVATE jitprofiling)
endif()
#if(CMAKE_BUILD_TYPE MATCHES "Debug")
# set(
# CMAKE_C_FLAGS
# "${CMAKE_C_FLAGS} -fsanitize=undefined -fsanitize=address"
# )
# target_link_options(core
# BEFORE PUBLIC -fsanitize=undefined PUBLIC -fsanitize=address
# )
#endif()

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,14 +21,11 @@
#include "NDS.h"
#include "DSi.h"
#include "ARM.h"
#include "Platform.h"
#include "ARMJIT_Memory.h"
#include "ARMJIT.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
#ifdef JIT_ENABLED
#include "ARMJIT.h"
#include "ARMJIT_Memory.h"
#endif
// access timing for cached regions
// this would be an average between cache hits and cache misses
@ -44,7 +41,6 @@ void ARMv5::CP15Reset()
CP15Control = 0x2078; // dunno
RNGSeed = 44203;
TraceProcessID = 0;
DTCMSetting = 0;
ITCMSetting = 0;
@ -125,7 +121,9 @@ void ARMv5::UpdateDTCMSetting()
if (newDTCMBase != DTCMBase || newDTCMMask != DTCMMask)
{
NDS.JIT.Memory.RemapDTCM(newDTCMBase, newDTCMSize);
#ifdef JIT_ENABLED
ARMJIT_Memory::RemapDTCM(newDTCMBase, newDTCMSize);
#endif
DTCMBase = newDTCMBase;
DTCMMask = newDTCMMask;
}
@ -187,14 +185,10 @@ void ARMv5::UpdatePURegion(u32 n)
return;
}
// notes:
// * min size of a pu region is 4KiB (12 bits)
// * size is calculated as size + 1, but the 12 lsb of address space are ignored, therefore we need it as size + 1 - 12, or size - 11
// * pu regions are aligned based on their size
u32 size = std::max((int)((rgn>>1) & 0x1F) - 11, 0); // obtain the size, subtract 11 and clamp to a min of 0.
u32 start = ((rgn >> 12) >> size) << size; // determine the start offset, and use shifts to force alignment with a multiple of the size.
u32 end = start + (1<<size); // add 1 left shifted by size to start to determine end point
// dont need to bounds check the end point because the force alignment inherently prevents it from breaking
u32 start = rgn >> 12;
u32 sz = 2 << ((rgn >> 1) & 0x1F);
u32 end = start + (sz >> 12);
// TODO: check alignment of start
u8 usermask = 0;
u8 privmask = 0;
@ -207,7 +201,7 @@ void ARMv5::UpdatePURegion(u32 n)
case 3: privmask |= 0x03; usermask |= 0x03; break;
case 5: privmask |= 0x01; break;
case 6: privmask |= 0x01; usermask |= 0x01; break;
default: Log(LogLevel::Warn, "!! BAD DATARW VALUE %d\n", datarw&0xF);
default: printf("!! BAD DATARW VALUE %d\n", datarw&0xF);
}
switch (coderw)
@ -218,7 +212,7 @@ void ARMv5::UpdatePURegion(u32 n)
case 3: privmask |= 0x04; usermask |= 0x04; break;
case 5: privmask |= 0x04; break;
case 6: privmask |= 0x04; usermask |= 0x04; break;
default: Log(LogLevel::Warn, "!! BAD CODERW VALUE %d\n", datarw&0xF);
default: printf("!! BAD CODERW VALUE %d\n", datarw&0xF);
}
if (datacache & 0x1)
@ -239,17 +233,8 @@ void ARMv5::UpdatePURegion(u32 n)
usermask |= 0x40;
}
Log(
LogLevel::Debug,
"PU region %d: %08X-%08X, user=%02X priv=%02X, %08X/%08X\n",
n,
start << 12,
(end << 12) - 1,
usermask,
privmask,
PU_DataRW,
PU_CodeRW
);
printf("PU region %d: %08X-%08X, user=%02X priv=%02X\n", n, start<<12, end<<12, usermask, privmask);
printf("%08X/%08X\n", PU_DataRW, PU_CodeRW);
for (u32 i = start; i < end; i++)
{
@ -300,7 +285,7 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend)
for (u32 i = addrstart; i < addrend; i++)
{
u8 pu = PU_Map[i];
u8* bustimings = NDS.ARM9MemTimings[i >> 2];
u8* bustimings = NDS::ARM9MemTimings[i >> 2];
if (pu & 0x40)
{
@ -308,7 +293,7 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend)
}
else
{
MemTimings[i][0] = bustimings[2] << NDS.ARM9ClockShift;
MemTimings[i][0] = bustimings[2] << NDS::ARM9ClockShift;
}
if (pu & 0x10)
@ -319,9 +304,9 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend)
}
else
{
MemTimings[i][1] = bustimings[0] << NDS.ARM9ClockShift;
MemTimings[i][2] = bustimings[2] << NDS.ARM9ClockShift;
MemTimings[i][3] = bustimings[3] << NDS.ARM9ClockShift;
MemTimings[i][1] = bustimings[0] << NDS::ARM9ClockShift;
MemTimings[i][2] = bustimings[2] << NDS::ARM9ClockShift;
MemTimings[i][3] = bustimings[3] << NDS::ARM9ClockShift;
}
}
}
@ -393,14 +378,14 @@ void ARMv5::ICacheLookup(u32 addr)
else
{
for (int i = 0; i < 32; i+=4)
*(u32*)&ptr[i] = NDS.ARM9Read32(addr+i);
*(u32*)&ptr[i] = NDS::ARM9Read32(addr+i);
}
ICacheTags[line] = tag;
// ouch :/
//printf("cache miss %08X: %d/%d\n", addr, NDS::ARM9MemTimings[addr >> 14][2], NDS::ARM9MemTimings[addr >> 14][3]);
CodeCycles = (NDS.ARM9MemTimings[addr >> 14][2] + (NDS.ARM9MemTimings[addr >> 14][3] * 7)) << NDS.ARM9ClockShift;
CodeCycles = (NDS::ARM9MemTimings[addr >> 14][2] + (NDS::ARM9MemTimings[addr >> 14][3] * 7)) << NDS::ARM9ClockShift;
CurICacheLine = ptr;
}
@ -458,7 +443,7 @@ void ARMv5::CP15Write(u32 id, u32 val)
{
UpdatePURegions((old & 0x1) != (val & 0x1));
}
if (val & (1<<7)) Log(LogLevel::Warn, "!!!! ARM9 BIG ENDIAN MODE. VERY BAD. SHIT GONNA ASPLODE NOW\n");
if (val & (1<<7)) printf("!!!! ARM9 BIG ENDIAN MODE. VERY BAD. SHIT GONNA ASPLODE NOW\n");
if (val & (1<<13)) ExceptionBase = 0xFFFF0000;
else ExceptionBase = 0x00000000;
}
@ -579,21 +564,11 @@ void ARMv5::CP15Write(u32 id, u32 val)
case 0x661:
case 0x670:
case 0x671:
char log_output[1024];
PU_Region[(id >> 4) & 0xF] = val;
std::snprintf(log_output,
sizeof(log_output),
"PU: region %d = %08X : %s, start: %08X size: %02X\n",
(id >> 4) & 0xF,
val,
val & 1 ? "enabled" : "disabled",
val & 0xFFFFF000,
(val & 0x3E) >> 1
);
Log(LogLevel::Debug, "%s", log_output);
// Some implementations of Log imply a newline, so we build up the line before printing it
printf("PU: region %d = %08X : ", (id>>4)&0xF, val);
printf("%s, ", val&1 ? "enabled":"disabled");
printf("%08X-", val&0xFFFFF000);
printf("%08X\n", (val&0xFFFFF000)+(2<<((val&0x3E)>>1)));
// TODO: smarter region update for this?
UpdatePURegions(true);
return;
@ -614,7 +589,7 @@ void ARMv5::CP15Write(u32 id, u32 val)
//Halt(255);
return;
case 0x752:
Log(LogLevel::Warn, "CP15: ICACHE INVALIDATE WEIRD. %08X\n", val);
printf("CP15: ICACHE INVALIDATE WEIRD. %08X\n", val);
//Halt(255);
return;
@ -644,10 +619,6 @@ void ARMv5::CP15Write(u32 id, u32 val)
UpdateITCMSetting();
return;
case 0xD01:
TraceProcessID = val;
return;
case 0xF00:
//printf("cache debug index register %08X\n", val);
return;
@ -674,10 +645,10 @@ void ARMv5::CP15Write(u32 id, u32 val)
return;
if ((id & 0xF00) != 0x700)
Log(LogLevel::Debug, "unknown CP15 write op %03X %08X\n", id, val);
printf("unknown CP15 write op %03X %08X\n", id, val);
}
u32 ARMv5::CP15Read(u32 id) const
u32 ARMv5::CP15Read(u32 id)
{
//printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]);
@ -765,15 +736,12 @@ u32 ARMv5::CP15Read(u32 id) const
return DTCMSetting;
case 0x911:
return ITCMSetting;
case 0xD01:
return TraceProcessID;
}
if ((id & 0xF00) == 0xF00) // test/debug shit?
return 0;
Log(LogLevel::Debug, "unknown CP15 read op %03X\n", id);
printf("unknown CP15 read op %03X\n", id);
return 0;
}
@ -935,7 +903,9 @@ void ARMv5::DataWrite8(u32 addr, u8 val)
{
DataCycles = 1;
*(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
#ifdef JIT_ENABLED
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
#endif
return;
}
if ((addr & DTCMMask) == DTCMBase)
@ -965,7 +935,9 @@ void ARMv5::DataWrite16(u32 addr, u16 val)
{
DataCycles = 1;
*(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
#ifdef JIT_ENABLED
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
#endif
return;
}
if ((addr & DTCMMask) == DTCMBase)
@ -995,7 +967,9 @@ void ARMv5::DataWrite32(u32 addr, u32 val)
{
DataCycles = 1;
*(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
#ifdef JIT_ENABLED
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
#endif
return;
}
if ((addr & DTCMMask) == DTCMBase)
@ -1018,7 +992,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
DataCycles += 1;
*(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
#ifdef JIT_ENABLED
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
#endif
return;
}
@ -1033,7 +1007,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
DataCycles += MemTimings[addr >> 12][3];
}
void ARMv5::GetCodeMemRegion(u32 addr, MemRegion* region)
void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region)
{
/*if (addr < ITCMSize)
{
@ -1042,7 +1016,6 @@ void ARMv5::GetCodeMemRegion(u32 addr, MemRegion* region)
return;
}*/
NDS.ARM9GetMemRegion(addr, false, &CodeMem);
GetMemRegion(addr, false, &CodeMem);
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -20,9 +20,10 @@
// http://www.codeproject.com/KB/recipes/crc32_large.aspx
namespace melonDS
{
constexpr u32 _reflect(u32 refl, char ch)
u32 crctable[256];
bool tableinited = false;
u32 _reflect(u32 refl, char ch)
{
u32 value = 0;
@ -36,33 +37,33 @@ constexpr u32 _reflect(u32 refl, char ch)
return value;
}
constexpr auto GetCRC32Table()
void _inittable()
{
std::array<u32, 256> Crc32Table { 0 };
u32 polynomial = 0x04C11DB7;
for (int i = 0; i < 0x100; i++)
{
Crc32Table[i] = _reflect(i, 8) << 24;
crctable[i] = _reflect(i, 8) << 24;
for (int j = 0; j < 8; j++)
Crc32Table[i] = (Crc32Table[i] << 1) ^ (Crc32Table[i] & (1 << 31) ? polynomial : 0);
crctable[i] = (crctable[i] << 1) ^ (crctable[i] & (1 << 31) ? polynomial : 0);
Crc32Table[i] = _reflect(Crc32Table[i], 32);
crctable[i] = _reflect(crctable[i], 32);
}
return Crc32Table;
}
u32 CRC32(const u8 *data, int len, u32 start)
u32 CRC32(u8 *data, int len, u32 start)
{
auto Crc32Table = GetCRC32Table();
if (!tableinited)
{
_inittable();
tableinited = true;
}
u32 crc = start ^ 0xFFFFFFFF;
while (len--)
crc = (crc >> 8) ^ Crc32Table[(crc & 0xFF) ^ *data++];
crc = (crc >> 8) ^ crctable[(crc & 0xFF) ^ *data++];
return (crc ^ 0xFFFFFFFF);
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -19,13 +19,8 @@
#ifndef CRC32_H
#define CRC32_H
#include <array>
#include "types.h"
namespace melonDS
{
u32 CRC32(const u8* data, int len, u32 start=0);
}
u32 CRC32(u8* data, int len, u32 start=0);
#endif // CRC32_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,14 +21,9 @@
#include "DSi.h"
#include "DMA.h"
#include "GPU.h"
#include "GPU3D.h"
#include "DMA_Timings.h"
#include "Platform.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
// DMA TIMINGS
//
@ -50,17 +45,21 @@ using Platform::LogLevel;
// TODO: timings are nonseq when address is fixed/decrementing
DMA::DMA(u32 cpu, u32 num, melonDS::NDS& nds) :
CPU(cpu),
Num(num),
NDS(nds)
DMA::DMA(u32 cpu, u32 num)
{
CPU = cpu;
Num = num;
if (cpu == 0)
CountMask = 0x001FFFFF;
else
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
}
DMA::~DMA()
{
}
void DMA::Reset()
{
SrcAddr = 0;
@ -78,10 +77,8 @@ void DMA::Reset()
Stall = false;
Running = false;
Executing = false;
InProgress = false;
MRAMBurstCount = 0;
MRAMBurstTable = DMATiming::MRAMDummy;
}
void DMA::DoSavestate(Savestate* file)
@ -106,10 +103,6 @@ void DMA::DoSavestate(Savestate* file)
file->Bool32(&InProgress);
file->Bool32(&IsGXFIFODMA);
file->Var32(&MRAMBurstCount);
file->Bool32(&Executing);
file->Bool32(&Stall);
file->VarArray(MRAMBurstTable.data(), sizeof(MRAMBurstTable));
}
void DMA::WriteCnt(u32 val)
@ -146,10 +139,10 @@ void DMA::WriteCnt(u32 val)
if ((StartMode & 0x7) == 0)
Start();
else if (StartMode == 0x07)
NDS.GPU.GPU3D.CheckFIFODMA();
GPU3D::CheckFIFODMA();
if (StartMode==0x06 || StartMode==0x13)
Log(LogLevel::Warn, "UNIMPLEMENTED ARM%d DMA%d START MODE %02X, %08X->%08X\n", CPU?7:9, Num, StartMode, SrcAddr, DstAddr);
printf("UNIMPLEMENTED ARM%d DMA%d START MODE %02X, %08X->%08X\n", CPU?7:9, Num, StartMode, SrcAddr, DstAddr);
}
}
@ -193,7 +186,7 @@ void DMA::Start()
MRAMBurstTable = DMATiming::MRAMDummy;
InProgress = true;
NDS.StopCPU(CPU, 1<<Num);
NDS::StopCPU(CPU, 1<<Num);
}
u32 DMA::UnitTimings9_16(bool burststart)
@ -201,18 +194,18 @@ u32 DMA::UnitTimings9_16(bool burststart)
u32 src_id = CurSrcAddr >> 14;
u32 dst_id = CurDstAddr >> 14;
u32 src_rgn = NDS.ARM9Regions[src_id];
u32 dst_rgn = NDS.ARM9Regions[dst_id];
u32 src_rgn = NDS::ARM9Regions[src_id];
u32 dst_rgn = NDS::ARM9Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS.ARM9MemTimings[src_id][4];
src_s = NDS.ARM9MemTimings[src_id][5];
dst_n = NDS.ARM9MemTimings[dst_id][4];
dst_s = NDS.ARM9MemTimings[dst_id][5];
src_n = NDS::ARM9MemTimings[src_id][4];
src_s = NDS::ARM9MemTimings[src_id][5];
dst_n = NDS::ARM9MemTimings[dst_id][4];
dst_s = NDS::ARM9MemTimings[dst_id][5];
if (src_rgn == Mem9_MainRAM)
if (src_rgn == NDS::Mem9_MainRAM)
{
if (dst_rgn == Mem9_MainRAM)
if (dst_rgn == NDS::Mem9_MainRAM)
return 16;
if (SrcAddrInc > 0)
@ -221,7 +214,7 @@ u32 DMA::UnitTimings9_16(bool burststart)
{
MRAMBurstCount = 0;
if (dst_rgn == Mem9_GBAROM)
if (dst_rgn == NDS::Mem9_GBAROM)
{
if (dst_s == 4)
MRAMBurstTable = DMATiming::MRAMRead16Bursts[1];
@ -242,7 +235,7 @@ u32 DMA::UnitTimings9_16(bool burststart)
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == Mem9_MainRAM)
else if (dst_rgn == NDS::Mem9_MainRAM)
{
if (DstAddrInc > 0)
{
@ -250,7 +243,7 @@ u32 DMA::UnitTimings9_16(bool burststart)
{
MRAMBurstCount = 0;
if (src_rgn == Mem9_GBAROM)
if (src_rgn == NDS::Mem9_GBAROM)
{
if (src_s == 4)
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1];
@ -287,18 +280,18 @@ u32 DMA::UnitTimings9_32(bool burststart)
u32 src_id = CurSrcAddr >> 14;
u32 dst_id = CurDstAddr >> 14;
u32 src_rgn = NDS.ARM9Regions[src_id];
u32 dst_rgn = NDS.ARM9Regions[dst_id];
u32 src_rgn = NDS::ARM9Regions[src_id];
u32 dst_rgn = NDS::ARM9Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS.ARM9MemTimings[src_id][6];
src_s = NDS.ARM9MemTimings[src_id][7];
dst_n = NDS.ARM9MemTimings[dst_id][6];
dst_s = NDS.ARM9MemTimings[dst_id][7];
src_n = NDS::ARM9MemTimings[src_id][6];
src_s = NDS::ARM9MemTimings[src_id][7];
dst_n = NDS::ARM9MemTimings[dst_id][6];
dst_s = NDS::ARM9MemTimings[dst_id][7];
if (src_rgn == Mem9_MainRAM)
if (src_rgn == NDS::Mem9_MainRAM)
{
if (dst_rgn == Mem9_MainRAM)
if (dst_rgn == NDS::Mem9_MainRAM)
return 18;
if (SrcAddrInc > 0)
@ -307,7 +300,7 @@ u32 DMA::UnitTimings9_32(bool burststart)
{
MRAMBurstCount = 0;
if (dst_rgn == Mem9_GBAROM)
if (dst_rgn == NDS::Mem9_GBAROM)
{
if (dst_s == 8)
MRAMBurstTable = DMATiming::MRAMRead32Bursts[2];
@ -330,7 +323,7 @@ u32 DMA::UnitTimings9_32(bool burststart)
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == Mem9_MainRAM)
else if (dst_rgn == NDS::Mem9_MainRAM)
{
if (DstAddrInc > 0)
{
@ -338,7 +331,7 @@ u32 DMA::UnitTimings9_32(bool burststart)
{
MRAMBurstCount = 0;
if (src_rgn == Mem9_GBAROM)
if (src_rgn == NDS::Mem9_GBAROM)
{
if (src_s == 8)
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2];
@ -379,18 +372,18 @@ u32 DMA::UnitTimings7_16(bool burststart)
u32 src_id = CurSrcAddr >> 15;
u32 dst_id = CurDstAddr >> 15;
u32 src_rgn = NDS.ARM7Regions[src_id];
u32 dst_rgn = NDS.ARM7Regions[dst_id];
u32 src_rgn = NDS::ARM7Regions[src_id];
u32 dst_rgn = NDS::ARM7Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS.ARM7MemTimings[src_id][0];
src_s = NDS.ARM7MemTimings[src_id][1];
dst_n = NDS.ARM7MemTimings[dst_id][0];
dst_s = NDS.ARM7MemTimings[dst_id][1];
src_n = NDS::ARM7MemTimings[src_id][0];
src_s = NDS::ARM7MemTimings[src_id][1];
dst_n = NDS::ARM7MemTimings[dst_id][0];
dst_s = NDS::ARM7MemTimings[dst_id][1];
if (src_rgn == Mem7_MainRAM)
if (src_rgn == NDS::Mem7_MainRAM)
{
if (dst_rgn == Mem7_MainRAM)
if (dst_rgn == NDS::Mem7_MainRAM)
return 16;
if (SrcAddrInc > 0)
@ -399,7 +392,7 @@ u32 DMA::UnitTimings7_16(bool burststart)
{
MRAMBurstCount = 0;
if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1)
if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1)
{
if (dst_s == 4)
MRAMBurstTable = DMATiming::MRAMRead16Bursts[1];
@ -420,7 +413,7 @@ u32 DMA::UnitTimings7_16(bool burststart)
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == Mem7_MainRAM)
else if (dst_rgn == NDS::Mem7_MainRAM)
{
if (DstAddrInc > 0)
{
@ -428,7 +421,7 @@ u32 DMA::UnitTimings7_16(bool burststart)
{
MRAMBurstCount = 0;
if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1)
if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1)
{
if (src_s == 4)
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1];
@ -465,18 +458,18 @@ u32 DMA::UnitTimings7_32(bool burststart)
u32 src_id = CurSrcAddr >> 15;
u32 dst_id = CurDstAddr >> 15;
u32 src_rgn = NDS.ARM7Regions[src_id];
u32 dst_rgn = NDS.ARM7Regions[dst_id];
u32 src_rgn = NDS::ARM7Regions[src_id];
u32 dst_rgn = NDS::ARM7Regions[dst_id];
u32 src_n, src_s, dst_n, dst_s;
src_n = NDS.ARM7MemTimings[src_id][2];
src_s = NDS.ARM7MemTimings[src_id][3];
dst_n = NDS.ARM7MemTimings[dst_id][2];
dst_s = NDS.ARM7MemTimings[dst_id][3];
src_n = NDS::ARM7MemTimings[src_id][2];
src_s = NDS::ARM7MemTimings[src_id][3];
dst_n = NDS::ARM7MemTimings[dst_id][2];
dst_s = NDS::ARM7MemTimings[dst_id][3];
if (src_rgn == Mem7_MainRAM)
if (src_rgn == NDS::Mem7_MainRAM)
{
if (dst_rgn == Mem7_MainRAM)
if (dst_rgn == NDS::Mem7_MainRAM)
return 18;
if (SrcAddrInc > 0)
@ -485,7 +478,7 @@ u32 DMA::UnitTimings7_32(bool burststart)
{
MRAMBurstCount = 0;
if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1)
if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1)
{
if (dst_s == 8)
MRAMBurstTable = DMATiming::MRAMRead32Bursts[2];
@ -508,7 +501,7 @@ u32 DMA::UnitTimings7_32(bool burststart)
(burststart ? dst_n : dst_s);
}
}
else if (dst_rgn == Mem7_MainRAM)
else if (dst_rgn == NDS::Mem7_MainRAM)
{
if (DstAddrInc > 0)
{
@ -516,7 +509,7 @@ u32 DMA::UnitTimings7_32(bool burststart)
{
MRAMBurstCount = 0;
if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1)
if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1)
{
if (src_s == 8)
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2];
@ -550,9 +543,10 @@ u32 DMA::UnitTimings7_32(bool burststart)
}
}
template <int ConsoleType>
void DMA::Run9()
{
if (NDS.ARM9Timestamp >= NDS.ARM9Target) return;
if (NDS::ARM9Timestamp >= NDS::ARM9Target) return;
Executing = true;
@ -564,34 +558,40 @@ void DMA::Run9()
{
while (IterCount > 0 && !Stall)
{
NDS.ARM9Timestamp += (UnitTimings9_16(burststart) << NDS.ARM9ClockShift);
NDS::ARM9Timestamp += (UnitTimings9_16(burststart) << NDS::ARM9ClockShift);
burststart = false;
NDS.ARM9Write16(CurDstAddr, NDS.ARM9Read16(CurSrcAddr));
if (ConsoleType == 1)
DSi::ARM9Write16(CurDstAddr, DSi::ARM9Read16(CurSrcAddr));
else
NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1;
IterCount--;
RemCount--;
if (NDS.ARM9Timestamp >= NDS.ARM9Target) break;
if (NDS::ARM9Timestamp >= NDS::ARM9Target) break;
}
}
else
{
while (IterCount > 0 && !Stall)
{
NDS.ARM9Timestamp += (UnitTimings9_32(burststart) << NDS.ARM9ClockShift);
NDS::ARM9Timestamp += (UnitTimings9_32(burststart) << NDS::ARM9ClockShift);
burststart = false;
NDS.ARM9Write32(CurDstAddr, NDS.ARM9Read32(CurSrcAddr));
if (ConsoleType == 1)
DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr));
else
NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
if (NDS.ARM9Timestamp >= NDS.ARM9Target) break;
if (NDS::ARM9Timestamp >= NDS::ARM9Target) break;
}
}
@ -603,10 +603,10 @@ void DMA::Run9()
if (IterCount == 0)
{
Running = 0;
NDS.ResumeCPU(0, 1<<Num);
NDS::ResumeCPU(0, 1<<Num);
if (StartMode == 0x07)
NDS.GPU.GPU3D.CheckFIFODMA();
GPU3D::CheckFIFODMA();
}
return;
@ -616,16 +616,17 @@ void DMA::Run9()
Cnt &= ~(1<<31);
if (Cnt & (1<<30))
NDS.SetIRQ(0, IRQ_DMA0 + Num);
NDS::SetIRQ(0, NDS::IRQ_DMA0 + Num);
Running = 0;
InProgress = false;
NDS.ResumeCPU(0, 1<<Num);
NDS::ResumeCPU(0, 1<<Num);
}
template <int ConsoleType>
void DMA::Run7()
{
if (NDS.ARM7Timestamp >= NDS.ARM7Target) return;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) return;
Executing = true;
@ -637,34 +638,40 @@ void DMA::Run7()
{
while (IterCount > 0 && !Stall)
{
NDS.ARM7Timestamp += UnitTimings7_16(burststart);
NDS::ARM7Timestamp += UnitTimings7_16(burststart);
burststart = false;
NDS.ARM7Write16(CurDstAddr, NDS.ARM7Read16(CurSrcAddr));
if (ConsoleType == 1)
DSi::ARM7Write16(CurDstAddr, DSi::ARM7Read16(CurSrcAddr));
else
NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1;
IterCount--;
RemCount--;
if (NDS.ARM7Timestamp >= NDS.ARM7Target) break;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) break;
}
}
else
{
while (IterCount > 0 && !Stall)
{
NDS.ARM7Timestamp += UnitTimings7_32(burststart);
NDS::ARM7Timestamp += UnitTimings7_32(burststart);
burststart = false;
NDS.ARM7Write32(CurDstAddr, NDS.ARM7Read32(CurSrcAddr));
if (ConsoleType == 1)
DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr));
else
NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
if (NDS.ARM7Timestamp >= NDS.ARM7Target) break;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) break;
}
}
@ -676,7 +683,7 @@ void DMA::Run7()
if (IterCount == 0)
{
Running = 0;
NDS.ResumeCPU(1, 1<<Num);
NDS::ResumeCPU(1, 1<<Num);
}
return;
@ -686,18 +693,20 @@ void DMA::Run7()
Cnt &= ~(1<<31);
if (Cnt & (1<<30))
NDS.SetIRQ(1, IRQ_DMA0 + Num);
NDS::SetIRQ(1, NDS::IRQ_DMA0 + Num);
Running = 0;
InProgress = false;
NDS.ResumeCPU(1, 1<<Num);
NDS::ResumeCPU(1, 1<<Num);
}
template <int ConsoleType>
void DMA::Run()
{
if (!Running) return;
if (CPU == 0) return Run9();
else return Run7();
if (CPU == 0) return Run9<ConsoleType>();
else return Run7<ConsoleType>();
}
}
template void DMA::Run<0>();
template void DMA::Run<1>();

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -19,19 +19,14 @@
#ifndef DMA_H
#define DMA_H
#include <array>
#include "types.h"
namespace melonDS
{
class NDS;
class Savestate;
#include "Savestate.h"
class DMA
{
public:
DMA(u32 cpu, u32 num, NDS& nds);
~DMA() = default;
DMA(u32 cpu, u32 num);
~DMA();
void Reset();
@ -45,16 +40,20 @@ public:
u32 UnitTimings7_16(bool burststart);
u32 UnitTimings7_32(bool burststart);
template <int ConsoleType>
void Run();
template <int ConsoleType>
void Run9();
template <int ConsoleType>
void Run7();
bool IsInMode(u32 mode) const noexcept
bool IsInMode(u32 mode)
{
return ((mode == StartMode) && (Cnt & 0x80000000));
}
bool IsRunning() const noexcept { return Running!=0; }
bool IsRunning() { return Running!=0; }
void StartIfNeeded(u32 mode)
{
@ -73,35 +72,32 @@ public:
if (Executing) Stall = true;
}
u32 SrcAddr {};
u32 DstAddr {};
u32 Cnt {};
u32 SrcAddr;
u32 DstAddr;
u32 Cnt;
private:
melonDS::NDS& NDS;
u32 CPU {};
u32 Num {};
u32 CPU, Num;
u32 StartMode {};
u32 CurSrcAddr {};
u32 CurDstAddr {};
u32 RemCount {};
u32 IterCount {};
s32 SrcAddrInc {};
s32 DstAddrInc {};
u32 CountMask {};
u32 StartMode;
u32 CurSrcAddr;
u32 CurDstAddr;
u32 RemCount;
u32 IterCount;
s32 SrcAddrInc;
s32 DstAddrInc;
u32 CountMask;
u32 Running {};
bool InProgress {};
u32 Running;
bool InProgress;
bool Executing {};
bool Stall {};
bool Executing;
bool Stall;
bool IsGXFIFODMA {};
bool IsGXFIFODMA;
u32 MRAMBurstCount {};
std::array<u8, 256> MRAMBurstTable;
u32 MRAMBurstCount;
const u8* MRAMBurstTable;
};
}
#endif

View File

@ -1,243 +0,0 @@
/*
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 "DMA_Timings.h"
#include "types.h"
namespace melonDS::DMATiming
{
// DMA timing tables
//
// DMA timings on the DS are normally straightforward, except in one case: when
// main RAM is involved.
// Main RAM to main RAM is the easy case: 16c/unit in 16bit mode, 18c/unit in 32bit
// mode.
// It gets more complicated when transferring from main RAM to somewhere else, or
// vice versa: main RAM supports burst accesses, but the rules dictating how long
// bursts can be are weird and inconsistent. Main RAM also supports parallel
// memory operations, to some extent.
// I haven't figured out the full logic behind it, let alone how to emulate it
// efficiently, so for now we will use these tables.
// A zero denotes the end of a burst pattern.
//
// Note: burst patterns only apply when the main RAM address is incrementing.
// A fixed or decrementing address results in nonsequential accesses.
//
// Note about GBA slot/wifi timings: these take into account the sequential timing
// setting. Timings are such that the nonseq setting only matters for the first
// access, and minor edge cases (like the last of a 0x20000-byte block).
extern const std::array<u8, 256> MRAMDummy = {0};
extern const std::array<u8, 256> MRAMRead16Bursts[] =
{
// main RAM to regular 16bit or 32bit bus (similar)
{7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3,
0},
// main RAM to GBA/wifi, seq=4
{8, 6, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
0},
// main RAM to GBA/wifi, seq=6
{10, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8,
0},
};
extern const std::array<u8, 256> MRAMRead32Bursts[] =
{
// main RAM to regular 16bit bus
{9, 4, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 9,
0},
// main RAM to regular 32bit bus
{9, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2,
0},
// main RAM to GBA/wifi, seq=4
{14, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13,
0},
// main RAM to GBA/wifi, seq=6
{18, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17,
0},
};
extern const std::array<u8, 256> MRAMWrite16Bursts[] =
{
// regular 16bit or 32bit bus to main RAM (similar)
{8, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
0},
// GBA/wifi to main RAM, seq=4
{10, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
0},
// GBA/wifi to main RAM, seq=6
{9, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7,
0},
};
extern const std::array<u8, 256> MRAMWrite32Bursts[4] =
{
// regular 16bit bus to main RAM
{9, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
0},
// regular 32bit bus to main RAM
{9, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
0},
// GBA/wifi to main RAM, seq=4
{15, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10,
0},
// GBA/wifi to main RAM, seq=6
{16, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14,
0},
};
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -19,10 +19,9 @@
#ifndef DMA_TIMINGS_H
#define DMA_TIMINGS_H
#include <array>
#include "types.h"
namespace melonDS::DMATiming
namespace DMATiming
{
// DMA timing tables
@ -46,15 +45,202 @@ namespace melonDS::DMATiming
// setting. Timings are such that the nonseq setting only matters for the first
// access, and minor edge cases (like the last of a 0x20000-byte block).
extern const std::array<u8, 256> MRAMDummy;
constexpr u8 MRAMDummy[1] = {0};
extern const std::array<u8, 256> MRAMRead16Bursts[3];
constexpr u8 MRAMRead16Bursts[][256] =
{
// main RAM to regular 16bit or 32bit bus (similar)
{7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3,
0},
// main RAM to GBA/wifi, seq=4
{8, 6, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
0},
// main RAM to GBA/wifi, seq=6
{10, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8,
0},
};
extern const std::array<u8, 256> MRAMRead32Bursts[4];
constexpr u8 MRAMRead32Bursts[][256] =
{
// main RAM to regular 16bit bus
{9, 4, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 9,
0},
// main RAM to regular 32bit bus
{9, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2,
0},
// main RAM to GBA/wifi, seq=4
{14, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13,
0},
// main RAM to GBA/wifi, seq=6
{18, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17,
0},
};
extern const std::array<u8, 256> MRAMWrite16Bursts[3];
constexpr u8 MRAMWrite16Bursts[][256] =
{
// regular 16bit or 32bit bus to main RAM (similar)
{8, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
0},
// GBA/wifi to main RAM, seq=4
{10, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
0},
// GBA/wifi to main RAM, seq=6
{9, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7,
0},
};
extern const std::array<u8, 256> MRAMWrite32Bursts[4];
constexpr u8 MRAMWrite32Bursts[][256] =
{
// regular 16bit bus to main RAM
{9, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
0},
// regular 32bit bus to main RAM
{9, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
0},
// GBA/wifi to main RAM, seq=4
{15, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10,
0},
// GBA/wifi to main RAM, seq=6
{16, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14,
0},
};
}

File diff suppressed because it is too large Load Diff

248
src/DSi.h
View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -20,168 +20,98 @@
#define DSI_H
#include "NDS.h"
#include "DSi_NDMA.h"
#include "DSi_SD.h"
#include "DSi_DSP.h"
#include "DSi_AES.h"
#include "DSi_Camera.h"
#include "DSi_NAND.h"
namespace melonDS
namespace DSi
{
class DSi_I2CHost;
class DSi_CamModule;
class DSi_AES;
class DSi_DSP;
class DSiArgs;
namespace DSi_NAND
{
class NANDImage;
extern u16 SCFG_BIOS;
extern u16 SCFG_Clock9;
extern u32 SCFG_EXT[2];
extern u8 ARM9iBIOS[0x10000];
extern u8 ARM7iBIOS[0x10000];
extern u8 eMMC_CID[16];
extern u64 ConsoleID;
extern DSi_SDHost* SDMMC;
extern DSi_SDHost* SDIO;
const u32 NWRAMSize = 0x40000;
extern u8* NWRAM_A;
extern u8* NWRAM_B;
extern u8* NWRAM_C;
extern u8* NWRAMMap_A[2][4];
extern u8* NWRAMMap_B[3][8];
extern u8* NWRAMMap_C[3][8];
extern u32 NWRAMStart[2][3];
extern u32 NWRAMEnd[2][3];
extern u32 NWRAMMask[2][3];
bool Init();
void DeInit();
void Reset();
void Stop();
void DoSavestate(Savestate* file);
void SetCartInserted(bool inserted);
void SetupDirectBoot();
void SoftReset();
bool LoadBIOS();
bool LoadNAND();
void RunNDMAs(u32 cpu);
void StallNDMAs();
bool NDMAsInMode(u32 cpu, u32 mode);
bool NDMAsRunning(u32 cpu);
void CheckNDMAs(u32 cpu, u32 mode);
void StopNDMAs(u32 cpu, u32 mode);
void MapNWRAM_A(u32 num, u8 val);
void MapNWRAM_B(u32 num, u8 val);
void MapNWRAM_C(u32 num, u8 val);
void MapNWRAMRange(u32 cpu, u32 num, u32 val);
u8 ARM9Read8(u32 addr);
u16 ARM9Read16(u32 addr);
u32 ARM9Read32(u32 addr);
void ARM9Write8(u32 addr, u8 val);
void ARM9Write16(u32 addr, u16 val);
void ARM9Write32(u32 addr, u32 val);
bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region);
u8 ARM7Read8(u32 addr);
u16 ARM7Read16(u32 addr);
u32 ARM7Read32(u32 addr);
void ARM7Write8(u32 addr, u8 val);
void ARM7Write16(u32 addr, u16 val);
void ARM7Write32(u32 addr, u32 val);
bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region);
u8 ARM9IORead8(u32 addr);
u16 ARM9IORead16(u32 addr);
u32 ARM9IORead32(u32 addr);
void ARM9IOWrite8(u32 addr, u8 val);
void ARM9IOWrite16(u32 addr, u16 val);
void ARM9IOWrite32(u32 addr, u32 val);
u8 ARM7IORead8(u32 addr);
u16 ARM7IORead16(u32 addr);
u32 ARM7IORead32(u32 addr);
void ARM7IOWrite8(u32 addr, u8 val);
void ARM7IOWrite16(u32 addr, u16 val);
void ARM7IOWrite32(u32 addr, u32 val);
}
class DSi final : public NDS
{
protected:
void DoSavestateExtra(Savestate* file) override;
public:
u16 SCFG_BIOS;
u16 SCFG_Clock9;
u32 SCFG_EXT[2];
std::array<u8, DSiBIOSSize> ARM9iBIOS;
std::array<u8, DSiBIOSSize> ARM7iBIOS;
DSi_SDHost SDMMC;
DSi_SDHost SDIO;
const u32 NWRAMSize = 0x40000;
u8* NWRAM_A;
u8* NWRAM_B;
u8* NWRAM_C;
u8* NWRAMMap_A[2][4];
u8* NWRAMMap_B[3][8];
u8* NWRAMMap_C[3][8];
u32 NWRAMStart[2][3];
u32 NWRAMEnd[2][3];
u32 NWRAMMask[2][3];
DSi_I2CHost I2C;
DSi_CamModule CamModule;
DSi_AES AES;
DSi_DSP DSP;
void Reset() override;
void Stop(Platform::StopReason reason) override;
bool DoSavestate(Savestate* file);
void SetCartInserted(bool inserted);
void SetupDirectBoot() override;
void SoftReset();
bool LoadNAND();
void RunNDMAs(u32 cpu);
void StallNDMAs();
bool NDMAsInMode(u32 cpu, u32 mode) const;
bool NDMAsRunning(u32 cpu) const;
void CheckNDMAs(u32 cpu, u32 mode);
void StopNDMAs(u32 cpu, u32 mode);
void MapNWRAM_A(u32 num, u8 val);
void MapNWRAM_B(u32 num, u8 val);
void MapNWRAM_C(u32 num, u8 val);
void MapNWRAMRange(u32 cpu, u32 num, u32 val);
u8 ARM9Read8(u32 addr) override;
u16 ARM9Read16(u32 addr) override;
u32 ARM9Read32(u32 addr) override;
void ARM9Write8(u32 addr, u8 val) override;
void ARM9Write16(u32 addr, u16 val) override;
void ARM9Write32(u32 addr, u32 val) override;
bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) override;
u8 ARM7Read8(u32 addr) override;
u16 ARM7Read16(u32 addr) override;
u32 ARM7Read32(u32 addr) override;
void ARM7Write8(u32 addr, u8 val) override;
void ARM7Write16(u32 addr, u16 val) override;
void ARM7Write32(u32 addr, u32 val) override;
bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) override;
u8 ARM9IORead8(u32 addr) override;
u16 ARM9IORead16(u32 addr) override;
u32 ARM9IORead32(u32 addr) override;
void ARM9IOWrite8(u32 addr, u8 val) override;
void ARM9IOWrite16(u32 addr, u16 val) override;
void ARM9IOWrite32(u32 addr, u32 val) override;
u8 ARM7IORead8(u32 addr) override;
u16 ARM7IORead16(u32 addr) override;
u32 ARM7IORead32(u32 addr) override;
void ARM7IOWrite8(u32 addr, u8 val) override;
void ARM7IOWrite16(u32 addr, u16 val) override;
void ARM7IOWrite32(u32 addr, u32 val) override;
public:
DSi(DSiArgs&& args, void* userdata = nullptr) noexcept;
//DSi() noexcept;
~DSi() noexcept override;
DSi(const DSi&) = delete;
DSi& operator=(const DSi&) = delete;
DSi(DSi&&) = delete;
DSi& operator=(DSi&&) = delete;
void SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart) override;
std::unique_ptr<NDSCart::CartCommon> EjectCart() override;
bool NeedsDirectBoot() const override
{
// for now, DSi mode requires original BIOS/NAND
return false;
}
[[nodiscard]] const DSi_NAND::NANDImage& GetNAND() const noexcept { return *SDMMC.GetNAND(); }
[[nodiscard]] DSi_NAND::NANDImage& GetNAND() noexcept { return *SDMMC.GetNAND(); }
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { SDMMC.SetNAND(std::move(nand)); }
u64 GetConsoleID() const noexcept { return SDMMC.GetNAND()->GetConsoleID(); }
[[nodiscard]] const FATStorage* GetSDCard() const noexcept { return SDMMC.GetSDCard(); }
void SetSDCard(FATStorage&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) override;
bool DMAsInMode(u32 cpu, u32 mode) const override;
bool DMAsRunning(u32 cpu) const override;
void StopDMAs(u32 cpu, u32 mode) override;
void CheckDMAs(u32 cpu, u32 mode) override;
u16 SCFG_Clock7;
u32 SCFG_MC;
u16 SCFG_RST;
u32 MBK[2][9];
u32 NDMACnt[2];
std::array<DSi_NDMA, 8> NDMAs;
// FIXME: these currently have no effect (and aren't stored in a savestate)
// ... not that they matter all that much
u8 GPIO_Data;
u8 GPIO_Dir;
u8 GPIO_IEdgeSel;
u8 GPIO_IE;
u8 GPIO_WiFi;
bool GetFullBIOSBoot() const noexcept { return FullBIOSBoot; }
void SetFullBIOSBoot(bool full) noexcept { FullBIOSBoot = full; }
private:
bool FullBIOSBoot;
void Set_SCFG_Clock9(u16 val);
void Set_SCFG_MC(u32 val);
void DecryptModcryptArea(u32 offset, u32 size, const u8* iv);
void ApplyNewRAMSize(u32 size);
};
}
#endif // DSI_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -19,15 +19,69 @@
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_NAND.h"
#include "DSi_AES.h"
#include "FIFO.h"
#include "tiny-AES-c/aes.hpp"
#include "Platform.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
namespace DSi_AES
{
u32 Cnt;
u32 BlkCnt;
u32 RemExtra;
u32 RemBlocks;
bool OutputFlush;
u32 InputDMASize, OutputDMASize;
u32 AESMode;
FIFO<u32, 16> InputFIFO;
FIFO<u32, 16> OutputFIFO;
u8 IV[16];
u8 MAC[16];
u8 KeyNormal[4][16];
u8 KeyX[4][16];
u8 KeyY[4][16];
u8 CurKey[16];
u8 CurMAC[16];
// output MAC for CCM encrypt
u8 OutputMAC[16];
bool OutputMACDue;
AES_ctx Ctx;
void Swap16(u8* dst, u8* src)
{
for (int i = 0; i < 16; i++)
dst[i] = src[15-i];
}
void ROL16(u8* val, u32 n)
{
u32 n_coarse = n >> 3;
u32 n_fine = n & 7;
u8 tmp[16];
for (u32 i = 0; i < 16; i++)
{
tmp[i] = val[(i - n_coarse) & 0xF];
}
for (u32 i = 0; i < 16; i++)
{
val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine));
}
}
#define _printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); }
#define _printhex2(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); }
@ -36,17 +90,19 @@ using Platform::LogLevel;
#define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); }
DSi_AES::DSi_AES(melonDS::DSi& dsi) : DSi(dsi)
bool Init()
{
const u8 zero[16] = {0};
AES_init_ctx_iv(&Ctx, zero, zero);
return true;
}
DSi_AES::~DSi_AES()
void DeInit()
{
}
void DSi_AES::Reset()
void Reset()
{
Cnt = 0;
@ -78,7 +134,6 @@ void DSi_AES::Reset()
OutputMACDue = false;
// initialize keys
u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID();
// slot 0: modcrypt
*(u32*)&KeyX[0][0] = 0x746E694E;
@ -87,20 +142,20 @@ void DSi_AES::Reset()
// slot 1: 'Tad'/dev.kp
*(u32*)&KeyX[1][0] = 0x4E00004A;
*(u32*)&KeyX[1][4] = 0x4A00004E;
*(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72;
*(u32*)&KeyX[1][12] = (u32)consoleid;
*(u32*)&KeyX[1][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xC80C4B72;
*(u32*)&KeyX[1][12] = (u32)DSi::ConsoleID;
// slot 3: console-unique eMMC crypto
*(u32*)&KeyX[3][0] = (u32)consoleid;
*(u32*)&KeyX[3][4] = (u32)consoleid ^ 0x24EE6906;
*(u32*)&KeyX[3][8] = (u32)(consoleid >> 32) ^ 0xE65B601D;
*(u32*)&KeyX[3][12] = (u32)(consoleid >> 32);
*(u32*)&KeyX[3][0] = (u32)DSi::ConsoleID;
*(u32*)&KeyX[3][4] = (u32)DSi::ConsoleID ^ 0x24EE6906;
*(u32*)&KeyX[3][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xE65B601D;
*(u32*)&KeyX[3][12] = (u32)(DSi::ConsoleID >> 32);
*(u32*)&KeyY[3][0] = 0x0AB9DC76;
*(u32*)&KeyY[3][4] = 0xBD4DC4D3;
*(u32*)&KeyY[3][8] = 0x202DDD1D;
}
void DSi_AES::DoSavestate(Savestate* file)
void DoSavestate(Savestate* file)
{
file->Section("AESi");
@ -138,7 +193,7 @@ void DSi_AES::DoSavestate(Savestate* file)
}
void DSi_AES::ProcessBlock_CCM_Extra()
void ProcessBlock_CCM_Extra()
{
u8 data[16];
u8 data_rev[16];
@ -148,13 +203,13 @@ void DSi_AES::ProcessBlock_CCM_Extra()
*(u32*)&data[8] = InputFIFO.Read();
*(u32*)&data[12] = InputFIFO.Read();
Bswap128(data_rev, data);
Swap16(data_rev, data);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_ECB_encrypt(&Ctx, CurMAC);
}
void DSi_AES::ProcessBlock_CCM_Decrypt()
void ProcessBlock_CCM_Decrypt()
{
u8 data[16];
u8 data_rev[16];
@ -166,13 +221,13 @@ void DSi_AES::ProcessBlock_CCM_Decrypt()
//printf("AES-CCM: "); _printhex2(data, 16);
Bswap128(data_rev, data);
Swap16(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_ECB_encrypt(&Ctx, CurMAC);
Bswap128(data, data_rev);
Swap16(data, data_rev);
//printf(" -> "); _printhex2(data, 16);
@ -182,7 +237,7 @@ void DSi_AES::ProcessBlock_CCM_Decrypt()
OutputFIFO.Write(*(u32*)&data[12]);
}
void DSi_AES::ProcessBlock_CCM_Encrypt()
void ProcessBlock_CCM_Encrypt()
{
u8 data[16];
u8 data_rev[16];
@ -194,13 +249,13 @@ void DSi_AES::ProcessBlock_CCM_Encrypt()
//printf("AES-CCM: "); _printhex2(data, 16);
Bswap128(data_rev, data);
Swap16(data_rev, data);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
AES_ECB_encrypt(&Ctx, CurMAC);
Bswap128(data, data_rev);
Swap16(data, data_rev);
//printf(" -> "); _printhex2(data, 16);
@ -210,7 +265,7 @@ void DSi_AES::ProcessBlock_CCM_Encrypt()
OutputFIFO.Write(*(u32*)&data[12]);
}
void DSi_AES::ProcessBlock_CTR()
void ProcessBlock_CTR()
{
u8 data[16];
u8 data_rev[16];
@ -222,9 +277,9 @@ void DSi_AES::ProcessBlock_CTR()
//printf("AES-CTR: "); _printhex2(data, 16);
Bswap128(data_rev, data);
Swap16(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
Bswap128(data, data_rev);
Swap16(data, data_rev);
//printf(" -> "); _printhex(data, 16);
@ -235,7 +290,7 @@ void DSi_AES::ProcessBlock_CTR()
}
u32 DSi_AES::ReadCnt() const
u32 ReadCnt()
{
u32 ret = Cnt;
@ -245,7 +300,7 @@ u32 DSi_AES::ReadCnt() const
return ret;
}
void DSi_AES::WriteCnt(u32 val)
void WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
Cnt = val & 0xFC1FF000;
@ -277,15 +332,15 @@ void DSi_AES::WriteCnt(u32 val)
OutputMACDue = false;
if (AESMode == 0 && (!(val & (1<<20)))) Log(LogLevel::Debug, "AES: CCM-DECRYPT MAC FROM WRFIFO, TODO\n");
if (AESMode == 0 && (!(val & (1<<20)))) printf("AES: CCM-DECRYPT MAC FROM WRFIFO, TODO\n");
if ((RemBlocks > 0) || (RemExtra > 0))
{
u8 key[16];
u8 iv[16];
Bswap128(key, CurKey);
Bswap128(iv, IV);
Swap16(key, CurKey);
Swap16(iv, IV);
if (AESMode < 2)
{
@ -313,7 +368,7 @@ void DSi_AES::WriteCnt(u32 val)
AES_init_ctx_iv(&Ctx, key, iv);
}
DSi.CheckNDMAs(1, 0x2A);
DSi::CheckNDMAs(1, 0x2A);
}
else
{
@ -328,14 +383,14 @@ void DSi_AES::WriteCnt(u32 val)
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks, BlkCnt);
}
void DSi_AES::WriteBlkCnt(u32 val)
void WriteBlkCnt(u32 val)
{
BlkCnt = val;
}
u32 DSi_AES::ReadOutputFIFO()
u32 ReadOutputFIFO()
{
if (OutputFIFO.IsEmpty()) Log(LogLevel::Warn, "!!! AES OUTPUT FIFO EMPTY\n");
if (OutputFIFO.IsEmpty()) printf("!!! AES OUTPUT FIFO EMPTY\n");
u32 ret = OutputFIFO.Read();
@ -347,9 +402,9 @@ u32 DSi_AES::ReadOutputFIFO()
else
{
if (OutputFIFO.Level() > 0)
DSi.CheckNDMAs(1, 0x2B);
DSi::CheckNDMAs(1, 0x2B);
else
DSi.StopNDMAs(1, 0x2B);
DSi::StopNDMAs(1, 0x2B);
if (OutputMACDue && OutputFIFO.Level() <= 12)
{
@ -364,11 +419,11 @@ u32 DSi_AES::ReadOutputFIFO()
return ret;
}
void DSi_AES::WriteInputFIFO(u32 val)
void WriteInputFIFO(u32 val)
{
// TODO: add some delay to processing
if (InputFIFO.IsFull()) Log(LogLevel::Warn, "!!! AES INPUT FIFO FULL\n");
if (InputFIFO.IsFull()) printf("!!! AES INPUT FIFO FULL\n");
InputFIFO.Write(val);
@ -377,29 +432,29 @@ void DSi_AES::WriteInputFIFO(u32 val)
Update();
}
void DSi_AES::CheckInputDMA()
void CheckInputDMA()
{
if (RemBlocks == 0 && RemExtra == 0) return;
if (InputFIFO.Level() <= InputDMASize)
{
// trigger input DMA
DSi.CheckNDMAs(1, 0x2A);
DSi::CheckNDMAs(1, 0x2A);
}
Update();
}
void DSi_AES::CheckOutputDMA()
void CheckOutputDMA()
{
if (OutputFIFO.Level() >= OutputDMASize)
{
// trigger output DMA
DSi.CheckNDMAs(1, 0x2B);
DSi::CheckNDMAs(1, 0x2B);
}
}
void DSi_AES::Update()
void Update()
{
if (RemExtra > 0)
{
@ -453,7 +508,7 @@ void DSi_AES::Update()
Ctx.Iv[15] = 0x00;
AES_CTR_xcrypt_buffer(&Ctx, CurMAC, 16);
Bswap128(OutputMAC, CurMAC);
Swap16(OutputMAC, CurMAC);
if (OutputFIFO.Level() <= 12)
{
@ -475,19 +530,19 @@ void DSi_AES::Update()
}
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) DSi.SetIRQ2(IRQ2_DSi_AES);
DSi.StopNDMAs(1, 0x2A);
if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES);
DSi::StopNDMAs(1, 0x2A);
if (!OutputFIFO.IsEmpty())
DSi.CheckNDMAs(1, 0x2B);
DSi::CheckNDMAs(1, 0x2B);
else
DSi.StopNDMAs(1, 0x2B);
DSi::StopNDMAs(1, 0x2B);
OutputFlush = false;
}
}
void DSi_AES::WriteIV(u32 offset, u32 val, u32 mask)
void WriteIV(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&IV[offset];
@ -496,7 +551,7 @@ void DSi_AES::WriteIV(u32 offset, u32 val, u32 mask)
//printf("AES: IV: "); _printhex(IV, 16);
}
void DSi_AES::WriteMAC(u32 offset, u32 val, u32 mask)
void WriteMAC(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&MAC[offset];
@ -505,24 +560,7 @@ void DSi_AES::WriteMAC(u32 offset, u32 val, u32 mask)
//printf("AES: MAC: "); _printhex(MAC, 16);
}
void DSi_AES::ROL16(u8* val, u32 n)
{
u32 n_coarse = n >> 3;
u32 n_fine = n & 7;
u8 tmp[16];
for (u32 i = 0; i < 16; i++)
{
tmp[i] = val[(i - n_coarse) & 0xF];
}
for (u32 i = 0; i < 16; i++)
{
val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine));
}
}
void DSi_AES::DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey)
void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey)
{
const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79};
u8 tmp[16];
@ -543,7 +581,7 @@ void DSi_AES::DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey)
memcpy(normalkey, tmp, 16);
}
void DSi_AES::WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyNormal[slot][offset];
@ -552,7 +590,7 @@ void DSi_AES::WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
//printf("KeyNormal(%d): ", slot); _printhex(KeyNormal[slot], 16);
}
void DSi_AES::WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyX[slot][offset];
@ -561,7 +599,7 @@ void DSi_AES::WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
//printf("KeyX(%d): ", slot); _printhex(KeyX[slot], 16);
}
void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyY[slot][offset];
@ -575,4 +613,4 @@ void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,96 +21,37 @@
#include "types.h"
#include "Savestate.h"
#include "FIFO.h"
#include "tiny-AES-c/aes.hpp"
namespace melonDS
namespace DSi_AES
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"
#if defined(__GNUC__) && (__GNUC__ >= 11) && defined(__SIZEOF_INT128__) // gcc 11.*
// NOTE: Yes, the compiler does *not* recognize this code pattern, so it is indeed an optimization.
__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src)
{
*(__int128*)Dst = __builtin_bswap128(*(__int128*)Src);
}
#else
__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src)
{
for (int i = 0; i < 16; ++i)
{
((u8*)Dst)[i] = ((u8*)Src)[15 - i];
}
}
#endif
#pragma GCC diagnostic pop
class DSi;
class DSi_AES
{
public:
DSi_AES(melonDS::DSi& dsi);
~DSi_AES();
void Reset();
void DoSavestate(Savestate* file);
extern u32 Cnt;
u32 ReadCnt() const;
void WriteCnt(u32 val);
void WriteBlkCnt(u32 val);
bool Init();
void DeInit();
void Reset();
u32 ReadOutputFIFO();
void WriteInputFIFO(u32 val);
void CheckInputDMA();
void CheckOutputDMA();
void Update();
void DoSavestate(Savestate* file);
void WriteIV(u32 offset, u32 val, u32 mask);
void WriteMAC(u32 offset, u32 val, u32 mask);
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask);
u32 ReadCnt();
void WriteCnt(u32 val);
void WriteBlkCnt(u32 val);
static void ROL16(u8* val, u32 n);
static void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey);
u32 ReadOutputFIFO();
void WriteInputFIFO(u32 val);
void CheckInputDMA();
void CheckOutputDMA();
void Update();
private:
melonDS::DSi& DSi;
u32 Cnt;
void WriteIV(u32 offset, u32 val, u32 mask);
void WriteMAC(u32 offset, u32 val, u32 mask);
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask);
u32 BlkCnt;
u32 RemExtra;
u32 RemBlocks;
bool OutputFlush;
u32 InputDMASize, OutputDMASize;
u32 AESMode;
FIFO<u32, 16> InputFIFO;
FIFO<u32, 16> OutputFIFO;
u8 IV[16];
u8 MAC[16];
u8 KeyNormal[4][16];
u8 KeyX[4][16];
u8 KeyY[4][16];
u8 CurKey[16];
u8 CurMAC[16];
// output MAC for CCM encrypt
u8 OutputMAC[16];
bool OutputMACDue;
AES_ctx Ctx;
void ProcessBlock_CCM_Extra();
void ProcessBlock_CCM_Decrypt();
void ProcessBlock_CCM_Encrypt();
void ProcessBlock_CTR();
};
void Swap16(u8* dst, u8* src);
void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey);
}
#endif // DSI_AES_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -16,48 +16,58 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_Camera.h"
#include "Platform.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
namespace DSi_CamModule
{
Camera* Camera0; // 78 / facing outside
Camera* Camera1; // 7A / selfie cam
u16 ModuleCnt;
u16 Cnt;
u32 CropStart, CropEnd;
// pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are
u32 DataBuffer[512];
u32 BufferReadPos, BufferWritePos;
u32 BufferNumLines;
Camera* CurCamera;
// note on camera data/etc intervals
// on hardware those are likely affected by several factors
// namely, how long cameras take to process frames
// camera IRQ is fired at roughly 15FPS with default config
const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS
const u32 DSi_CamModule::kTransferStart = 60000;
const u32 kIRQInterval = 1120000; // ~30 FPS
const u32 kTransferStart = 60000;
DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi)
bool Init()
{
DSi.RegisterEventFuncs(Event_DSi_CamIRQ, this, {MakeEventThunk(DSi_CamModule, IRQ)});
DSi.RegisterEventFuncs(Event_DSi_CamTransfer, this, {MakeEventThunk(DSi_CamModule, TransferScanline)});
Camera0 = new Camera(0);
Camera1 = new Camera(1);
Camera0 = DSi.I2C.GetOuterCamera();
Camera1 = DSi.I2C.GetInnerCamera();
return true;
}
DSi_CamModule::~DSi_CamModule()
void DeInit()
{
Camera0 = nullptr;
Camera1 = nullptr;
DSi.UnregisterEventFuncs(Event_DSi_CamIRQ);
DSi.UnregisterEventFuncs(Event_DSi_CamTransfer);
delete Camera0;
delete Camera1;
}
void DSi_CamModule::Reset()
void Reset()
{
Camera0->Reset();
Camera1->Reset();
ModuleCnt = 0; // CHECKME
Cnt = 0;
@ -70,16 +80,16 @@ void DSi_CamModule::Reset()
BufferNumLines = 0;
CurCamera = nullptr;
DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0);
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
}
void DSi_CamModule::Stop()
void Stop()
{
Camera0->Stop();
Camera1->Stop();
}
void DSi_CamModule::DoSavestate(Savestate* file)
void DoSavestate(Savestate* file)
{
file->Section("CAMi");
@ -89,12 +99,15 @@ void DSi_CamModule::DoSavestate(Savestate* file)
/*file->VarArray(FrameBuffer, sizeof(FrameBuffer));
file->Var32(&TransferPos);
file->Var32(&FrameLength);*/
Camera0->DoSavestate(file);
Camera1->DoSavestate(file);
}
void DSi_CamModule::IRQ(u32 param)
void IRQ(u32 param)
{
DSi_Camera* activecam = nullptr;
Camera* activecam = nullptr;
// TODO: cameras don't have any priority!
// activating both together will jumble the image data together
@ -106,7 +119,7 @@ void DSi_CamModule::IRQ(u32 param)
activecam->StartTransfer();
if (Cnt & (1<<11))
DSi.SetIRQ(0, IRQ_DSi_Camera);
NDS::SetIRQ(0, NDS::IRQ_DSi_Camera);
if (Cnt & (1<<15))
{
@ -114,14 +127,14 @@ void DSi_CamModule::IRQ(u32 param)
BufferWritePos = 0;
BufferNumLines = 0;
CurCamera = activecam;
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0);
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, TransferScanline, 0);
}
}
DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0);
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
}
void DSi_CamModule::TransferScanline(u32 line)
void TransferScanline(u32 line)
{
u32* dstbuf = &DataBuffer[BufferWritePos];
int maxlen = 512 - BufferWritePos;
@ -144,7 +157,7 @@ void DSi_CamModule::TransferScanline(u32 line)
if (line < ystart || line > yend)
{
if (!CurCamera->TransferDone())
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1);
return;
}
@ -212,7 +225,7 @@ void DSi_CamModule::TransferScanline(u32 line)
BufferReadPos = 0; // checkme
BufferWritePos = 0;
BufferNumLines = 0;
DSi.CheckNDMAs(0, 0x0B);
DSi::CheckNDMAs(0, 0x0B);
}
else
{
@ -224,19 +237,19 @@ void DSi_CamModule::TransferScanline(u32 line)
if (CurCamera->TransferDone())
return;
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1);
}
u8 DSi_CamModule::Read8(u32 addr)
u8 Read8(u32 addr)
{
//
Log(LogLevel::Debug, "unknown DSi cam read8 %08X\n", addr);
printf("unknown DSi cam read8 %08X\n", addr);
return 0;
}
u16 DSi_CamModule::Read16(u32 addr)
u16 Read16(u32 addr)
{
switch (addr)
{
@ -244,11 +257,11 @@ u16 DSi_CamModule::Read16(u32 addr)
case 0x04004202: return Cnt;
}
Log(LogLevel::Debug, "unknown DSi cam read16 %08X\n", addr);
printf("unknown DSi cam read16 %08X\n", addr);
return 0;
}
u32 DSi_CamModule::Read32(u32 addr)
u32 Read32(u32 addr)
{
switch (addr)
{
@ -270,18 +283,18 @@ u32 DSi_CamModule::Read32(u32 addr)
case 0x04004214: return CropEnd;
}
Log(LogLevel::Debug, "unknown DSi cam read32 %08X\n", addr);
printf("unknown DSi cam read32 %08X\n", addr);
return 0;
}
void DSi_CamModule::Write8(u32 addr, u8 val)
void Write8(u32 addr, u8 val)
{
//
Log(LogLevel::Debug, "unknown DSi cam write8 %08X %02X\n", addr, val);
printf("unknown DSi cam write8 %08X %02X\n", addr, val);
}
void DSi_CamModule::Write16(u32 addr, u16 val)
void Write16(u32 addr, u16 val)
{
switch (addr)
{
@ -357,10 +370,10 @@ void DSi_CamModule::Write16(u32 addr, u16 val)
return;
}
Log(LogLevel::Debug, "unknown DSi cam write16 %08X %04X\n", addr, val);
printf("unknown DSi cam write16 %08X %04X\n", addr, val);
}
void DSi_CamModule::Write32(u32 addr, u32 val)
void Write32(u32 addr, u32 val)
{
switch (addr)
{
@ -374,20 +387,21 @@ void DSi_CamModule::Write32(u32 addr, u32 val)
return;
}
Log(LogLevel::Debug, "unknown DSi cam write32 %08X %08X\n", addr, val);
printf("unknown DSi cam write32 %08X %08X\n", addr, val);
}
DSi_Camera::DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num) : DSi_I2CDevice(dsi, host), Num(num)
Camera::Camera(u32 num)
{
Num = num;
}
Camera::~Camera()
{
}
DSi_Camera::~DSi_Camera()
{
}
void DSi_Camera::DoSavestate(Savestate* file)
void Camera::DoSavestate(Savestate* file)
{
char magic[5] = "CAMx";
magic[3] = '0' + Num;
@ -408,9 +422,9 @@ void DSi_Camera::DoSavestate(Savestate* file)
file->VarArray(MCURegs, 0x8000);
}
void DSi_Camera::Reset()
void Camera::Reset()
{
Platform::Camera_Stop(Num, DSi.UserData);
Platform::Camera_Stop(Num);
DataPos = 0;
RegAddr = 0;
@ -433,12 +447,12 @@ void DSi_Camera::Reset()
memset(FrameBuffer, 0, (640*480/2)*sizeof(u32));
}
void DSi_Camera::Stop()
void Camera::Stop()
{
Platform::Camera_Stop(Num, DSi.UserData);
Platform::Camera_Stop(Num);
}
bool DSi_Camera::IsActivated() const
bool Camera::IsActivated()
{
if (StandbyCnt & (1<<14)) return false; // standby
if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled
@ -447,7 +461,7 @@ bool DSi_Camera::IsActivated() const
}
void DSi_Camera::StartTransfer()
void Camera::StartTransfer()
{
TransferY = 0;
@ -474,15 +488,15 @@ void DSi_Camera::StartTransfer()
FrameFormat = 0;
}
Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true, DSi.UserData);
Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true);
}
bool DSi_Camera::TransferDone() const
bool Camera::TransferDone()
{
return TransferY >= FrameHeight;
}
int DSi_Camera::TransferScanline(u32* buffer, int maxlen)
int Camera::TransferScanline(u32* buffer, int maxlen)
{
if (TransferY >= FrameHeight)
return 0;
@ -492,7 +506,7 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen)
(FrameWidth & 1))
{
// TODO work out something for these cases?
Log(LogLevel::Warn, "CAM%d: invalid resolution %dx%d\n", Num, FrameWidth, FrameHeight);
printf("CAM%d: invalid resolution %dx%d\n", Num, FrameWidth, FrameHeight);
//memset(buffer, 0, width*height*sizeof(u16));
return 0;
}
@ -535,12 +549,12 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen)
}
void DSi_Camera::Acquire()
void Camera::I2C_Start()
{
DataPos = 0;
}
u8 DSi_Camera::Read(bool last)
u8 Camera::I2C_Read(bool last)
{
u8 ret;
@ -561,7 +575,7 @@ u8 DSi_Camera::Read(bool last)
return ret;
}
void DSi_Camera::Write(u8 val, bool last)
void Camera::I2C_Write(u8 val, bool last)
{
if (DataPos < 2)
{
@ -570,7 +584,7 @@ void DSi_Camera::Write(u8 val, bool last)
else
RegAddr |= val;
if (RegAddr & 0x1) Log(LogLevel::Warn, "DSi_Camera: !! UNALIGNED REG ADDRESS %04X\n", RegAddr);
if (RegAddr & 0x1) printf("DSi_Camera: !! UNALIGNED REG ADDRESS %04X\n", RegAddr);
}
else
{
@ -590,7 +604,7 @@ void DSi_Camera::Write(u8 val, bool last)
else DataPos++;
}
u16 DSi_Camera::I2C_ReadReg(u16 addr) const
u16 Camera::I2C_ReadReg(u16 addr)
{
switch (addr)
{
@ -622,11 +636,11 @@ u16 DSi_Camera::I2C_ReadReg(u16 addr) const
case 0x301A: return ((~StandbyCnt) & 0x4000) >> 12;
}
if(Num==1) Log(LogLevel::Debug, "DSi_Camera%d: unknown read %04X\n", Num, addr);
if(Num==1)printf("DSi_Camera%d: unknown read %04X\n", Num, addr);
return 0;
}
void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
void Camera::I2C_WriteReg(u16 addr, u16 val)
{
switch (addr)
{
@ -655,8 +669,8 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
StandbyCnt = val;
//printf("CAM%d STBCNT=%04X (%04X)\n", Num, StandbyCnt, val);
bool isactive = IsActivated();
if (isactive && !wasactive) Platform::Camera_Start(Num, DSi.UserData);
else if (wasactive && !isactive) Platform::Camera_Stop(Num, DSi.UserData);
if (isactive && !wasactive) Platform::Camera_Start(Num);
else if (wasactive && !isactive) Platform::Camera_Stop(Num);
}
return;
case 0x001A:
@ -665,8 +679,8 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
MiscCnt = val & 0x0B7B;
//printf("CAM%d MISCCNT=%04X (%04X)\n", Num, MiscCnt, val);
bool isactive = IsActivated();
if (isactive && !wasactive) Platform::Camera_Start(Num, DSi.UserData);
else if (wasactive && !isactive) Platform::Camera_Stop(Num, DSi.UserData);
if (isactive && !wasactive) Platform::Camera_Start(Num);
else if (wasactive && !isactive) Platform::Camera_Stop(Num);
}
return;
@ -688,21 +702,21 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
return;
}
if(Num==1) Log(LogLevel::Debug, "DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
if(Num==1)printf("DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
}
// TODO: not sure at all what is the accessible range
// or if there is any overlap in the address range
u8 DSi_Camera::MCU_Read(u16 addr) const
u8 Camera::MCU_Read(u16 addr)
{
addr &= 0x7FFF;
return MCURegs[addr];
}
void DSi_Camera::MCU_Write(u16 addr, u8 val)
void Camera::MCU_Write(u16 addr, u8 val)
{
addr &= 0x7FFF;
@ -713,7 +727,7 @@ void DSi_Camera::MCU_Write(u16 addr, u8 val)
if (val == 2) MCURegs[0x2104] = 7; // capture mode
else if (val == 1) MCURegs[0x2104] = 3; // preview mode
else if (val != 5 && val != 6)
Log(LogLevel::Debug, "CAM%d: atypical SEQ_CMD %04X\n", Num, val);
printf("CAM%d: atypical SEQ_CMD %04X\n", Num, val);
return;
case 0x2104: // SEQ_STATE, read-only
@ -724,7 +738,7 @@ void DSi_Camera::MCU_Write(u16 addr, u8 val)
}
void DSi_Camera::InputFrame(const u32* data, int width, int height, bool rgb)
void Camera::InputFrame(u32* data, int width, int height, bool rgb)
{
// TODO: double-buffering?
@ -796,4 +810,18 @@ void DSi_Camera::InputFrame(const u32* data, int width, int height, bool rgb)
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -21,36 +21,56 @@
#include "types.h"
#include "Savestate.h"
#include "DSi_I2C.h"
namespace melonDS
namespace DSi_CamModule
{
class DSi;
class DSi_CamModule;
class DSi_Camera : public DSi_I2CDevice
class Camera;
extern Camera* Camera0;
extern Camera* Camera1;
bool Init();
void DeInit();
void Reset();
void Stop();
void DoSavestate(Savestate* file);
void IRQ(u32 param);
void TransferScanline(u32 line);
u8 Read8(u32 addr);
u16 Read16(u32 addr);
u32 Read32(u32 addr);
void Write8(u32 addr, u8 val);
void Write16(u32 addr, u16 val);
void Write32(u32 addr, u32 val);
class Camera
{
public:
DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num);
~DSi_Camera();
Camera(u32 num);
~Camera();
void DoSavestate(Savestate* file) override;
void DoSavestate(Savestate* file);
void Reset() override;
void Reset();
void Stop();
bool IsActivated() const;
bool IsActivated();
void StartTransfer();
bool TransferDone() const;
bool TransferDone();
// lengths in words
int TransferScanline(u32* buffer, int maxlen);
void Acquire() override;
u8 Read(bool last) override;
void Write(u8 val, bool last) override;
void I2C_Start();
u8 I2C_Read(bool last);
void I2C_Write(u8 val, bool last);
void InputFrame(const u32* data, int width, int height, bool rgb);
void InputFrame(u32* data, int width, int height, bool rgb);
u32 Num;
@ -59,7 +79,7 @@ private:
u32 RegAddr;
u16 RegData;
u16 I2C_ReadReg(u16 addr) const;
u16 I2C_ReadReg(u16 addr);
void I2C_WriteReg(u16 addr, u16 val);
u16 PLLDiv;
@ -72,7 +92,7 @@ private:
u16 MCUAddr;
u8 MCURegs[0x8000];
u8 MCU_Read(u16 addr) const;
u8 MCU_Read(u16 addr);
void MCU_Write(u16 addr, u8 val);
u16 FrameWidth, FrameHeight;
@ -81,51 +101,6 @@ private:
u32 FrameBuffer[640*480/2]; // YUYV framebuffer, two pixels per word
};
class DSi_CamModule
{
public:
DSi_CamModule(melonDS::DSi& dsi);
~DSi_CamModule();
void Reset();
void Stop();
void DoSavestate(Savestate* file);
const DSi_Camera* GetOuterCamera() const { return Camera0; }
DSi_Camera* GetOuterCamera() { return Camera0; }
const DSi_Camera* GetInnerCamera() const { return Camera1; }
DSi_Camera* GetInnerCamera() { return Camera1; }
void IRQ(u32 param);
void TransferScanline(u32 line);
u8 Read8(u32 addr);
u16 Read16(u32 addr);
u32 Read32(u32 addr);
void Write8(u32 addr, u8 val);
void Write16(u32 addr, u16 val);
void Write32(u32 addr, u32 val);
private:
melonDS::DSi& DSi;
DSi_Camera* Camera0; // 78 / facing outside
DSi_Camera* Camera1; // 7A / selfie cam
u16 ModuleCnt;
u16 Cnt;
u32 CropStart, CropEnd;
// pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are
u32 DataBuffer[512];
u32 BufferReadPos, BufferWritePos;
u32 BufferNumLines;
DSi_Camera* CurCamera;
static const u32 kIRQInterval;
static const u32 kTransferStart;
};
}
#endif // DSI_CAMERA_H

View File

@ -22,19 +22,36 @@
#include "DSi_DSP.h"
#include "FIFO.h"
#include "NDS.h"
#include "Platform.h"
namespace melonDS
namespace DSi_DSP
{
using Platform::Log;
using Platform::LogLevel;
// not sure whether to not rather put it somewhere else
u16 SNDExCnt;
const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h
Teakra::Teakra* TeakraCore;
bool SCFG_RST;
u16 DSP_PADR;
u16 DSP_PCFG;
u16 DSP_PSTS;
u16 DSP_PSEM;
u16 DSP_PMASK;
u16 DSP_PCLEAR;
u16 DSP_CMD[3];
u16 DSP_REP[3];
u64 DSPTimestamp;
FIFO<u16, 16> PDATAReadFifo/*, *PDATAWriteFifo*/;
int PDataDMALen = 0;
constexpr u32 DataMemoryOffset = 0x20000; // from Teakra memory_interface.h
// NOTE: ^ IS IN DSP WORDS, NOT IN BYTES!
u16 DSi_DSP::GetPSTS() const
u16 GetPSTS()
{
u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit
//r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers
@ -53,101 +70,97 @@ u16 DSi_DSP::GetPSTS() const
return r;
}
void DSi_DSP::IrqRep0()
void IrqRep0()
{
if (DSP_PCFG & (1<< 9)) DSi.SetIRQ(0, IRQ_DSi_DSP);
if (DSP_PCFG & (1<< 9)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void DSi_DSP::IrqRep1()
void IrqRep1()
{
if (DSP_PCFG & (1<<10)) DSi.SetIRQ(0, IRQ_DSi_DSP);
if (DSP_PCFG & (1<<10)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void DSi_DSP::IrqRep2()
void IrqRep2()
{
if (DSP_PCFG & (1<<11)) DSi.SetIRQ(0, IRQ_DSi_DSP);
if (DSP_PCFG & (1<<11)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void DSi_DSP::IrqSem()
void IrqSem()
{
DSP_PSTS |= 1<<9;
// apparently these are always fired?
DSi.SetIRQ(0, IRQ_DSi_DSP);
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
u16 DSi_DSP::DSPRead16(u32 addr)
u16 DSPRead16(u32 addr)
{
if (!(addr & 0x40000))
{
u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7];
u8* ptr = DSi::NWRAMMap_B[2][(addr >> 15) & 0x7];
return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0;
}
else
{
u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7];
u8* ptr = DSi::NWRAMMap_C[2][(addr >> 15) & 0x7];
return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0;
}
}
void DSi_DSP::DSPWrite16(u32 addr, u16 val)
void DSPWrite16(u32 addr, u16 val)
{
// TODO: does the rule for overlapping NWRAM slots also apply to the DSP side?
if (!(addr & 0x40000))
{
u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7];
u8* ptr = DSi::NWRAMMap_B[2][(addr >> 15) & 0x7];
if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val;
}
else
{
u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7];
u8* ptr = DSi::NWRAMMap_C[2][(addr >> 15) & 0x7];
if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val;
}
}
void DSi_DSP::AudioCb(std::array<s16, 2> frame)
void AudioCb(std::array<s16, 2> frame)
{
// TODO
}
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
bool Init()
{
DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
TeakraCore = new Teakra::Teakra();
SCFG_RST = false;
// ????
//if (!TeakraCore) return false;
if (!TeakraCore) return false;
using namespace std::placeholders;
TeakraCore->SetRecvDataHandler(0, IrqRep0);
TeakraCore->SetRecvDataHandler(1, IrqRep1);
TeakraCore->SetRecvDataHandler(2, IrqRep2);
TeakraCore->SetRecvDataHandler(0, std::bind(&DSi_DSP::IrqRep0, this));
TeakraCore->SetRecvDataHandler(1, std::bind(&DSi_DSP::IrqRep1, this));
TeakraCore->SetRecvDataHandler(2, std::bind(&DSi_DSP::IrqRep2, this));
TeakraCore->SetSemaphoreHandler(std::bind(&DSi_DSP::IrqSem, this));
TeakraCore->SetSemaphoreHandler(IrqSem);
Teakra::SharedMemoryCallback smcb;
smcb.read16 = std::bind(&DSi_DSP::DSPRead16, this, _1);
smcb.write16 = std::bind(&DSi_DSP::DSPWrite16, this, _1, _2);
smcb.read16 = DSPRead16;
smcb.write16 = DSPWrite16;
TeakraCore->SetSharedMemoryCallback(smcb);
// these happen instantaneously and without too much regard for bus aribtration
// rules, so, this might have to be changed later on
Teakra::AHBMCallback cb;
cb.read8 = [this](auto addr) { return DSi.ARM9Read8(addr); };
cb.write8 = [this](auto addr, auto val) { DSi.ARM9Write8(addr, val); };
cb.read16 = [this](auto addr) { return DSi.ARM9Read16(addr); };
cb.write16 = [this](auto addr, auto val) { DSi.ARM9Write16(addr, val); };
cb.read32 = [this](auto addr) { return DSi.ARM9Read32(addr); };
cb.write32 = [this](auto addr, auto val) { DSi.ARM9Write32(addr, val); };
cb.read8 = DSi::ARM9Read8;
cb.write8 = DSi::ARM9Write8;
cb.read16 = DSi::ARM9Read16;
cb.write16 = DSi::ARM9Write16;
cb.read32 = DSi::ARM9Read32;
cb.write32 = DSi::ARM9Write32;
TeakraCore->SetAHBMCallback(cb);
TeakraCore->SetAudioCallback(std::bind(&DSi_DSP::AudioCb, this, _1));
TeakraCore->SetAudioCallback(AudioCb);
//PDATAReadFifo = new FIFO<u16>(16);
//PDATAWriteFifo = new FIFO<u16>(16);
}
DSi_DSP::~DSi_DSP()
return true;
}
void DeInit()
{
//if (PDATAWriteFifo) delete PDATAWriteFifo;
if (TeakraCore) delete TeakraCore;
@ -155,11 +168,9 @@ DSi_DSP::~DSi_DSP()
//PDATAReadFifo = NULL;
//PDATAWriteFifo = NULL;
TeakraCore = NULL;
DSi.UnregisterEventFuncs(Event_DSi_DSP);
}
void DSi_DSP::Reset()
void Reset()
{
DSPTimestamp = 0;
@ -177,46 +188,46 @@ void DSi_DSP::Reset()
//PDATAWriteFifo->Clear();
TeakraCore->Reset();
DSi.CancelEvent(Event_DSi_DSP);
NDS::CancelEvent(NDS::Event_DSi_DSP);
SNDExCnt = 0;
}
bool DSi_DSP::IsRstReleased() const
bool IsRstReleased()
{
return SCFG_RST;
}
void DSi_DSP::SetRstLine(bool release)
void SetRstLine(bool release)
{
SCFG_RST = release;
Reset();
DSPTimestamp = DSi.ARM9Timestamp; // only start now!
DSPTimestamp = NDS::ARM9Timestamp; // only start now!
}
inline bool DSi_DSP::IsDSPCoreEnabled() const
inline bool IsDSPCoreEnabled()
{
return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0)));
return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0)));
}
inline bool DSi_DSP::IsDSPIOEnabled() const
inline bool IsDSPIOEnabled()
{
return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST;
return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST;
}
bool DSi_DSP::DSPCatchUp()
bool DSPCatchUp()
{
//asm volatile("int3");
if (!IsDSPCoreEnabled())
{
// nothing to do, but advance the current time so that we don't do an
// unreasonable amount of cycles when rst is released
if (DSPTimestamp < DSi.ARM9Timestamp)
DSPTimestamp = DSi.ARM9Timestamp;
if (DSPTimestamp < NDS::ARM9Timestamp)
DSPTimestamp = NDS::ARM9Timestamp;
return false;
}
u64 curtime = DSi.ARM9Timestamp;
u64 curtime = NDS::ARM9Timestamp;
if (DSPTimestamp >= curtime) return true; // ummmm?!
@ -231,9 +242,9 @@ bool DSi_DSP::DSPCatchUp()
return true;
}
void DSi_DSP::DSPCatchUpU32(u32 _) { DSPCatchUp(); }
void DSPCatchUpU32(u32 _) { DSPCatchUp(); }
void DSi_DSP::PDataDMAWrite(u16 wrval)
void PDataDMAWrite(u16 wrval)
{
u32 addr = DSP_PADR;
@ -257,7 +268,7 @@ void DSi_DSP::PDataDMAWrite(u16 wrval)
{
switch (TeakraCore->AHBMGetUnitSize(0))
{
case 0: /* 8bit */ DSi.ARM9Write8 (addr, (u8)wrval); break;
case 0: /* 8bit */ DSi::ARM9Write8 (addr, (u8)wrval); break;
case 1: /* 16 b */ TeakraCore->AHBMWrite16(addr, wrval); break;
// does it work like this, or should it first buffer two u16's
// until it has enough data to write to the actual destination?
@ -272,10 +283,10 @@ void DSi_DSP::PDataDMAWrite(u16 wrval)
if (DSP_PCFG & (1<<1)) // auto-increment
++DSP_PADR; // overflows and stays within a 64k 'page' // TODO: is this +1 or +2?
DSi.SetIRQ(0, IRQ_DSi_DSP); // wrfifo empty
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); // wrfifo empty
}
// TODO: FIFO interrupts! (rd full, nonempty)
u16 DSi_DSP::PDataDMARead()
u16 PDataDMARead()
{
u16 r = 0;
u32 addr = DSP_PADR;
@ -299,7 +310,7 @@ u16 DSi_DSP::PDataDMARead()
{
switch (TeakraCore->AHBMGetUnitSize(0))
{
case 0: /* 8bit */ r = DSi.ARM9Read8 (addr); break;
case 0: /* 8bit */ r = DSi::ARM9Read8 (addr); break;
case 1: /* 16 b */ r = TeakraCore->AHBMRead16(addr); break;
case 2: /* 32 b */ r = (u16)TeakraCore->AHBMRead32(addr); break;
}
@ -313,7 +324,7 @@ u16 DSi_DSP::PDataDMARead()
return r;
}
void DSi_DSP::PDataDMAFetch()
void PDataDMAFetch()
{
if (!PDataDMALen) return;
@ -321,7 +332,7 @@ void DSi_DSP::PDataDMAFetch()
if (PDataDMALen > 0) --PDataDMALen;
}
void DSi_DSP::PDataDMAStart()
void PDataDMAStart()
{
switch ((DSP_PSTS & (3<<2)) >> 2)
{
@ -337,16 +348,16 @@ void DSi_DSP::PDataDMAStart()
for (int i = 0; i < amt; ++i)
PDataDMAFetch();
DSi.SetIRQ(0, IRQ_DSi_DSP);
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
}
void DSi_DSP::PDataDMACancel()
void PDataDMACancel()
{
PDataDMALen = 0;
PDATAReadFifo.Clear();
}
u16 DSi_DSP::PDataDMAReadMMIO()
u16 PDataDMAReadMMIO()
{
u16 ret;
@ -372,12 +383,12 @@ u16 DSi_DSP::PDataDMAReadMMIO()
}
if (!PDATAReadFifo.IsEmpty() || PDATAReadFifo.IsFull())
DSi.SetIRQ(0, IRQ_DSi_DSP);
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
return ret;
}
u8 DSi_DSP::Read8(u32 addr)
u8 Read8(u32 addr)
{
//if (!IsDSPIOEnabled()) return 0;
DSPCatchUp();
@ -404,7 +415,7 @@ u8 DSi_DSP::Read8(u32 addr)
return 0;
}
u16 DSi_DSP::Read16(u32 addr)
u16 Read16(u32 addr)
{
//printf("DSP READ16 %d %08X %08X\n", IsDSPCoreEnabled(), addr, NDS::GetPC(0));
//if (!IsDSPIOEnabled()) return 0;
@ -447,14 +458,14 @@ u16 DSi_DSP::Read16(u32 addr)
return 0;
}
u32 DSi_DSP::Read32(u32 addr)
u32 Read32(u32 addr)
{
addr &= 0x3C;
return Read16(addr); // *shrug* (doesn't do anything unintended due to the
// 4byte spacing between regs while they're all 16bit)
}
void DSi_DSP::Write8(u32 addr, u8 val)
void Write8(u32 addr, u8 val)
{
//if (!IsDSPIOEnabled()) return;
DSPCatchUp();
@ -475,9 +486,9 @@ void DSi_DSP::Write8(u32 addr, u8 val)
// no REPx writes
}
}
void DSi_DSP::Write16(u32 addr, u16 val)
void Write16(u32 addr, u16 val)
{
Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, DSi.GetPC(0));
printf("DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, NDS::GetPC(0));
//if (!IsDSPIOEnabled()) return;
DSPCatchUp();
@ -530,16 +541,14 @@ void DSi_DSP::Write16(u32 addr, u16 val)
}
}
void DSi_DSP::Write32(u32 addr, u32 val)
void Write32(u32 addr, u32 val)
{
addr &= 0x3C;
Write16(addr, val & 0xFFFF);
}
void DSi_DSP::WriteSNDExCnt(u16 val, u16 mask)
void WriteSNDExCnt(u16 val)
{
val = (val & mask) | (SNDExCnt & ~mask);
// it can be written even in NDS mode
// mic frequency can only be changed if it was disabled
@ -553,7 +562,7 @@ void DSi_DSP::WriteSNDExCnt(u16 val, u16 mask)
SNDExCnt = val & 0xE00F;
}
void DSi_DSP::Run(u32 cycles)
void Run(u32 cycles)
{
if (!IsDSPCoreEnabled())
{
@ -565,12 +574,12 @@ void DSi_DSP::Run(u32 cycles)
DSPTimestamp += cycles;
DSi.CancelEvent(Event_DSi_DSP);
DSi.ScheduleEvent(Event_DSi_DSP, false,
16384/*from citra (TeakraSlice)*/, 0, 0);
NDS::CancelEvent(NDS::Event_DSi_DSP);
NDS::ScheduleEvent(NDS::Event_DSi_DSP, false,
16384/*from citra (TeakraSlice)*/, DSPCatchUpU32, 0);
}
void DSi_DSP::DoSavestate(Savestate* file)
void DoSavestate(Savestate* file)
{
file->Section("DSPi");
@ -596,4 +605,5 @@ void DSi_DSP::DoSavestate(Savestate* file)
// TODO: save the Teakra state!!!
}
}
}

View File

@ -25,89 +25,50 @@
// TODO: for actual sound output
// * audio callbacks
namespace Teakra { class Teakra; }
namespace melonDS
namespace DSi_DSP
{
class DSi;
class DSi_DSP
{
public:
DSi_DSP(melonDS::DSi& dsi);
~DSi_DSP();
void Reset();
void DoSavestate(Savestate* file);
void DSPCatchUpU32(u32 _);
extern u16 SNDExCnt;
// SCFG_RST bit0
bool IsRstReleased() const;
void SetRstLine(bool release);
extern u16 DSP_PDATA;
extern u16 DSP_PADR;
extern u16 DSP_PCFG;
extern u16 DSP_PSTS;
extern u16 DSP_PSEM;
extern u16 DSP_PMASK;
extern u16 DSP_PCLEAR;
extern u16 DSP_SEM;
extern u16 DSP_CMD[3];
extern u16 DSP_REP[3];
// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT)
u8 Read8(u32 addr);
void Write8(u32 addr, u8 val);
bool Init();
void DeInit();
void Reset();
u16 Read16(u32 addr);
void Write16(u32 addr, u16 val);
void DoSavestate(Savestate* file);
u32 Read32(u32 addr);
void Write32(u32 addr, u32 val);
void DSPCatchUpU32(u32 _);
u16 ReadSNDExCnt() const { return SNDExCnt; }
void WriteSNDExCnt(u16 val, u16 mask);
// SCFG_RST bit0
bool IsRstReleased();
void SetRstLine(bool release);
// NOTE: checks SCFG_CLK9
void Run(u32 cycles);
// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT)
u8 Read8(u32 addr);
void Write8(u32 addr, u8 val);
void IrqRep0();
void IrqRep1();
void IrqRep2();
void IrqSem();
u16 DSPRead16(u32 addr);
void DSPWrite16(u32 addr, u16 val);
void AudioCb(std::array<s16, 2> frame);
u16 Read16(u32 addr);
void Write16(u32 addr, u16 val);
private:
melonDS::DSi& DSi;
// not sure whether to not rather put it somewhere else
u16 SNDExCnt;
u32 Read32(u32 addr);
void Write32(u32 addr, u32 val);
Teakra::Teakra* TeakraCore;
void WriteSNDExCnt(u16 val);
bool SCFG_RST;
u16 DSP_PADR;
u16 DSP_PCFG;
u16 DSP_PSTS;
u16 DSP_PSEM;
u16 DSP_PMASK;
u16 DSP_PCLEAR;
u16 DSP_CMD[3];
u16 DSP_REP[3];
u64 DSPTimestamp;
FIFO<u16, 16> PDATAReadFifo/*, *PDATAWriteFifo*/;
int PDataDMALen;
static const u32 DataMemoryOffset;
u16 GetPSTS() const;
inline bool IsDSPCoreEnabled() const;
inline bool IsDSPIOEnabled() const;
bool DSPCatchUp();
void PDataDMAWrite(u16 wrval);
u16 PDataDMARead();
void PDataDMAFetch();
void PDataDMAStart();
void PDataDMACancel();
u16 PDataDMAReadMMIO();
};
// NOTE: checks SCFG_CLK9
void Run(u32 cycles);
}
#endif // DSI_DSP_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -18,54 +18,29 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "DSi.h"
#include "DSi_I2C.h"
#include "DSi_Camera.h"
#include "ARM.h"
#include "SPI.h"
#include "Platform.h"
namespace melonDS
namespace DSi_BPTWL
{
using Platform::Log;
using Platform::LogLevel;
u8 Registers[0x100];
u32 CurPos;
// TODO: These are purely approximations
const double DSi_BPTWL::PowerButtonShutdownTime = 0.5;
const double DSi_BPTWL::PowerButtonForcedShutdownTime = 5.0;
const double DSi_BPTWL::VolumeSwitchRepeatStart = 0.5;
const double DSi_BPTWL::VolumeSwitchRepeatRate = 1.0 / 6;
// Could not find a pattern or a decent formula for these,
// regardless, they're only 64 bytes in size
const u8 DSi_BPTWL::VolumeDownTable[32] =
bool Init()
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03,
0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
};
return true;
}
const u8 DSi_BPTWL::VolumeUpTable[32] =
{
0x02, 0x03, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
0x1D, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
};
DSi_BPTWL::DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host) : DSi_I2CDevice(dsi, host)
void DeInit()
{
}
DSi_BPTWL::~DSi_BPTWL()
{
}
void DSi_BPTWL::Reset()
void Reset()
{
CurPos = -1;
memset(Registers, 0x5A, 0x100);
@ -73,9 +48,9 @@ void DSi_BPTWL::Reset()
Registers[0x00] = 0x33; // TODO: support others??
Registers[0x01] = 0x00;
Registers[0x02] = 0x50;
Registers[0x10] = 0x00; // irq flag
Registers[0x10] = 0x00; // power btn
Registers[0x11] = 0x00; // reset
Registers[0x12] = 0x00; // irq mode
Registers[0x12] = 0x00; // power btn tap
Registers[0x20] = 0x8F; // battery
Registers[0x21] = 0x07;
Registers[0x30] = 0x13;
@ -96,19 +71,9 @@ void DSi_BPTWL::Reset()
Registers[0x77] = 0x00;
Registers[0x80] = 0x10;
Registers[0x81] = 0x64;
// Ideally these should be replaced by a proper BPTWL core emulator
PowerButtonTime = 0.0;
PowerButtonDownFlag = false;
PowerButtonShutdownFlag = false;
VolumeSwitchTime = 0.0;
VolumeSwitchRepeatTime = 0.0;
VolumeSwitchKeysDown = 0;
VolumeSwitchDownFlag = false;
}
void DSi_BPTWL::DoSavestate(Savestate* file)
void DoSavestate(Savestate* file)
{
file->Section("I2BP");
@ -116,280 +81,30 @@ void DSi_BPTWL::DoSavestate(Savestate* file)
file->Var32(&CurPos);
}
// TODO: Needs more investigation on the other bits
inline bool DSi_BPTWL::GetIRQMode() const
{
return Registers[0x12] & 0x01;
}
u8 GetBootFlag() { return Registers[0x70]; }
u8 DSi_BPTWL::GetBootFlag() const { return Registers[0x70]; }
bool DSi_BPTWL::GetBatteryCharging() const { return Registers[0x20] >> 7; }
void DSi_BPTWL::SetBatteryCharging(bool charging)
bool GetBatteryCharging() { return Registers[0x20] >> 7; }
void SetBatteryCharging(bool charging)
{
Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F));
}
u8 DSi_BPTWL::GetBatteryLevel() const { return Registers[0x20] & 0xF; }
void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel)
u8 GetBatteryLevel() { return Registers[0x20] & 0xF; }
void SetBatteryLevel(u8 batteryLevel)
{
Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F));
//SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false);
if (batteryLevel <= 1)
{
SetIRQ(batteryLevel ? IRQ_BatteryLow : IRQ_BatteryEmpty);
}
SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false);
}
u8 DSi_BPTWL::GetVolumeLevel() const { return Registers[0x40]; }
void DSi_BPTWL::SetVolumeLevel(u8 volume)
{
Registers[0x40] = volume & 0x1F;
}
u8 DSi_BPTWL::GetBacklightLevel() const { return Registers[0x41]; }
void DSi_BPTWL::SetBacklightLevel(u8 backlight)
{
Registers[0x41] = backlight > 4 ? 4 : backlight;
}
void DSi_BPTWL::ResetButtonState()
{
PowerButtonTime = 0.0;
PowerButtonDownFlag = false;
PowerButtonShutdownFlag = false;
VolumeSwitchKeysDown = 0;
VolumeSwitchDownFlag = false;
VolumeSwitchTime = 0.0;
VolumeSwitchRepeatTime = 0.0;
}
void DSi_BPTWL::DoHardwareReset(bool direct)
{
ResetButtonState();
Log(LogLevel::Debug, "BPTWL: soft-reset\n");
if (direct)
{
// TODO: This doesn't seem to stop the SPU
DSi.SoftReset();
return;
}
// TODO: soft-reset might need to be scheduled later!
// TODO: this has been moved for the JIT to work, nothing is confirmed here
DSi.ARM7.Halt(4);
}
void DSi_BPTWL::DoShutdown()
{
ResetButtonState();
DSi.Stop(Platform::StopReason::PowerOff);
}
void DSi_BPTWL::SetPowerButtonHeld(double time)
{
if (!PowerButtonDownFlag)
{
PowerButtonDownFlag = true;
PowerButtonTime = time;
DoPowerButtonPress();
return;
}
double elapsed = time - PowerButtonTime;
if (elapsed < 0)
return;
if (elapsed >= PowerButtonForcedShutdownTime)
{
Log(LogLevel::Debug, "Force power off via DSi power button\n");
DoPowerButtonForceShutdown();
return;
}
if (elapsed >= PowerButtonShutdownTime)
{
DoPowerButtonShutdown();
}
}
void DSi_BPTWL::SetPowerButtonReleased(double time)
{
double elapsed = time - PowerButtonTime;
if (elapsed >= 0 && elapsed < PowerButtonShutdownTime)
{
DoPowerButtonReset();
}
PowerButtonTime = 0.0;
PowerButtonDownFlag = false;
PowerButtonShutdownFlag = false;
}
void DSi_BPTWL::SetVolumeSwitchHeld(u32 key)
{
VolumeSwitchKeysDown |= (1 << key);
}
void DSi_BPTWL::SetVolumeSwitchReleased(u32 key)
{
VolumeSwitchKeysDown &= ~(1 << key);
VolumeSwitchDownFlag = false;
VolumeSwitchTime = 0.0;
VolumeSwitchRepeatTime = 0.0;
}
inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() const
{
bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up);
bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down);
return up != down;
}
s32 DSi_BPTWL::ProcessVolumeSwitchInput(double time)
{
if (!CheckVolumeSwitchKeysValid())
return -1;
s32 key = VolumeSwitchKeysDown & (1 << volumeKey_Up) ? volumeKey_Up : volumeKey_Down;
// Always fire an IRQ when first pressed
if (!VolumeSwitchDownFlag)
{
VolumeSwitchDownFlag = true;
VolumeSwitchTime = time;
DoVolumeSwitchPress(key);
return key;
}
// Handle key repetition mechanic
if (VolumeSwitchRepeatTime == 0)
{
double elapsed = time - VolumeSwitchTime;
if (elapsed < VolumeSwitchRepeatStart)
return -1;
VolumeSwitchRepeatTime = time;
DoVolumeSwitchPress(key);
return key;
}
double elapsed = time - VolumeSwitchRepeatTime;
if (elapsed < VolumeSwitchRepeatRate)
return -1;
double rem = fmod(elapsed, VolumeSwitchRepeatRate);
VolumeSwitchRepeatTime = time - rem;
DoVolumeSwitchPress(key);
return key;
}
void DSi_BPTWL::DoPowerButtonPress()
{
// Set button pressed IRQ
SetIRQ(IRQ_PowerButtonPressed);
// There is no default hardware behavior for pressing the power button
}
void DSi_BPTWL::DoPowerButtonReset()
{
// Reset via IRQ, handled by software
SetIRQ(IRQ_PowerButtonReset);
// Reset automatically via hardware
if (!GetIRQMode())
{
// Assumes this isn't called during normal CPU execution
DoHardwareReset(true);
}
}
void DSi_BPTWL::DoPowerButtonShutdown()
{
// Shutdown via IRQ, handled by software
if (!PowerButtonShutdownFlag)
{
SetIRQ(IRQ_PowerButtonShutdown);
}
PowerButtonShutdownFlag = true;
// Shutdown automatically via hardware
if (!GetIRQMode())
{
DoShutdown();
}
// The IRQ is only fired once (hence the need for an if guard),
// but the hardware shutdown is continuously triggered.
// That way when switching the IRQ mode while holding
// down the power button, the DSi will still shut down
}
void DSi_BPTWL::DoPowerButtonForceShutdown()
{
DoShutdown();
}
void DSi_BPTWL::DoVolumeSwitchPress(u32 key)
{
u8 volume = Registers[0x40];
switch (key)
{
case volumeKey_Up:
volume = VolumeUpTable[volume];
break;
case volumeKey_Down:
volume = VolumeDownTable[volume];
break;
}
Registers[0x40] = volume;
SetIRQ(IRQ_VolumeSwitchPressed);
}
void DSi_BPTWL::SetIRQ(u8 irqFlag)
{
Registers[0x10] |= irqFlag & IRQ_ValidMask;
if (GetIRQMode())
{
DSi.SetIRQ2(IRQ2_DSi_BPTWL);
}
}
void DSi_BPTWL::Acquire()
void Start()
{
//printf("BPTWL: start\n");
}
u8 DSi_BPTWL::Read(bool last)
u8 Read(bool last)
{
//printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1));
u8 ret = Registers[CurPos];
// IRQ flags are automatically cleared upon read
if (CurPos == 0x10)
{
Registers[0x10] = 0;
}
CurPos++;
u8 ret = Registers[CurPos++];
if (last)
{
@ -399,7 +114,7 @@ u8 DSi_BPTWL::Read(bool last)
return ret;
}
void DSi_BPTWL::Write(u8 val, bool last)
void Write(u8 val, bool last)
{
if (last)
{
@ -416,29 +131,19 @@ void DSi_BPTWL::Write(u8 val, bool last)
if (CurPos == 0x11 && val == 0x01)
{
// Assumes this is called during normal CPU execution
DoHardwareReset(false);
printf("BPTWL: soft-reset\n");
val = 0; // checkme
// TODO: soft-reset might need to be scheduled later!
// TODO: this has been moved for the JIT to work, nothing is confirmed here
NDS::ARM7->Halt(4);
CurPos = -1;
return;
}
// Mask volume level
if (CurPos == 0x40)
{
val &= 0x1F;
}
// Clamp backlight level
if (CurPos == 0x41)
{
val = val > 4 ? 4 : val;
}
if (CurPos == 0x11 || CurPos == 0x12 ||
CurPos == 0x21 ||
CurPos == 0x30 || CurPos == 0x31 ||
CurPos == 0x40 || CurPos == 0x41 ||
CurPos == 0x40 || CurPos == 0x31 ||
CurPos == 0x60 || CurPos == 0x63 ||
(CurPos >= 0x70 && CurPos <= 0x77) ||
CurPos == 0x80 || CurPos == 0x81)
@ -450,69 +155,51 @@ void DSi_BPTWL::Write(u8 val, bool last)
CurPos++; // CHECKME
}
DSi_I2CHost::DSi_I2CHost(melonDS::DSi& dsi) : DSi(dsi)
{
BPTWL = new DSi_BPTWL(dsi, this);
Camera0 = new DSi_Camera(dsi, this, 0);
Camera1 = new DSi_Camera(dsi, this, 1);
}
DSi_I2CHost::~DSi_I2CHost()
namespace DSi_I2C
{
delete BPTWL; BPTWL = nullptr;
delete Camera0; Camera0 = nullptr;
delete Camera1; Camera1 = nullptr;
u8 Cnt;
u8 Data;
u32 Device;
bool Init()
{
if (!DSi_BPTWL::Init()) return false;
return true;
}
void DSi_I2CHost::Reset()
void DeInit()
{
DSi_BPTWL::DeInit();
}
void Reset()
{
Cnt = 0;
Data = 0;
CurDeviceID = 0;
CurDevice = nullptr;
Device = -1;
BPTWL->Reset();
Camera0->Reset();
Camera1->Reset();
DSi_BPTWL::Reset();
}
void DSi_I2CHost::DoSavestate(Savestate* file)
void DoSavestate(Savestate* file)
{
file->Section("I2Ci");
file->Var8(&Cnt);
file->Var8(&Data);
file->Var8(&CurDeviceID);
file->Var32(&Device);
if (!file->Saving)
{
GetCurDevice();
}
BPTWL->DoSavestate(file);
Camera0->DoSavestate(file);
Camera1->DoSavestate(file);
DSi_BPTWL::DoSavestate(file);
}
void DSi_I2CHost::GetCurDevice()
{
switch (CurDeviceID)
{
case 0x4A: CurDevice = BPTWL; break;
case 0x78: CurDevice = Camera0; break;
case 0x7A: CurDevice = Camera1; break;
case 0xA0:
case 0xE0: CurDevice = nullptr; break;
default:
Log(LogLevel::Warn, "I2C: unknown device %02X\n", CurDeviceID);
CurDevice = nullptr;
break;
}
}
void DSi_I2CHost::WriteCnt(u8 val)
void WriteCnt(u8 val)
{
//printf("I2C: write CNT %02X, %02X, %08X\n", val, Data, NDS::GetPC(1));
@ -530,13 +217,17 @@ void DSi_I2CHost::WriteCnt(u8 val)
// read
val &= 0xF7;
if (CurDevice)
{
Data = CurDevice->Read(islast);
}
else
switch (Device)
{
case 0x4A: Data = DSi_BPTWL::Read(islast); break;
case 0x78: Data = DSi_CamModule::Camera0->I2C_Read(islast); break;
case 0x7A: Data = DSi_CamModule::Camera1->I2C_Read(islast); break;
case 0xA0:
case 0xE0: Data = 0xFF; break;
default:
printf("I2C: read on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, 0, islast);
Data = 0xFF;
break;
}
//printf("I2C read, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
@ -549,30 +240,37 @@ void DSi_I2CHost::WriteCnt(u8 val)
if (val & (1<<1))
{
CurDeviceID = Data & 0xFE;
Device = Data & 0xFE;
//printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device);
GetCurDevice();
if (CurDevice)
{
CurDevice->Acquire();
}
else
switch (Device)
{
case 0x4A: DSi_BPTWL::Start(); break;
case 0x78: DSi_CamModule::Camera0->I2C_Start(); break;
case 0x7A: DSi_CamModule::Camera1->I2C_Start(); break;
case 0xA0:
case 0xE0: ack = false; break;
default:
printf("I2C: %s start on unknown device %02X\n", (Data&0x01)?"read":"write", Device);
ack = false;
break;
}
}
else
{
//printf("I2C write, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
if (CurDevice)
{
CurDevice->Write(Data, islast);
}
else
switch (Device)
{
case 0x4A: DSi_BPTWL::Write(Data, islast); break;
case 0x78: DSi_CamModule::Camera0->I2C_Write(Data, islast); break;
case 0x7A: DSi_CamModule::Camera1->I2C_Write(Data, islast); break;
case 0xA0:
case 0xE0: ack = false; break;
default:
printf("I2C: write on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
ack = false;
break;
}
}
@ -585,14 +283,14 @@ void DSi_I2CHost::WriteCnt(u8 val)
Cnt = val;
}
u8 DSi_I2CHost::ReadData()
u8 ReadData()
{
return Data;
}
void DSi_I2CHost::WriteData(u8 val)
void WriteData(u8 val)
{
Data = val;
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -22,168 +22,45 @@
#include "types.h"
#include "Savestate.h"
namespace melonDS
namespace DSi_BPTWL
{
class DSi_I2CHost;
class DSi_Camera;
class DSi;
class DSi_I2CDevice
u8 GetBootFlag();
bool GetBatteryCharging();
void SetBatteryCharging(bool charging);
enum
{
public:
DSi_I2CDevice(melonDS::DSi& dsi, DSi_I2CHost* host) : DSi(dsi), Host(host) {}
virtual ~DSi_I2CDevice() {}
virtual void Reset() = 0;
virtual void DoSavestate(Savestate* file) = 0;
virtual void Acquire() = 0;
virtual u8 Read(bool last) = 0;
virtual void Write(u8 val, bool last) = 0;
protected:
melonDS::DSi& DSi;
DSi_I2CHost* Host;
batteryLevel_Critical = 0x0,
batteryLevel_AlmostEmpty = 0x1,
batteryLevel_Low = 0x3,
batteryLevel_Half = 0x7,
batteryLevel_ThreeQuarters = 0xB,
batteryLevel_Full = 0xF
};
class DSi_BPTWL : public DSi_I2CDevice
u8 GetBatteryLevel();
void SetBatteryLevel(u8 batteryLevel);
}
namespace DSi_I2C
{
public:
enum
{
batteryLevel_Critical = 0x0,
batteryLevel_AlmostEmpty = 0x1,
batteryLevel_Low = 0x3,
batteryLevel_Half = 0x7,
batteryLevel_ThreeQuarters = 0xB,
batteryLevel_Full = 0xF
};
extern u8 Cnt;
enum
{
volumeKey_Up,
volumeKey_Down,
};
bool Init();
void DeInit();
void Reset();
void DoSavestate(Savestate* file);
enum
{
IRQ_PowerButtonReset = 0x01, // Triggered after releasing the power button quickly
IRQ_PowerButtonShutdown = 0x02, // Triggered after holding the power button for less than a second
IRQ_PowerButtonPressed = 0x08, // Triggered after pressing the power button
IRQ_BatteryEmpty = 0x10, //
IRQ_BatteryLow = 0x20, // Triggered when the battery level reaches 1
IRQ_VolumeSwitchPressed = 0x40, // Triggered once when the volume sliders are first pressed and repeatedly when held down
/*
Bit 2 (0x04) could be set when holding the power button for more than 5 seconds? (forced power off)
It is unknown whether it is set as the console powers off immediately.
Bit 7 (0x80) is unused?
Both bits are never used by the official ARM7 libraries, but could have some undocumented hardware functionality (?).
*/
IRQ_ValidMask = 0x7B,
};
void WriteCnt(u8 val);
DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host);
~DSi_BPTWL() override;
void Reset() override;
void DoSavestate(Savestate* file) override;
u8 ReadData();
void WriteData(u8 val);
u8 GetBootFlag() const;
void SetBootFlag(u8 boot) noexcept { Registers[0x70] = boot; }
bool GetBatteryCharging() const;
void SetBatteryCharging(bool charging);
u8 GetBatteryLevel() const;
void SetBatteryLevel(u8 batteryLevel);
// 0-31
u8 GetVolumeLevel() const;
void SetVolumeLevel(u8 volume);
// 0-4
u8 GetBacklightLevel() const;
void SetBacklightLevel(u8 backlight);
void DoHardwareReset(bool direct);
void DoShutdown();
// Used by hotkeys
void SetPowerButtonHeld(double time);
void SetPowerButtonReleased(double time);
void SetVolumeSwitchHeld(u32 key);
void SetVolumeSwitchReleased(u32 key);
s32 ProcessVolumeSwitchInput(double time);
void DoPowerButtonPress();
void DoPowerButtonReset();
void DoPowerButtonShutdown();
void DoPowerButtonForceShutdown();
void DoVolumeSwitchPress(u32 key);
void SetIRQ(u8 irqFlag);
void Acquire() override;
u8 Read(bool last) override;
void Write(u8 val, bool last) override;
private:
static const double PowerButtonShutdownTime;
static const double PowerButtonForcedShutdownTime;
static const double VolumeSwitchRepeatStart;
static const double VolumeSwitchRepeatRate;
static const u8 VolumeDownTable[32];
static const u8 VolumeUpTable[32];
double PowerButtonTime;
bool PowerButtonDownFlag;
bool PowerButtonShutdownFlag;
double VolumeSwitchTime;
double VolumeSwitchRepeatTime;
bool VolumeSwitchDownFlag ;
u32 VolumeSwitchKeysDown;
u8 Registers[0x100];
u32 CurPos;
bool GetIRQMode() const;
void ResetButtonState();
bool CheckVolumeSwitchKeysValid() const;
};
class DSi_I2CHost
{
public:
DSi_I2CHost(melonDS::DSi& dsi);
~DSi_I2CHost();
void Reset();
void DoSavestate(Savestate* file);
DSi_BPTWL* GetBPTWL() { return BPTWL; }
DSi_Camera* GetOuterCamera() { return Camera0; }
DSi_Camera* GetInnerCamera() { return Camera1; }
u8 ReadCnt() { return Cnt; }
void WriteCnt(u8 val);
u8 ReadData();
void WriteData(u8 val);
private:
melonDS::DSi& DSi;
u8 Cnt;
u8 Data;
DSi_BPTWL* BPTWL; // 4A / BPTWL IC
DSi_Camera* Camera0; // 78 / facing outside
DSi_Camera* Camera1; // 7A / selfie cam
u8 CurDeviceID;
DSi_I2CDevice* CurDevice;
void GetCurDevice();
};
//void TransferDone(u32 param);
}
#endif // DSI_I2C_H

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -20,18 +20,11 @@
#define DSI_NAND_H
#include "types.h"
#include "fatfs/ff.h"
#include "NDS_Header.h"
#include "DSi_TMD.h"
#include "SPI_Firmware.h"
#include <array>
#include <memory>
#include <vector>
#include <string>
struct AES_ctx;
namespace melonDS::DSi_NAND
namespace DSi_NAND
{
enum
@ -41,228 +34,27 @@ enum
TitleData_BannerSav,
};
union DSiFirmwareSystemSettings;
union DSiSerialData;
using DSiHardwareInfoN = std::array<u8, 0x9C>;
using DSiKey = std::array<u8, 16>;
bool Init(u8* es_keyY);
void DeInit();
class NANDImage
{
public:
explicit NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept;
explicit NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept;
~NANDImage();
NANDImage(const NANDImage&) = delete;
NANDImage& operator=(const NANDImage&) = delete;
FILE* GetFile();
NANDImage(NANDImage&& other) noexcept;
NANDImage& operator=(NANDImage&& other) noexcept;
void GetIDs(u8* emmc_cid, u64& consoleid);
Platform::FileHandle* GetFile() { return CurFile; }
void ReadHardwareInfo(u8* dataS, u8* dataN);
[[nodiscard]] const DSiKey& GetEMMCID() const noexcept { return eMMC_CID; }
[[nodiscard]] u64 GetConsoleID() const noexcept { return ConsoleID; }
[[nodiscard]] u64 GetLength() const noexcept { return Length; }
void ReadUserData(u8* data);
void PatchUserData();
explicit operator bool() const { return CurFile != nullptr; }
private:
friend class NANDMount;
void SetupFATCrypto(AES_ctx* ctx, u32 ctr);
u32 ReadFATBlock(u64 addr, u32 len, u8* buf);
u32 WriteFATBlock(u64 addr, u32 len, const u8* buf);
bool ESEncrypt(u8* data, u32 len) const;
bool ESDecrypt(u8* data, u32 len) const;
Platform::FileHandle* CurFile = nullptr;
DSiKey eMMC_CID;
u64 ConsoleID;
DSiKey FATIV;
DSiKey FATKey;
DSiKey ESKey;
u64 Length;
};
void ListTitles(u32 category, std::vector<u32>& titlelist);
bool TitleExists(u32 category, u32 titleid);
void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner);
bool ImportTitle(const char* appfile, u8* tmd, bool readonly);
void DeleteTitle(u32 category, u32 titleid);
class NANDMount
{
public:
explicit NANDMount(NANDImage& nand) noexcept;
~NANDMount() noexcept;
NANDMount(const NANDMount&) = delete;
NANDMount& operator=(const NANDMount&) = delete;
// Move constructor deleted so that the closure passed to FATFS can't be invalidated
NANDMount(NANDMount&&) = delete;
NANDMount& operator=(NANDMount&&) = delete;
bool ReadSerialData(DSiSerialData& dataS);
bool ReadHardwareInfoN(DSiHardwareInfoN& dataN);
void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN);
bool ReadUserData(DSiFirmwareSystemSettings& data);
/// Saves the given system settings to the DSi NAND,
/// to both TWLCFG0.dat and TWLCFG1.dat.
bool ApplyUserData(const DSiFirmwareSystemSettings& data);
void ListTitles(u32 category, std::vector<u32>& titlelist);
bool TitleExists(u32 category, u32 titleid);
void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner);
bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly);
bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly);
void DeleteTitle(u32 category, u32 titleid);
u32 GetTitleDataMask(u32 category, u32 titleid);
bool ImportTitleData(u32 category, u32 titleid, int type, const char* file);
bool ExportTitleData(u32 category, u32 titleid, int type, const char* file);
bool ImportFile(const char* path, const u8* data, size_t len);
bool ImportFile(const char* path, const char* in);
bool ExportFile(const char* path, const char* out);
void RemoveFile(const char* path);
void RemoveDir(const char* path);
explicit operator bool() const { return Image != nullptr && CurFS != nullptr; }
private:
u32 GetTitleVersion(u32 category, u32 titleid);
bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version);
bool CreateSaveFile(const char* path, u32 len);
bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly);
UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num);
UINT FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num);
NANDImage* Image;
// We keep a pointer to CurFS because fatfs maintains a global pointer to it;
// therefore if we embed the FATFS directly in the object,
// we can't give it move semantics.
std::unique_ptr<FATFS> CurFS;
};
typedef std::array<u8, 20> SHA1Hash;
typedef std::array<u8, 8> TitleID;
/// Firmware settings for the DSi, saved to the NAND as TWLCFG0.dat or TWLCFG1.dat.
/// The DSi mirrors this information to its own firmware for compatibility with NDS games.
/// @note The file is normally 16KiB, but only the first 432 bytes are used;
/// the rest is FF-padded.
/// This struct excludes the padding.
/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaresystemsettingsdatafiles
union DSiFirmwareSystemSettings
{
struct
{
SHA1Hash Hash;
u8 Zero00[108];
u8 Version;
u8 UpdateCounter;
u8 Zero01[2];
u32 BelowRAMAreaSize;
u32 ConfigFlags;
u8 Zero02;
u8 CountryCode;
Firmware::Language Language;
u8 RTCYear;
u32 RTCOffset;
u8 Zero3[4];
u8 EULAVersion;
u8 Zero04[9];
u8 AlarmHour;
u8 AlarmMinute;
u8 Zero05[2];
bool AlarmEnable;
u8 Zero06[2];
u8 SystemMenuUsedTitleSlots;
u8 SystemMenuFreeTitleSlots;
u8 Unknown0;
u8 Unknown1;
u8 Zero07[3];
TitleID SystemMenuMostRecentTitleID;
std::array<u16, 2> TouchCalibrationADC1;
std::array<u8, 2> TouchCalibrationPixel1;
std::array<u16, 2> TouchCalibrationADC2;
std::array<u8, 2> TouchCalibrationPixel2;
u8 Unknown2[4];
u8 Zero08[4];
u8 FavoriteColor;
u8 Zero09;
u8 BirthdayMonth;
u8 BirthdayDay;
char16_t Nickname[11];
char16_t Message[27];
u8 ParentalControlsFlags;
u8 Zero10[6];
u8 ParentalControlsRegion;
u8 ParentalControlsYearsOfAgeRating;
u8 ParentalControlsSecretQuestion;
u8 Unknown3;
u8 Zero11[2];
char ParentalControlsPIN[5];
char16_t ParentalControlsSecretAnswer[65];
};
u8 Bytes[432];
void UpdateHash();
};
static_assert(sizeof(DSiFirmwareSystemSettings) == 432, "DSiFirmwareSystemSettings must be exactly 432 bytes");
enum class ConsoleRegion : u8
{
Japan,
USA,
Europe,
Australia,
China,
Korea,
};
/// Languages that the given NAND image supports.
/// @see https://problemkaputt.de/gbatek.htm#dsiregions
enum DSiSupportedLanguageMask : u32 {
NoLanguagesSet = 0,
JapaneseSupported = 1 << 0,
EnglishSupported = 1 << 1,
FrenchSupported = 1 << 2,
GermanSupported = 1 << 3,
ItalianSupported = 1 << 4,
SpanishSupported = 1 << 5,
ChineseSupported = 1 << 6,
KoreanSupported = 1 << 7,
JapanLanguages = JapaneseSupported,
AmericaLanguages = EnglishSupported | FrenchSupported | SpanishSupported,
EuropeLanguages = EnglishSupported | FrenchSupported | GermanSupported | ItalianSupported | SpanishSupported,
AustraliaLanguages = EnglishSupported,
// "Unknown (supposedly Chinese/Mandarin?, and maybe English or so)"
ChinaLanguages = ChineseSupported | EnglishSupported,
KoreaLanguages = KoreanSupported,
};
/// Data file saved to 0:/sys/HWINFO_S.dat.
/// @note The file is normally 16KiB, but only the first 164 bytes are used;
/// the rest is FF-padded.
/// This struct excludes the padding.
/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaremiscfiles
union DSiSerialData
{
struct
{
u8 RsaSha1HMAC[0x80];
u32 Version;
u32 EntrySize;
DSiSupportedLanguageMask SupportedLanguages;
u8 Unknown0[4];
ConsoleRegion Region;
char Serial[12];
u8 Unknown1[3];
u8 TitleIDLSBs[4];
};
u8 Bytes[164];
};
static_assert(sizeof(DSiSerialData) == 164, "DSiSerialData must be exactly 164 bytes");
u32 GetTitleDataMask(u32 category, u32 titleid);
bool ImportTitleData(u32 category, u32 titleid, int type, const char* file);
bool ExportTitleData(u32 category, u32 titleid, int type, const char* file);
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -22,14 +22,10 @@
#include "DSi_NDMA.h"
#include "GPU.h"
#include "DSi_AES.h"
#include "GPU3D.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi) : DSi(dsi), CPU(cpu), Num(num)
DSi_NDMA::DSi_NDMA(u32 cpu, u32 num)
{
CPU = cpu;
Num = num;
@ -110,7 +106,7 @@ void DSi_NDMA::WriteCnt(u32 val)
case 0: DstAddrInc = 1; break;
case 1: DstAddrInc = -1; break;
case 2: DstAddrInc = 0; break;
case 3: DstAddrInc = 1; Log(LogLevel::Warn, "BAD NDMA DST INC MODE 3\n"); break;
case 3: DstAddrInc = 1; printf("BAD NDMA DST INC MODE 3\n"); break;
}
switch ((Cnt >> 13) & 0x3)
@ -128,7 +124,7 @@ void DSi_NDMA::WriteCnt(u32 val)
if ((StartMode & 0x1F) == 0x10)
Start();
else if (StartMode == 0x0A)
DSi.GPU.GPU3D.CheckFIFODMA();
GPU3D::CheckFIFODMA();
// TODO: unsupported start modes:
// * timers (00-03)
@ -138,7 +134,7 @@ void DSi_NDMA::WriteCnt(u32 val)
if (StartMode <= 0x03 || StartMode == 0x05 || (StartMode >= 0x0C && StartMode <= 0x0F) ||
(StartMode >= 0x20 && StartMode <= 0x23) || StartMode == 0x25 || StartMode == 0x27 || (StartMode >= 0x2C && StartMode <= 0x2F))
Log(LogLevel::Warn, "UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X LEN=%d BLK=%d CNT=%08X\n",
printf("UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X LEN=%d BLK=%d CNT=%08X\n",
CPU?7:9, Num, StartMode, SrcAddr, DstAddr, TotalLength, BlockLength, Cnt);
}
}
@ -182,13 +178,13 @@ void DSi_NDMA::Start()
//if (SubblockTimer & 0xFFFF)
// printf("TODO! NDMA SUBBLOCK TIMER: %08X\n", SubblockTimer);
if (DSi.DMAsRunning(CPU))
if (NDS::DMAsRunning(CPU))
Running = 1;
else
Running = 2;
InProgress = true;
DSi.StopCPU(CPU, 1<<(Num+4));
NDS::StopCPU(CPU, 1<<(Num+4));
}
void DSi_NDMA::Run()
@ -200,7 +196,7 @@ void DSi_NDMA::Run()
void DSi_NDMA::Run9()
{
if (DSi.ARM9Timestamp >= DSi.ARM9Target) return;
if (NDS::ARM9Timestamp >= NDS::ARM9Target) return;
Executing = true;
@ -215,11 +211,11 @@ void DSi_NDMA::Run9()
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][2] + DSi.ARM9MemTimings[CurDstAddr >> 14][2];
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2];
}
else
{
unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][3] + DSi.ARM9MemTimings[CurDstAddr >> 14][3];
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3];
if ((CurSrcAddr >> 24) == (CurDstAddr >> 24))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
@ -235,12 +231,12 @@ void DSi_NDMA::Run9()
while (IterCount > 0 && !Stall)
{
DSi.ARM9Timestamp += (unitcycles << DSi.ARM9ClockShift);
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
if (dofill)
DSi.ARM9Write32(CurDstAddr, FillData);
DSi::ARM9Write32(CurDstAddr, FillData);
else
DSi.ARM9Write32(CurDstAddr, DSi.ARM9Read32(CurSrcAddr));
DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
@ -248,7 +244,7 @@ void DSi_NDMA::Run9()
RemCount--;
TotalRemCount--;
if (DSi.ARM9Timestamp >= DSi.ARM9Target) break;
if (NDS::ARM9Timestamp >= NDS::ARM9Target) break;
}
Executing = false;
@ -259,10 +255,10 @@ void DSi_NDMA::Run9()
if (IterCount == 0)
{
Running = 0;
DSi.ResumeCPU(0, 1<<(Num+4));
NDS::ResumeCPU(0, 1<<(Num+4));
if (StartMode == 0x0A)
DSi.GPU.GPU3D.CheckFIFODMA();
GPU3D::CheckFIFODMA();
}
return;
@ -271,25 +267,25 @@ void DSi_NDMA::Run9()
if ((StartMode & 0x1F) == 0x10) // CHECKME
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num);
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
}
else if (!(Cnt & (1<<29)))
{
if (TotalRemCount == 0)
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num);
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
}
}
Running = 0;
InProgress = false;
DSi.ResumeCPU(0, 1<<(Num+4));
NDS::ResumeCPU(0, 1<<(Num+4));
}
void DSi_NDMA::Run7()
{
if (DSi.ARM7Timestamp >= DSi.ARM7Target) return;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) return;
Executing = true;
@ -304,11 +300,11 @@ void DSi_NDMA::Run7()
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][2] + DSi.ARM7MemTimings[CurDstAddr >> 15][2];
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2];
}
else
{
unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][3] + DSi.ARM7MemTimings[CurDstAddr >> 15][3];
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3];
if ((CurSrcAddr >> 23) == (CurDstAddr >> 23))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
@ -324,12 +320,12 @@ void DSi_NDMA::Run7()
while (IterCount > 0 && !Stall)
{
DSi.ARM7Timestamp += unitcycles;
NDS::ARM7Timestamp += unitcycles;
if (dofill)
DSi.ARM7Write32(CurDstAddr, FillData);
DSi::ARM7Write32(CurDstAddr, FillData);
else
DSi.ARM7Write32(CurDstAddr, DSi.ARM7Read32(CurSrcAddr));
DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
@ -337,7 +333,7 @@ void DSi_NDMA::Run7()
RemCount--;
TotalRemCount--;
if (DSi.ARM7Timestamp >= DSi.ARM7Target) break;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) break;
}
Executing = false;
@ -348,10 +344,10 @@ void DSi_NDMA::Run7()
if (IterCount == 0)
{
Running = 0;
DSi.ResumeCPU(1, 1<<(Num+4));
NDS::ResumeCPU(1, 1<<(Num+4));
DSi.AES.CheckInputDMA();
DSi.AES.CheckOutputDMA();
DSi_AES::CheckInputDMA();
DSi_AES::CheckOutputDMA();
}
return;
@ -360,23 +356,21 @@ void DSi_NDMA::Run7()
if ((StartMode & 0x1F) == 0x10) // CHECKME
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num);
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
}
else if (!(Cnt & (1<<29)))
{
if (TotalRemCount == 0)
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num);
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
}
}
Running = 0;
InProgress = false;
DSi.ResumeCPU(1, 1<<(Num+4));
NDS::ResumeCPU(1, 1<<(Num+4));
DSi.AES.CheckInputDMA();
DSi.AES.CheckOutputDMA();
DSi_AES::CheckInputDMA();
DSi_AES::CheckOutputDMA();
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -22,14 +22,10 @@
#include "types.h"
#include "Savestate.h"
namespace melonDS
{
class DSi;
class DSi_NDMA
{
public:
DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi);
DSi_NDMA(u32 cpu, u32 num);
~DSi_NDMA();
void Reset();
@ -44,12 +40,12 @@ public:
void Run9();
void Run7();
bool IsInMode(u32 mode) const
bool IsInMode(u32 mode)
{
return ((mode == StartMode) && (Cnt & 0x80000000));
}
bool IsRunning() const { return Running!=0; }
bool IsRunning() { return Running!=0; }
void StartIfNeeded(u32 mode)
{
@ -77,7 +73,6 @@ public:
u32 Cnt;
private:
melonDS::DSi& DSi;
u32 CPU, Num;
u32 StartMode;
@ -99,5 +94,4 @@ private:
bool IsGXFIFODMA;
};
}
#endif // DSI_NDMA_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -24,14 +24,8 @@
#include "WifiAP.h"
#include "Platform.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
u8 CIS0[256] =
const u8 CIS0[256] =
{
0x01, 0x03, 0xD9, 0x01, 0xFF,
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
@ -70,7 +64,7 @@ u8 CIS0[256] =
0x00, 0x00, 0x00
};
u8 CIS1[256] =
const u8 CIS1[256] =
{
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
0x21, 0x02, 0x0C, 0x00,
@ -119,32 +113,33 @@ u8 CIS1[256] =
};
DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) :
DSi_SDDevice(host),
Mailbox
{
// HACK
// the mailboxes are supposed to be 0x80 bytes
// however, as we do things instantly, emulating this is meaningless
// and only adds complication
DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600),
DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600),
// mailbox 8: extra mailbox acting as a bigger RX buffer
DynamicFIFO<u8>(0x8000)
},
DSi(dsi)
{
DSi.RegisterEventFuncs(Event_DSi_NWifi, this, {MakeEventThunk(DSi_NWifi, MSTimer)});
DSi_NWifi* Ctx = nullptr;
DSi_NWifi::DSi_NWifi(DSi_SDHost* host)
: DSi_SDDevice(host),
Mailbox
{
// HACK
// the mailboxes are supposed to be 0x80 bytes
// however, as we do things instantly, emulating this is meaningless
// and only adds complication
DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600),
DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600), DynamicFIFO<u8>(0x600),
// mailbox 8: extra mailbox acting as a bigger RX buffer
DynamicFIFO<u8>(0x8000)
}
{
// this seems to control whether the firmware upload is done
EEPROMReady = 0;
Ctx = this;
}
DSi_NWifi::~DSi_NWifi()
{
DSi.CancelEvent(Event_DSi_NWifi);
DSi.UnregisterEventFuncs(Event_DSi_NWifi);
NDS::CancelEvent(NDS::Event_DSi_NWifi);
Ctx = nullptr;
}
void DSi_NWifi::Reset()
@ -165,50 +160,45 @@ void DSi_NWifi::Reset()
for (int i = 0; i < 9; i++)
Mailbox[i].Clear();
const Firmware& fw = DSi.SPI.GetFirmware();
MacAddress mac = fw.GetHeader().MacAddr;
Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
u8* mac = SPI_Firmware::GetWifiMAC();
printf("NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Firmware::WifiBoard type = fw.GetHeader().WifiBoard;
u8 type = SPI_Firmware::GetNWifiVersion();
switch (type)
{
case Firmware::WifiBoard::W015: // AR6002
case 1: // AR6002
ROMID = 0x20000188;
ChipID = 0x02000001;
HostIntAddr = 0x00500400;
break;
case Firmware::WifiBoard::W024: // AR6013
case 2: // AR6013
ROMID = 0x23000024;
ChipID = 0x0D000000;
HostIntAddr = 0x00520000;
break;
case Firmware::WifiBoard::W028: // AR6014 (3DS)
case 3: // AR6014 (3DS)
ROMID = 0x2300006F;
ChipID = 0x0D000001;
HostIntAddr = 0x00520000;
Log(LogLevel::Info, "NWifi: hardware is 3DS type, unchecked\n");
printf("NWifi: hardware is 3DS type, unchecked\n");
break;
default:
Log(LogLevel::Warn, "NWifi: unknown hardware type 0x%x, assuming AR6002\n", static_cast<u8>(type));
printf("NWifi: unknown hardware type, assuming AR6002\n");
ROMID = 0x20000188;
ChipID = 0x02000001;
HostIntAddr = 0x00500400;
break;
}
CIS0[9] = ChipID >= 0x0D000000;
CIS1[4] = CIS0[9];
memset(EEPROM, 0, 0x400);
*(u32*)&EEPROM[0x000] = 0x300;
*(u16*)&EEPROM[0x008] = 0x8348; // TODO: determine properly (country code)
memcpy(&EEPROM[0x00A], mac.data(), mac.size());
memcpy(&EEPROM[0x00A], mac, 6);
*(u32*)&EEPROM[0x010] = 0x60000000;
memset(&EEPROM[0x03C], 0xFF, 0x70);
@ -230,9 +220,7 @@ void DSi_NWifi::Reset()
BeaconTimer = 0x10A2220ULL;
ConnectionStatus = 0;
SendBSSInfo = true;
DSi.CancelEvent(Event_DSi_NWifi);
NDS::CancelEvent(NDS::Event_DSi_NWifi);
}
void DSi_NWifi::DoSavestate(Savestate* file)
@ -365,7 +353,7 @@ u8 DSi_NWifi::F0_Read(u32 addr)
return CIS1[addr & 0xFF];
}
Log(LogLevel::Debug, "NWIFI: unknown func0 read %05X\n", addr);
printf("NWIFI: unknown func0 read %05X\n", addr);
return 0;
}
@ -379,7 +367,7 @@ void DSi_NWifi::F0_Write(u32 addr, u8 val)
return;
}
Log(LogLevel::Debug, "NWIFI: unknown func0 write %05X %02X\n", addr, val);
printf("NWIFI: unknown func0 write %05X %02X\n", addr, val);
}
@ -492,7 +480,7 @@ void DSi_NWifi::F1_Write(u32 addr, u8 val)
{
if (addr < 0x100)
{
if (Mailbox[0].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX0 FULL\n");
if (Mailbox[0].IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0].Write(val);
if (addr == 0xFF) HandleCommand();
UpdateIRQ_F1();
@ -500,21 +488,21 @@ void DSi_NWifi::F1_Write(u32 addr, u8 val)
}
else if (addr < 0x200)
{
if (Mailbox[1].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX1 FULL\n");
if (Mailbox[1].IsFull()) printf("!!! NWIFI: MBOX1 FULL\n");
Mailbox[1].Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x300)
{
if (Mailbox[2].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX2 FULL\n");
if (Mailbox[2].IsFull()) printf("!!! NWIFI: MBOX2 FULL\n");
Mailbox[2].Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x400)
{
if (Mailbox[3].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX3 FULL\n");
if (Mailbox[3].IsFull()) printf("!!! NWIFI: MBOX3 FULL\n");
Mailbox[3].Write(val);
UpdateIRQ_F1();
return;
@ -555,7 +543,7 @@ void DSi_NWifi::F1_Write(u32 addr, u8 val)
}
else if (addr < 0x1000)
{
if (Mailbox[0].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX0 FULL\n");
if (Mailbox[0].IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0].Write(val);
if (addr == 0xFFF) HandleCommand();
UpdateIRQ_F1();
@ -563,35 +551,35 @@ void DSi_NWifi::F1_Write(u32 addr, u8 val)
}
else if (addr < 0x1800)
{
if (Mailbox[1].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX1 FULL\n");
if (Mailbox[1].IsFull()) printf("!!! NWIFI: MBOX1 FULL\n");
Mailbox[1].Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x2000)
{
if (Mailbox[2].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX2 FULL\n");
if (Mailbox[2].IsFull()) printf("!!! NWIFI: MBOX2 FULL\n");
Mailbox[2].Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x2800)
{
if (Mailbox[3].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX3 FULL\n");
if (Mailbox[3].IsFull()) printf("!!! NWIFI: MBOX3 FULL\n");
Mailbox[3].Write(val);
UpdateIRQ_F1();
return;
}
else
{
if (Mailbox[0].IsFull()) Log(LogLevel::Debug, "!!! NWIFI: MBOX0 FULL\n");
if (Mailbox[0].IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0].Write(val);
if (addr == 0x3FFF) HandleCommand(); // CHECKME
UpdateIRQ_F1();
return;
}
Log(LogLevel::Debug, "NWIFI: unknown func1 write %05X %02X\n", addr, val);
printf("NWIFI: unknown func1 write %05X %02X\n", addr, val);
}
@ -603,7 +591,7 @@ u8 DSi_NWifi::SDIO_Read(u32 func, u32 addr)
case 1: return F1_Read(addr);
}
Log(LogLevel::Debug, "NWIFI: unknown SDIO read %d %05X\n", func, addr);
printf("NWIFI: unknown SDIO read %d %05X\n", func, addr);
return 0;
}
@ -615,7 +603,7 @@ void DSi_NWifi::SDIO_Write(u32 func, u32 addr, u8 val)
case 1: return F1_Write(addr, val);
}
Log(LogLevel::Debug, "NWIFI: unknown SDIO write %d %05X %02X\n", func, addr, val);
printf("NWIFI: unknown SDIO write %d %05X %02X\n", func, addr, val);
}
@ -688,12 +676,12 @@ void DSi_NWifi::SendCMD(u8 cmd, u32 param)
return;
}
Log(LogLevel::Warn, "NWIFI: unknown CMD %d %08X\n", cmd, param);
printf("NWIFI: unknown CMD %d %08X\n", cmd, param);
}
void DSi_NWifi::SendACMD(u8 cmd, u32 param)
{
Log(LogLevel::Warn, "NWIFI: unknown ACMD %d %08X\n", cmd, param);
printf("NWIFI: unknown ACMD %d %08X\n", cmd, param);
}
void DSi_NWifi::ContinueTransfer()
@ -784,7 +772,7 @@ void DSi_NWifi::BMI_Command()
{
case 0x01: // BMI_DONE
{
Log(LogLevel::Debug, "BMI_DONE\n");
printf("BMI_DONE\n");
EEPROMReady = 1; // GROSS FUCKING HACK
u8 ready_msg[6] = {0x0A, 0x00, 0x08, 0x06, 0x16, 0x00};
SendWMIEvent(0, 0x0001, ready_msg, 6);
@ -796,7 +784,7 @@ void DSi_NWifi::BMI_Command()
{
u32 addr = MB_Read32(0);
u32 len = MB_Read32(0);
Log(LogLevel::Debug, "BMI mem write %08X %08X\n", addr, len);
printf("BMI mem write %08X %08X\n", addr, len);
for (u32 i = 0; i < len; i++)
{
@ -812,7 +800,7 @@ void DSi_NWifi::BMI_Command()
u32 entry = MB_Read32(0);
u32 arg = MB_Read32(0);
Log(LogLevel::Debug, "BMI_EXECUTE %08X %08X\n", entry, arg);
printf("BMI_EXECUTE %08X %08X\n", entry, arg);
}
return;
@ -842,14 +830,14 @@ void DSi_NWifi::BMI_Command()
case 0x0D: // BMI_LZ_STREAM_START
{
u32 addr = MB_Read32(0);
Log(LogLevel::Debug, "BMI_LZ_STREAM_START %08X\n", addr);
printf("BMI_LZ_STREAM_START %08X\n", addr);
}
return;
case 0x0E: // BMI_LZ_DATA
{
u32 len = MB_Read32(0);
Log(LogLevel::Debug, "BMI LZ write %08X\n", len);
printf("BMI LZ write %08X\n", len);
//FILE* f = fopen("debug/wififirm.bin", "ab");
for (u32 i = 0; i < len; i++)
@ -864,7 +852,7 @@ void DSi_NWifi::BMI_Command()
return;
default:
Log(LogLevel::Warn, "unknown BMI command %08X\n", cmd);
printf("unknown BMI command %08X\n", cmd);
return;
}
}
@ -883,7 +871,7 @@ void DSi_NWifi::HTC_Command()
{
u16 svc_id = MB_Read16(0);
u16 conn_flags = MB_Read16(0);
Log(LogLevel::Debug, "service connect %04X %04X %04X\n", svc_id, conn_flags, MB_Read16(0));
printf("service connect %04X %04X %04X\n", svc_id, conn_flags, MB_Read16(0));
u8 svc_resp[8];
// responses from hardware:
@ -904,7 +892,7 @@ void DSi_NWifi::HTC_Command()
case 0x0004: // setup complete
{
u8 ready_evt[12];
memcpy(&ready_evt[0], &EEPROM[0xA], 6); // MAC address
memcpy(&ready_evt[0], SPI_Firmware::GetWifiMAC(), 6);
ready_evt[6] = 0x02;
ready_evt[7] = 0;
*(u32*)&ready_evt[8] = 0x2300006C;
@ -915,12 +903,12 @@ void DSi_NWifi::HTC_Command()
SendWMIEvent(1, 0x1006, regdomain_evt, 4);
BootPhase = 2;
DSi.ScheduleEvent(Event_DSi_NWifi, false, 33611, 0, 0);
NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, MSTimer, 0);
}
break;
default:
Log(LogLevel::Warn, "unknown HTC command %04X\n", cmd);
printf("unknown HTC command %04X\n", cmd);
for (int i = 0; i < len; i++)
{
printf("%02X ", Mailbox[0].Read());
@ -959,9 +947,9 @@ void DSi_NWifi::WMI_Command()
case 0x0003: // disconnect
{
if (ConnectionStatus != 1)
Log(LogLevel::Warn, "WMI: ?? trying to disconnect while not connected\n");
printf("WMI: ?? trying to disconnect while not connected\n");
Log(LogLevel::Debug, "WMI: disconnect\n");
printf("WMI: disconnect\n");
ConnectionStatus = 0;
u8 reply[11];
@ -997,16 +985,16 @@ void DSi_NWifi::WMI_Command()
u8 scantype = Mailbox[0].Read();
u8 nchannels = Mailbox[0].Read();
Log(LogLevel::Debug, "WMI: start scan, forceFG=%d, legacy=%d, scanTime=%d, interval=%d, scanType=%d, chan=%d\n",
printf("WMI: start scan, forceFG=%d, legacy=%d, scanTime=%d, interval=%d, scanType=%d, chan=%d\n",
forcefg, legacy, scantime, forceinterval, scantype, nchannels);
if (ScanTimer > 0)
{
Log(LogLevel::Debug, "!! CHECKME: START SCAN BUT WAS ALREADY SCANNING (%d)\n", ScanTimer);
printf("!! CHECKME: START SCAN BUT WAS ALREADY SCANNING (%d)\n", ScanTimer);
}
// checkme
ScanTimer = scantime*8;
ScanTimer = scantime*5;
}
break;
@ -1025,7 +1013,7 @@ void DSi_NWifi::WMI_Command()
Mailbox[0].Read();
u32 iemask = MB_Read32(0);
Log(LogLevel::Debug, "WMI: set BSS filter, filter=%02X, iemask=%08X\n", bssfilter, iemask);
printf("WMI: set BSS filter, filter=%02X, iemask=%08X\n", bssfilter, iemask);
}
break;
@ -1040,8 +1028,7 @@ void DSi_NWifi::WMI_Command()
ssid[i] = Mailbox[0].Read();
// TODO: store it somewhere
Log(LogLevel::Debug, "WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid);
SendBSSInfo = flags == 0 || strcmp(ssid, WifiAP::APName) == 0;
printf("WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid);
}
break;
@ -1079,7 +1066,7 @@ void DSi_NWifi::WMI_Command()
channels[i] = MB_Read16(0);
// TODO: store it somewhere
Log(LogLevel::Debug, "WMI: set channel params: scan=%d, phymode=%d, len=%d, channels=", scan, phymode, len);
printf("WMI: set channel params: scan=%d, phymode=%d, len=%d, channels=", scan, phymode, len);
for (int i = 0; i < len && i < 32; i++)
printf("%d,", channels[i]);
printf("\n");
@ -1123,7 +1110,7 @@ void DSi_NWifi::WMI_Command()
break;
default:
Log(LogLevel::Warn, "WMI: unknown ext cmd 002E:%04X\n", extcmd);
printf("WMI: unknown ext cmd 002E:%04X\n", extcmd);
break;
}
}
@ -1174,7 +1161,7 @@ void DSi_NWifi::WMI_Command()
break;
default:
Log(LogLevel::Warn, "unknown WMI command %04X (header: %04X:%04X:%04X)\n", cmd, h0, len, h2);
printf("unknown WMI command %04X (header: %04X:%04X:%04X)\n", cmd, h0, len, h2);
for (int i = 0; i < len-2; i++)
{
printf("%02X ", Mailbox[0].Read());
@ -1223,12 +1210,12 @@ void DSi_NWifi::WMI_ConnectToNetwork()
(gCryptoType != 0x01) ||
(memcmp(bssid, WifiAP::APMac, 6)))
{
Log(LogLevel::Error, "WMI_Connect: bad parameters\n");
printf("WMI_Connect: bad parameters\n");
// TODO: send disconnect??
return;
}
Log(LogLevel::Debug, "WMI: connecting to network %s\n", ssid);
printf("WMI: connecting to network %s\n", ssid);
u8 reply[20];
@ -1253,7 +1240,7 @@ void DSi_NWifi::WMI_SendPacket(u16 len)
{
if (ConnectionStatus != 1)
{
Log(LogLevel::Warn, "WMI: !! trying to send shit while not connected\n");
printf("WMI: !! trying to send shit while not connected\n");
// TODO: report error??
return;
}
@ -1268,7 +1255,7 @@ void DSi_NWifi::WMI_SendPacket(u16 len)
if (type == 2) // data sync
{
Log(LogLevel::Debug, "WMI: data sync\n");
printf("WMI: data sync\n");
/*Mailbox[8].Write(2); // eid
Mailbox[8].Write(0x00); // flags
@ -1283,7 +1270,7 @@ void DSi_NWifi::WMI_SendPacket(u16 len)
if (type)
{
Log(LogLevel::Debug, "WMI: special frame %04X len=%d\n", hdr, len);
printf("WMI: special frame %04X len=%d\n", hdr, len);
for (int i = 0; i < len-2; i++)
{
printf("%02X ", Mailbox[0].Read());
@ -1293,7 +1280,7 @@ void DSi_NWifi::WMI_SendPacket(u16 len)
return;
}
Log(LogLevel::Debug, "WMI: send packet, hdr=%04X, len=%d\n", hdr, len);
printf("WMI: send packet, hdr=%04X, len=%d\n", hdr, len);
u8 dstmac[6];
u8 srcmac[6];
@ -1308,7 +1295,7 @@ void DSi_NWifi::WMI_SendPacket(u16 len)
if (plen > len-16)
{
Log(LogLevel::Error, "WMI: bad packet length %d > %d\n", plen, len-16);
printf("WMI: bad packet length %d > %d\n", plen, len-16);
return;
}
@ -1317,7 +1304,7 @@ void DSi_NWifi::WMI_SendPacket(u16 len)
if (h0 != 0x0003AAAA || h1 != 0x0000)
{
Log(LogLevel::Error, "WMI: bad LLC/SLIP header\n");
printf("WMI: bad LLC/SLIP header\n");
return;
}
@ -1340,14 +1327,14 @@ void DSi_NWifi::WMI_SendPacket(u16 len)
}
printf("\n");*/
Platform::Net_SendPacket(LANBuffer, lan_len, DSi.UserData);
Platform::LAN_SendPacket(LANBuffer, lan_len);
}
void DSi_NWifi::SendWMIEvent(u8 ep, u16 id, u8* data, u32 len)
{
if (!Mailbox[8].CanFit(6+len+2+8))
{
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI event %04X\n", id);
printf("NWifi: !! not enough space in RX buffer for WMI event %04X\n", id);
return;
}
@ -1380,7 +1367,7 @@ void DSi_NWifi::SendWMIAck(u8 ep)
{
if (!Mailbox[8].CanFit(6+12))
{
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI ack (ep #%d)\n", ep);
printf("NWifi: !! not enough space in RX buffer for WMI ack (ep #%d)\n", ep);
return;
}
@ -1411,14 +1398,9 @@ void DSi_NWifi::SendWMIAck(u8 ep)
void DSi_NWifi::SendWMIBSSInfo(u8 type, u8* data, u32 len)
{
if (!SendBSSInfo) {
Log(LogLevel::Info, "NWifi: melonAP filtered, not sending WMI BSSINFO event\n");
return;
}
if (!Mailbox[8].CanFit(6+len+2+16))
{
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n");
printf("NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n");
return;
}
@ -1453,29 +1435,24 @@ void DSi_NWifi::CheckRX()
if (!Mailbox[8].CanFit(2048))
return;
int rxlen = Platform::Net_RecvPacket(LANBuffer, DSi.UserData);
while (rxlen > 0)
int rxlen = Platform::LAN_RecvPacket(LANBuffer);
if (rxlen > 0)
{
//printf("WMI packet recv %04X %04X %04X\n", *(u16*)&LANBuffer[0], *(u16*)&LANBuffer[2], *(u16*)&LANBuffer[4]);
// check destination MAC
if (*(u32*)&LANBuffer[0] != 0xFFFFFFFF || *(u16*)&LANBuffer[4] != 0xFFFF)
{
if (memcmp(&LANBuffer[0], &EEPROM[0x00A], 6))
{
rxlen = Platform::Net_RecvPacket(LANBuffer, DSi.UserData);
continue;
}
return;
}
// check source MAC, in case we get a packet we just sent out
if (!memcmp(&LANBuffer[6], &EEPROM[0x00A], 6))
{
rxlen = Platform::Net_RecvPacket(LANBuffer, DSi.UserData);
continue;
}
return;
// packet is good
Log(LogLevel::Debug, "WMI: receive packet %04X, len=%d\n", *(u16*)&LANBuffer[12], rxlen);
printf("WMI: receive packet %04X, len=%d\n", *(u16*)&LANBuffer[12], rxlen);
/*for (int i = 0; i < rxlen; i++)
{
@ -1518,14 +1495,13 @@ void DSi_NWifi::CheckRX()
Mailbox[8].Write(LANBuffer[14+i]);
DrainRXBuffer();
return;
}
}
u32 DSi_NWifi::WindowRead(u32 addr)
{
Log(LogLevel::Debug, "NWifi: window read %08X\n", addr);
printf("NWifi: window read %08X\n", addr);
if ((addr & 0xFFFF00) == HostIntAddr)
{
@ -1564,30 +1540,11 @@ u32 DSi_NWifi::WindowRead(u32 addr)
void DSi_NWifi::WindowWrite(u32 addr, u32 val)
{
Log(LogLevel::Debug, "NWifi: window write %08X %08X\n", addr, val);
printf("NWifi: window write %08X %08X\n", addr, val);
}
void DSi_NWifi::DrainRXBuffer()
{
while (Mailbox[8].Level() >= 6)
{
u16 len = Mailbox[8].Peek(2) | (Mailbox[8].Peek(3) << 8);
u32 totallen = len + 6;
u32 required = (totallen + 0x7F) & ~0x7F;
if (!Mailbox[4].CanFit(required))
break;
u32 i = 0;
for (; i < totallen; i++) Mailbox[4].Write(Mailbox[8].Read());
for (; i < required; i++) Mailbox[4].Write(0);
}
UpdateIRQ_F1();
}
void DSi_NWifi::MSTimer(u32 param)
void DSi_NWifi::_MSTimer()
{
BeaconTimer++;
@ -1610,7 +1567,7 @@ void DSi_NWifi::MSTimer(u32 param)
};
SendWMIBSSInfo(0x01, beacon, sizeof(beacon));
Log(LogLevel::Debug, "send beacon\n");
printf("send beacon\n");
}
if (ScanTimer == 0)
@ -1625,8 +1582,29 @@ void DSi_NWifi::MSTimer(u32 param)
//if (Mailbox[4].IsEmpty())
CheckRX();
}
DSi.ScheduleEvent(Event_DSi_NWifi, true, 33611, 0, 0);
}
}
void DSi_NWifi::DrainRXBuffer()
{
while (Mailbox[8].Level() >= 6)
{
u16 len = Mailbox[8].Peek(2) | (Mailbox[8].Peek(3) << 8);
u32 totallen = len + 6;
u32 required = (totallen + 0x7F) & ~0x7F;
if (!Mailbox[4].CanFit(required))
break;
u32 i = 0;
for (; i < totallen; i++) Mailbox[4].Write(Mailbox[8].Read());
for (; i < required; i++) Mailbox[4].Write(0);
}
UpdateIRQ_F1();
}
void DSi_NWifi::MSTimer(u32 param)
{
Ctx->_MSTimer();
NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, MSTimer, 0);
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -23,12 +23,10 @@
#include "FIFO.h"
#include "Savestate.h"
namespace melonDS
{
class DSi_NWifi : public DSi_SDDevice
{
public:
DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host);
DSi_NWifi(DSi_SDHost* host);
~DSi_NWifi();
void Reset();
@ -42,10 +40,11 @@ public:
void SetIRQ_F1_Counter(u32 n);
void MSTimer(u32 param);
void _MSTimer();
static void MSTimer(u32 param);
private:
melonDS::DSi& DSi;
u32 TransferCmd;
u32 TransferAddr;
u32 RemSize;
@ -147,9 +146,6 @@ private:
u32 ConnectionStatus;
u8 LANBuffer[2048];
bool SendBSSInfo;
};
}
#endif // DSI_NWIFI_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2024 melonDS team
Copyright 2016-2022 melonDS team
This file is part of melonDS.
@ -18,20 +18,11 @@
#include <stdio.h>
#include <string.h>
#include "Args.h"
#include "DSi.h"
#include "DSi_SD.h"
#include "DSi_NAND.h"
#include "DSi_NWifi.h"
#include "Platform.h"
namespace melonDS
{
using std::holds_alternative;
using std::unique_ptr;
using std::get_if;
using std::get;
using namespace Platform;
// observed IRQ behavior during transfers
//
@ -55,40 +46,27 @@ using namespace Platform;
#define SD_DESC Num?"SDIO":"SD/MMC"
enum
DSi_SDHost::DSi_SDHost(u32 num)
{
Transfer_TX = 0,
Transfer_RX,
};
Num = num;
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
{
DSi.RegisterEventFuncs(Event_DSi_SDMMCTransfer, this,
{MakeEventThunk(DSi_SDHost, FinishTX),
MakeEventThunk(DSi_SDHost, FinishRX)});
Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr;
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
Ports[1] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(nand));
}
// Creates an SDIO host
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
{
DSi.RegisterEventFuncs(Event_DSi_SDIOTransfer, this,
{MakeEventThunk(DSi_SDHost, FinishTX),
MakeEventThunk(DSi_SDHost, FinishRX)});
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
Ports[0] = nullptr;
Ports[1] = nullptr;
}
DSi_SDHost::~DSi_SDHost()
{
DSi.UnregisterEventFuncs(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer);
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
}
// unique_ptr's destructor will clean up the ports
void DSi_SDHost::CloseHandles()
{
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
Ports[0] = nullptr;
Ports[1] = nullptr;
}
void DSi_SDHost::Reset()
@ -131,70 +109,51 @@ void DSi_SDHost::Reset()
TXReq = false;
if (Ports[0]) Ports[0]->Reset();
if (Ports[1]) Ports[1]->Reset();
}
CloseHandles();
FATStorage* DSi_SDHost::GetSDCard() noexcept
{
if (Num != 0) return nullptr;
return static_cast<DSi_MMCStorage*>(Ports[0].get())->GetSDCard();
}
const FATStorage* DSi_SDHost::GetSDCard() const noexcept
{
if (Num != 0) return nullptr;
return static_cast<const DSi_MMCStorage*>(Ports[0].get())->GetSDCard();
}
DSi_NAND::NANDImage* DSi_SDHost::GetNAND() noexcept
{
if (Num != 0) return nullptr;
return static_cast<DSi_MMCStorage*>(Ports[1].get())->GetNAND();
}
const DSi_NAND::NANDImage* DSi_SDHost::GetNAND() const noexcept
{
if (Num != 0) return nullptr;
return static_cast<const DSi_MMCStorage*>(Ports[1].get())->GetNAND();
}
void DSi_SDHost::SetSDCard(FATStorage&& sdcard) noexcept
{
if (Num != 0) return;
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(sdcard));
}
void DSi_SDHost::SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
{
if (Num != 0) return;
if (sdcard)
if (Num == 0)
{
if (!Ports[0])
DSi_MMCStorage* sd;
DSi_MMCStorage* mmc;
if (Platform::GetConfigBool(Platform::DSiSD_Enable))
{
Ports[0] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard));
std::string folderpath;
if (Platform::GetConfigBool(Platform::DSiSD_FolderSync))
folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath);
else
folderpath = "";
sd = new DSi_MMCStorage(this,
false,
Platform::GetConfigString(Platform::DSiSD_ImagePath),
(u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024,
Platform::GetConfigBool(Platform::DSiSD_ReadOnly),
folderpath);
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
sd->SetCID(sd_cid);
}
else
{
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(*sdcard));
}
sd = nullptr;
std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath);
std::string instnand = nandpath + Platform::InstanceFileSuffix();
mmc = new DSi_MMCStorage(this, true, instnand);
mmc->SetCID(DSi::eMMC_CID);
Ports[0] = sd;
Ports[1] = mmc;
}
else
{
Ports[0] = nullptr;
DSi_NWifi* nwifi = new DSi_NWifi(this);
Ports[0] = nwifi;
}
sdcard = std::nullopt;
// a moved-from optional isn't empty, it contains a moved-from object
}
void DSi_SDHost::SetNAND(DSi_NAND::NANDImage&& nand) noexcept
{
if (Num != 0) return;
static_cast<DSi_MMCStorage*>(Ports[1].get())->SetNAND(std::move(nand));
if (Ports[0]) Ports[0]->Reset();
if (Ports[1]) Ports[1]->Reset();
}
void DSi_SDHost::DoSavestate(Savestate* file)
@ -252,7 +211,7 @@ void DSi_SDHost::UpdateData32IRQ()
newflags &= (Data32IRQ >> 11);
if ((oldflags == 0) && (newflags != 0))
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::ClearIRQ(u32 irq)
@ -268,7 +227,7 @@ void DSi_SDHost::SetIRQ(u32 irq)
u32 newflags = IRQStatus & ~IRQMask;
if ((oldflags == 0) && (newflags != 0))
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::UpdateIRQ(u32 oldmask)
@ -277,7 +236,7 @@ void DSi_SDHost::UpdateIRQ(u32 oldmask)
u32 newflags = IRQStatus & ~IRQMask;
if ((oldflags == 0) && (newflags != 0))
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::SetCardIRQ()
@ -285,7 +244,7 @@ void DSi_SDHost::SetCardIRQ()
if (!(CardIRQCtl & (1<<0))) return;
u16 oldflags = CardIRQStatus & ~CardIRQMask;
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev->IRQ) CardIRQStatus |= (1<<0);
else CardIRQStatus &= ~(1<<0);
@ -294,8 +253,8 @@ void DSi_SDHost::SetCardIRQ()
if ((oldflags == 0) && (newflags != 0)) // checkme
{
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1);
}
}
@ -306,8 +265,8 @@ void DSi_SDHost::UpdateCardIRQ(u16 oldmask)
if ((oldflags == 0) && (newflags != 0)) // checkme
{
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1);
}
}
@ -323,17 +282,19 @@ void DSi_SDHost::SendResponse(u32 val, bool last)
void DSi_SDHost::FinishRX(u32 param)
{
CheckSwapFIFO();
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
if (DataMode == 1)
UpdateFIFO32();
host->CheckSwapFIFO();
if (host->DataMode == 1)
host->UpdateFIFO32();
else
SetIRQ(24);
host->SetIRQ(24);
}
u32 DSi_SDHost::DataRX(const u8* data, u32 len)
u32 DSi_SDHost::DataRX(u8* data, u32 len)
{
if (len != BlockLen16) { Log(LogLevel::Warn, "!! BAD BLOCKLEN\n"); len = BlockLen16; }
if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; }
bool last = (BlockCountInternal == 0);
@ -348,19 +309,21 @@ u32 DSi_SDHost::DataRX(const u8* data, u32 len)
// we need a delay because DSi boot2 will send a command and then wait for IRQ0
// but if IRQ24 is thrown instantly, the handler clears IRQ0 before the
// send-command function starts polling IRQ status
DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
false, 512, Transfer_RX, 0);
u32 param = Num | (last << 1);
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
false, 512, FinishRX, param);
return len;
}
void DSi_SDHost::FinishTX(u32 param)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1];
if (BlockCountInternal == 0)
if (host->BlockCountInternal == 0)
{
if (StopAction & (1<<8))
if (host->StopAction & (1<<8))
{
if (dev) dev->SendCMD(12, 0);
}
@ -368,8 +331,8 @@ void DSi_SDHost::FinishTX(u32 param)
// CHECKME: presumably IRQ2 should not trigger here, but rather
// when the data transfer is done
//SetIRQ(0);
SetIRQ(2);
TXReq = false;
host->SetIRQ(2);
host->TXReq = false;
}
else
{
@ -390,14 +353,14 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len)
if (DataFIFO32.IsEmpty())
{
SetIRQ(25);
DSi.CheckNDMAs(1, Num ? 0x29 : 0x28);
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
}
return 0;
}
// drain FIFO32 into FIFO16
if (!DataFIFO[f].IsEmpty()) Log(LogLevel::Warn, "VERY BAD!! TRYING TO DRAIN FIFO32 INTO FIFO16 BUT IT CONTAINS SHIT ALREADY\n");
if (!DataFIFO[f].IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO32 INTO FIFO16 BUT IT CONTAINS SHIT ALREADY\n");
for (;;)
{
u32 f = CurFIFO;
@ -429,13 +392,13 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len)
CurFIFO ^= 1;
BlockCountInternal--;
DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
false, 512, Transfer_TX, 0);
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
false, 512, FinishTX, Num);
return len;
}
u32 DSi_SDHost::GetTransferrableLen(u32 len) const
u32 DSi_SDHost::GetTransferrableLen(u32 len)
{
if (len > BlockLen16) len = BlockLen16; // checkme
return len;
@ -443,7 +406,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len) const
void DSi_SDHost::CheckRX()
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
CheckSwapFIFO();
@ -483,7 +446,7 @@ void DSi_SDHost::CheckTX()
return;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev) dev->ContinueTransfer();
}
@ -560,7 +523,7 @@ u16 DSi_SDHost::Read(u32 addr)
case 0x10A: return 0;
}
Log(LogLevel::Warn, "unknown %s read %08X @ %08X\n", SD_DESC, addr, DSi.GetPC(1));
printf("unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1));
return 0;
}
@ -574,6 +537,7 @@ u16 DSi_SDHost::ReadFIFO16()
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u16 ret = DataFIFO[f].Read();
if (DataFIFO[f].IsEmpty())
@ -594,6 +558,7 @@ u32 DSi_SDHost::ReadFIFO32()
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 ret = DataFIFO32.Read();
if (DataFIFO32.IsEmpty())
@ -615,7 +580,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
Command = val;
u8 cmd = Command & 0x3F;
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev)
{
// CHECKME
@ -626,11 +591,11 @@ void DSi_SDHost::Write(u32 addr, u16 val)
case 0: dev->SendCMD(cmd, Param); break;
case 1: /*dev->SendCMD(55, 0);*/ dev->SendCMD(cmd, Param); break;
default:
Log(LogLevel::Warn, "%s: unknown command type %d, %02X %08X\n", SD_DESC, (Command>>6)&0x3, cmd, Param);
printf("%s: unknown command type %d, %02X %08X\n", SD_DESC, (Command>>6)&0x3, cmd, Param);
break;
}
}
else Log(LogLevel::Debug, "%s: SENDING CMD %04X TO NULL DEVICE\n", SD_DESC, val);
else printf("%s: SENDING CMD %04X TO NULL DEVICE\n", SD_DESC, val);
}
return;
@ -694,7 +659,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
case 0x0E0:
if ((SoftReset & 0x0001) && !(val & 0x0001))
{
Log(LogLevel::Debug, "%s: RESET\n", SD_DESC);
printf("%s: RESET\n", SD_DESC);
StopAction = 0;
memset(ResponseBuffer, 0, sizeof(ResponseBuffer));
IRQStatus = 0;
@ -724,16 +689,17 @@ void DSi_SDHost::Write(u32 addr, u16 val)
case 0x10A: return;
}
Log(LogLevel::Warn, "unknown %s write %08X %04X\n", SD_DESC, addr, val);
printf("unknown %s write %08X %04X\n", SD_DESC, addr, val);
}
void DSi_SDHost::WriteFIFO16(u16 val)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 f = CurFIFO;
if (DataFIFO[f].IsFull())
{
// TODO
Log(LogLevel::Error, "!!!! %s FIFO (16) FULL\n", SD_DESC);
printf("!!!! %s FIFO (16) FULL\n", SD_DESC);
return;
}
@ -749,7 +715,7 @@ void DSi_SDHost::WriteFIFO32(u32 val)
if (DataFIFO32.IsFull())
{
// TODO
Log(LogLevel::Error, "!!!! %s FIFO (32) FULL\n", SD_DESC);
printf("!!!! %s FIFO (32) FULL\n", SD_DESC);
return;
}
@ -766,7 +732,7 @@ void DSi_SDHost::UpdateFIFO32()
if (DataMode != 1) return;
if (!DataFIFO32.IsEmpty()) Log(LogLevel::Warn, "VERY BAD!! TRYING TO DRAIN FIFO16 INTO FIFO32 BUT IT CONTAINS SHIT ALREADY\n");
if (!DataFIFO32.IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO16 INTO FIFO32 BUT IT CONTAINS SHIT ALREADY\n");
for (;;)
{
u32 f = CurFIFO;
@ -782,7 +748,7 @@ void DSi_SDHost::UpdateFIFO32()
if ((DataFIFO32.Level() << 2) >= BlockLen32)
{
DSi.CheckNDMAs(1, Num ? 0x29 : 0x28);
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
}
}
@ -801,23 +767,41 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard")
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand))
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename)
: DSi_SDDevice(host)
{
Internal = internal;
File = Platform::OpenLocalFile(filename, "r+b");
SD = nullptr;
ReadOnly = false;
SetCID(get<DSi_NAND::NANDImage>(Storage).GetEMMCID().data());
}
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard))
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir)
: DSi_SDDevice(host)
{
ReadOnly = get<FATStorage>(Storage).IsReadOnly();
SetCID(DSiSDCardCID);
Internal = internal;
File = nullptr;
SD = new FATStorage(filename, size, readonly, sourcedir);
SD->Open();
ReadOnly = readonly;
}
// The FATStorage or NANDImage is owned by this object;
// std::variant's destructor will clean it up.
DSi_MMCStorage::~DSi_MMCStorage() = default;
DSi_MMCStorage::~DSi_MMCStorage()
{
if (SD)
{
SD->Close();
delete SD;
}
if (File)
{
fclose(File);
}
}
void DSi_MMCStorage::Reset()
{
@ -846,7 +830,7 @@ void DSi_MMCStorage::Reset()
void DSi_MMCStorage::DoSavestate(Savestate* file)
{
file->Section(holds_alternative<DSi_NAND::NANDImage>(Storage) ? "NAND" : "SDCR");
file->Section(Internal ? "NAND" : "SDCR");
file->VarArray(CID, 16);
file->VarArray(CSD, 16);
@ -881,7 +865,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 1: // SEND_OP_COND
// CHECKME!!
// also TODO: it's different for the SD card
if (std::holds_alternative<DSi_NAND::NANDImage>(Storage))
if (Internal)
{
param &= ~(1<<30);
OCR &= 0xBF000000;
@ -891,7 +875,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
}
else
{
Log(LogLevel::Debug, "CMD1 on SD card!!\n");
printf("CMD1 on SD card!!\n");
}
return;
@ -905,7 +889,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
return;
case 3: // get/set RCA
if (holds_alternative<DSi_NAND::NANDImage>(Storage))
if (Internal)
{
RCA = param >> 16;
Host->SendResponse(CSR|0x10000, true); // huh??
@ -913,7 +897,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
else
{
// TODO
Log(LogLevel::Debug, "CMD3 on SD card: TODO\n");
printf("CMD3 on SD card: TODO\n");
Host->SendResponse((CSR & 0x1FFF) | ((CSR >> 6) & 0x2000) | ((CSR >> 8) & 0xC000) | (1 << 16), true);
}
return;
@ -940,8 +924,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 12: // stop operation
SetState(0x04);
if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
FileFlush(nand->GetFile());
if (File) fflush(File);
RWCommand = 0;
Host->SendResponse(CSR, true);
return;
@ -955,14 +938,13 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
if (BlockSize > 0x200)
{
// TODO! raise error
Log(LogLevel::Warn, "!! SD/MMC: BAD BLOCK LEN %d\n", BlockSize);
printf("!! SD/MMC: BAD BLOCK LEN %d\n", BlockSize);
BlockSize = 0x200;
}
SetState(0x04); // CHECKME
Host->SendResponse(CSR, true);
return;
case 17: // read single block
case 18: // read multiple blocks
//printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
RWAddress = param;
@ -971,14 +953,12 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
RWAddress <<= 9;
BlockSize = 512;
}
if (cmd == 18)
RWCommand = 18;
RWCommand = 18;
Host->SendResponse(CSR, true);
RWAddress += ReadBlock(RWAddress);
SetState(0x05);
return;
case 24: // write single block
case 25: // write multiple blocks
//printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
RWAddress = param;
@ -987,8 +967,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
RWAddress <<= 9;
BlockSize = 512;
}
if (cmd == 25)
RWCommand = 25;
RWCommand = 25;
Host->SendResponse(CSR, true);
RWAddress += WriteBlock(RWAddress);
SetState(0x04);
@ -1000,7 +979,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
return;
}
Log(LogLevel::Warn, "MMC: unknown CMD %d %08X\n", cmd, param);
printf("MMC: unknown CMD %d %08X\n", cmd, param);
}
void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
@ -1022,7 +1001,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
// DSi boot2 sets this to 0x40100000 (hardcoded)
// then has two codepaths depending on whether bit30 did get set
// is it settable at all on the MMC? probably not.
if (holds_alternative<DSi_NAND::NANDImage>(Storage)) param &= ~(1<<30);
if (Internal) param &= ~(1<<30);
OCR &= 0xBF000000;
OCR |= (param & 0x40FFFFFF);
Host->SendResponse(OCR, true);
@ -1039,7 +1018,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
return;
}
Log(LogLevel::Warn, "MMC: unknown ACMD %d %08X\n", cmd, param);
printf("MMC: unknown ACMD %d %08X\n", cmd, param);
}
void DSi_MMCStorage::ContinueTransfer()
@ -1068,14 +1047,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (auto* sd = std::get_if<FATStorage>(&Storage))
if (SD)
{
sd->ReadSectors((u32)(addr >> 9), 1, data);
SD->ReadSectors((u32)(addr >> 9), 1, data);
}
else if (auto* nand = std::get_if<DSi_NAND::NANDImage>(&Storage))
else if (File)
{
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile());
fseek(File, addr, SEEK_SET);
fread(&data[addr & 0x1FF], 1, len, File);
}
return Host->DataRX(&data[addr & 0x1FF], len);
@ -1089,28 +1068,26 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
u8 data[0x200];
if (len < 0x200)
{
if (auto* sd = get_if<FATStorage>(&Storage))
if (SD)
{
sd->ReadSectors((u32)(addr >> 9), 1, data);
SD->ReadSectors((u32)(addr >> 9), 1, data);
}
}
if ((len = Host->DataTX(&data[addr & 0x1FF], len)))
{
if (!ReadOnly)
{
if (auto* sd = get_if<FATStorage>(&Storage))
if (SD)
{
sd->WriteSectors((u32)(addr >> 9), 1, data);
SD->WriteSectors((u32)(addr >> 9), 1, data);
}
else if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
else if (File)
{
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile());
fseek(File, addr, SEEK_SET);
fwrite(&data[addr & 0x1FF], 1, len, File);
}
}
}
return len;
}
}

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