Merge branch 'master' into compute-shader-renderer
# Conflicts: # src/GPU.cpp # src/GPU.h # src/GPU2D_Soft.cpp # src/GPU3D.h # src/GPU3D_OpenGL.h # src/GPU_OpenGL.cpp # src/GPU_OpenGL.h # src/NDS.cpp # src/OpenGLSupport.cpp # src/frontend/qt_sdl/OSD.cpp # src/frontend/qt_sdl/main.cpp # src/frontend/qt_sdl/main.h
This commit is contained in:
commit
78828ecfca
|
@ -1,55 +0,0 @@
|
|||
name: CMake Build (AppImage x86-64)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
||||
sudo apt update
|
||||
sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libqt5multimedia5-plugins qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades
|
||||
- name: Create build environment
|
||||
run: mkdir ${{runner.workspace}}/build
|
||||
- name: Configure
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE
|
||||
- name: Make
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
make -j$(nproc --all)
|
||||
- name: Prepare AppDir for AppImage
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
make install DESTDIR=AppDir
|
||||
mv ./AppDir/usr/local/bin ./AppDir/usr/bin
|
||||
mv ./AppDir/usr/local/share ./AppDir/usr/share
|
||||
rm -rf ./AppDir/usr/local
|
||||
- name: Prepare necessary Tools for building the AppImage
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
- name: Build the AppImage
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
|
||||
mkdir dist
|
||||
cp ./melonDS*.AppImage ./dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: melonDS-appimage-x86_64
|
||||
path: ${{runner.workspace}}/build/dist
|
|
@ -1,76 +0,0 @@
|
|||
name: CMake Build (macOS Universal)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: [self-hosted, macOS, ARM64]
|
||||
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
run: rm -rf ${{runner.workspace}}/build
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
build-arm64:
|
||||
needs: prepare
|
||||
runs-on: [self-hosted, macOS, ARM64]
|
||||
env:
|
||||
homebrew_prefix: /opt/homebrew
|
||||
|
||||
steps:
|
||||
- name: Create build directory
|
||||
run: mkdir -p ${{runner.workspace}}/build/arm64
|
||||
|
||||
- name: Configure
|
||||
working-directory: ${{runner.workspace}}/build/arm64
|
||||
run: arch -arm64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
|
||||
|
||||
- name: Make
|
||||
working-directory: ${{runner.workspace}}/build/arm64
|
||||
run: arch -arm64 make -j$(sysctl -n hw.logicalcpu)
|
||||
|
||||
build-x86_64:
|
||||
needs: prepare
|
||||
runs-on: [self-hosted, macOS, ARM64]
|
||||
env:
|
||||
homebrew_prefix: /usr/local
|
||||
|
||||
steps:
|
||||
- name: Create build directory
|
||||
run: mkdir -p ${{runner.workspace}}/build/x86_64
|
||||
|
||||
- name: Configure
|
||||
working-directory: ${{runner.workspace}}/build/x86_64
|
||||
run: arch -x86_64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
|
||||
|
||||
- name: Make
|
||||
working-directory: ${{runner.workspace}}/build/x86_64
|
||||
run: arch -x86_64 make -j$(sysctl -n hw.logicalcpu)
|
||||
|
||||
universal-binary:
|
||||
needs: [build-arm64, build-x86_64]
|
||||
runs-on: [self-hosted, macOS, ARM64]
|
||||
|
||||
steps:
|
||||
- name: Merge binaries
|
||||
run: $GITHUB_WORKSPACE/tools/mac-universal.py ${{runner.workspace}}/build/arm64/melonDS.app ${{runner.workspace}}/build/x86_64/melonDS.app ${{runner.workspace}}/build/universal/melonDS.app
|
||||
|
||||
- name: Codesign app
|
||||
run: codesign -s - --deep -f ${{runner.workspace}}/build/universal/melonDS.app
|
||||
|
||||
- name: Create DMG
|
||||
run: hdiutil create -fs HFS+ -volname melonDS -srcfolder ${{runner.workspace}}/build/universal/melonDS.app -ov -format UDBZ ${{runner.workspace}}/build/universal/melonDS.dmg
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macOS-universal
|
||||
path: ${{runner.workspace}}/build/universal/melonDS.dmg
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
name: macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
|
||||
name: ${{ matrix.arch }}
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Check out sources
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies for package building
|
||||
run: |
|
||||
brew install autoconf automake autoconf-archive libtool python-setuptools
|
||||
- name: Set up CMake
|
||||
uses: lukka/get-cmake@latest
|
||||
- name: Set up vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
vcpkgGitCommitId: 53bef8994c541b6561884a8395ea35715ece75db
|
||||
- name: Build
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
configurePreset: release-mac-${{ matrix.arch }}
|
||||
buildPreset: release-mac-${{ matrix.arch }}
|
||||
- name: Compress app bundle
|
||||
shell: bash
|
||||
run: |
|
||||
cd build/release-mac-${{ matrix.arch }}
|
||||
zip -r -y ../../macOS-${{ matrix.arch }}.zip melonDS.app
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macOS-${{ matrix.arch }}
|
||||
path: macOS-${{ matrix.arch }}.zip
|
||||
retention-days: 1
|
||||
|
||||
universal-binary:
|
||||
name: Universal binary
|
||||
needs: [build-macos]
|
||||
runs-on: macos-13
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Download x86_64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: macOS-x86_64
|
||||
path: x86_64
|
||||
- name: Download arm64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: macOS-arm64
|
||||
path: arm64
|
||||
- name: Combine app bundles
|
||||
shell: bash
|
||||
run: |
|
||||
unzip x86_64/*.zip -d x86_64
|
||||
unzip arm64/*.zip -d arm64
|
||||
lipo {x86_64,arm64}/melonDS.app/Contents/MacOS/melonDS -create -output melonDS
|
||||
cp -a arm64/melonDS.app melonDS.app
|
||||
cp melonDS melonDS.app/Contents/MacOS/melonDS
|
||||
codesign -s - --deep melonDS.app
|
||||
zip -r -y macOS-universal.zip melonDS.app
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macOS-universal
|
||||
path: macOS-universal.zip
|
||||
# - name: Clean up architecture-specific artifacts
|
||||
# uses: geekyeggo/delete-artifact@v4
|
||||
# with:
|
||||
# failOnError: false
|
||||
# name: |
|
||||
# macOS-x86_64
|
||||
# macOS-arm64
|
|
@ -1,50 +0,0 @@
|
|||
name: CMake Build (Ubuntu aarch64)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
container: ubuntu:20.04
|
||||
|
||||
steps:
|
||||
- name: Prepare system
|
||||
shell: bash
|
||||
run: |
|
||||
apt update
|
||||
apt -y full-upgrade
|
||||
apt -y install git
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v1
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
dpkg --add-architecture arm64
|
||||
sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
|
||||
rm /etc/apt/sources.list
|
||||
mv /etc/apt/sources.list{.new,}
|
||||
apt update
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu {libsdl2,qtbase5,qtbase5-private,qtmultimedia5,libslirp,libarchive,libzstd}-dev:arm64 zstd:arm64 cmake extra-cmake-modules dpkg-dev
|
||||
- name: Configure
|
||||
shell: bash
|
||||
run: |
|
||||
CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -B build
|
||||
- name: Make
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build build -j$(nproc --all)
|
||||
mkdir dist
|
||||
cp build/melonDS dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: melonDS-ubuntu-aarch64
|
||||
path: dist
|
|
@ -1,4 +1,4 @@
|
|||
name: CMake Build (Ubuntu x86-64)
|
||||
name: Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -9,29 +9,77 @@ on:
|
|||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
build-x86_64:
|
||||
name: x86_64
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
name: Check out sources
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
||||
sudo apt update
|
||||
sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades
|
||||
- name: Create build environment
|
||||
run: mkdir ${{runner.workspace}}/build
|
||||
sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev \
|
||||
qt6-{base,base-private,multimedia}-dev libslirp0 libslirp-dev libarchive-dev libzstd-dev libfuse2
|
||||
- name: Configure
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE
|
||||
- name: Make
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- name: Build
|
||||
run: |
|
||||
make -j$(nproc --all)
|
||||
mkdir dist
|
||||
cp melonDS dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
cmake --build build
|
||||
DESTDIR=AppDir cmake --install build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-ubuntu-x86_64
|
||||
path: ${{runner.workspace}}/build/dist
|
||||
path: AppDir/usr/bin/melonDS
|
||||
- name: Fetch AppImage tools
|
||||
run: |
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-*.AppImage
|
||||
- name: Build the AppImage
|
||||
env:
|
||||
QMAKE: /usr/lib/qt6/bin/qmake
|
||||
run: |
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-appimage-x86_64
|
||||
path: melonDS*.AppImage
|
||||
|
||||
build-aarch64:
|
||||
name: aarch64
|
||||
runs-on: ubuntu-latest
|
||||
container: ubuntu:22.04
|
||||
|
||||
steps:
|
||||
- name: Prepare system
|
||||
shell: bash
|
||||
run: |
|
||||
dpkg --add-architecture arm64
|
||||
sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
|
||||
rm /etc/apt/sources.list
|
||||
mv /etc/apt/sources.list{.new,}
|
||||
apt update
|
||||
apt -y full-upgrade
|
||||
apt -y install git {gcc-12,g++-12}-aarch64-linux-gnu cmake ninja-build extra-cmake-modules \
|
||||
{libsdl2,qt6-{base,base-private,multimedia},libslirp,libarchive,libzstd}-dev:arm64 \
|
||||
pkg-config dpkg-dev
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
- name: Configure
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \
|
||||
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \
|
||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \
|
||||
-DUSE_QT6=ON
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-ubuntu-aarch64
|
||||
path: build/melonDS
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name: CMake Build (Windows x86-64)
|
||||
name: Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
cmake_policy(VERSION 3.15)
|
||||
if (POLICY CMP0076)
|
||||
|
@ -8,6 +8,12 @@ endif()
|
|||
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake")
|
||||
|
||||
option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
|
||||
if (USE_VCPKG)
|
||||
include(ConfigureVcpkg)
|
||||
endif()
|
||||
|
||||
project(melonDS
|
||||
VERSION 0.9.5
|
||||
|
@ -28,8 +34,6 @@ set(CMAKE_CXX_STANDARD 17)
|
|||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
add_compile_definitions(MELONDS_VERSION="${melonDS_VERSION}")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
|
@ -73,11 +77,6 @@ if (ENABLE_LTO)
|
|||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
|
||||
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
if (NOT APPLE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
|
||||
endif()
|
||||
|
@ -99,6 +98,11 @@ if (CCACHE)
|
|||
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
|
||||
endif()
|
||||
|
||||
option(ENABLE_GDBSTUB "Enable GDB stub" ON)
|
||||
if (ENABLE_GDBSTUB)
|
||||
add_definitions(-DGDBSTUB_ENABLED)
|
||||
endif()
|
||||
|
||||
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "release",
|
||||
"displayName": "Release",
|
||||
"description": "Default release build configuration.",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/release"
|
||||
},
|
||||
{
|
||||
"inherits": "release",
|
||||
"name": "release-vcpkg",
|
||||
"displayName": "Release (vcpkg)",
|
||||
"description": "Release build with packages from vcpkg.",
|
||||
"cacheVariables": {
|
||||
"USE_VCPKG": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release-mac-x86_64",
|
||||
"inherits": "release-vcpkg",
|
||||
"displayName": "macOS release (x86_64)",
|
||||
"binaryDir": "${sourceDir}/build/release-mac-x86_64",
|
||||
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "x86_64" }
|
||||
},
|
||||
{
|
||||
"name": "release-mac-arm64",
|
||||
"inherits": "release-vcpkg",
|
||||
"displayName": "macOS release (arm64)",
|
||||
"binaryDir": "${sourceDir}/build/release-mac-arm64",
|
||||
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" }
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "release",
|
||||
"configurePreset": "release"
|
||||
},
|
||||
{
|
||||
"name": "release-vcpkg",
|
||||
"configurePreset": "release-vcpkg"
|
||||
},
|
||||
{
|
||||
"name": "release-mac-x86_64",
|
||||
"configurePreset": "release-mac-x86_64"
|
||||
},
|
||||
{
|
||||
"name": "release-mac-arm64",
|
||||
"configurePreset": "release-mac-arm64"
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "release",
|
||||
"displayName": "Release",
|
||||
"steps": [
|
||||
{ "type": "configure", "name": "release" },
|
||||
{ "type": "build", "name": "release" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "release-vcpkg",
|
||||
"displayName": "Release (vcpkg)",
|
||||
"steps": [
|
||||
{ "type": "configure", "name": "release-vcpkg" },
|
||||
{ "type": "build", "name": "release-vcpkg" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "release-mac-x86_64",
|
||||
"steps": [
|
||||
{ "type": "configure", "name": "release-mac-x86_64" },
|
||||
{ "type": "build", "name": "release-mac-x86_64" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "release-mac-arm64",
|
||||
"steps": [
|
||||
{ "type": "configure", "name": "release-mac-arm64" },
|
||||
{ "type": "build", "name": "release-mac-arm64" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -6,10 +6,9 @@
|
|||
<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>
|
||||
<br>
|
||||
<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/actions/workflow/status/melonDS-emu/melonDS/build-windows.yml?label=Windows%20x86-64&logo=GitHub&branch=master"></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/actions/workflow/status/melonDS-emu/melonDS/build-ubuntu.yml?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/actions/workflow/status/melonDS-emu/melonDS/build-ubuntu-aarch64.yml?label=Linux%20ARM64&logo=GitHub"></img></a>
|
||||
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos-universal.yml?query=event%3Apush"><img src="https://img.shields.io/github/actions/workflow/status/melonDS-emu/melonDS/build-macos-universal.yml?label=macOS%20Universal&logo=GitHub"></img></a>
|
||||
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml/badge.svg" /></a>
|
||||
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml/badge.svg" /></a>
|
||||
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml/badge.svg" /></a>
|
||||
</p>
|
||||
DS emulator, sorta
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
include(FetchContent)
|
||||
|
||||
set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||
set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository")
|
||||
|
||||
if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
|
||||
file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE)
|
||||
FetchContent_Declare(vcpkg
|
||||
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
|
||||
GIT_TAG 2024.01.12
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||
FetchContent_MakeAvailable(vcpkg)
|
||||
endif()
|
||||
|
||||
set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets")
|
||||
|
||||
option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
|
||||
|
||||
if (CMAKE_OSX_ARCHITECTURES MATCHES ";")
|
||||
message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.")
|
||||
endif()
|
||||
|
||||
if (USE_RECOMMENDED_TRIPLETS)
|
||||
execute_process(
|
||||
COMMAND uname -m
|
||||
OUTPUT_VARIABLE _HOST_PROCESSOR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
set(_CAN_TARGET_AS_HOST OFF)
|
||||
|
||||
if (APPLE)
|
||||
if (NOT CMAKE_OSX_ARCHITECTURES)
|
||||
if (_HOST_PROCESSOR STREQUAL arm64)
|
||||
set(CMAKE_OSX_ARCHITECTURES arm64)
|
||||
else()
|
||||
set(CMAKE_OSX_ARCHITECTURES x86_64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
|
||||
set(_WANTED_TRIPLET arm64-osx-11-release)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
||||
else()
|
||||
set(_WANTED_TRIPLET x64-osx-1015-release)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
# TODO Windows arm64 if possible
|
||||
set(_CAN_TARGET_AS_HOST ON)
|
||||
set(_WANTED_TRIPLET x64-mingw-static)
|
||||
endif()
|
||||
|
||||
# Don't override it if the user set something else
|
||||
if (NOT VCPKG_TARGET_TRIPLET)
|
||||
set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}")
|
||||
else()
|
||||
set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
if (_HOST_PROCESSOR MATCHES arm64)
|
||||
if (_WANTED_TRIPLET MATCHES "^arm64-osx-")
|
||||
set(_CAN_TARGET_AS_HOST ON)
|
||||
elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release")
|
||||
# Use the default triplet for when building for arm64
|
||||
# because we're probably making a universal build
|
||||
set(VCPKG_HOST_TRIPLET arm64-osx-11-release)
|
||||
endif()
|
||||
else()
|
||||
if (_WANTED_TRIPLET MATCHES "^x64-osx-")
|
||||
set(_CAN_TARGET_AS_HOST ON)
|
||||
elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release")
|
||||
set(VCPKG_HOST_TRIPLET x64-osx-1015-release)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries.
|
||||
# In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant.
|
||||
if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET)
|
||||
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON)
|
||||
else()
|
||||
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF)
|
||||
endif()
|
||||
|
||||
if (VCPKG_TARGET_AS_HOST)
|
||||
set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
|
|
@ -0,0 +1,9 @@
|
|||
if (CMAKE_C_COMPILER_ID STREQUAL GNU)
|
||||
set(CMAKE_C_FLAGS_DEBUG_INIT "-g -Og")
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og")
|
||||
endif()
|
||||
|
||||
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}")
|
||||
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}")
|
|
@ -19,6 +19,13 @@ function(fix_interface_includes)
|
|||
if (PARENT_DIR MATCHES "include$")
|
||||
list(APPEND NEW_DIRS "${PARENT_DIR}")
|
||||
endif()
|
||||
|
||||
# HACK
|
||||
# The libarchive pkg-config file in MSYS2 seems to include a UNIX-style path for its
|
||||
# include directory and CMake doesn't like that.
|
||||
if (WIN32 AND MINGW AND target STREQUAL PkgConfig::LibArchive)
|
||||
list(FILTER DIRS EXCLUDE REGEX "^/[^.]+64/.*")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
list(APPEND DIRS ${NEW_DIRS})
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
set(VCPKG_TARGET_ARCHITECTURE arm64)
|
||||
set(VCPKG_CRT_LINKAGE dynamic)
|
||||
set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
||||
set(VCPKG_CMAKE_SYSTEM_VERSION 11.0)
|
||||
set(VCPKG_OSX_ARCHITECTURES arm64)
|
||||
set(VCPKG_BUILD_TYPE release)
|
||||
set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0)
|
||||
|
||||
set(VCPKG_C_FLAGS -mmacosx-version-min=11.0)
|
||||
set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0)
|
|
@ -0,0 +1,12 @@
|
|||
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||
set(VCPKG_CRT_LINKAGE dynamic)
|
||||
set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
||||
set(VCPKG_CMAKE_SYSTEM_VERSION 10.15)
|
||||
set(VCPKG_OSX_ARCHITECTURES x86_64)
|
||||
set(VCPKG_BUILD_TYPE release)
|
||||
set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
set(VCPKG_C_FLAGS -mmacosx-version-min=10.15)
|
||||
set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15)
|
|
@ -517,6 +517,9 @@ swi_get_crc16:
|
|||
mov const_0x1E, #0x1E
|
||||
adr crc_table_ptr, crc_table
|
||||
|
||||
bic crc_value, crc_value, #0xFF000000
|
||||
bic crc_value, crc_value, #0x00FF0000
|
||||
|
||||
movs crc_length, crc_length, lsr #1
|
||||
beq 1f
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -18,7 +18,7 @@ FILETYPE VFT_APP
|
|||
VALUE "FileVersion", "${melonDS_VERSION}"
|
||||
VALUE "FileDescription", "melonDS emulator"
|
||||
VALUE "InternalName", "SDnolem"
|
||||
VALUE "LegalCopyright", "2016-2022 melonDS team"
|
||||
VALUE "LegalCopyright", "2016-2023 melonDS team"
|
||||
VALUE "LegalTrademarks", ""
|
||||
VALUE "OriginalFilename", "melonDS.exe"
|
||||
VALUE "ProductName", "melonDS"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,6 +21,8 @@
|
|||
#include "ARCodeFile.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using namespace Platform;
|
||||
|
||||
// TODO: import codes from other sources (usrcheat.dat, ...)
|
||||
|
@ -162,23 +164,25 @@ bool ARCodeFile::Save()
|
|||
{
|
||||
ARCodeCat& cat = *it;
|
||||
|
||||
if (it != Categories.begin()) FileWriteFormatted(f, "\r\n");
|
||||
FileWriteFormatted(f, "CAT %s\r\n\r\n", cat.Name.c_str());
|
||||
if (it != Categories.begin()) FileWriteFormatted(f, "\n");
|
||||
FileWriteFormatted(f, "CAT %s\n\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\r\n", code.Enabled, code.Name.c_str());
|
||||
FileWriteFormatted(f, "CODE %d %s\n", code.Enabled, code.Name.c_str());
|
||||
|
||||
for (size_t i = 0; i < code.Code.size(); i+=2)
|
||||
{
|
||||
FileWriteFormatted(f, "%08X %08X\r\n", code.Code[i], code.Code[i + 1]);
|
||||
FileWriteFormatted(f, "%08X %08X\n", code.Code[i], code.Code[i + 1]);
|
||||
}
|
||||
|
||||
FileWriteFormatted(f, "\r\n");
|
||||
FileWriteFormatted(f, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
CloseFile(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -24,6 +24,8 @@
|
|||
#include <vector>
|
||||
#include "types.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
struct ARCode
|
||||
{
|
||||
std::string Name;
|
||||
|
@ -59,4 +61,5 @@ private:
|
|||
std::string Filename;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // ARCODEFILE_H
|
||||
|
|
120
src/AREngine.cpp
120
src/AREngine.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -23,84 +23,33 @@
|
|||
#include "AREngine.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace AREngine
|
||||
{
|
||||
|
||||
// AR code file - frontend is responsible for managing this
|
||||
ARCodeFile* CodeFile;
|
||||
|
||||
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()
|
||||
AREngine::AREngine(melonDS::NDS& nds) : NDS(nds)
|
||||
{
|
||||
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 RunCheat(ARCode& arcode)
|
||||
void AREngine::RunCheat(const ARCode& arcode)
|
||||
{
|
||||
u32* code = &arcode.Code[0];
|
||||
const u32* code = &arcode.Code[0];
|
||||
|
||||
u32 offset = 0;
|
||||
u32 datareg = 0;
|
||||
u32 cond = 1;
|
||||
u32 condstack = 0;
|
||||
|
||||
u32* loopstart = code;
|
||||
const u32* loopstart = code;
|
||||
u32 loopcount = 0;
|
||||
u32 loopcond = 1;
|
||||
u32 loopcondstack = 0;
|
||||
|
@ -135,15 +84,15 @@ void RunCheat(ARCode& arcode)
|
|||
switch (op)
|
||||
{
|
||||
case16(0x00): // 32-bit write
|
||||
BusWrite32((a & 0x0FFFFFFF) + offset, b);
|
||||
NDS.ARM7Write32((a & 0x0FFFFFFF) + offset, b);
|
||||
break;
|
||||
|
||||
case16(0x10): // 16-bit write
|
||||
BusWrite16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
|
||||
NDS.ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
|
||||
break;
|
||||
|
||||
case16(0x20): // 8-bit write
|
||||
BusWrite8((a & 0x0FFFFFFF) + offset, b & 0xFF);
|
||||
NDS.ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF);
|
||||
break;
|
||||
|
||||
case16(0x30): // IF b > u32[a]
|
||||
|
@ -153,7 +102,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u32 chk = BusRead32(addr);
|
||||
u32 chk = NDS.ARM7Read32(addr);
|
||||
|
||||
cond = (b > chk) ? 1:0;
|
||||
}
|
||||
|
@ -166,7 +115,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u32 chk = BusRead32(addr);
|
||||
u32 chk = NDS.ARM7Read32(addr);
|
||||
|
||||
cond = (b < chk) ? 1:0;
|
||||
}
|
||||
|
@ -179,7 +128,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u32 chk = BusRead32(addr);
|
||||
u32 chk = NDS.ARM7Read32(addr);
|
||||
|
||||
cond = (b == chk) ? 1:0;
|
||||
}
|
||||
|
@ -192,7 +141,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u32 chk = BusRead32(addr);
|
||||
u32 chk = NDS.ARM7Read32(addr);
|
||||
|
||||
cond = (b != chk) ? 1:0;
|
||||
}
|
||||
|
@ -205,7 +154,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u16 val = BusRead16(addr);
|
||||
u16 val = NDS.ARM7Read16(addr);
|
||||
u16 chk = ~(b >> 16);
|
||||
chk &= val;
|
||||
|
||||
|
@ -220,7 +169,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u16 val = BusRead16(addr);
|
||||
u16 val = NDS.ARM7Read16(addr);
|
||||
u16 chk = ~(b >> 16);
|
||||
chk &= val;
|
||||
|
||||
|
@ -235,7 +184,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u16 val = BusRead16(addr);
|
||||
u16 val = NDS.ARM7Read16(addr);
|
||||
u16 chk = ~(b >> 16);
|
||||
chk &= val;
|
||||
|
||||
|
@ -250,7 +199,7 @@ void RunCheat(ARCode& arcode)
|
|||
|
||||
u32 addr = a & 0x0FFFFFFF;
|
||||
if (!addr) addr = offset;
|
||||
u16 val = BusRead16(addr);
|
||||
u16 val = NDS.ARM7Read16(addr);
|
||||
u16 chk = ~(b >> 16);
|
||||
chk &= val;
|
||||
|
||||
|
@ -259,7 +208,7 @@ void RunCheat(ARCode& arcode)
|
|||
break;
|
||||
|
||||
case16(0xB0): // offset = u32[a + offset]
|
||||
offset = BusRead32((a & 0x0FFFFFFF) + offset);
|
||||
offset = NDS.ARM7Read32((a & 0x0FFFFFFF) + offset);
|
||||
break;
|
||||
|
||||
case 0xC0: // FOR 0..b
|
||||
|
@ -296,7 +245,7 @@ void RunCheat(ARCode& arcode)
|
|||
break;
|
||||
|
||||
case 0xC6: // u32[b] = offset
|
||||
BusWrite32(b, offset);
|
||||
NDS.ARM7Write32(b, offset);
|
||||
break;
|
||||
|
||||
case 0xD0: // ENDIF
|
||||
|
@ -345,30 +294,30 @@ void RunCheat(ARCode& arcode)
|
|||
break;
|
||||
|
||||
case 0xD6: // u32[b+offset] = datareg / offset += 4
|
||||
BusWrite32(b + offset, datareg);
|
||||
NDS.ARM7Write32(b + offset, datareg);
|
||||
offset += 4;
|
||||
break;
|
||||
|
||||
case 0xD7: // u16[b+offset] = datareg / offset += 2
|
||||
BusWrite16(b + offset, datareg & 0xFFFF);
|
||||
NDS.ARM7Write16(b + offset, datareg & 0xFFFF);
|
||||
offset += 2;
|
||||
break;
|
||||
|
||||
case 0xD8: // u8[b+offset] = datareg / offset += 1
|
||||
BusWrite8(b + offset, datareg & 0xFF);
|
||||
NDS.ARM7Write8(b + offset, datareg & 0xFF);
|
||||
offset += 1;
|
||||
break;
|
||||
|
||||
case 0xD9: // datareg = u32[b+offset]
|
||||
datareg = BusRead32(b + offset);
|
||||
datareg = NDS.ARM7Read32(b + offset);
|
||||
break;
|
||||
|
||||
case 0xDA: // datareg = u16[b+offset]
|
||||
datareg = BusRead16(b + offset);
|
||||
datareg = NDS.ARM7Read16(b + offset);
|
||||
break;
|
||||
|
||||
case 0xDB: // datareg = u8[b+offset]
|
||||
datareg = BusRead8(b + offset);
|
||||
datareg = NDS.ARM7Read8(b + offset);
|
||||
break;
|
||||
|
||||
case 0xDC: // offset += b
|
||||
|
@ -383,8 +332,8 @@ void RunCheat(ARCode& arcode)
|
|||
u32 bytesleft = b;
|
||||
while (bytesleft >= 8)
|
||||
{
|
||||
BusWrite32(dstaddr, *code++); dstaddr += 4;
|
||||
BusWrite32(dstaddr, *code++); dstaddr += 4;
|
||||
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
|
||||
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
|
||||
bytesleft -= 8;
|
||||
}
|
||||
if (bytesleft > 0)
|
||||
|
@ -393,13 +342,13 @@ void RunCheat(ARCode& arcode)
|
|||
code += 2;
|
||||
if (bytesleft >= 4)
|
||||
{
|
||||
BusWrite32(dstaddr, *(u32*)leftover); dstaddr += 4;
|
||||
NDS.ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
|
||||
leftover += 4;
|
||||
bytesleft -= 4;
|
||||
}
|
||||
while (bytesleft > 0)
|
||||
{
|
||||
BusWrite8(dstaddr, *leftover++); dstaddr++;
|
||||
NDS.ARM7Write8(dstaddr, *leftover++); dstaddr++;
|
||||
bytesleft--;
|
||||
}
|
||||
}
|
||||
|
@ -415,14 +364,14 @@ void RunCheat(ARCode& arcode)
|
|||
u32 bytesleft = b;
|
||||
while (bytesleft >= 4)
|
||||
{
|
||||
BusWrite32(dstaddr, BusRead32(srcaddr));
|
||||
NDS.ARM7Write32(dstaddr, NDS.ARM7Read32(srcaddr));
|
||||
srcaddr += 4;
|
||||
dstaddr += 4;
|
||||
bytesleft -= 4;
|
||||
}
|
||||
while (bytesleft > 0)
|
||||
{
|
||||
BusWrite8(dstaddr, BusRead8(srcaddr));
|
||||
NDS.ARM7Write8(dstaddr, NDS.ARM7Read8(srcaddr));
|
||||
srcaddr++;
|
||||
dstaddr++;
|
||||
bytesleft--;
|
||||
|
@ -437,7 +386,7 @@ void RunCheat(ARCode& arcode)
|
|||
}
|
||||
}
|
||||
|
||||
void RunCheats()
|
||||
void AREngine::RunCheats()
|
||||
{
|
||||
if (!CodeFile) return;
|
||||
|
||||
|
@ -454,5 +403,4 @@ void RunCheats()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,18 +21,23 @@
|
|||
|
||||
#include "ARCodeFile.h"
|
||||
|
||||
namespace AREngine
|
||||
namespace melonDS
|
||||
{
|
||||
class NDS;
|
||||
class AREngine
|
||||
{
|
||||
public:
|
||||
AREngine(melonDS::NDS& nds);
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
void Reset();
|
||||
ARCodeFile* GetCodeFile() { return CodeFile; }
|
||||
void SetCodeFile(ARCodeFile* file) { CodeFile = file; }
|
||||
|
||||
ARCodeFile* GetCodeFile();
|
||||
void SetCodeFile(ARCodeFile* file);
|
||||
|
||||
void RunCheats();
|
||||
void RunCheats();
|
||||
void RunCheat(const ARCode& arcode);
|
||||
private:
|
||||
melonDS::NDS& NDS;
|
||||
ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ARENGINE_H
|
||||
|
|
642
src/ARM.cpp
642
src/ARM.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include "NDS.h"
|
||||
#include "DSi.h"
|
||||
|
@ -25,15 +26,53 @@
|
|||
#include "AREngine.h"
|
||||
#include "ARMJIT.h"
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
#include "ARMJIT.h"
|
||||
#include "GPU.h"
|
||||
#include "ARMJIT_Memory.h"
|
||||
#endif
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
void ARM::GdbCheckA()
|
||||
{
|
||||
if (!IsSingleStep && !BreakReq)
|
||||
{ // check if eg. break signal is incoming etc.
|
||||
Gdb::StubState st = GdbStub.Enter(false, Gdb::TgtStatus::NoEvent, ~(u32)0u, BreakOnStartup);
|
||||
BreakOnStartup = false;
|
||||
IsSingleStep = st == Gdb::StubState::Step;
|
||||
BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break;
|
||||
}
|
||||
}
|
||||
void ARM::GdbCheckB()
|
||||
{
|
||||
if (IsSingleStep || BreakReq)
|
||||
{ // use else here or we single-step the same insn twice in gdb
|
||||
u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4);
|
||||
Gdb::StubState st = GdbStub.Enter(true, Gdb::TgtStatus::SingleStep, pc_real);
|
||||
IsSingleStep = st == Gdb::StubState::Step;
|
||||
BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break;
|
||||
}
|
||||
}
|
||||
void ARM::GdbCheckC()
|
||||
{
|
||||
u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4);
|
||||
Gdb::StubState st = GdbStub.CheckBkpt(pc_real, true, true);
|
||||
if (st != Gdb::StubState::CheckNoHit)
|
||||
{
|
||||
IsSingleStep = st == Gdb::StubState::Step;
|
||||
BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break;
|
||||
}
|
||||
else GdbCheckB();
|
||||
}
|
||||
#else
|
||||
void ARM::GdbCheckA() {}
|
||||
void ARM::GdbCheckB() {}
|
||||
void ARM::GdbCheckC() {}
|
||||
#endif
|
||||
|
||||
|
||||
// instruction timing notes
|
||||
//
|
||||
// * simple instruction: 1S (code)
|
||||
|
@ -48,7 +87,7 @@ using Platform::LogLevel;
|
|||
|
||||
|
||||
|
||||
u32 ARM::ConditionTable[16] =
|
||||
const u32 ARM::ConditionTable[16] =
|
||||
{
|
||||
0xF0F0, // EQ
|
||||
0x0F0F, // NE
|
||||
|
@ -68,11 +107,22 @@ u32 ARM::ConditionTable[16] =
|
|||
0x0000 // NE
|
||||
};
|
||||
|
||||
|
||||
ARM::ARM(u32 num)
|
||||
ARM::ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, melonDS::NDS& nds) :
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0),
|
||||
#endif
|
||||
Num(num), // well uh
|
||||
NDS(nds)
|
||||
{
|
||||
// well uh
|
||||
Num = num;
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
if (gdb
|
||||
#ifdef JIT_ENABLED
|
||||
&& !jit // TODO: Should we support toggling the GdbStub without destroying the ARM?
|
||||
#endif
|
||||
)
|
||||
GdbStub.Init();
|
||||
IsSingleStep = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ARM::~ARM()
|
||||
|
@ -80,25 +130,21 @@ ARM::~ARM()
|
|||
// dorp
|
||||
}
|
||||
|
||||
ARMv5::ARMv5() : ARM(0)
|
||||
ARMv5::ARMv5(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit) : ARM(0, jit, gdb, nds)
|
||||
{
|
||||
#ifndef JIT_ENABLED
|
||||
DTCM = new u8[DTCMPhysicalSize];
|
||||
#endif
|
||||
DTCM = NDS.JIT.Memory.GetARM9DTCM();
|
||||
|
||||
PU_Map = PU_PrivMap;
|
||||
}
|
||||
|
||||
ARMv4::ARMv4() : ARM(1)
|
||||
ARMv4::ARMv4(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit) : ARM(1, jit, gdb, nds)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
ARMv5::~ARMv5()
|
||||
{
|
||||
#ifndef JIT_ENABLED
|
||||
delete[] DTCM;
|
||||
#endif
|
||||
// DTCM is owned by Memory, not going to delete it
|
||||
}
|
||||
|
||||
void ARM::Reset()
|
||||
|
@ -139,62 +185,22 @@ void ARM::Reset()
|
|||
FastBlockLookupSize = 0;
|
||||
#endif
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
IsSingleStep = false;
|
||||
BreakReq = false;
|
||||
#endif
|
||||
|
||||
// zorp
|
||||
JumpTo(ExceptionBase);
|
||||
}
|
||||
|
||||
void ARMv5::Reset()
|
||||
{
|
||||
if (NDS::ConsoleType == 1)
|
||||
{
|
||||
BusRead8 = DSi::ARM9Read8;
|
||||
BusRead16 = DSi::ARM9Read16;
|
||||
BusRead32 = DSi::ARM9Read32;
|
||||
BusWrite8 = DSi::ARM9Write8;
|
||||
BusWrite16 = DSi::ARM9Write16;
|
||||
BusWrite32 = DSi::ARM9Write32;
|
||||
GetMemRegion = DSi::ARM9GetMemRegion;
|
||||
}
|
||||
else
|
||||
{
|
||||
BusRead8 = NDS::ARM9Read8;
|
||||
BusRead16 = NDS::ARM9Read16;
|
||||
BusRead32 = NDS::ARM9Read32;
|
||||
BusWrite8 = NDS::ARM9Write8;
|
||||
BusWrite16 = NDS::ARM9Write16;
|
||||
BusWrite32 = NDS::ARM9Write32;
|
||||
GetMemRegion = NDS::ARM9GetMemRegion;
|
||||
}
|
||||
|
||||
PU_Map = PU_PrivMap;
|
||||
|
||||
ARM::Reset();
|
||||
}
|
||||
|
||||
void ARMv4::Reset()
|
||||
{
|
||||
if (NDS::ConsoleType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
ARM::Reset();
|
||||
}
|
||||
|
||||
|
||||
void ARM::DoSavestate(Savestate* file)
|
||||
{
|
||||
|
@ -217,7 +223,7 @@ void ARM::DoSavestate(Savestate* file)
|
|||
file->VarArray(R_UND, 3*sizeof(u32));
|
||||
file->Var32(&CurInstr);
|
||||
#ifdef JIT_ENABLED
|
||||
if (file->Saving && NDS::EnableJIT)
|
||||
if (file->Saving && NDS.IsJITEnabled())
|
||||
{
|
||||
// hack, the JIT doesn't really pipeline
|
||||
// but we still want JIT save states to be
|
||||
|
@ -343,7 +349,7 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr)
|
|||
return;
|
||||
}
|
||||
|
||||
NDS::MonitorARM9Jump(addr);
|
||||
NDS.MonitorARM9Jump(addr);
|
||||
}
|
||||
|
||||
void ARMv4::JumpTo(u32 addr, bool restorecpsr)
|
||||
|
@ -371,7 +377,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr)
|
|||
|
||||
NextInstr[0] = CodeRead16(addr);
|
||||
NextInstr[1] = CodeRead16(addr+2);
|
||||
Cycles += NDS::ARM7MemTimings[CodeCycles][0] + NDS::ARM7MemTimings[CodeCycles][1];
|
||||
Cycles += NDS.ARM7MemTimings[CodeCycles][0] + NDS.ARM7MemTimings[CodeCycles][1];
|
||||
|
||||
CPSR |= 0x20;
|
||||
}
|
||||
|
@ -384,7 +390,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr)
|
|||
|
||||
NextInstr[0] = CodeRead32(addr);
|
||||
NextInstr[1] = CodeRead32(addr+4);
|
||||
Cycles += NDS::ARM7MemTimings[CodeCycles][2] + NDS::ARM7MemTimings[CodeCycles][3];
|
||||
Cycles += NDS.ARM7MemTimings[CodeCycles][2] + NDS.ARM7MemTimings[CodeCycles][3];
|
||||
|
||||
CPSR &= ~0x20;
|
||||
}
|
||||
|
@ -529,8 +535,8 @@ void ARM::TriggerIRQ()
|
|||
// normally, those work by hijacking the ARM7 VBlank handler
|
||||
if (Num == 1)
|
||||
{
|
||||
if ((NDS::IF[1] & NDS::IE[1]) & (1<<NDS::IRQ_VBlank))
|
||||
AREngine::RunCheats();
|
||||
if ((NDS.IF[1] & NDS.IE[1]) & (1<<IRQ_VBlank))
|
||||
NDS.AREngine.RunCheats();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -548,7 +554,7 @@ void ARMv5::PrefetchAbort()
|
|||
if (!(PU_Map[ExceptionBase>>12] & 0x04))
|
||||
{
|
||||
Log(LogLevel::Error, "!!!!! EXCEPTION REGION NOT EXECUTABLE. THIS IS VERY BAD!!\n");
|
||||
NDS::Stop(Platform::StopReason::BadExceptionRegion);
|
||||
NDS.Stop(Platform::StopReason::BadExceptionRegion);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -571,31 +577,40 @@ void ARMv5::DataAbort()
|
|||
JumpTo(ExceptionBase + 0x10);
|
||||
}
|
||||
|
||||
void ARM::CheckGdbIncoming()
|
||||
{
|
||||
GdbCheckA();
|
||||
}
|
||||
|
||||
void ARMv5::Execute()
|
||||
{
|
||||
GdbCheckB();
|
||||
|
||||
if (Halted)
|
||||
{
|
||||
if (Halted == 2)
|
||||
{
|
||||
Halted = 0;
|
||||
}
|
||||
else if (NDS::HaltInterrupted(0))
|
||||
else if (NDS.HaltInterrupted(0))
|
||||
{
|
||||
Halted = 0;
|
||||
if (NDS::IME[0] & 0x1)
|
||||
if (NDS.IME[0] & 0x1)
|
||||
TriggerIRQ();
|
||||
}
|
||||
else
|
||||
{
|
||||
NDS::ARM9Timestamp = NDS::ARM9Target;
|
||||
NDS.ARM9Timestamp = NDS.ARM9Target;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (NDS::ARM9Timestamp < NDS::ARM9Target)
|
||||
while (NDS.ARM9Timestamp < NDS.ARM9Target)
|
||||
{
|
||||
if (CPSR & 0x20) // THUMB
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 2;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -609,6 +624,8 @@ void ARMv5::Execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 4;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -632,9 +649,9 @@ void ARMv5::Execute()
|
|||
// TODO optimize this shit!!!
|
||||
if (Halted)
|
||||
{
|
||||
if (Halted == 1 && NDS::ARM9Timestamp < NDS::ARM9Target)
|
||||
if (Halted == 1 && NDS.ARM9Timestamp < NDS.ARM9Target)
|
||||
{
|
||||
NDS::ARM9Timestamp = NDS::ARM9Target;
|
||||
NDS.ARM9Timestamp = NDS.ARM9Target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -645,7 +662,7 @@ void ARMv5::Execute()
|
|||
}*/
|
||||
if (IRQ) TriggerIRQ();
|
||||
|
||||
NDS::ARM9Timestamp += Cycles;
|
||||
NDS.ARM9Timestamp += Cycles;
|
||||
Cycles = 0;
|
||||
}
|
||||
|
||||
|
@ -662,37 +679,37 @@ void ARMv5::ExecuteJIT()
|
|||
{
|
||||
Halted = 0;
|
||||
}
|
||||
else if (NDS::HaltInterrupted(0))
|
||||
else if (NDS.HaltInterrupted(0))
|
||||
{
|
||||
Halted = 0;
|
||||
if (NDS::IME[0] & 0x1)
|
||||
if (NDS.IME[0] & 0x1)
|
||||
TriggerIRQ();
|
||||
}
|
||||
else
|
||||
{
|
||||
NDS::ARM9Timestamp = NDS::ARM9Target;
|
||||
NDS.ARM9Timestamp = NDS.ARM9Target;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (NDS::ARM9Timestamp < NDS::ARM9Target)
|
||||
while (NDS.ARM9Timestamp < NDS.ARM9Target)
|
||||
{
|
||||
u32 instrAddr = R[15] - ((CPSR&0x20)?2:4);
|
||||
|
||||
if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize))
|
||||
&& !ARMJIT::SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize))
|
||||
&& !NDS.JIT.SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize))
|
||||
{
|
||||
NDS::ARM9Timestamp = NDS::ARM9Target;
|
||||
NDS.ARM9Timestamp = NDS.ARM9Target;
|
||||
Log(LogLevel::Error, "ARMv5 PC in non executable region %08X\n", R[15]);
|
||||
return;
|
||||
}
|
||||
|
||||
ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(0, FastBlockLookup,
|
||||
JitBlockEntry block = NDS.JIT.LookUpBlock(0, FastBlockLookup,
|
||||
instrAddr - FastBlockLookupStart, instrAddr);
|
||||
if (block)
|
||||
ARM_Dispatch(this, block);
|
||||
else
|
||||
ARMJIT::CompileBlock(this);
|
||||
NDS.JIT.CompileBlock(this);
|
||||
|
||||
if (StopExecution)
|
||||
{
|
||||
|
@ -702,17 +719,17 @@ void ARMv5::ExecuteJIT()
|
|||
|
||||
if (Halted || IdleLoop)
|
||||
{
|
||||
if ((Halted == 1 || IdleLoop) && NDS::ARM9Timestamp < NDS::ARM9Target)
|
||||
if ((Halted == 1 || IdleLoop) && NDS.ARM9Timestamp < NDS.ARM9Target)
|
||||
{
|
||||
Cycles = 0;
|
||||
NDS::ARM9Timestamp = NDS::ARM9Target;
|
||||
NDS.ARM9Timestamp = NDS.ARM9Target;
|
||||
}
|
||||
IdleLoop = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NDS::ARM9Timestamp += Cycles;
|
||||
NDS.ARM9Timestamp += Cycles;
|
||||
Cycles = 0;
|
||||
}
|
||||
|
||||
|
@ -723,29 +740,33 @@ void ARMv5::ExecuteJIT()
|
|||
|
||||
void ARMv4::Execute()
|
||||
{
|
||||
GdbCheckB();
|
||||
|
||||
if (Halted)
|
||||
{
|
||||
if (Halted == 2)
|
||||
{
|
||||
Halted = 0;
|
||||
}
|
||||
else if (NDS::HaltInterrupted(1))
|
||||
else if (NDS.HaltInterrupted(1))
|
||||
{
|
||||
Halted = 0;
|
||||
if (NDS::IME[1] & 0x1)
|
||||
if (NDS.IME[1] & 0x1)
|
||||
TriggerIRQ();
|
||||
}
|
||||
else
|
||||
{
|
||||
NDS::ARM7Timestamp = NDS::ARM7Target;
|
||||
NDS.ARM7Timestamp = NDS.ARM7Target;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (NDS::ARM7Timestamp < NDS::ARM7Target)
|
||||
while (NDS.ARM7Timestamp < NDS.ARM7Target)
|
||||
{
|
||||
if (CPSR & 0x20) // THUMB
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 2;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -758,6 +779,8 @@ void ARMv4::Execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 4;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -777,9 +800,9 @@ void ARMv4::Execute()
|
|||
// TODO optimize this shit!!!
|
||||
if (Halted)
|
||||
{
|
||||
if (Halted == 1 && NDS::ARM7Timestamp < NDS::ARM7Target)
|
||||
if (Halted == 1 && NDS.ARM7Timestamp < NDS.ARM7Target)
|
||||
{
|
||||
NDS::ARM7Timestamp = NDS::ARM7Target;
|
||||
NDS.ARM7Timestamp = NDS.ARM7Target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -790,7 +813,7 @@ void ARMv4::Execute()
|
|||
}*/
|
||||
if (IRQ) TriggerIRQ();
|
||||
|
||||
NDS::ARM7Timestamp += Cycles;
|
||||
NDS.ARM7Timestamp += Cycles;
|
||||
Cycles = 0;
|
||||
}
|
||||
|
||||
|
@ -799,7 +822,9 @@ void ARMv4::Execute()
|
|||
|
||||
if (Halted == 4)
|
||||
{
|
||||
DSi::SoftReset();
|
||||
assert(NDS.ConsoleType == 1);
|
||||
auto& dsi = dynamic_cast<melonDS::DSi&>(NDS);
|
||||
dsi.SoftReset();
|
||||
Halted = 2;
|
||||
}
|
||||
}
|
||||
|
@ -813,37 +838,37 @@ void ARMv4::ExecuteJIT()
|
|||
{
|
||||
Halted = 0;
|
||||
}
|
||||
else if (NDS::HaltInterrupted(1))
|
||||
else if (NDS.HaltInterrupted(1))
|
||||
{
|
||||
Halted = 0;
|
||||
if (NDS::IME[1] & 0x1)
|
||||
if (NDS.IME[1] & 0x1)
|
||||
TriggerIRQ();
|
||||
}
|
||||
else
|
||||
{
|
||||
NDS::ARM7Timestamp = NDS::ARM7Target;
|
||||
NDS.ARM7Timestamp = NDS.ARM7Target;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (NDS::ARM7Timestamp < NDS::ARM7Target)
|
||||
while (NDS.ARM7Timestamp < NDS.ARM7Target)
|
||||
{
|
||||
u32 instrAddr = R[15] - ((CPSR&0x20)?2:4);
|
||||
|
||||
if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize))
|
||||
&& !ARMJIT::SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize))
|
||||
&& !NDS.JIT.SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize))
|
||||
{
|
||||
NDS::ARM7Timestamp = NDS::ARM7Target;
|
||||
NDS.ARM7Timestamp = NDS.ARM7Target;
|
||||
Log(LogLevel::Error, "ARMv4 PC in non executable region %08X\n", R[15]);
|
||||
return;
|
||||
}
|
||||
|
||||
ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(1, FastBlockLookup,
|
||||
JitBlockEntry block = NDS.JIT.LookUpBlock(1, FastBlockLookup,
|
||||
instrAddr - FastBlockLookupStart, instrAddr);
|
||||
if (block)
|
||||
ARM_Dispatch(this, block);
|
||||
else
|
||||
ARMJIT::CompileBlock(this);
|
||||
NDS.JIT.CompileBlock(this);
|
||||
|
||||
if (StopExecution)
|
||||
{
|
||||
|
@ -852,17 +877,17 @@ void ARMv4::ExecuteJIT()
|
|||
|
||||
if (Halted || IdleLoop)
|
||||
{
|
||||
if ((Halted == 1 || IdleLoop) && NDS::ARM7Timestamp < NDS::ARM7Target)
|
||||
if ((Halted == 1 || IdleLoop) && NDS.ARM7Timestamp < NDS.ARM7Target)
|
||||
{
|
||||
Cycles = 0;
|
||||
NDS::ARM7Timestamp = NDS::ARM7Target;
|
||||
NDS.ARM7Timestamp = NDS.ARM7Target;
|
||||
}
|
||||
IdleLoop = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NDS::ARM7Timestamp += Cycles;
|
||||
NDS.ARM7Timestamp += Cycles;
|
||||
Cycles = 0;
|
||||
}
|
||||
|
||||
|
@ -871,7 +896,9 @@ void ARMv4::ExecuteJIT()
|
|||
|
||||
if (Halted == 4)
|
||||
{
|
||||
DSi::SoftReset();
|
||||
assert(NDS.ConsoleType == 1);
|
||||
auto& dsi = dynamic_cast<melonDS::DSi&>(NDS);
|
||||
dsi.SoftReset();
|
||||
Halted = 2;
|
||||
}
|
||||
}
|
||||
|
@ -916,3 +943,402 @@ void ARMv4::FillPipeline()
|
|||
NextInstr[1] = CodeRead32(R[15]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
u32 ARM::ReadReg(Gdb::Register reg)
|
||||
{
|
||||
using Gdb::Register;
|
||||
int r = static_cast<int>(reg);
|
||||
|
||||
if (reg < Register::pc) return R[r];
|
||||
else if (reg == Register::pc)
|
||||
{
|
||||
return R[r] - ((CPSR & 0x20) ? 2 : 4);
|
||||
}
|
||||
else if (reg == Register::cpsr) return CPSR;
|
||||
else if (reg == Register::sp_usr || reg == Register::lr_usr)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_usr);
|
||||
if (ModeIs(0x10) || ModeIs(0x1f))
|
||||
{
|
||||
return R[13 + r];
|
||||
}
|
||||
else switch (CPSR & 0x1f)
|
||||
{
|
||||
case 0x11: return R_FIQ[5 + r];
|
||||
case 0x12: return R_IRQ[0 + r];
|
||||
case 0x13: return R_SVC[0 + r];
|
||||
case 0x17: return R_ABT[0 + r];
|
||||
case 0x1b: return R_UND[0 + r];
|
||||
}
|
||||
}
|
||||
else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq)
|
||||
{
|
||||
r -= static_cast<int>(Register::r8_fiq);
|
||||
return ModeIs(0x11) ? R[ 8 + r] : R_FIQ[r];
|
||||
}
|
||||
else if (reg == Register::sp_irq || reg == Register::lr_irq)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_irq);
|
||||
return ModeIs(0x12) ? R[13 + r] : R_IRQ[r];
|
||||
}
|
||||
else if (reg == Register::sp_svc || reg == Register::lr_svc)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_svc);
|
||||
return ModeIs(0x13) ? R[13 + r] : R_SVC[r];
|
||||
}
|
||||
else if (reg == Register::sp_abt || reg == Register::lr_abt)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_abt);
|
||||
return ModeIs(0x17) ? R[13 + r] : R_ABT[r];
|
||||
}
|
||||
else if (reg == Register::sp_und || reg == Register::lr_und)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_und);
|
||||
return ModeIs(0x1b) ? R[13 + r] : R_UND[r];
|
||||
}
|
||||
else if (reg == Register::spsr_fiq) return ModeIs(0x11) ? CPSR : R_FIQ[7];
|
||||
else if (reg == Register::spsr_irq) return ModeIs(0x12) ? CPSR : R_IRQ[2];
|
||||
else if (reg == Register::spsr_svc) return ModeIs(0x13) ? CPSR : R_SVC[2];
|
||||
else if (reg == Register::spsr_abt) return ModeIs(0x17) ? CPSR : R_ABT[2];
|
||||
else if (reg == Register::spsr_und) return ModeIs(0x1b) ? CPSR : R_UND[2];
|
||||
|
||||
Log(LogLevel::Warn, "GDB reg read: unknown reg no %d\n", r);
|
||||
return 0xdeadbeef;
|
||||
}
|
||||
void ARM::WriteReg(Gdb::Register reg, u32 v)
|
||||
{
|
||||
using Gdb::Register;
|
||||
int r = static_cast<int>(reg);
|
||||
|
||||
if (reg < Register::pc) R[r] = v;
|
||||
else if (reg == Register::pc) JumpTo(v);
|
||||
else if (reg == Register::cpsr) CPSR = v;
|
||||
else if (reg == Register::sp_usr || reg == Register::lr_usr)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_usr);
|
||||
if (ModeIs(0x10) || ModeIs(0x1f))
|
||||
{
|
||||
R[13 + r] = v;
|
||||
}
|
||||
else switch (CPSR & 0x1f)
|
||||
{
|
||||
case 0x11: R_FIQ[5 + r] = v; break;
|
||||
case 0x12: R_IRQ[0 + r] = v; break;
|
||||
case 0x13: R_SVC[0 + r] = v; break;
|
||||
case 0x17: R_ABT[0 + r] = v; break;
|
||||
case 0x1b: R_UND[0 + r] = v; break;
|
||||
}
|
||||
}
|
||||
else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq)
|
||||
{
|
||||
r -= static_cast<int>(Register::r8_fiq);
|
||||
*(ModeIs(0x11) ? &R[ 8 + r] : &R_FIQ[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_irq || reg == Register::lr_irq)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_irq);
|
||||
*(ModeIs(0x12) ? &R[13 + r] : &R_IRQ[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_svc || reg == Register::lr_svc)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_svc);
|
||||
*(ModeIs(0x13) ? &R[13 + r] : &R_SVC[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_abt || reg == Register::lr_abt)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_abt);
|
||||
*(ModeIs(0x17) ? &R[13 + r] : &R_ABT[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_und || reg == Register::lr_und)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_und);
|
||||
*(ModeIs(0x1b) ? &R[13 + r] : &R_UND[r]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_fiq)
|
||||
{
|
||||
*(ModeIs(0x11) ? &CPSR : &R_FIQ[7]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_irq)
|
||||
{
|
||||
*(ModeIs(0x12) ? &CPSR : &R_IRQ[2]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_svc)
|
||||
{
|
||||
*(ModeIs(0x13) ? &CPSR : &R_SVC[2]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_abt)
|
||||
{
|
||||
*(ModeIs(0x17) ? &CPSR : &R_ABT[2]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_und)
|
||||
{
|
||||
*(ModeIs(0x1b) ? &CPSR : &R_UND[2]) = v;
|
||||
}
|
||||
else Log(LogLevel::Warn, "GDB reg write: unknown reg no %d (write 0x%08x)\n", r, v);
|
||||
}
|
||||
u32 ARM::ReadMem(u32 addr, int size)
|
||||
{
|
||||
if (size == 8) return BusRead8(addr);
|
||||
else if (size == 16) return BusRead16(addr);
|
||||
else if (size == 32) return BusRead32(addr);
|
||||
else return 0xfeedface;
|
||||
}
|
||||
void ARM::WriteMem(u32 addr, int size, u32 v)
|
||||
{
|
||||
if (size == 8) BusWrite8(addr, (u8)v);
|
||||
else if (size == 16) BusWrite16(addr, (u16)v);
|
||||
else if (size == 32) BusWrite32(addr, v);
|
||||
}
|
||||
|
||||
void ARM::ResetGdb()
|
||||
{
|
||||
NDS.Reset();
|
||||
NDS.GPU.StartFrame(); // need this to properly kick off the scheduler & frame output
|
||||
}
|
||||
int ARM::RemoteCmd(const u8* cmd, size_t len)
|
||||
{
|
||||
(void)len;
|
||||
|
||||
Log(LogLevel::Info, "[ARMGDB] Rcmd: \"%s\"\n", cmd);
|
||||
if (!strcmp((const char*)cmd, "reset") || !strcmp((const char*)cmd, "r"))
|
||||
{
|
||||
Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1; // not implemented (yet)
|
||||
}
|
||||
|
||||
void ARMv5::WriteMem(u32 addr, int size, u32 v)
|
||||
{
|
||||
if (addr < ITCMSize)
|
||||
{
|
||||
if (size == 8) *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u8)v;
|
||||
else if (size == 16) *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u16)v;
|
||||
else if (size == 32) *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u32)v;
|
||||
else {}
|
||||
return;
|
||||
}
|
||||
else if ((addr & DTCMMask) == DTCMBase)
|
||||
{
|
||||
if (size == 8) *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u8)v;
|
||||
else if (size == 16) *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u16)v;
|
||||
else if (size == 32) *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u32)v;
|
||||
else {}
|
||||
return;
|
||||
}
|
||||
|
||||
ARM::WriteMem(addr, size, v);
|
||||
}
|
||||
u32 ARMv5::ReadMem(u32 addr, int size)
|
||||
{
|
||||
if (addr < ITCMSize)
|
||||
{
|
||||
if (size == 8) return *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)];
|
||||
else if (size == 16) return *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)];
|
||||
else if (size == 32) return *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)];
|
||||
else return 0xfeedface;
|
||||
}
|
||||
else if ((addr & DTCMMask) == DTCMBase)
|
||||
{
|
||||
if (size == 8) return *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)];
|
||||
else if (size == 16) return *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)];
|
||||
else if (size == 32) return *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)];
|
||||
else return 0xfeedface;
|
||||
}
|
||||
|
||||
return ARM::ReadMem(addr, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ARMv4::DataRead8(u32 addr, u32* val)
|
||||
{
|
||||
*val = BusRead8(addr);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS.ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void ARMv4::DataRead16(u32 addr, u32* val)
|
||||
{
|
||||
addr &= ~1;
|
||||
|
||||
*val = BusRead16(addr);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS.ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void ARMv4::DataRead32(u32 addr, u32* val)
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
*val = BusRead32(addr);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS.ARM7MemTimings[addr >> 15][2];
|
||||
}
|
||||
|
||||
void ARMv4::DataRead32S(u32 addr, u32* val)
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
*val = BusRead32(addr);
|
||||
DataCycles += NDS.ARM7MemTimings[addr >> 15][3];
|
||||
}
|
||||
|
||||
void ARMv4::DataWrite8(u32 addr, u8 val)
|
||||
{
|
||||
BusWrite8(addr, val);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS.ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void ARMv4::DataWrite16(u32 addr, u16 val)
|
||||
{
|
||||
addr &= ~1;
|
||||
|
||||
BusWrite16(addr, val);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS.ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void ARMv4::DataWrite32(u32 addr, u32 val)
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
BusWrite32(addr, val);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS.ARM7MemTimings[addr >> 15][2];
|
||||
}
|
||||
|
||||
void ARMv4::DataWrite32S(u32 addr, u32 val)
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
BusWrite32(addr, val);
|
||||
DataCycles += NDS.ARM7MemTimings[addr >> 15][3];
|
||||
}
|
||||
|
||||
|
||||
void ARMv4::AddCycles_C()
|
||||
{
|
||||
// code only. this code fetch is sequential.
|
||||
Cycles += NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
|
||||
}
|
||||
|
||||
void ARMv4::AddCycles_CI(s32 num)
|
||||
{
|
||||
// code+internal. results in a nonseq code fetch.
|
||||
Cycles += NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
|
||||
}
|
||||
|
||||
void ARMv4::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 ARMv4::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;
|
||||
}
|
||||
}
|
||||
|
||||
u8 ARMv5::BusRead8(u32 addr)
|
||||
{
|
||||
return NDS.ARM9Read8(addr);
|
||||
}
|
||||
|
||||
u16 ARMv5::BusRead16(u32 addr)
|
||||
{
|
||||
return NDS.ARM9Read16(addr);
|
||||
}
|
||||
|
||||
u32 ARMv5::BusRead32(u32 addr)
|
||||
{
|
||||
return NDS.ARM9Read32(addr);
|
||||
}
|
||||
|
||||
void ARMv5::BusWrite8(u32 addr, u8 val)
|
||||
{
|
||||
NDS.ARM9Write8(addr, val);
|
||||
}
|
||||
|
||||
void ARMv5::BusWrite16(u32 addr, u16 val)
|
||||
{
|
||||
NDS.ARM9Write16(addr, val);
|
||||
}
|
||||
|
||||
void ARMv5::BusWrite32(u32 addr, u32 val)
|
||||
{
|
||||
NDS.ARM9Write32(addr, val);
|
||||
}
|
||||
|
||||
u8 ARMv4::BusRead8(u32 addr)
|
||||
{
|
||||
return NDS.ARM7Read8(addr);
|
||||
}
|
||||
|
||||
u16 ARMv4::BusRead16(u32 addr)
|
||||
{
|
||||
return NDS.ARM7Read16(addr);
|
||||
}
|
||||
|
||||
u32 ARMv4::BusRead32(u32 addr)
|
||||
{
|
||||
return NDS.ARM7Read32(addr);
|
||||
}
|
||||
|
||||
void ARMv4::BusWrite8(u32 addr, u8 val)
|
||||
{
|
||||
NDS.ARM7Write8(addr, val);
|
||||
}
|
||||
|
||||
void ARMv4::BusWrite16(u32 addr, u16 val)
|
||||
{
|
||||
NDS.ARM7Write16(addr, val);
|
||||
}
|
||||
|
||||
void ARMv4::BusWrite32(u32 addr, u32 val)
|
||||
{
|
||||
NDS.ARM7Write32(addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
309
src/ARM.h
309
src/ARM.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,10 +20,18 @@
|
|||
#define ARM_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "types.h"
|
||||
#include "NDS.h"
|
||||
#include "MemRegion.h"
|
||||
#include "MemConstants.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));
|
||||
|
@ -35,13 +43,20 @@ enum
|
|||
RWFlags_ForceUser = (1<<21),
|
||||
};
|
||||
|
||||
const u32 ITCMPhysicalSize = 0x8000;
|
||||
const u32 DTCMPhysicalSize = 0x4000;
|
||||
struct GDBArgs;
|
||||
class ARMJIT;
|
||||
class GPU;
|
||||
class ARMJIT_Memory;
|
||||
class NDS;
|
||||
class Savestate;
|
||||
|
||||
class ARM
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
: public Gdb::StubCallbacks
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
ARM(u32 num);
|
||||
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
|
||||
virtual ~ARM(); // destroy shit
|
||||
|
||||
virtual void Reset();
|
||||
|
@ -59,12 +74,13 @@ public:
|
|||
Halted = halt;
|
||||
}
|
||||
|
||||
void NocashPrint(u32 addr) noexcept;
|
||||
virtual void Execute() = 0;
|
||||
#ifdef JIT_ENABLED
|
||||
virtual void ExecuteJIT() = 0;
|
||||
#endif
|
||||
|
||||
bool CheckCondition(u32 code)
|
||||
bool CheckCondition(u32 code) const
|
||||
{
|
||||
if (code == 0xE) return true;
|
||||
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
|
||||
|
@ -93,6 +109,18 @@ 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();
|
||||
|
@ -114,6 +142,7 @@ public:
|
|||
virtual void AddCycles_CDI() = 0;
|
||||
virtual void AddCycles_CD() = 0;
|
||||
|
||||
void CheckGdbIncoming();
|
||||
|
||||
u32 Num;
|
||||
|
||||
|
@ -147,75 +176,103 @@ public:
|
|||
|
||||
u32 ExceptionBase;
|
||||
|
||||
NDS::MemRegion CodeMem;
|
||||
MemRegion CodeMem;
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
u32 FastBlockLookupStart, FastBlockLookupSize;
|
||||
u64* FastBlockLookup;
|
||||
#endif
|
||||
|
||||
static u32 ConditionTable[16];
|
||||
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;
|
||||
|
||||
protected:
|
||||
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);
|
||||
#endif
|
||||
|
||||
void GdbCheckA();
|
||||
void GdbCheckB();
|
||||
void GdbCheckC();
|
||||
};
|
||||
|
||||
class ARMv5 : public ARM
|
||||
{
|
||||
public:
|
||||
ARMv5();
|
||||
ARMv5(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
|
||||
~ARMv5();
|
||||
|
||||
void Reset();
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file);
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
void UpdateRegionTimings(u32 addrstart, u32 addrend);
|
||||
|
||||
void FillPipeline();
|
||||
void FillPipeline() override;
|
||||
|
||||
void JumpTo(u32 addr, bool restorecpsr = false);
|
||||
void JumpTo(u32 addr, bool restorecpsr = false) override;
|
||||
|
||||
void PrefetchAbort();
|
||||
void DataAbort();
|
||||
|
||||
void Execute();
|
||||
void Execute() override;
|
||||
#ifdef JIT_ENABLED
|
||||
void ExecuteJIT();
|
||||
void ExecuteJIT() override;
|
||||
#endif
|
||||
|
||||
// all code accesses are forced nonseq 32bit
|
||||
u32 CodeRead32(u32 addr, bool branch);
|
||||
|
||||
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 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()
|
||||
void AddCycles_C() override
|
||||
{
|
||||
// code only. always nonseq 32-bit for ARM9.
|
||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||
Cycles += numC;
|
||||
}
|
||||
|
||||
void AddCycles_CI(s32 numI)
|
||||
void AddCycles_CI(s32 numI) override
|
||||
{
|
||||
// code+internal
|
||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||
Cycles += numC + numI;
|
||||
}
|
||||
|
||||
void AddCycles_CDI()
|
||||
void AddCycles_CDI() override
|
||||
{
|
||||
// LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
|
||||
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
||||
|
@ -228,7 +285,7 @@ public:
|
|||
// Cycles += numC + numD;
|
||||
}
|
||||
|
||||
void AddCycles_CD()
|
||||
void AddCycles_CD() override
|
||||
{
|
||||
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||
|
@ -240,7 +297,7 @@ public:
|
|||
// Cycles += numC + numD;
|
||||
}
|
||||
|
||||
void GetCodeMemRegion(u32 addr, NDS::MemRegion* region);
|
||||
void GetCodeMemRegion(u32 addr, MemRegion* region);
|
||||
|
||||
void CP15Reset();
|
||||
void CP15DoSavestate(Savestate* file);
|
||||
|
@ -258,7 +315,7 @@ public:
|
|||
void ICacheInvalidateAll();
|
||||
|
||||
void CP15Write(u32 id, u32 val);
|
||||
u32 CP15Read(u32 id);
|
||||
u32 CP15Read(u32 id) const;
|
||||
|
||||
u32 CP15Control;
|
||||
|
||||
|
@ -301,23 +358,34 @@ public:
|
|||
|
||||
u8* CurICacheLine;
|
||||
|
||||
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
|
||||
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;
|
||||
};
|
||||
|
||||
class ARMv4 : public ARM
|
||||
{
|
||||
public:
|
||||
ARMv4();
|
||||
ARMv4(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
|
||||
|
||||
void Reset();
|
||||
void FillPipeline() override;
|
||||
|
||||
void FillPipeline();
|
||||
void JumpTo(u32 addr, bool restorecpsr = false) override;
|
||||
|
||||
void JumpTo(u32 addr, bool restorecpsr = false);
|
||||
|
||||
void Execute();
|
||||
void Execute() override;
|
||||
#ifdef JIT_ENABLED
|
||||
void ExecuteJIT();
|
||||
void ExecuteJIT() override;
|
||||
#endif
|
||||
|
||||
u16 CodeRead16(u32 addr)
|
||||
|
@ -330,134 +398,25 @@ public:
|
|||
return BusRead32(addr);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
namespace ARMInterpreter
|
||||
|
@ -467,13 +426,5 @@ void A_UNK(ARM* cpu);
|
|||
void T_UNK(ARM* cpu);
|
||||
|
||||
}
|
||||
|
||||
namespace NDS
|
||||
{
|
||||
|
||||
extern ARMv5* ARM9;
|
||||
extern ARMv4* ARM7;
|
||||
|
||||
}
|
||||
|
||||
#endif // ARM_H
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -24,16 +24,22 @@
|
|||
#include "ARMInterpreter_LoadStore.h"
|
||||
#include "Platform.h"
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
#include "debug/GdbStub.h"
|
||||
#endif
|
||||
|
||||
namespace ARMInterpreter
|
||||
namespace melonDS::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(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-8);
|
||||
#endif
|
||||
//for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
|
||||
//NDS::Halt();
|
||||
u32 oldcpsr = cpu->CPSR;
|
||||
|
@ -49,6 +55,9 @@ 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(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-4);
|
||||
#endif
|
||||
//NDS::Halt();
|
||||
u32 oldcpsr = cpu->CPSR;
|
||||
cpu->CPSR &= ~0xBF;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
|||
#include "types.h"
|
||||
#include "ARM.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
namespace ARMInterpreter
|
||||
{
|
||||
|
||||
|
@ -41,4 +43,5 @@ void A_BLX_IMM(ARM* cpu); // I'm a special one look at me
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
#endif // ARMINTERPRETER_H
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -18,8 +18,9 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include "ARM.h"
|
||||
#include "NDS.h"
|
||||
|
||||
namespace ARMInterpreter
|
||||
namespace melonDS::ARMInterpreter
|
||||
{
|
||||
|
||||
inline bool CarryAdd(u32 a, u32 b)
|
||||
|
@ -692,7 +693,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
|
||||
NDS::NocashPrint(cpu->Num, addr);
|
||||
cpu->NDS.NocashPrint(cpu->Num, addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1561,7 +1562,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
|
||||
NDS::NocashPrint(cpu->Num, addr);
|
||||
cpu->NDS.NocashPrint(cpu->Num, addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
|||
#ifndef ARMINTERPRETER_ALU_H
|
||||
#define ARMINTERPRETER_ALU_H
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
namespace ARMInterpreter
|
||||
{
|
||||
|
||||
|
@ -134,4 +136,5 @@ void T_ADD_SP(ARM* cpu);
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,12 +19,11 @@
|
|||
#include "ARM.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS::ARMInterpreter
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace ARMInterpreter
|
||||
{
|
||||
|
||||
|
||||
void A_B(ARM* cpu)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
|||
#ifndef ARMINTERPRETER_BRANCH_H
|
||||
#define ARMINTERPRETER_BRANCH_H
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
namespace ARMInterpreter
|
||||
{
|
||||
|
||||
|
@ -36,4 +38,5 @@ void T_BL_LONG_2(ARM* cpu);
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
|||
#include "ARM.h"
|
||||
|
||||
|
||||
namespace ARMInterpreter
|
||||
namespace melonDS::ARMInterpreter
|
||||
{
|
||||
|
||||
|
||||
|
@ -62,14 +62,20 @@ namespace ARMInterpreter
|
|||
|
||||
#define A_STR \
|
||||
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
|
||||
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
|
||||
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
|
||||
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
|
||||
storeval += 4; \
|
||||
cpu->DataWrite32(offset, storeval); \
|
||||
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]; \
|
||||
cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
|
||||
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
|
||||
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
|
||||
storeval += 4; \
|
||||
cpu->DataWrite32(addr, storeval); \
|
||||
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
|
||||
cpu->AddCycles_CD();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
|||
#ifndef ARMINTERPRETER_LOADSTORE_H
|
||||
#define ARMINTERPRETER_LOADSTORE_H
|
||||
|
||||
namespace ARMInterpreter
|
||||
namespace melonDS::ARMInterpreter
|
||||
{
|
||||
|
||||
#define A_PROTO_WB_LDRSTR(x) \
|
||||
|
|
346
src/ARMJIT.cpp
346
src/ARMJIT.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 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>
|
||||
|
@ -43,138 +43,51 @@
|
|||
#include "Wifi.h"
|
||||
#include "NDSCart.h"
|
||||
#include "Platform.h"
|
||||
#include "ARMJIT_x64/ARMJIT_Offsets.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
#include "ARMJIT_x64/ARMJIT_Offsets.h"
|
||||
static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset, "");
|
||||
static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset, "");
|
||||
static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset, "");
|
||||
|
||||
namespace ARMJIT
|
||||
{
|
||||
|
||||
#define JIT_DEBUGPRINT(msg, ...)
|
||||
//#define JIT_DEBUGPRINT(msg, ...) Platform::Log(Platform::LogLevel::Debug, 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,
|
||||
sizeof(NDS::ARM9BIOS),
|
||||
NDS::MainRAMMaxSize,
|
||||
NDS::SharedWRAMSize,
|
||||
ARM9BIOSSize,
|
||||
MainRAMMaxSize,
|
||||
SharedWRAMSize,
|
||||
0,
|
||||
0x100000,
|
||||
sizeof(NDS::ARM7BIOS),
|
||||
NDS::ARM7WRAMSize,
|
||||
ARM7BIOSSize,
|
||||
ARM7WRAMSize,
|
||||
0,
|
||||
0,
|
||||
0x40000,
|
||||
0x10000,
|
||||
0x10000,
|
||||
DSi::NWRAMSize,
|
||||
DSi::NWRAMSize,
|
||||
DSi::NWRAMSize,
|
||||
NWRAMSize,
|
||||
NWRAMSize,
|
||||
NWRAMSize,
|
||||
};
|
||||
|
||||
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)
|
||||
u32 ARMJIT::LocaliseCodeAddress(u32 num, u32 addr) const noexcept
|
||||
{
|
||||
int region = num == 0
|
||||
? ARMJIT_Memory::ClassifyAddress9(addr)
|
||||
: ARMJIT_Memory::ClassifyAddress7(addr);
|
||||
? Memory.ClassifyAddress9(addr)
|
||||
: Memory.ClassifyAddress7(addr);
|
||||
|
||||
if (CodeMemRegions[region])
|
||||
return ARMJIT_Memory::LocaliseAddress(region, num, addr);
|
||||
return Memory.LocaliseAddress(region, num, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -190,11 +103,31 @@ 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 = (ConsoleType == 0 ? NDS::ARM9Read32 : DSi::ARM9Read32)(addr);
|
||||
val = NDS::Current->ARM9Read32(addr);
|
||||
else if (std::is_same<T, u16>::value)
|
||||
val = (ConsoleType == 0 ? NDS::ARM9Read16 : DSi::ARM9Read16)(addr);
|
||||
val = NDS::Current->ARM9Read16(addr);
|
||||
else
|
||||
val = (ConsoleType == 0 ? NDS::ARM9Read8 : DSi::ARM9Read8)(addr);
|
||||
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);
|
||||
|
||||
if (std::is_same<T, u32>::value)
|
||||
return ROR(val, offset << 3);
|
||||
|
@ -209,7 +142,7 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
|
|||
|
||||
if (addr < cpu->ITCMSize)
|
||||
{
|
||||
CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
cpu->NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
*(T*)&cpu->ITCM[addr & 0x7FFF] = val;
|
||||
}
|
||||
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
|
||||
|
@ -218,49 +151,29 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
|
|||
}
|
||||
else if (std::is_same<T, u32>::value)
|
||||
{
|
||||
(ConsoleType == 0 ? NDS::ARM9Write32 : DSi::ARM9Write32)(addr, val);
|
||||
NDS::Current->ARM9Write32(addr, val);
|
||||
}
|
||||
else if (std::is_same<T, u16>::value)
|
||||
{
|
||||
(ConsoleType == 0 ? NDS::ARM9Write16 : DSi::ARM9Write16)(addr, val);
|
||||
NDS::Current->ARM9Write16(addr, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
(ConsoleType == 0 ? NDS::ARM9Write8 : DSi::ARM9Write8)(addr, val);
|
||||
NDS::Current->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)
|
||||
(ConsoleType == 0 ? NDS::ARM7Write32 : DSi::ARM7Write32)(addr, val);
|
||||
NDS::Current->ARM7Write32(addr, val);
|
||||
else if (std::is_same<T, u16>::value)
|
||||
(ConsoleType == 0 ? NDS::ARM7Write16 : DSi::ARM7Write16)(addr, val);
|
||||
NDS::Current->ARM7Write16(addr, val);
|
||||
else
|
||||
(ConsoleType == 0 ? NDS::ARM7Write8 : DSi::ARM7Write8)(addr, val);
|
||||
NDS::Current->ARM7Write8(addr, val);
|
||||
}
|
||||
|
||||
template <bool Write, int ConsoleType>
|
||||
|
@ -316,39 +229,18 @@ void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
|
|||
INSTANTIATE_SLOWMEM(0)
|
||||
INSTANTIATE_SLOWMEM(1)
|
||||
|
||||
void Init()
|
||||
{
|
||||
JITCompiler = new Compiler();
|
||||
|
||||
ARMJIT_Memory::Init();
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
ARMJIT::~ARMJIT() noexcept
|
||||
{
|
||||
JitEnableWrite();
|
||||
ResetBlockCache();
|
||||
ARMJIT_Memory::DeInit();
|
||||
|
||||
delete JITCompiler;
|
||||
JITCompiler = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void ARMJIT::Reset() noexcept
|
||||
{
|
||||
MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize);
|
||||
LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations);
|
||||
BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations);
|
||||
FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory);
|
||||
|
||||
if (MaxBlockSize < 1)
|
||||
MaxBlockSize = 1;
|
||||
if (MaxBlockSize > 32)
|
||||
MaxBlockSize = 32;
|
||||
|
||||
JitEnableWrite();
|
||||
ResetBlockCache();
|
||||
|
||||
ARMJIT_Memory::Reset();
|
||||
Memory.Reset();
|
||||
}
|
||||
|
||||
void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags)
|
||||
|
@ -575,7 +467,7 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
|
|||
};
|
||||
#undef F
|
||||
|
||||
void RetireJitBlock(JitBlock* block)
|
||||
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
|
||||
{
|
||||
auto it = RestoreCandidates.find(block->InstrHash);
|
||||
if (it != RestoreCandidates.end())
|
||||
|
@ -589,7 +481,57 @@ void RetireJitBlock(JitBlock* block)
|
|||
}
|
||||
}
|
||||
|
||||
void CompileBlock(ARM* cpu)
|
||||
void ARMJIT::SetJITArgs(JITArgs args) noexcept
|
||||
{
|
||||
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
|
||||
|
||||
if (MaxBlockSize != args.MaxBlockSize
|
||||
|| LiteralOptimizations != args.LiteralOptimizations
|
||||
|| BranchOptimizations != args.BranchOptimizations
|
||||
|| FastMemory != args.FastMemory)
|
||||
ResetBlockCache();
|
||||
|
||||
MaxBlockSize = args.MaxBlockSize;
|
||||
LiteralOptimizations = args.LiteralOptimizations;
|
||||
BranchOptimizations = args.BranchOptimizations;
|
||||
FastMemory = args.FastMemory;
|
||||
}
|
||||
|
||||
void ARMJIT::SetMaxBlockSize(int size) noexcept
|
||||
{
|
||||
size = std::clamp(size, 1, 32);
|
||||
|
||||
if (size != MaxBlockSize)
|
||||
ResetBlockCache();
|
||||
|
||||
MaxBlockSize = size;
|
||||
}
|
||||
|
||||
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
|
||||
{
|
||||
if (LiteralOptimizations != enabled)
|
||||
ResetBlockCache();
|
||||
|
||||
LiteralOptimizations = enabled;
|
||||
}
|
||||
|
||||
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
|
||||
{
|
||||
if (BranchOptimizations != enabled)
|
||||
ResetBlockCache();
|
||||
|
||||
BranchOptimizations = enabled;
|
||||
}
|
||||
|
||||
void ARMJIT::SetFastMemory(bool enabled) noexcept
|
||||
{
|
||||
if (FastMemory != enabled)
|
||||
ResetBlockCache();
|
||||
|
||||
FastMemory = enabled;
|
||||
}
|
||||
|
||||
void ARMJIT::CompileBlock(ARM* cpu) noexcept
|
||||
{
|
||||
bool thumb = cpu->CPSR & 0x20;
|
||||
|
||||
|
@ -616,7 +558,7 @@ void CompileBlock(ARM* cpu)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -717,7 +659,7 @@ void CompileBlock(ARM* cpu)
|
|||
nextInstr[1] = cpuv4->CodeRead32(r15);
|
||||
instrs[i].CodeCycles = cpu->CodeCycles;
|
||||
}
|
||||
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr);
|
||||
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr, LiteralOptimizations);
|
||||
|
||||
hasMemoryInstr |= thumb
|
||||
? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA)
|
||||
|
@ -875,7 +817,7 @@ void CompileBlock(ARM* cpu)
|
|||
|
||||
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);
|
||||
|
@ -956,7 +898,7 @@ void CompileBlock(ARM* cpu)
|
|||
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);
|
||||
|
@ -977,7 +919,7 @@ void CompileBlock(ARM* cpu)
|
|||
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
|
||||
|
||||
if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512]))
|
||||
ARMJIT_Memory::SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
|
||||
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
|
||||
|
||||
AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512];
|
||||
range->Code |= addressMasks[j];
|
||||
|
@ -991,10 +933,10 @@ void CompileBlock(ARM* cpu)
|
|||
|
||||
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 InvalidateByAddr(u32 localAddr)
|
||||
void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
|
||||
{
|
||||
JIT_DEBUGPRINT("invalidating by addr %x\n", localAddr);
|
||||
|
||||
|
@ -1031,7 +973,7 @@ void InvalidateByAddr(u32 localAddr)
|
|||
if (range->Blocks.Length == 0
|
||||
&& !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512]))
|
||||
{
|
||||
ARMJIT_Memory::SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
|
||||
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
|
||||
}
|
||||
|
||||
bool literalInvalidation = false;
|
||||
|
@ -1064,7 +1006,7 @@ void InvalidateByAddr(u32 localAddr)
|
|||
if (otherRange->Blocks.Length == 0)
|
||||
{
|
||||
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512]))
|
||||
ARMJIT_Memory::SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
|
||||
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
|
||||
|
||||
otherRange->Code = 0;
|
||||
}
|
||||
|
@ -1088,7 +1030,7 @@ void InvalidateByAddr(u32 localAddr)
|
|||
}
|
||||
}
|
||||
|
||||
void CheckAndInvalidateITCM()
|
||||
void ARMJIT::CheckAndInvalidateITCM() noexcept
|
||||
{
|
||||
for (u32 i = 0; i < ITCMPhysicalSize; i+=512)
|
||||
{
|
||||
|
@ -1106,7 +1048,7 @@ void CheckAndInvalidateITCM()
|
|||
}
|
||||
}
|
||||
|
||||
void CheckAndInvalidateWVRAM(int bank)
|
||||
void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept
|
||||
{
|
||||
u32 start = bank == 1 ? 0x20000 : 0;
|
||||
for (u32 i = start; i < start+0x20000; i+=512)
|
||||
|
@ -1122,38 +1064,30 @@ void CheckAndInvalidateWVRAM(int bank)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
JitBlockEntry ARMJIT::LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept
|
||||
{
|
||||
u64* entry = &entries[offset / 2];
|
||||
if (*entry >> 32 == (addr | num))
|
||||
return JITCompiler->AddEntryOffset((u32)*entry);
|
||||
return JITCompiler.AddEntryOffset((u32)*entry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry)
|
||||
void ARMJIT::blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept
|
||||
{
|
||||
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 SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size)
|
||||
bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept
|
||||
{
|
||||
// amazingly ignoring the DTCM is the proper behaviour for code fetches
|
||||
int region = num == 0
|
||||
? ARMJIT_Memory::ClassifyAddress9(blockAddr)
|
||||
: ARMJIT_Memory::ClassifyAddress7(blockAddr);
|
||||
? Memory.ClassifyAddress9(blockAddr)
|
||||
: Memory.ClassifyAddress7(blockAddr);
|
||||
|
||||
u32 memoryOffset;
|
||||
if (FastBlockLookupRegions[region]
|
||||
&& ARMJIT_Memory::GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size))
|
||||
&& 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;
|
||||
|
@ -1162,28 +1096,28 @@ bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32&
|
|||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
void ResetBlockCache()
|
||||
void ARMJIT::ResetBlockCache() noexcept
|
||||
{
|
||||
Log(LogLevel::Debug, "Resetting JIT block cache...\n");
|
||||
|
||||
// could be replace through a function which only resets
|
||||
// the permissions but we're too lazy
|
||||
ARMJIT_Memory::Reset();
|
||||
Memory.Reset();
|
||||
|
||||
InvalidLiterals.Clear();
|
||||
for (int i = 0; i < ARMJIT_Memory::memregions_Count; i++)
|
||||
|
@ -1221,10 +1155,10 @@ void ResetBlockCache()
|
|||
JitBlocks9.clear();
|
||||
JitBlocks7.clear();
|
||||
|
||||
JITCompiler->Reset();
|
||||
JITCompiler.Reset();
|
||||
}
|
||||
|
||||
void JitEnableWrite()
|
||||
void ARMJIT::JitEnableWrite() noexcept
|
||||
{
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
if (__builtin_available(macOS 11.0, *))
|
||||
|
@ -1232,7 +1166,7 @@ void JitEnableWrite()
|
|||
#endif
|
||||
}
|
||||
|
||||
void JitEnableExecute()
|
||||
void ARMJIT::JitEnableExecute() noexcept
|
||||
{
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
if (__builtin_available(macOS 11.0, *))
|
||||
|
|
196
src/ARMJIT.h
196
src/ARMJIT.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,49 +19,195 @@
|
|||
#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"
|
||||
|
||||
#include "ARM.h"
|
||||
#include "ARM_InstrInfo.h"
|
||||
#ifdef JIT_ENABLED
|
||||
#include "JitBlock.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace ARMJIT
|
||||
#include "ARMJIT_Compiler.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class ARM;
|
||||
|
||||
typedef void (*JitBlockEntry)();
|
||||
class JitBlock;
|
||||
class ARMJIT
|
||||
{
|
||||
public:
|
||||
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept :
|
||||
NDS(nds),
|
||||
Memory(nds),
|
||||
JITCompiler(nds),
|
||||
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
|
||||
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
|
||||
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
|
||||
FastMemory(jit.has_value() ? jit->FastMemory : false)
|
||||
{}
|
||||
~ARMJIT() 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;
|
||||
|
||||
extern int MaxBlockSize;
|
||||
extern bool LiteralOptimizations;
|
||||
extern bool BranchOptimizations;
|
||||
extern bool FastMemory;
|
||||
template <u32 num, int region>
|
||||
void CheckAndInvalidate(u32 addr) noexcept
|
||||
{
|
||||
u32 localAddr = Memory.LocaliseAddress(region, num, addr);
|
||||
if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16)))
|
||||
InvalidateByAddr(localAddr);
|
||||
}
|
||||
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept;
|
||||
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept;
|
||||
u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept;
|
||||
|
||||
void Init();
|
||||
void DeInit();
|
||||
ARMJIT_Memory Memory;
|
||||
private:
|
||||
int MaxBlockSize {};
|
||||
bool LiteralOptimizations = false;
|
||||
bool BranchOptimizations = false;
|
||||
bool FastMemory = false;
|
||||
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] {};
|
||||
|
||||
void JitEnableWrite();
|
||||
void JitEnableExecute();
|
||||
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
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry);
|
||||
// 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
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
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 = ::ROR(CurInstr.Instr & 0xFF, shift);
|
||||
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
|
||||
|
||||
if (S && shift && (CurInstr.SetFlags & 0x2))
|
||||
{
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
*/
|
||||
|
||||
#include "ARMJIT_Compiler.h"
|
||||
#include "../NDS.h"
|
||||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
// hack
|
||||
const int kCodeCacheTiming = 3;
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
|
@ -132,7 +133,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;
|
||||
}
|
||||
|
@ -144,7 +145,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;
|
||||
}
|
||||
|
@ -235,7 +236,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;
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "../ARMJIT_Internal.h"
|
||||
#include "../ARMInterpreter.h"
|
||||
#include "../ARMJIT.h"
|
||||
#include "../NDS.h"
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
#include <switch.h>
|
||||
|
@ -38,7 +40,7 @@ using namespace Arm64Gen;
|
|||
|
||||
extern "C" void ARM_Ret();
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
/*
|
||||
|
@ -105,7 +107,7 @@ void Compiler::A_Comp_MSR()
|
|||
if (CurInstr.Instr & (1 << 25))
|
||||
{
|
||||
val = W0;
|
||||
MOVI2R(val, ::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
|
||||
MOVI2R(val, melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -219,7 +221,7 @@ void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
|
|||
}
|
||||
}
|
||||
|
||||
Compiler::Compiler()
|
||||
Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
|
||||
{
|
||||
#ifdef __SWITCH__
|
||||
JitRWBase = aligned_alloc(0x1000, JitMemSize);
|
||||
|
@ -274,7 +276,7 @@ Compiler::Compiler()
|
|||
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
||||
#elif defined(__APPLE__)
|
||||
pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
|
||||
JitEnableWrite();
|
||||
nds.JIT.JitEnableWrite();
|
||||
#else
|
||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
@ -704,12 +706,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
|||
if (JitMemMainSize - GetCodeOffset() < 1024 * 16)
|
||||
{
|
||||
Log(LogLevel::Debug, "JIT near memory full, resetting...\n");
|
||||
ResetBlockCache();
|
||||
NDS.JIT.ResetBlockCache();
|
||||
}
|
||||
if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8)
|
||||
{
|
||||
Log(LogLevel::Debug, "JIT far memory full, resetting...\n");
|
||||
ResetBlockCache();
|
||||
NDS.JIT.ResetBlockCache();
|
||||
}
|
||||
|
||||
JitBlockEntry res = (JitBlockEntry)GetRXPtr();
|
||||
|
@ -722,7 +724,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
|||
CPSRDirty = false;
|
||||
|
||||
if (hasMemInstr)
|
||||
MOVP2R(RMemBase, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
|
||||
MOVP2R(RMemBase, Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start);
|
||||
|
||||
for (int i = 0; i < instrsCount; i++)
|
||||
{
|
||||
|
@ -870,7 +872,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)
|
||||
|
@ -884,7 +886,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)
|
||||
|
@ -898,7 +900,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);
|
||||
|
@ -918,7 +920,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
|
||||
|
@ -963,7 +965,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)
|
||||
|
|
|
@ -19,8 +19,9 @@
|
|||
#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"
|
||||
|
||||
|
@ -29,9 +30,9 @@
|
|||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
class ARMJIT;
|
||||
const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26;
|
||||
const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27;
|
||||
const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28;
|
||||
|
@ -68,7 +69,7 @@ struct Op2
|
|||
bool IsSimpleReg()
|
||||
{ return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; }
|
||||
bool ImmFits12Bit()
|
||||
{ return IsImm && (Imm & 0xFFF == Imm); }
|
||||
{ return IsImm && ((Imm & 0xFFF) == Imm); }
|
||||
bool IsZero()
|
||||
{ return IsImm && !Imm; }
|
||||
|
||||
|
@ -97,8 +98,8 @@ class Compiler : public Arm64Gen::ARM64XEmitter
|
|||
public:
|
||||
typedef void (Compiler::*CompileFunc)();
|
||||
|
||||
Compiler();
|
||||
~Compiler();
|
||||
explicit Compiler(melonDS::NDS& nds);
|
||||
~Compiler() override;
|
||||
|
||||
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
|
||||
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
|
||||
|
@ -113,7 +114,7 @@ public:
|
|||
|
||||
bool CanCompile(bool thumb, u16 kind);
|
||||
|
||||
bool FlagsNZNeeded()
|
||||
bool FlagsNZNeeded() const
|
||||
{
|
||||
return CurInstr.SetFlags & 0xC;
|
||||
}
|
||||
|
@ -233,7 +234,7 @@ public:
|
|||
return (u8*)entry - GetRXBase();
|
||||
}
|
||||
|
||||
bool IsJITFault(u8* pc);
|
||||
bool IsJITFault(const u8* pc);
|
||||
u8* RewriteMemAccess(u8* pc);
|
||||
|
||||
void SwapCodeRegion()
|
||||
|
@ -243,6 +244,7 @@ public:
|
|||
OtherCodeRegion = offset;
|
||||
}
|
||||
|
||||
melonDS::NDS& NDS;
|
||||
ptrdiff_t OtherCodeRegion;
|
||||
|
||||
bool Exit;
|
||||
|
@ -287,3 +289,5 @@ public:
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -94,3 +94,8 @@ ARM_RestoreContext:
|
|||
mov sp, x17
|
||||
|
||||
br x18
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__WIN32__)
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
#endif
|
||||
|
||||
|
|
|
@ -21,13 +21,14 @@
|
|||
#include "../ARMJIT.h"
|
||||
|
||||
#include "../ARMJIT_Memory.h"
|
||||
#include "../NDS.h"
|
||||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
bool Compiler::IsJITFault(u8* pc)
|
||||
bool Compiler::IsJITFault(const u8* pc)
|
||||
{
|
||||
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
|
||||
}
|
||||
|
@ -62,9 +63,9 @@ u8* Compiler::RewriteMemAccess(u8* pc)
|
|||
|
||||
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
||||
{
|
||||
u32 localAddr = LocaliseCodeAddress(Num, addr);
|
||||
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
|
||||
|
||||
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
|
||||
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
|
||||
if (invalidLiteralIdx != -1)
|
||||
{
|
||||
return false;
|
||||
|
@ -79,7 +80,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
|||
if (size == 32)
|
||||
{
|
||||
CurCPU->DataRead32(addr & ~0x3, &val);
|
||||
val = ::ROR(val, (addr & 0x3) << 3);
|
||||
val = melonDS::ROR(val, (addr & 0x3) << 3);
|
||||
}
|
||||
else if (size == 16)
|
||||
{
|
||||
|
@ -111,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
|||
if (size == 16)
|
||||
addressMask = ~1;
|
||||
|
||||
if (ARMJIT::LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
||||
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
||||
{
|
||||
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
|
||||
|
||||
|
@ -146,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
|||
MOV(W0, rnMapped);
|
||||
}
|
||||
|
||||
bool addrIsStatic = ARMJIT::LiteralOptimizations
|
||||
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
|
||||
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
|
||||
u32 staticAddress;
|
||||
if (addrIsStatic)
|
||||
|
@ -185,18 +186,18 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
|||
MOV(rnMapped, W0);
|
||||
|
||||
u32 expectedTarget = Num == 0
|
||||
? ARMJIT_Memory::ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
|
||||
: ARMJIT_Memory::ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
|
||||
? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
|
||||
: NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
|
||||
|
||||
if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
|
||||
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.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)
|
||||
|
@ -225,7 +226,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
|||
{
|
||||
void* func = NULL;
|
||||
if (addrIsStatic)
|
||||
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
||||
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
||||
|
||||
PushRegs(false, false);
|
||||
|
||||
|
@ -263,7 +264,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;
|
||||
|
@ -275,7 +276,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;
|
||||
|
@ -291,7 +292,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;
|
||||
|
@ -303,7 +304,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;
|
||||
|
@ -452,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel()
|
|||
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
|
||||
u32 addr = (R15 & ~0x2) + offset;
|
||||
|
||||
if (!ARMJIT::LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
||||
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
||||
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
|
||||
}
|
||||
|
||||
|
@ -494,11 +495,11 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
|||
Comp_AddCycles_CDI();
|
||||
|
||||
int expectedTarget = Num == 0
|
||||
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
|
||||
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
|
||||
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
|
||||
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
|
||||
|
||||
bool compileFastPath = ARMJIT::FastMemory
|
||||
&& store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
|
||||
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
|
||||
&& store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
|
||||
|
||||
{
|
||||
s32 offset = decrement
|
||||
|
@ -680,7 +681,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;
|
||||
|
@ -690,7 +691,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;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef ARMJIT_COMPILER_H
|
||||
#define ARMJIT_COMPILER_H
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#include "ARMJIT_x64/ARMJIT_Compiler.h"
|
||||
#elif defined(__aarch64__)
|
||||
|
@ -27,9 +29,6 @@
|
|||
#error "The current target platform doesn't have a JIT backend"
|
||||
#endif
|
||||
|
||||
namespace ARMJIT
|
||||
{
|
||||
extern Compiler* JITCompiler;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,13 +24,17 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "ARMJIT.h"
|
||||
#include "ARMJIT_Memory.h"
|
||||
#include "ARM_InstrInfo.h"
|
||||
#include "JitBlock.h"
|
||||
#include "TinyVector.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class ARM;
|
||||
class ARMv5;
|
||||
|
||||
// here lands everything which doesn't fit into ARMJIT.h
|
||||
// where it would be included by pretty much everything
|
||||
namespace ARMJIT
|
||||
{
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -69,139 +73,6 @@ 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
|
||||
{
|
||||
|
@ -214,11 +85,7 @@ typedef void (*InterpreterFunc)(ARM* cpu);
|
|||
extern InterpreterFunc InterpretARM[];
|
||||
extern InterpreterFunc InterpretTHUMB[];
|
||||
|
||||
extern TinyVector<u32> InvalidLiterals;
|
||||
|
||||
extern AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count];
|
||||
|
||||
inline bool PageContainsCode(AddressRange* range)
|
||||
inline bool PageContainsCode(const AddressRange* range)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
|
@ -228,11 +95,6 @@ inline bool PageContainsCode(AddressRange* range)
|
|||
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
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,62 +20,209 @@
|
|||
#define ARMJIT_MEMORY
|
||||
|
||||
#include "types.h"
|
||||
#include "MemConstants.h"
|
||||
|
||||
#include "ARM.h"
|
||||
#ifdef JIT_ENABLED
|
||||
# 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
|
||||
|
||||
namespace ARMJIT_Memory
|
||||
namespace melonDS
|
||||
{
|
||||
#ifdef JIT_ENABLED
|
||||
namespace Platform { struct DynamicLibrary; }
|
||||
class Compiler;
|
||||
class ARMJIT;
|
||||
#endif
|
||||
|
||||
extern void* FastMem9Start;
|
||||
extern void* FastMem7Start;
|
||||
|
||||
void Init();
|
||||
void DeInit();
|
||||
|
||||
void Reset();
|
||||
|
||||
enum
|
||||
constexpr u32 RoundUp(u32 size) noexcept
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
#ifdef _WIN32
|
||||
return (size + 0xFFFF) & ~0xFFFF;
|
||||
#else
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
|
||||
const u32 MemBlockMainRAMOffset = 0;
|
||||
const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
|
||||
const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
|
||||
const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
|
||||
const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
|
||||
const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
|
||||
const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
|
||||
const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
|
||||
|
||||
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;
|
||||
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)
|
||||
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
|
||||
HANDLE MemoryFile = INVALID_HANDLE_VALUE;
|
||||
LPVOID ExceptionHandlerHandle = nullptr;
|
||||
#else
|
||||
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
|
||||
int MemoryFile = -1;
|
||||
#endif
|
||||
#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
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#ifndef ARMJIT_REGCACHE_H
|
||||
#define ARMJIT_REGCACHE_H
|
||||
|
||||
#include "ARMJIT.h"
|
||||
#include "ARMJIT_Internal.h"
|
||||
#include "Platform.h"
|
||||
|
||||
|
@ -28,10 +27,11 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
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>
|
||||
|
@ -99,7 +99,7 @@ public:
|
|||
LiteralsLoaded &= ~(1 << reg);
|
||||
}
|
||||
|
||||
bool IsLiteral(int reg)
|
||||
bool IsLiteral(int reg) const
|
||||
{
|
||||
return LiteralsLoaded & (1 << reg);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -17,10 +17,11 @@
|
|||
*/
|
||||
|
||||
#include "ARMJIT_Compiler.h"
|
||||
#include "../ARM.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
// uses RSCRATCH3
|
||||
|
@ -128,7 +129,7 @@ OpArg Compiler::A_Comp_GetALUOp2(bool S, bool& carryUsed)
|
|||
Comp_AddCycles_C();
|
||||
|
||||
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
|
||||
u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift);
|
||||
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
|
||||
|
||||
carryUsed = false;
|
||||
if (S && shift)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -17,10 +17,12 @@
|
|||
*/
|
||||
|
||||
#include "ARMJIT_Compiler.h"
|
||||
#include "../ARM.h"
|
||||
#include "../NDS.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
|
@ -119,7 +121,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;
|
||||
}
|
||||
|
@ -131,7 +133,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;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -18,7 +18,9 @@
|
|||
|
||||
#include "ARMJIT_Compiler.h"
|
||||
|
||||
#include "../ARMJIT.h"
|
||||
#include "../ARMInterpreter.h"
|
||||
#include "../NDS.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
@ -33,10 +35,11 @@
|
|||
#endif
|
||||
|
||||
using namespace Gen;
|
||||
using namespace Common;
|
||||
|
||||
extern "C" void ARM_Ret();
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
template <>
|
||||
const X64Reg RegisterCache<Compiler, X64Reg>::NativeRegAllocOrder[] =
|
||||
|
@ -140,7 +143,7 @@ void Compiler::A_Comp_MSR()
|
|||
Comp_AddCycles_C();
|
||||
|
||||
OpArg val = CurInstr.Instr & (1 << 25)
|
||||
? Imm32(::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
|
||||
? Imm32(melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
|
||||
: MapReg(CurInstr.A_Reg(0));
|
||||
|
||||
u32 mask = 0;
|
||||
|
@ -232,7 +235,7 @@ void Compiler::A_Comp_MSR()
|
|||
*/
|
||||
u8 CodeMemory[1024 * 1024 * 32];
|
||||
|
||||
Compiler::Compiler()
|
||||
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
|
||||
{
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -648,7 +651,7 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
|
|||
};
|
||||
#undef F
|
||||
|
||||
bool Compiler::CanCompile(bool thumb, u16 kind)
|
||||
bool Compiler::CanCompile(bool thumb, u16 kind) const
|
||||
{
|
||||
return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL;
|
||||
}
|
||||
|
@ -664,7 +667,7 @@ void Compiler::Reset()
|
|||
LoadStorePatches.clear();
|
||||
}
|
||||
|
||||
bool Compiler::IsJITFault(u8* addr)
|
||||
bool Compiler::IsJITFault(const u8* addr)
|
||||
{
|
||||
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
|
||||
}
|
||||
|
@ -712,12 +715,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
|||
if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess...
|
||||
{
|
||||
Log(LogLevel::Debug, "near reset\n");
|
||||
ResetBlockCache();
|
||||
NDS.JIT.ResetBlockCache();
|
||||
}
|
||||
if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess...
|
||||
{
|
||||
Log(LogLevel::Debug, "far reset\n");
|
||||
ResetBlockCache();
|
||||
NDS.JIT.ResetBlockCache();
|
||||
}
|
||||
|
||||
ConstantCycles = 0;
|
||||
|
@ -861,7 +864,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)
|
||||
|
@ -873,7 +876,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)
|
||||
|
@ -885,7 +888,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)
|
||||
|
@ -910,7 +913,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
|
||||
|
@ -955,7 +958,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)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,9 +19,10 @@
|
|||
#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"
|
||||
|
||||
|
@ -31,9 +32,12 @@
|
|||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ARMJIT
|
||||
{
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class ARMJIT;
|
||||
class ARMJIT_Memory;
|
||||
class NDS;
|
||||
const Gen::X64Reg RCPU = Gen::RBP;
|
||||
const Gen::X64Reg RCPSR = Gen::R15;
|
||||
|
||||
|
@ -79,7 +83,7 @@ struct Op2
|
|||
class Compiler : public Gen::XEmitter
|
||||
{
|
||||
public:
|
||||
Compiler();
|
||||
explicit Compiler(melonDS::NDS& nds);
|
||||
|
||||
void Reset();
|
||||
|
||||
|
@ -88,7 +92,7 @@ public:
|
|||
void LoadReg(int reg, Gen::X64Reg nativeReg);
|
||||
void SaveReg(int reg, Gen::X64Reg nativeReg);
|
||||
|
||||
bool CanCompile(bool thumb, u16 kind);
|
||||
bool CanCompile(bool thumb, u16 kind) const;
|
||||
|
||||
typedef void (Compiler::*CompileFunc)();
|
||||
|
||||
|
@ -167,7 +171,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, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
|
||||
s32 Comp_MemAccessBlock(int rn, Common::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&),
|
||||
|
@ -230,7 +234,7 @@ public:
|
|||
SetCodePtr(FarCode);
|
||||
}
|
||||
|
||||
bool IsJITFault(u8* addr);
|
||||
bool IsJITFault(const u8* addr);
|
||||
|
||||
u8* RewriteMemAccess(u8* pc);
|
||||
|
||||
|
@ -238,44 +242,46 @@ public:
|
|||
void CreateMethod(const char* namefmt, void* start, ...);
|
||||
#endif
|
||||
|
||||
u8* FarCode;
|
||||
u8* NearCode;
|
||||
u32 FarSize;
|
||||
u32 NearSize;
|
||||
melonDS::NDS& NDS;
|
||||
u8* FarCode {};
|
||||
u8* NearCode {};
|
||||
u32 FarSize {};
|
||||
u32 NearSize {};
|
||||
|
||||
u8* NearStart;
|
||||
u8* FarStart;
|
||||
u8* NearStart {};
|
||||
u8* FarStart {};
|
||||
|
||||
void* PatchedStoreFuncs[2][2][3][16];
|
||||
void* PatchedLoadFuncs[2][2][3][2][16];
|
||||
void* PatchedStoreFuncs[2][2][3][16] {};
|
||||
void* PatchedLoadFuncs[2][2][3][2][16] {};
|
||||
|
||||
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches;
|
||||
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
|
||||
|
||||
u8* ResetStart;
|
||||
u32 CodeMemSize;
|
||||
u8* 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
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
#include "../ARM.h"
|
||||
|
||||
using namespace melonDS;
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
FILE* f = fopen("ARMJIT_Offsets.h", "w");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -104,3 +104,8 @@ ARM_Ret:
|
|||
#endif
|
||||
|
||||
ret
|
||||
|
||||
#if !defined(__APPLE__) && !defined(WIN64)
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -17,10 +17,12 @@
|
|||
*/
|
||||
|
||||
#include "ARMJIT_Compiler.h"
|
||||
#include "../ARMJIT.h"
|
||||
#include "../NDS.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
namespace ARMJIT
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
|
@ -67,9 +69,9 @@ u8* Compiler::RewriteMemAccess(u8* pc)
|
|||
|
||||
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
||||
{
|
||||
u32 localAddr = LocaliseCodeAddress(Num, addr);
|
||||
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
|
||||
|
||||
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
|
||||
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
|
||||
if (invalidLiteralIdx != -1)
|
||||
{
|
||||
return false;
|
||||
|
@ -84,7 +86,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
|||
if (size == 32)
|
||||
{
|
||||
CurCPU->DataRead32(addr & ~0x3, &val);
|
||||
val = ::ROR(val, (addr & 0x3) << 3);
|
||||
val = melonDS::ROR(val, (addr & 0x3) << 3);
|
||||
}
|
||||
else if (size == 16)
|
||||
{
|
||||
|
@ -117,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
if (size == 16)
|
||||
addressMask = ~1;
|
||||
|
||||
if (LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
||||
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
||||
{
|
||||
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
|
||||
|
||||
|
@ -134,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
Comp_AddCycles_CDI();
|
||||
}
|
||||
|
||||
bool addrIsStatic = LiteralOptimizations
|
||||
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
|
||||
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
|
||||
u32 staticAddress;
|
||||
if (addrIsStatic)
|
||||
|
@ -195,10 +197,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
|
||||
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
|
||||
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
|
||||
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
|
||||
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
|
||||
|
||||
if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
|
||||
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
|
||||
{
|
||||
if (rdMapped.IsImm())
|
||||
{
|
||||
|
@ -211,12 +213,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 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
|
||||
MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
|
||||
|
||||
X64Reg maskedAddr = RSCRATCH3;
|
||||
if (size > 8)
|
||||
|
@ -267,7 +269,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
|
||||
void* func = NULL;
|
||||
if (addrIsStatic)
|
||||
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
||||
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
||||
|
||||
if (func)
|
||||
{
|
||||
|
@ -312,7 +314,7 @@ 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: CALL((void*)&SlowWrite9<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowWrite9<u16, 0>); break;
|
||||
|
@ -324,7 +326,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
}
|
||||
else
|
||||
{
|
||||
switch (size | NDS::ConsoleType)
|
||||
switch (size | NDS.ConsoleType)
|
||||
{
|
||||
case 32: CALL((void*)&SlowRead9<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowRead9<u16, 0>); break;
|
||||
|
@ -343,7 +345,7 @@ 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: CALL((void*)&SlowWrite7<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowWrite7<u16, 0>); break;
|
||||
|
@ -355,7 +357,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
|||
}
|
||||
else
|
||||
{
|
||||
switch (size | NDS::ConsoleType)
|
||||
switch (size | NDS.ConsoleType)
|
||||
{
|
||||
case 32: CALL((void*)&SlowRead7<u32, 0>); break;
|
||||
case 16: CALL((void*)&SlowRead7<u16, 0>); break;
|
||||
|
@ -421,16 +423,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
|||
s32 offset = (regsCount * 4) * (decrement ? -1 : 1);
|
||||
|
||||
int expectedTarget = Num == 0
|
||||
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
|
||||
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
|
||||
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
|
||||
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
|
||||
|
||||
if (!store)
|
||||
Comp_AddCycles_CDI();
|
||||
else
|
||||
Comp_AddCycles_CD();
|
||||
|
||||
bool compileFastPath = FastMemory
|
||||
&& !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
|
||||
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
|
||||
&& !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
|
||||
|
||||
// we need to make sure that the stack stays aligned to 16 bytes
|
||||
#ifdef _WIN32
|
||||
|
@ -453,7 +455,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 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
|
||||
MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
|
||||
ADD(64, R(RSCRATCH2), R(RSCRATCH4));
|
||||
|
||||
u32 offset = 0;
|
||||
|
@ -522,7 +524,7 @@ 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: CALL((void*)&SlowBlockTransfer9<false, 0>); break;
|
||||
case 1: CALL((void*)&SlowBlockTransfer9<false, 1>); break;
|
||||
|
@ -626,7 +628,7 @@ 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: CALL((void*)&SlowBlockTransfer9<true, 0>); break;
|
||||
case 1: CALL((void*)&SlowBlockTransfer9<true, 1>); break;
|
||||
|
@ -807,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel()
|
|||
{
|
||||
u32 offset = (CurInstr.Instr & 0xFF) << 2;
|
||||
u32 addr = (R15 & ~0x2) + offset;
|
||||
if (!LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
||||
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
||||
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "ARMJIT.h"
|
||||
|
||||
namespace ARMInstrInfo
|
||||
namespace melonDS::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)
|
||||
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
|
||||
{
|
||||
const u8 FlagsReadPerCond[7] = {
|
||||
flag_Z,
|
||||
|
@ -386,7 +386,7 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
|||
{
|
||||
if (res.Kind == tk_LDR_PCREL)
|
||||
{
|
||||
if (!ARMJIT::LiteralOptimizations)
|
||||
if (!literaloptimizations)
|
||||
res.SrcRegs |= 1 << 15;
|
||||
res.SpecialKind = special_LoadLiteral;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
namespace ARMInstrInfo
|
||||
namespace melonDS::ARMInstrInfo
|
||||
{
|
||||
|
||||
// Instruction kinds, for faster dispatch
|
||||
|
@ -274,7 +274,7 @@ struct Info
|
|||
}
|
||||
};
|
||||
|
||||
Info Decode(bool thumb, u32 num, u32 instr);
|
||||
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
melonDS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#ifndef MELONDS_ARGS_H
|
||||
#define MELONDS_ARGS_H
|
||||
|
||||
#include <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 ROM to install.
|
||||
/// Defaults to nullptr, which means no cart.
|
||||
/// Should be populated with the desired save data beforehand,
|
||||
/// including an SD card if applicable.
|
||||
std::unique_ptr<NDSCart::CartCommon> NDSROM = nullptr;
|
||||
|
||||
/// GBA ROM to install.
|
||||
/// Defaults to nullptr, which means no cart.
|
||||
/// Should be populated with the desired save data beforehand.
|
||||
/// Ignored in DSi mode.
|
||||
std::unique_ptr<GBACart::CartCommon> GBAROM = nullptr;
|
||||
|
||||
/// NDS ARM9 BIOS to install.
|
||||
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
|
||||
std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
|
||||
|
||||
/// 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
|
|
@ -15,6 +15,7 @@ add_library(core STATIC
|
|||
CRC32.cpp
|
||||
DMA.cpp
|
||||
DMA_Timings.h
|
||||
DMA_Timings.cpp
|
||||
DSi.cpp
|
||||
DSi_AES.cpp
|
||||
DSi_Camera.cpp
|
||||
|
@ -25,6 +26,7 @@ add_library(core STATIC
|
|||
DSi_NWifi.cpp
|
||||
DSi_SD.cpp
|
||||
DSi_SPI_TSC.cpp
|
||||
FATIO.cpp
|
||||
FATStorage.cpp
|
||||
FIFO.h
|
||||
GBACart.cpp
|
||||
|
@ -38,6 +40,7 @@ add_library(core STATIC
|
|||
melonDLDI.h
|
||||
NDS.cpp
|
||||
NDSCart.cpp
|
||||
NDSCartR4.cpp
|
||||
Platform.h
|
||||
ROMList.h
|
||||
ROMList.cpp
|
||||
|
@ -49,11 +52,11 @@ add_library(core STATIC
|
|||
SPI_Firmware.cpp
|
||||
SPU.cpp
|
||||
types.h
|
||||
version.h
|
||||
Utils.cpp
|
||||
Utils.h
|
||||
Wifi.cpp
|
||||
WifiAP.cpp
|
||||
|
||||
fatfs/diskio.c
|
||||
fatfs/ff.c
|
||||
fatfs/ffsystem.c
|
||||
fatfs/ffunicode.c
|
||||
|
@ -63,6 +66,15 @@ 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
|
||||
|
@ -115,11 +127,26 @@ if (ENABLE_JIT)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version")
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
|
||||
target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h")
|
||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
add_subdirectory(teakra EXCLUDE_FROM_ALL)
|
||||
# Workaround for building teakra with -O0 on Windows either failing or hanging forever
|
||||
target_compile_options(teakra PRIVATE "$<$<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 silence any equivalent warnings there.
|
||||
|
||||
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)
|
||||
|
@ -136,15 +163,28 @@ if (ENABLE_JIT)
|
|||
endif()
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(core PRIVATE ole32 comctl32 ws2_32)
|
||||
elseif(NOT APPLE)
|
||||
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
|
||||
elseif(NOT APPLE AND NOT HAIKU)
|
||||
check_library_exists(rt shm_open "" NEED_LIBRT)
|
||||
if (NEED_LIBRT)
|
||||
target_link_libraries(core PRIVATE rt)
|
||||
endif()
|
||||
elseif(HAIKU)
|
||||
target_link_libraries(core PRIVATE network)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JIT_PROFILING)
|
||||
target_include_directories(core PRIVATE "${VTUNE_INCLUDE_DIR}")
|
||||
target_link_libraries(core PRIVATE "${VTUNE_LIBRARY}")
|
||||
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()
|
||||
|
||||
|
|
66
src/CP15.cpp
66
src/CP15.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,12 +22,11 @@
|
|||
#include "DSi.h"
|
||||
#include "ARM.h"
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
#include "ARMJIT.h"
|
||||
#include "ARMJIT_Memory.h"
|
||||
#endif
|
||||
#include "ARMJIT.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
|
@ -125,9 +124,7 @@ void ARMv5::UpdateDTCMSetting()
|
|||
|
||||
if (newDTCMBase != DTCMBase || newDTCMMask != DTCMMask)
|
||||
{
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT_Memory::RemapDTCM(newDTCMBase, newDTCMSize);
|
||||
#endif
|
||||
NDS.JIT.Memory.RemapDTCM(newDTCMBase, newDTCMSize);
|
||||
DTCMBase = newDTCMBase;
|
||||
DTCMMask = newDTCMMask;
|
||||
}
|
||||
|
@ -189,10 +186,14 @@ void ARMv5::UpdatePURegion(u32 n)
|
|||
return;
|
||||
}
|
||||
|
||||
u32 start = rgn >> 12;
|
||||
u32 sz = 2 << ((rgn >> 1) & 0x1F);
|
||||
u32 end = start + (sz >> 12);
|
||||
// TODO: check alignment of start
|
||||
// notes:
|
||||
// * min size of a pu region is 4KiB (12 bits)
|
||||
// * size is calculated as size + 1, but the 12 lsb of address space are ignored, therefore we need it as size + 1 - 12, or size - 11
|
||||
// * pu regions are aligned based on their size
|
||||
u32 size = std::max((int)((rgn>>1) & 0x1F) - 11, 0); // obtain the size, subtract 11 and clamp to a min of 0.
|
||||
u32 start = ((rgn >> 12) >> size) << size; // determine the start offset, and use shifts to force alignment with a multiple of the size.
|
||||
u32 end = start + (1<<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
|
||||
|
||||
u8 usermask = 0;
|
||||
u8 privmask = 0;
|
||||
|
@ -242,7 +243,7 @@ void ARMv5::UpdatePURegion(u32 n)
|
|||
"PU region %d: %08X-%08X, user=%02X priv=%02X, %08X/%08X\n",
|
||||
n,
|
||||
start << 12,
|
||||
end << 12,
|
||||
(end << 12) - 1,
|
||||
usermask,
|
||||
privmask,
|
||||
PU_DataRW,
|
||||
|
@ -298,7 +299,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)
|
||||
{
|
||||
|
@ -306,7 +307,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)
|
||||
|
@ -317,9 +318,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -391,14 +392,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;
|
||||
}
|
||||
|
||||
|
@ -582,12 +583,12 @@ void ARMv5::CP15Write(u32 id, u32 val)
|
|||
|
||||
std::snprintf(log_output,
|
||||
sizeof(log_output),
|
||||
"PU: region %d = %08X : %s, %08X-%08X\n",
|
||||
"PU: region %d = %08X : %s, start: %08X size: %02X\n",
|
||||
(id >> 4) & 0xF,
|
||||
val,
|
||||
val & 1 ? "enabled" : "disabled",
|
||||
val & 0xFFFFF000,
|
||||
(val & 0xFFFFF000) + (2 << ((val & 0x3E) >> 1))
|
||||
(val & 0x3E) >> 1
|
||||
);
|
||||
Log(LogLevel::Debug, "%s", log_output);
|
||||
// Some implementations of Log imply a newline, so we build up the line before printing it
|
||||
|
@ -671,7 +672,7 @@ void ARMv5::CP15Write(u32 id, u32 val)
|
|||
Log(LogLevel::Debug, "unknown CP15 write op %03X %08X\n", id, val);
|
||||
}
|
||||
|
||||
u32 ARMv5::CP15Read(u32 id)
|
||||
u32 ARMv5::CP15Read(u32 id) const
|
||||
{
|
||||
//printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]);
|
||||
|
||||
|
@ -926,9 +927,7 @@ void ARMv5::DataWrite8(u32 addr, u8 val)
|
|||
{
|
||||
DataCycles = 1;
|
||||
*(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
#endif
|
||||
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
return;
|
||||
}
|
||||
if ((addr & DTCMMask) == DTCMBase)
|
||||
|
@ -958,9 +957,7 @@ void ARMv5::DataWrite16(u32 addr, u16 val)
|
|||
{
|
||||
DataCycles = 1;
|
||||
*(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
#endif
|
||||
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
return;
|
||||
}
|
||||
if ((addr & DTCMMask) == DTCMBase)
|
||||
|
@ -990,9 +987,7 @@ void ARMv5::DataWrite32(u32 addr, u32 val)
|
|||
{
|
||||
DataCycles = 1;
|
||||
*(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
#endif
|
||||
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
return;
|
||||
}
|
||||
if ((addr & DTCMMask) == DTCMBase)
|
||||
|
@ -1015,7 +1010,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
|
|||
DataCycles += 1;
|
||||
*(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val;
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -1030,7 +1025,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
|
|||
DataCycles += MemTimings[addr >> 12][3];
|
||||
}
|
||||
|
||||
void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region)
|
||||
void ARMv5::GetCodeMemRegion(u32 addr, MemRegion* region)
|
||||
{
|
||||
/*if (addr < ITCMSize)
|
||||
{
|
||||
|
@ -1039,6 +1034,7 @@ void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region)
|
|||
return;
|
||||
}*/
|
||||
|
||||
GetMemRegion(addr, false, &CodeMem);
|
||||
NDS.ARM9GetMemRegion(addr, false, &CodeMem);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
|||
|
||||
// http://www.codeproject.com/KB/recipes/crc32_large.aspx
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
constexpr u32 _reflect(u32 refl, char ch)
|
||||
{
|
||||
u32 value = 0;
|
||||
|
@ -62,3 +64,5 @@ u32 CRC32(const u8 *data, int len, u32 start)
|
|||
|
||||
return (crc ^ 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -23,6 +23,9 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
u32 CRC32(const u8* data, int len, u32 start=0);
|
||||
}
|
||||
|
||||
#endif // CRC32_H
|
||||
|
|
177
src/DMA.cpp
177
src/DMA.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -25,6 +25,8 @@
|
|||
#include "DMA_Timings.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
|
@ -48,21 +50,17 @@ using Platform::LogLevel;
|
|||
// TODO: timings are nonseq when address is fixed/decrementing
|
||||
|
||||
|
||||
DMA::DMA(u32 cpu, u32 num)
|
||||
DMA::DMA(u32 cpu, u32 num, melonDS::NDS& nds) :
|
||||
CPU(cpu),
|
||||
Num(num),
|
||||
NDS(nds)
|
||||
{
|
||||
CPU = cpu;
|
||||
Num = num;
|
||||
|
||||
if (cpu == 0)
|
||||
CountMask = 0x001FFFFF;
|
||||
else
|
||||
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
|
||||
}
|
||||
|
||||
DMA::~DMA()
|
||||
{
|
||||
}
|
||||
|
||||
void DMA::Reset()
|
||||
{
|
||||
SrcAddr = 0;
|
||||
|
@ -83,6 +81,7 @@ void DMA::Reset()
|
|||
Executing = false;
|
||||
InProgress = false;
|
||||
MRAMBurstCount = 0;
|
||||
MRAMBurstTable = DMATiming::MRAMDummy;
|
||||
}
|
||||
|
||||
void DMA::DoSavestate(Savestate* file)
|
||||
|
@ -107,6 +106,10 @@ 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)
|
||||
|
@ -143,7 +146,7 @@ void DMA::WriteCnt(u32 val)
|
|||
if ((StartMode & 0x7) == 0)
|
||||
Start();
|
||||
else if (StartMode == 0x07)
|
||||
GPU3D::CheckFIFODMA();
|
||||
NDS.GPU.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);
|
||||
|
@ -190,7 +193,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)
|
||||
|
@ -198,18 +201,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 == NDS::Mem9_MainRAM)
|
||||
if (src_rgn == Mem9_MainRAM)
|
||||
{
|
||||
if (dst_rgn == NDS::Mem9_MainRAM)
|
||||
if (dst_rgn == Mem9_MainRAM)
|
||||
return 16;
|
||||
|
||||
if (SrcAddrInc > 0)
|
||||
|
@ -218,7 +221,7 @@ u32 DMA::UnitTimings9_16(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (dst_rgn == NDS::Mem9_GBAROM)
|
||||
if (dst_rgn == Mem9_GBAROM)
|
||||
{
|
||||
if (dst_s == 4)
|
||||
MRAMBurstTable = DMATiming::MRAMRead16Bursts[1];
|
||||
|
@ -239,7 +242,7 @@ u32 DMA::UnitTimings9_16(bool burststart)
|
|||
(burststart ? dst_n : dst_s);
|
||||
}
|
||||
}
|
||||
else if (dst_rgn == NDS::Mem9_MainRAM)
|
||||
else if (dst_rgn == Mem9_MainRAM)
|
||||
{
|
||||
if (DstAddrInc > 0)
|
||||
{
|
||||
|
@ -247,7 +250,7 @@ u32 DMA::UnitTimings9_16(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (src_rgn == NDS::Mem9_GBAROM)
|
||||
if (src_rgn == Mem9_GBAROM)
|
||||
{
|
||||
if (src_s == 4)
|
||||
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1];
|
||||
|
@ -284,18 +287,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 == NDS::Mem9_MainRAM)
|
||||
if (src_rgn == Mem9_MainRAM)
|
||||
{
|
||||
if (dst_rgn == NDS::Mem9_MainRAM)
|
||||
if (dst_rgn == Mem9_MainRAM)
|
||||
return 18;
|
||||
|
||||
if (SrcAddrInc > 0)
|
||||
|
@ -304,7 +307,7 @@ u32 DMA::UnitTimings9_32(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (dst_rgn == NDS::Mem9_GBAROM)
|
||||
if (dst_rgn == Mem9_GBAROM)
|
||||
{
|
||||
if (dst_s == 8)
|
||||
MRAMBurstTable = DMATiming::MRAMRead32Bursts[2];
|
||||
|
@ -327,7 +330,7 @@ u32 DMA::UnitTimings9_32(bool burststart)
|
|||
(burststart ? dst_n : dst_s);
|
||||
}
|
||||
}
|
||||
else if (dst_rgn == NDS::Mem9_MainRAM)
|
||||
else if (dst_rgn == Mem9_MainRAM)
|
||||
{
|
||||
if (DstAddrInc > 0)
|
||||
{
|
||||
|
@ -335,7 +338,7 @@ u32 DMA::UnitTimings9_32(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (src_rgn == NDS::Mem9_GBAROM)
|
||||
if (src_rgn == Mem9_GBAROM)
|
||||
{
|
||||
if (src_s == 8)
|
||||
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2];
|
||||
|
@ -376,18 +379,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 == NDS::Mem7_MainRAM)
|
||||
if (src_rgn == Mem7_MainRAM)
|
||||
{
|
||||
if (dst_rgn == NDS::Mem7_MainRAM)
|
||||
if (dst_rgn == Mem7_MainRAM)
|
||||
return 16;
|
||||
|
||||
if (SrcAddrInc > 0)
|
||||
|
@ -396,7 +399,7 @@ u32 DMA::UnitTimings7_16(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1)
|
||||
if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1)
|
||||
{
|
||||
if (dst_s == 4)
|
||||
MRAMBurstTable = DMATiming::MRAMRead16Bursts[1];
|
||||
|
@ -417,7 +420,7 @@ u32 DMA::UnitTimings7_16(bool burststart)
|
|||
(burststart ? dst_n : dst_s);
|
||||
}
|
||||
}
|
||||
else if (dst_rgn == NDS::Mem7_MainRAM)
|
||||
else if (dst_rgn == Mem7_MainRAM)
|
||||
{
|
||||
if (DstAddrInc > 0)
|
||||
{
|
||||
|
@ -425,7 +428,7 @@ u32 DMA::UnitTimings7_16(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1)
|
||||
if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1)
|
||||
{
|
||||
if (src_s == 4)
|
||||
MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1];
|
||||
|
@ -462,18 +465,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 == NDS::Mem7_MainRAM)
|
||||
if (src_rgn == Mem7_MainRAM)
|
||||
{
|
||||
if (dst_rgn == NDS::Mem7_MainRAM)
|
||||
if (dst_rgn == Mem7_MainRAM)
|
||||
return 18;
|
||||
|
||||
if (SrcAddrInc > 0)
|
||||
|
@ -482,7 +485,7 @@ u32 DMA::UnitTimings7_32(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1)
|
||||
if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1)
|
||||
{
|
||||
if (dst_s == 8)
|
||||
MRAMBurstTable = DMATiming::MRAMRead32Bursts[2];
|
||||
|
@ -505,7 +508,7 @@ u32 DMA::UnitTimings7_32(bool burststart)
|
|||
(burststart ? dst_n : dst_s);
|
||||
}
|
||||
}
|
||||
else if (dst_rgn == NDS::Mem7_MainRAM)
|
||||
else if (dst_rgn == Mem7_MainRAM)
|
||||
{
|
||||
if (DstAddrInc > 0)
|
||||
{
|
||||
|
@ -513,7 +516,7 @@ u32 DMA::UnitTimings7_32(bool burststart)
|
|||
{
|
||||
MRAMBurstCount = 0;
|
||||
|
||||
if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1)
|
||||
if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1)
|
||||
{
|
||||
if (src_s == 8)
|
||||
MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2];
|
||||
|
@ -547,10 +550,9 @@ 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;
|
||||
|
||||
|
@ -562,40 +564,34 @@ void DMA::Run9()
|
|||
{
|
||||
while (IterCount > 0 && !Stall)
|
||||
{
|
||||
NDS::ARM9Timestamp += (UnitTimings9_16(burststart) << NDS::ARM9ClockShift);
|
||||
NDS.ARM9Timestamp += (UnitTimings9_16(burststart) << NDS.ARM9ClockShift);
|
||||
burststart = false;
|
||||
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM9Write16(CurDstAddr, DSi::ARM9Read16(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr));
|
||||
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;
|
||||
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -607,10 +603,10 @@ void DMA::Run9()
|
|||
if (IterCount == 0)
|
||||
{
|
||||
Running = 0;
|
||||
NDS::ResumeCPU(0, 1<<Num);
|
||||
NDS.ResumeCPU(0, 1<<Num);
|
||||
|
||||
if (StartMode == 0x07)
|
||||
GPU3D::CheckFIFODMA();
|
||||
NDS.GPU.GPU3D.CheckFIFODMA();
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -620,17 +616,16 @@ void DMA::Run9()
|
|||
Cnt &= ~(1<<31);
|
||||
|
||||
if (Cnt & (1<<30))
|
||||
NDS::SetIRQ(0, NDS::IRQ_DMA0 + Num);
|
||||
NDS.SetIRQ(0, 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;
|
||||
|
||||
|
@ -642,40 +637,34 @@ void DMA::Run7()
|
|||
{
|
||||
while (IterCount > 0 && !Stall)
|
||||
{
|
||||
NDS::ARM7Timestamp += UnitTimings7_16(burststart);
|
||||
NDS.ARM7Timestamp += UnitTimings7_16(burststart);
|
||||
burststart = false;
|
||||
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM7Write16(CurDstAddr, DSi::ARM7Read16(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr));
|
||||
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;
|
||||
|
||||
if (ConsoleType == 1)
|
||||
DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr));
|
||||
else
|
||||
NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -687,7 +676,7 @@ void DMA::Run7()
|
|||
if (IterCount == 0)
|
||||
{
|
||||
Running = 0;
|
||||
NDS::ResumeCPU(1, 1<<Num);
|
||||
NDS.ResumeCPU(1, 1<<Num);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -697,20 +686,18 @@ void DMA::Run7()
|
|||
Cnt &= ~(1<<31);
|
||||
|
||||
if (Cnt & (1<<30))
|
||||
NDS::SetIRQ(1, NDS::IRQ_DMA0 + Num);
|
||||
NDS.SetIRQ(1, 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<ConsoleType>();
|
||||
else return Run7<ConsoleType>();
|
||||
if (CPU == 0) return Run9();
|
||||
else return Run7();
|
||||
}
|
||||
|
||||
template void DMA::Run<0>();
|
||||
template void DMA::Run<1>();
|
||||
}
|
62
src/DMA.h
62
src/DMA.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,14 +19,19 @@
|
|||
#ifndef DMA_H
|
||||
#define DMA_H
|
||||
|
||||
#include <array>
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class NDS;
|
||||
class Savestate;
|
||||
|
||||
class DMA
|
||||
{
|
||||
public:
|
||||
DMA(u32 cpu, u32 num);
|
||||
~DMA();
|
||||
DMA(u32 cpu, u32 num, NDS& nds);
|
||||
~DMA() = default;
|
||||
|
||||
void Reset();
|
||||
|
||||
|
@ -40,20 +45,16 @@ 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)
|
||||
bool IsInMode(u32 mode) const noexcept
|
||||
{
|
||||
return ((mode == StartMode) && (Cnt & 0x80000000));
|
||||
}
|
||||
|
||||
bool IsRunning() { return Running!=0; }
|
||||
bool IsRunning() const noexcept { return Running!=0; }
|
||||
|
||||
void StartIfNeeded(u32 mode)
|
||||
{
|
||||
|
@ -72,32 +73,35 @@ public:
|
|||
if (Executing) Stall = true;
|
||||
}
|
||||
|
||||
u32 SrcAddr;
|
||||
u32 DstAddr;
|
||||
u32 Cnt;
|
||||
u32 SrcAddr {};
|
||||
u32 DstAddr {};
|
||||
u32 Cnt {};
|
||||
|
||||
private:
|
||||
u32 CPU, Num;
|
||||
melonDS::NDS& NDS;
|
||||
u32 CPU {};
|
||||
u32 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;
|
||||
const u8* MRAMBurstTable;
|
||||
u32 MRAMBurstCount {};
|
||||
std::array<u8, 256> MRAMBurstTable;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
melonDS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#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},
|
||||
};
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,9 +19,10 @@
|
|||
#ifndef DMA_TIMINGS_H
|
||||
#define DMA_TIMINGS_H
|
||||
|
||||
#include <array>
|
||||
#include "types.h"
|
||||
|
||||
namespace DMATiming
|
||||
namespace melonDS::DMATiming
|
||||
{
|
||||
|
||||
// DMA timing tables
|
||||
|
@ -45,202 +46,15 @@ namespace 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).
|
||||
|
||||
constexpr u8 MRAMDummy[1] = {0};
|
||||
extern const std::array<u8, 256> MRAMDummy;
|
||||
|
||||
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> MRAMRead16Bursts[3];
|
||||
|
||||
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> MRAMRead32Bursts[4];
|
||||
|
||||
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> MRAMWrite16Bursts[3];
|
||||
|
||||
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},
|
||||
};
|
||||
extern const std::array<u8, 256> MRAMWrite32Bursts[4];
|
||||
|
||||
}
|
||||
|
||||
|
|
1190
src/DSi.cpp
1190
src/DSi.cpp
File diff suppressed because it is too large
Load Diff
246
src/DSi.h
246
src/DSi.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,97 +20,167 @@
|
|||
#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 DSi
|
||||
namespace melonDS
|
||||
{
|
||||
class DSi_I2CHost;
|
||||
class DSi_CamModule;
|
||||
class DSi_AES;
|
||||
class DSi_DSP;
|
||||
class DSiArgs;
|
||||
|
||||
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 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);
|
||||
|
||||
namespace DSi_NAND
|
||||
{
|
||||
class NANDImage;
|
||||
}
|
||||
|
||||
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) 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
|
||||
|
|
153
src/DSi_AES.cpp
153
src/DSi_AES.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,64 +19,15 @@
|
|||
#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 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]); }
|
||||
|
@ -85,19 +36,17 @@ void ROL16(u8* val, u32 n)
|
|||
#define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); }
|
||||
|
||||
|
||||
bool Init()
|
||||
DSi_AES::DSi_AES(melonDS::DSi& dsi) : DSi(dsi)
|
||||
{
|
||||
const u8 zero[16] = {0};
|
||||
AES_init_ctx_iv(&Ctx, zero, zero);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
DSi_AES::~DSi_AES()
|
||||
{
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void DSi_AES::Reset()
|
||||
{
|
||||
Cnt = 0;
|
||||
|
||||
|
@ -129,6 +78,7 @@ void Reset()
|
|||
OutputMACDue = false;
|
||||
|
||||
// initialize keys
|
||||
u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID();
|
||||
|
||||
// slot 0: modcrypt
|
||||
*(u32*)&KeyX[0][0] = 0x746E694E;
|
||||
|
@ -137,20 +87,20 @@ void Reset()
|
|||
// slot 1: 'Tad'/dev.kp
|
||||
*(u32*)&KeyX[1][0] = 0x4E00004A;
|
||||
*(u32*)&KeyX[1][4] = 0x4A00004E;
|
||||
*(u32*)&KeyX[1][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xC80C4B72;
|
||||
*(u32*)&KeyX[1][12] = (u32)DSi::ConsoleID;
|
||||
*(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72;
|
||||
*(u32*)&KeyX[1][12] = (u32)consoleid;
|
||||
|
||||
// slot 3: console-unique eMMC crypto
|
||||
*(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*)&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*)&KeyY[3][0] = 0x0AB9DC76;
|
||||
*(u32*)&KeyY[3][4] = 0xBD4DC4D3;
|
||||
*(u32*)&KeyY[3][8] = 0x202DDD1D;
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
void DSi_AES::DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section("AESi");
|
||||
|
||||
|
@ -188,7 +138,7 @@ void DoSavestate(Savestate* file)
|
|||
}
|
||||
|
||||
|
||||
void ProcessBlock_CCM_Extra()
|
||||
void DSi_AES::ProcessBlock_CCM_Extra()
|
||||
{
|
||||
u8 data[16];
|
||||
u8 data_rev[16];
|
||||
|
@ -204,7 +154,7 @@ void ProcessBlock_CCM_Extra()
|
|||
AES_ECB_encrypt(&Ctx, CurMAC);
|
||||
}
|
||||
|
||||
void ProcessBlock_CCM_Decrypt()
|
||||
void DSi_AES::ProcessBlock_CCM_Decrypt()
|
||||
{
|
||||
u8 data[16];
|
||||
u8 data_rev[16];
|
||||
|
@ -232,7 +182,7 @@ void ProcessBlock_CCM_Decrypt()
|
|||
OutputFIFO.Write(*(u32*)&data[12]);
|
||||
}
|
||||
|
||||
void ProcessBlock_CCM_Encrypt()
|
||||
void DSi_AES::ProcessBlock_CCM_Encrypt()
|
||||
{
|
||||
u8 data[16];
|
||||
u8 data_rev[16];
|
||||
|
@ -260,7 +210,7 @@ void ProcessBlock_CCM_Encrypt()
|
|||
OutputFIFO.Write(*(u32*)&data[12]);
|
||||
}
|
||||
|
||||
void ProcessBlock_CTR()
|
||||
void DSi_AES::ProcessBlock_CTR()
|
||||
{
|
||||
u8 data[16];
|
||||
u8 data_rev[16];
|
||||
|
@ -285,7 +235,7 @@ void ProcessBlock_CTR()
|
|||
}
|
||||
|
||||
|
||||
u32 ReadCnt()
|
||||
u32 DSi_AES::ReadCnt() const
|
||||
{
|
||||
u32 ret = Cnt;
|
||||
|
||||
|
@ -295,7 +245,7 @@ u32 ReadCnt()
|
|||
return ret;
|
||||
}
|
||||
|
||||
void WriteCnt(u32 val)
|
||||
void DSi_AES::WriteCnt(u32 val)
|
||||
{
|
||||
u32 oldcnt = Cnt;
|
||||
Cnt = val & 0xFC1FF000;
|
||||
|
@ -363,7 +313,7 @@ void WriteCnt(u32 val)
|
|||
AES_init_ctx_iv(&Ctx, key, iv);
|
||||
}
|
||||
|
||||
DSi::CheckNDMAs(1, 0x2A);
|
||||
DSi.CheckNDMAs(1, 0x2A);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -378,12 +328,12 @@ void WriteCnt(u32 val)
|
|||
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks, BlkCnt);
|
||||
}
|
||||
|
||||
void WriteBlkCnt(u32 val)
|
||||
void DSi_AES::WriteBlkCnt(u32 val)
|
||||
{
|
||||
BlkCnt = val;
|
||||
}
|
||||
|
||||
u32 ReadOutputFIFO()
|
||||
u32 DSi_AES::ReadOutputFIFO()
|
||||
{
|
||||
if (OutputFIFO.IsEmpty()) Log(LogLevel::Warn, "!!! AES OUTPUT FIFO EMPTY\n");
|
||||
|
||||
|
@ -397,9 +347,9 @@ u32 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)
|
||||
{
|
||||
|
@ -414,7 +364,7 @@ u32 ReadOutputFIFO()
|
|||
return ret;
|
||||
}
|
||||
|
||||
void WriteInputFIFO(u32 val)
|
||||
void DSi_AES::WriteInputFIFO(u32 val)
|
||||
{
|
||||
// TODO: add some delay to processing
|
||||
|
||||
|
@ -427,29 +377,29 @@ void WriteInputFIFO(u32 val)
|
|||
Update();
|
||||
}
|
||||
|
||||
void CheckInputDMA()
|
||||
void DSi_AES::CheckInputDMA()
|
||||
{
|
||||
if (RemBlocks == 0 && RemExtra == 0) return;
|
||||
|
||||
if (InputFIFO.Level() <= InputDMASize)
|
||||
{
|
||||
// trigger input DMA
|
||||
DSi::CheckNDMAs(1, 0x2A);
|
||||
DSi.CheckNDMAs(1, 0x2A);
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void CheckOutputDMA()
|
||||
void DSi_AES::CheckOutputDMA()
|
||||
{
|
||||
if (OutputFIFO.Level() >= OutputDMASize)
|
||||
{
|
||||
// trigger output DMA
|
||||
DSi::CheckNDMAs(1, 0x2B);
|
||||
DSi.CheckNDMAs(1, 0x2B);
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
void DSi_AES::Update()
|
||||
{
|
||||
if (RemExtra > 0)
|
||||
{
|
||||
|
@ -525,19 +475,19 @@ void Update()
|
|||
}
|
||||
|
||||
Cnt &= ~(1<<31);
|
||||
if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES);
|
||||
DSi::StopNDMAs(1, 0x2A);
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ2(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 WriteIV(u32 offset, u32 val, u32 mask)
|
||||
void DSi_AES::WriteIV(u32 offset, u32 val, u32 mask)
|
||||
{
|
||||
u32 old = *(u32*)&IV[offset];
|
||||
|
||||
|
@ -546,7 +496,7 @@ void WriteIV(u32 offset, u32 val, u32 mask)
|
|||
//printf("AES: IV: "); _printhex(IV, 16);
|
||||
}
|
||||
|
||||
void WriteMAC(u32 offset, u32 val, u32 mask)
|
||||
void DSi_AES::WriteMAC(u32 offset, u32 val, u32 mask)
|
||||
{
|
||||
u32 old = *(u32*)&MAC[offset];
|
||||
|
||||
|
@ -555,7 +505,24 @@ void WriteMAC(u32 offset, u32 val, u32 mask)
|
|||
//printf("AES: MAC: "); _printhex(MAC, 16);
|
||||
}
|
||||
|
||||
void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey)
|
||||
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)
|
||||
{
|
||||
const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79};
|
||||
u8 tmp[16];
|
||||
|
@ -576,7 +543,7 @@ void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey)
|
|||
memcpy(normalkey, tmp, 16);
|
||||
}
|
||||
|
||||
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
|
||||
void DSi_AES::WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
|
||||
{
|
||||
u32 old = *(u32*)&KeyNormal[slot][offset];
|
||||
|
||||
|
@ -585,7 +552,7 @@ void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
|
|||
//printf("KeyNormal(%d): ", slot); _printhex(KeyNormal[slot], 16);
|
||||
}
|
||||
|
||||
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
|
||||
void DSi_AES::WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
|
||||
{
|
||||
u32 old = *(u32*)&KeyX[slot][offset];
|
||||
|
||||
|
@ -594,7 +561,7 @@ void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
|
|||
//printf("KeyX(%d): ", slot); _printhex(KeyX[slot], 16);
|
||||
}
|
||||
|
||||
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
|
||||
void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
|
||||
{
|
||||
u32 old = *(u32*)&KeyY[slot][offset];
|
||||
|
||||
|
@ -608,4 +575,4 @@ void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,55 +21,96 @@
|
|||
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
#include "FIFO.h"
|
||||
#include "tiny-AES-c/aes.hpp"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wattributes"
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 11) // 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, void* Src)
|
||||
__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, void* Src)
|
||||
__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];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
((u8*)Dst)[i] = ((u8*)Src)[15 - i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace DSi_AES
|
||||
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;
|
||||
|
||||
void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey);
|
||||
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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DSI_AES_H
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -16,63 +16,48 @@
|
|||
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 kIRQInterval = 1120000; // ~30 FPS
|
||||
const u32 kTransferStart = 60000;
|
||||
const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS
|
||||
const u32 DSi_CamModule::kTransferStart = 60000;
|
||||
|
||||
|
||||
bool Init()
|
||||
DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi)
|
||||
{
|
||||
Camera0 = new Camera(0);
|
||||
Camera1 = new Camera(1);
|
||||
DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ));
|
||||
DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline));
|
||||
|
||||
return true;
|
||||
Camera0 = DSi.I2C.GetOuterCamera();
|
||||
Camera1 = DSi.I2C.GetInnerCamera();
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
DSi_CamModule::~DSi_CamModule()
|
||||
{
|
||||
delete Camera0;
|
||||
delete Camera1;
|
||||
|
||||
Camera0 = nullptr;
|
||||
Camera1 = nullptr;
|
||||
|
||||
DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0);
|
||||
DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void DSi_CamModule::Reset()
|
||||
{
|
||||
Camera0->Reset();
|
||||
Camera1->Reset();
|
||||
|
||||
ModuleCnt = 0; // CHECKME
|
||||
Cnt = 0;
|
||||
|
||||
|
@ -85,16 +70,16 @@ void Reset()
|
|||
BufferNumLines = 0;
|
||||
CurCamera = nullptr;
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
||||
DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0);
|
||||
}
|
||||
|
||||
void Stop()
|
||||
void DSi_CamModule::Stop()
|
||||
{
|
||||
Camera0->Stop();
|
||||
Camera1->Stop();
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
void DSi_CamModule::DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section("CAMi");
|
||||
|
||||
|
@ -104,15 +89,12 @@ void DoSavestate(Savestate* file)
|
|||
/*file->VarArray(FrameBuffer, sizeof(FrameBuffer));
|
||||
file->Var32(&TransferPos);
|
||||
file->Var32(&FrameLength);*/
|
||||
|
||||
Camera0->DoSavestate(file);
|
||||
Camera1->DoSavestate(file);
|
||||
}
|
||||
|
||||
|
||||
void IRQ(u32 param)
|
||||
void DSi_CamModule::IRQ(u32 param)
|
||||
{
|
||||
Camera* activecam = nullptr;
|
||||
DSi_Camera* activecam = nullptr;
|
||||
|
||||
// TODO: cameras don't have any priority!
|
||||
// activating both together will jumble the image data together
|
||||
|
@ -124,7 +106,7 @@ void IRQ(u32 param)
|
|||
activecam->StartTransfer();
|
||||
|
||||
if (Cnt & (1<<11))
|
||||
NDS::SetIRQ(0, NDS::IRQ_DSi_Camera);
|
||||
DSi.SetIRQ(0, IRQ_DSi_Camera);
|
||||
|
||||
if (Cnt & (1<<15))
|
||||
{
|
||||
|
@ -132,14 +114,14 @@ void IRQ(u32 param)
|
|||
BufferWritePos = 0;
|
||||
BufferNumLines = 0;
|
||||
CurCamera = activecam;
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, TransferScanline, 0);
|
||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
||||
DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0);
|
||||
}
|
||||
|
||||
void TransferScanline(u32 line)
|
||||
void DSi_CamModule::TransferScanline(u32 line)
|
||||
{
|
||||
u32* dstbuf = &DataBuffer[BufferWritePos];
|
||||
int maxlen = 512 - BufferWritePos;
|
||||
|
@ -162,7 +144,7 @@ void TransferScanline(u32 line)
|
|||
if (line < ystart || line > yend)
|
||||
{
|
||||
if (!CurCamera->TransferDone())
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1);
|
||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -230,7 +212,7 @@ void TransferScanline(u32 line)
|
|||
BufferReadPos = 0; // checkme
|
||||
BufferWritePos = 0;
|
||||
BufferNumLines = 0;
|
||||
DSi::CheckNDMAs(0, 0x0B);
|
||||
DSi.CheckNDMAs(0, 0x0B);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -242,11 +224,11 @@ void TransferScanline(u32 line)
|
|||
if (CurCamera->TransferDone())
|
||||
return;
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1);
|
||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
|
||||
}
|
||||
|
||||
|
||||
u8 Read8(u32 addr)
|
||||
u8 DSi_CamModule::Read8(u32 addr)
|
||||
{
|
||||
//
|
||||
|
||||
|
@ -254,7 +236,7 @@ u8 Read8(u32 addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
u16 Read16(u32 addr)
|
||||
u16 DSi_CamModule::Read16(u32 addr)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
|
@ -266,7 +248,7 @@ u16 Read16(u32 addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
u32 Read32(u32 addr)
|
||||
u32 DSi_CamModule::Read32(u32 addr)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
|
@ -292,14 +274,14 @@ u32 Read32(u32 addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Write8(u32 addr, u8 val)
|
||||
void DSi_CamModule::Write8(u32 addr, u8 val)
|
||||
{
|
||||
//
|
||||
|
||||
Log(LogLevel::Debug, "unknown DSi cam write8 %08X %02X\n", addr, val);
|
||||
}
|
||||
|
||||
void Write16(u32 addr, u16 val)
|
||||
void DSi_CamModule::Write16(u32 addr, u16 val)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
|
@ -378,7 +360,7 @@ void Write16(u32 addr, u16 val)
|
|||
Log(LogLevel::Debug, "unknown DSi cam write16 %08X %04X\n", addr, val);
|
||||
}
|
||||
|
||||
void Write32(u32 addr, u32 val)
|
||||
void DSi_CamModule::Write32(u32 addr, u32 val)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
|
@ -397,16 +379,15 @@ void Write32(u32 addr, u32 val)
|
|||
|
||||
|
||||
|
||||
Camera::Camera(u32 num)
|
||||
{
|
||||
Num = num;
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
DSi_Camera::DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num) : DSi_I2CDevice(dsi, host), Num(num)
|
||||
{
|
||||
}
|
||||
|
||||
void Camera::DoSavestate(Savestate* file)
|
||||
DSi_Camera::~DSi_Camera()
|
||||
{
|
||||
}
|
||||
|
||||
void DSi_Camera::DoSavestate(Savestate* file)
|
||||
{
|
||||
char magic[5] = "CAMx";
|
||||
magic[3] = '0' + Num;
|
||||
|
@ -427,7 +408,7 @@ void Camera::DoSavestate(Savestate* file)
|
|||
file->VarArray(MCURegs, 0x8000);
|
||||
}
|
||||
|
||||
void Camera::Reset()
|
||||
void DSi_Camera::Reset()
|
||||
{
|
||||
Platform::Camera_Stop(Num);
|
||||
|
||||
|
@ -452,12 +433,12 @@ void Camera::Reset()
|
|||
memset(FrameBuffer, 0, (640*480/2)*sizeof(u32));
|
||||
}
|
||||
|
||||
void Camera::Stop()
|
||||
void DSi_Camera::Stop()
|
||||
{
|
||||
Platform::Camera_Stop(Num);
|
||||
}
|
||||
|
||||
bool Camera::IsActivated()
|
||||
bool DSi_Camera::IsActivated() const
|
||||
{
|
||||
if (StandbyCnt & (1<<14)) return false; // standby
|
||||
if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled
|
||||
|
@ -466,7 +447,7 @@ bool Camera::IsActivated()
|
|||
}
|
||||
|
||||
|
||||
void Camera::StartTransfer()
|
||||
void DSi_Camera::StartTransfer()
|
||||
{
|
||||
TransferY = 0;
|
||||
|
||||
|
@ -496,12 +477,12 @@ void Camera::StartTransfer()
|
|||
Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true);
|
||||
}
|
||||
|
||||
bool Camera::TransferDone()
|
||||
bool DSi_Camera::TransferDone() const
|
||||
{
|
||||
return TransferY >= FrameHeight;
|
||||
}
|
||||
|
||||
int Camera::TransferScanline(u32* buffer, int maxlen)
|
||||
int DSi_Camera::TransferScanline(u32* buffer, int maxlen)
|
||||
{
|
||||
if (TransferY >= FrameHeight)
|
||||
return 0;
|
||||
|
@ -554,12 +535,12 @@ int Camera::TransferScanline(u32* buffer, int maxlen)
|
|||
}
|
||||
|
||||
|
||||
void Camera::I2C_Start()
|
||||
void DSi_Camera::Acquire()
|
||||
{
|
||||
DataPos = 0;
|
||||
}
|
||||
|
||||
u8 Camera::I2C_Read(bool last)
|
||||
u8 DSi_Camera::Read(bool last)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
|
@ -580,7 +561,7 @@ u8 Camera::I2C_Read(bool last)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void Camera::I2C_Write(u8 val, bool last)
|
||||
void DSi_Camera::Write(u8 val, bool last)
|
||||
{
|
||||
if (DataPos < 2)
|
||||
{
|
||||
|
@ -609,7 +590,7 @@ void Camera::I2C_Write(u8 val, bool last)
|
|||
else DataPos++;
|
||||
}
|
||||
|
||||
u16 Camera::I2C_ReadReg(u16 addr)
|
||||
u16 DSi_Camera::I2C_ReadReg(u16 addr) const
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
|
@ -645,7 +626,7 @@ u16 Camera::I2C_ReadReg(u16 addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Camera::I2C_WriteReg(u16 addr, u16 val)
|
||||
void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
|
@ -714,14 +695,14 @@ void Camera::I2C_WriteReg(u16 addr, u16 val)
|
|||
// TODO: not sure at all what is the accessible range
|
||||
// or if there is any overlap in the address range
|
||||
|
||||
u8 Camera::MCU_Read(u16 addr)
|
||||
u8 DSi_Camera::MCU_Read(u16 addr) const
|
||||
{
|
||||
addr &= 0x7FFF;
|
||||
|
||||
return MCURegs[addr];
|
||||
}
|
||||
|
||||
void Camera::MCU_Write(u16 addr, u8 val)
|
||||
void DSi_Camera::MCU_Write(u16 addr, u8 val)
|
||||
{
|
||||
addr &= 0x7FFF;
|
||||
|
||||
|
@ -743,7 +724,7 @@ void Camera::MCU_Write(u16 addr, u8 val)
|
|||
}
|
||||
|
||||
|
||||
void Camera::InputFrame(u32* data, int width, int height, bool rgb)
|
||||
void DSi_Camera::InputFrame(const u32* data, int width, int height, bool rgb)
|
||||
{
|
||||
// TODO: double-buffering?
|
||||
|
||||
|
@ -815,18 +796,4 @@ void Camera::InputFrame(u32* data, int width, int height, bool rgb)
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
103
src/DSi_Camera.h
103
src/DSi_Camera.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,56 +21,36 @@
|
|||
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
#include "DSi_I2C.h"
|
||||
|
||||
namespace DSi_CamModule
|
||||
namespace melonDS
|
||||
{
|
||||
class DSi;
|
||||
class DSi_CamModule;
|
||||
|
||||
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
|
||||
class DSi_Camera : public DSi_I2CDevice
|
||||
{
|
||||
public:
|
||||
Camera(u32 num);
|
||||
~Camera();
|
||||
DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num);
|
||||
~DSi_Camera();
|
||||
|
||||
void DoSavestate(Savestate* file);
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
void Reset();
|
||||
void Reset() override;
|
||||
void Stop();
|
||||
bool IsActivated();
|
||||
bool IsActivated() const;
|
||||
|
||||
void StartTransfer();
|
||||
bool TransferDone();
|
||||
bool TransferDone() const;
|
||||
|
||||
// lengths in words
|
||||
int TransferScanline(u32* buffer, int maxlen);
|
||||
|
||||
void I2C_Start();
|
||||
u8 I2C_Read(bool last);
|
||||
void I2C_Write(u8 val, bool last);
|
||||
void Acquire() override;
|
||||
u8 Read(bool last) override;
|
||||
void Write(u8 val, bool last) override;
|
||||
|
||||
void InputFrame(u32* data, int width, int height, bool rgb);
|
||||
void InputFrame(const u32* data, int width, int height, bool rgb);
|
||||
|
||||
u32 Num;
|
||||
|
||||
|
@ -79,7 +59,7 @@ private:
|
|||
u32 RegAddr;
|
||||
u16 RegData;
|
||||
|
||||
u16 I2C_ReadReg(u16 addr);
|
||||
u16 I2C_ReadReg(u16 addr) const;
|
||||
void I2C_WriteReg(u16 addr, u16 val);
|
||||
|
||||
u16 PLLDiv;
|
||||
|
@ -92,7 +72,7 @@ private:
|
|||
u16 MCUAddr;
|
||||
u8 MCURegs[0x8000];
|
||||
|
||||
u8 MCU_Read(u16 addr);
|
||||
u8 MCU_Read(u16 addr) const;
|
||||
void MCU_Write(u16 addr, u8 val);
|
||||
|
||||
u16 FrameWidth, FrameHeight;
|
||||
|
@ -101,6 +81,51 @@ 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
|
||||
|
|
183
src/DSi_DSP.cpp
183
src/DSi_DSP.cpp
|
@ -24,37 +24,17 @@
|
|||
#include "NDS.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace DSi_DSP
|
||||
{
|
||||
|
||||
// not sure whether to not rather put it somewhere else
|
||||
u16 SNDExCnt;
|
||||
|
||||
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
|
||||
const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h
|
||||
// NOTE: ^ IS IN DSP WORDS, NOT IN BYTES!
|
||||
|
||||
u16 GetPSTS()
|
||||
|
||||
u16 DSi_DSP::GetPSTS() const
|
||||
{
|
||||
u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit
|
||||
//r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers
|
||||
|
@ -73,97 +53,101 @@ u16 GetPSTS()
|
|||
return r;
|
||||
}
|
||||
|
||||
void IrqRep0()
|
||||
void DSi_DSP::IrqRep0()
|
||||
{
|
||||
if (DSP_PCFG & (1<< 9)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
|
||||
if (DSP_PCFG & (1<< 9)) DSi.SetIRQ(0, IRQ_DSi_DSP);
|
||||
}
|
||||
void IrqRep1()
|
||||
void DSi_DSP::IrqRep1()
|
||||
{
|
||||
if (DSP_PCFG & (1<<10)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
|
||||
if (DSP_PCFG & (1<<10)) DSi.SetIRQ(0, IRQ_DSi_DSP);
|
||||
}
|
||||
void IrqRep2()
|
||||
void DSi_DSP::IrqRep2()
|
||||
{
|
||||
if (DSP_PCFG & (1<<11)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
|
||||
if (DSP_PCFG & (1<<11)) DSi.SetIRQ(0, IRQ_DSi_DSP);
|
||||
}
|
||||
void IrqSem()
|
||||
void DSi_DSP::IrqSem()
|
||||
{
|
||||
DSP_PSTS |= 1<<9;
|
||||
// apparently these are always fired?
|
||||
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
|
||||
DSi.SetIRQ(0, IRQ_DSi_DSP);
|
||||
}
|
||||
|
||||
u16 DSPRead16(u32 addr)
|
||||
u16 DSi_DSP::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 DSPWrite16(u32 addr, u16 val)
|
||||
void DSi_DSP::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 AudioCb(std::array<s16, 2> frame)
|
||||
void DSi_DSP::AudioCb(std::array<s16, 2> frame)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool Init()
|
||||
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
||||
{
|
||||
DSi.RegisterEventFunc(Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32));
|
||||
|
||||
TeakraCore = new Teakra::Teakra();
|
||||
SCFG_RST = false;
|
||||
|
||||
if (!TeakraCore) return false;
|
||||
// ????
|
||||
//if (!TeakraCore) return false;
|
||||
|
||||
TeakraCore->SetRecvDataHandler(0, IrqRep0);
|
||||
TeakraCore->SetRecvDataHandler(1, IrqRep1);
|
||||
TeakraCore->SetRecvDataHandler(2, IrqRep2);
|
||||
using namespace std::placeholders;
|
||||
|
||||
TeakraCore->SetSemaphoreHandler(IrqSem);
|
||||
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));
|
||||
|
||||
Teakra::SharedMemoryCallback smcb;
|
||||
smcb.read16 = DSPRead16;
|
||||
smcb.write16 = DSPWrite16;
|
||||
smcb.read16 = std::bind(&DSi_DSP::DSPRead16, this, _1);
|
||||
smcb.write16 = std::bind(&DSi_DSP::DSPWrite16, this, _1, _2);
|
||||
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 = DSi::ARM9Read8;
|
||||
cb.write8 = DSi::ARM9Write8;
|
||||
cb.read16 = DSi::ARM9Read16;
|
||||
cb.write16 = DSi::ARM9Write16;
|
||||
cb.read32 = DSi::ARM9Read32;
|
||||
cb.write32 = DSi::ARM9Write32;
|
||||
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); };
|
||||
TeakraCore->SetAHBMCallback(cb);
|
||||
|
||||
TeakraCore->SetAudioCallback(AudioCb);
|
||||
TeakraCore->SetAudioCallback(std::bind(&DSi_DSP::AudioCb, this, _1));
|
||||
|
||||
//PDATAReadFifo = new FIFO<u16>(16);
|
||||
//PDATAWriteFifo = new FIFO<u16>(16);
|
||||
|
||||
return true;
|
||||
}
|
||||
void DeInit()
|
||||
|
||||
DSi_DSP::~DSi_DSP()
|
||||
{
|
||||
//if (PDATAWriteFifo) delete PDATAWriteFifo;
|
||||
if (TeakraCore) delete TeakraCore;
|
||||
|
@ -171,9 +155,11 @@ void DeInit()
|
|||
//PDATAReadFifo = NULL;
|
||||
//PDATAWriteFifo = NULL;
|
||||
TeakraCore = NULL;
|
||||
|
||||
DSi.UnregisterEventFunc(Event_DSi_DSP, 0);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void DSi_DSP::Reset()
|
||||
{
|
||||
DSPTimestamp = 0;
|
||||
|
||||
|
@ -191,46 +177,46 @@ void Reset()
|
|||
//PDATAWriteFifo->Clear();
|
||||
TeakraCore->Reset();
|
||||
|
||||
NDS::CancelEvent(NDS::Event_DSi_DSP);
|
||||
DSi.CancelEvent(Event_DSi_DSP);
|
||||
|
||||
SNDExCnt = 0;
|
||||
}
|
||||
|
||||
bool IsRstReleased()
|
||||
bool DSi_DSP::IsRstReleased() const
|
||||
{
|
||||
return SCFG_RST;
|
||||
}
|
||||
void SetRstLine(bool release)
|
||||
void DSi_DSP::SetRstLine(bool release)
|
||||
{
|
||||
SCFG_RST = release;
|
||||
Reset();
|
||||
DSPTimestamp = NDS::ARM9Timestamp; // only start now!
|
||||
DSPTimestamp = DSi.ARM9Timestamp; // only start now!
|
||||
}
|
||||
|
||||
inline bool IsDSPCoreEnabled()
|
||||
inline bool DSi_DSP::IsDSPCoreEnabled() const
|
||||
{
|
||||
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 IsDSPIOEnabled()
|
||||
inline bool DSi_DSP::IsDSPIOEnabled() const
|
||||
{
|
||||
return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST;
|
||||
return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST;
|
||||
}
|
||||
|
||||
bool DSPCatchUp()
|
||||
bool DSi_DSP::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 < NDS::ARM9Timestamp)
|
||||
DSPTimestamp = NDS::ARM9Timestamp;
|
||||
if (DSPTimestamp < DSi.ARM9Timestamp)
|
||||
DSPTimestamp = DSi.ARM9Timestamp;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 curtime = NDS::ARM9Timestamp;
|
||||
u64 curtime = DSi.ARM9Timestamp;
|
||||
|
||||
if (DSPTimestamp >= curtime) return true; // ummmm?!
|
||||
|
||||
|
@ -245,9 +231,9 @@ bool DSPCatchUp()
|
|||
|
||||
return true;
|
||||
}
|
||||
void DSPCatchUpU32(u32 _) { DSPCatchUp(); }
|
||||
void DSi_DSP::DSPCatchUpU32(u32 _) { DSPCatchUp(); }
|
||||
|
||||
void PDataDMAWrite(u16 wrval)
|
||||
void DSi_DSP::PDataDMAWrite(u16 wrval)
|
||||
{
|
||||
u32 addr = DSP_PADR;
|
||||
|
||||
|
@ -271,7 +257,7 @@ void 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?
|
||||
|
@ -286,10 +272,10 @@ void 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?
|
||||
|
||||
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); // wrfifo empty
|
||||
DSi.SetIRQ(0, IRQ_DSi_DSP); // wrfifo empty
|
||||
}
|
||||
// TODO: FIFO interrupts! (rd full, nonempty)
|
||||
u16 PDataDMARead()
|
||||
u16 DSi_DSP::PDataDMARead()
|
||||
{
|
||||
u16 r = 0;
|
||||
u32 addr = DSP_PADR;
|
||||
|
@ -313,7 +299,7 @@ u16 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;
|
||||
}
|
||||
|
@ -327,7 +313,7 @@ u16 PDataDMARead()
|
|||
|
||||
return r;
|
||||
}
|
||||
void PDataDMAFetch()
|
||||
void DSi_DSP::PDataDMAFetch()
|
||||
{
|
||||
if (!PDataDMALen) return;
|
||||
|
||||
|
@ -335,7 +321,7 @@ void PDataDMAFetch()
|
|||
|
||||
if (PDataDMALen > 0) --PDataDMALen;
|
||||
}
|
||||
void PDataDMAStart()
|
||||
void DSi_DSP::PDataDMAStart()
|
||||
{
|
||||
switch ((DSP_PSTS & (3<<2)) >> 2)
|
||||
{
|
||||
|
@ -351,16 +337,16 @@ void PDataDMAStart()
|
|||
for (int i = 0; i < amt; ++i)
|
||||
PDataDMAFetch();
|
||||
|
||||
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
|
||||
DSi.SetIRQ(0, IRQ_DSi_DSP);
|
||||
|
||||
}
|
||||
void PDataDMACancel()
|
||||
void DSi_DSP::PDataDMACancel()
|
||||
{
|
||||
PDataDMALen = 0;
|
||||
PDATAReadFifo.Clear();
|
||||
|
||||
}
|
||||
u16 PDataDMAReadMMIO()
|
||||
u16 DSi_DSP::PDataDMAReadMMIO()
|
||||
{
|
||||
u16 ret;
|
||||
|
||||
|
@ -386,12 +372,12 @@ u16 PDataDMAReadMMIO()
|
|||
}
|
||||
|
||||
if (!PDATAReadFifo.IsEmpty() || PDATAReadFifo.IsFull())
|
||||
NDS::SetIRQ(0, NDS::IRQ_DSi_DSP);
|
||||
DSi.SetIRQ(0, IRQ_DSi_DSP);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 Read8(u32 addr)
|
||||
u8 DSi_DSP::Read8(u32 addr)
|
||||
{
|
||||
//if (!IsDSPIOEnabled()) return 0;
|
||||
DSPCatchUp();
|
||||
|
@ -418,7 +404,7 @@ u8 Read8(u32 addr)
|
|||
|
||||
return 0;
|
||||
}
|
||||
u16 Read16(u32 addr)
|
||||
u16 DSi_DSP::Read16(u32 addr)
|
||||
{
|
||||
//printf("DSP READ16 %d %08X %08X\n", IsDSPCoreEnabled(), addr, NDS::GetPC(0));
|
||||
//if (!IsDSPIOEnabled()) return 0;
|
||||
|
@ -461,14 +447,14 @@ u16 Read16(u32 addr)
|
|||
|
||||
return 0;
|
||||
}
|
||||
u32 Read32(u32 addr)
|
||||
u32 DSi_DSP::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 Write8(u32 addr, u8 val)
|
||||
void DSi_DSP::Write8(u32 addr, u8 val)
|
||||
{
|
||||
//if (!IsDSPIOEnabled()) return;
|
||||
DSPCatchUp();
|
||||
|
@ -489,9 +475,9 @@ void Write8(u32 addr, u8 val)
|
|||
// no REPx writes
|
||||
}
|
||||
}
|
||||
void Write16(u32 addr, u16 val)
|
||||
void DSi_DSP::Write16(u32 addr, u16 val)
|
||||
{
|
||||
Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, NDS::GetPC(0));
|
||||
Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, DSi.GetPC(0));
|
||||
//if (!IsDSPIOEnabled()) return;
|
||||
DSPCatchUp();
|
||||
|
||||
|
@ -544,14 +530,16 @@ void Write16(u32 addr, u16 val)
|
|||
}
|
||||
}
|
||||
|
||||
void Write32(u32 addr, u32 val)
|
||||
void DSi_DSP::Write32(u32 addr, u32 val)
|
||||
{
|
||||
addr &= 0x3C;
|
||||
Write16(addr, val & 0xFFFF);
|
||||
}
|
||||
|
||||
void WriteSNDExCnt(u16 val)
|
||||
void DSi_DSP::WriteSNDExCnt(u16 val, u16 mask)
|
||||
{
|
||||
val = (val & mask) | (SNDExCnt & ~mask);
|
||||
|
||||
// it can be written even in NDS mode
|
||||
|
||||
// mic frequency can only be changed if it was disabled
|
||||
|
@ -565,7 +553,7 @@ void WriteSNDExCnt(u16 val)
|
|||
SNDExCnt = val & 0xE00F;
|
||||
}
|
||||
|
||||
void Run(u32 cycles)
|
||||
void DSi_DSP::Run(u32 cycles)
|
||||
{
|
||||
if (!IsDSPCoreEnabled())
|
||||
{
|
||||
|
@ -577,12 +565,12 @@ void Run(u32 cycles)
|
|||
|
||||
DSPTimestamp += cycles;
|
||||
|
||||
NDS::CancelEvent(NDS::Event_DSi_DSP);
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_DSP, false,
|
||||
16384/*from citra (TeakraSlice)*/, DSPCatchUpU32, 0);
|
||||
DSi.CancelEvent(Event_DSi_DSP);
|
||||
DSi.ScheduleEvent(Event_DSi_DSP, false,
|
||||
16384/*from citra (TeakraSlice)*/, 0, 0);
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
void DSi_DSP::DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section("DSPi");
|
||||
|
||||
|
@ -608,5 +596,4 @@ void DoSavestate(Savestate* file)
|
|||
// TODO: save the Teakra state!!!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
101
src/DSi_DSP.h
101
src/DSi_DSP.h
|
@ -25,50 +25,89 @@
|
|||
// TODO: for actual sound output
|
||||
// * audio callbacks
|
||||
|
||||
namespace DSi_DSP
|
||||
namespace Teakra { class Teakra; }
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class DSi;
|
||||
class DSi_DSP
|
||||
{
|
||||
public:
|
||||
DSi_DSP(melonDS::DSi& dsi);
|
||||
~DSi_DSP();
|
||||
void Reset();
|
||||
void DoSavestate(Savestate* file);
|
||||
|
||||
extern u16 SNDExCnt;
|
||||
void DSPCatchUpU32(u32 _);
|
||||
|
||||
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];
|
||||
// SCFG_RST bit0
|
||||
bool IsRstReleased() const;
|
||||
void SetRstLine(bool release);
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
void Reset();
|
||||
// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT)
|
||||
u8 Read8(u32 addr);
|
||||
void Write8(u32 addr, u8 val);
|
||||
|
||||
void DoSavestate(Savestate* file);
|
||||
u16 Read16(u32 addr);
|
||||
void Write16(u32 addr, u16 val);
|
||||
|
||||
void DSPCatchUpU32(u32 _);
|
||||
u32 Read32(u32 addr);
|
||||
void Write32(u32 addr, u32 val);
|
||||
|
||||
// SCFG_RST bit0
|
||||
bool IsRstReleased();
|
||||
void SetRstLine(bool release);
|
||||
u16 ReadSNDExCnt() const { return SNDExCnt; }
|
||||
void WriteSNDExCnt(u16 val, u16 mask);
|
||||
|
||||
// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT)
|
||||
u8 Read8(u32 addr);
|
||||
void Write8(u32 addr, u8 val);
|
||||
// NOTE: checks SCFG_CLK9
|
||||
void Run(u32 cycles);
|
||||
|
||||
u16 Read16(u32 addr);
|
||||
void Write16(u32 addr, u16 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);
|
||||
|
||||
u32 Read32(u32 addr);
|
||||
void Write32(u32 addr, u32 val);
|
||||
private:
|
||||
melonDS::DSi& DSi;
|
||||
// not sure whether to not rather put it somewhere else
|
||||
u16 SNDExCnt;
|
||||
|
||||
void WriteSNDExCnt(u16 val);
|
||||
Teakra::Teakra* TeakraCore;
|
||||
|
||||
// NOTE: checks SCFG_CLK9
|
||||
void Run(u32 cycles);
|
||||
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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DSI_DSP_H
|
||||
|
||||
|
|
229
src/DSi_I2C.cpp
229
src/DSi_I2C.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -26,21 +26,21 @@
|
|||
#include "SPI.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace DSi_BPTWL
|
||||
{
|
||||
|
||||
// TODO: These are purely approximations
|
||||
const double PowerButtonShutdownTime = 0.5;
|
||||
const double PowerButtonForcedShutdownTime = 5.0;
|
||||
const double VolumeSwitchRepeatStart = 0.5;
|
||||
const double VolumeSwitchRepeatRate = 1.0 / 6;
|
||||
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 VolumeDownTable[32] =
|
||||
const u8 DSi_BPTWL::VolumeDownTable[32] =
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03,
|
||||
0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A,
|
||||
|
@ -48,7 +48,7 @@ const u8 VolumeDownTable[32] =
|
|||
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
|
||||
};
|
||||
|
||||
const u8 VolumeUpTable[32] =
|
||||
const u8 DSi_BPTWL::VolumeUpTable[32] =
|
||||
{
|
||||
0x02, 0x03, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C,
|
||||
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
|
||||
|
@ -56,27 +56,16 @@ const u8 VolumeUpTable[32] =
|
|||
0x1D, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
};
|
||||
|
||||
double PowerButtonTime = 0.0;
|
||||
bool PowerButtonDownFlag = false;
|
||||
bool PowerButtonShutdownFlag = false;
|
||||
double VolumeSwitchTime = 0.0;
|
||||
double VolumeSwitchRepeatTime = 0.0;
|
||||
bool VolumeSwitchDownFlag = false;
|
||||
u32 VolumeSwitchKeysDown = 0;
|
||||
|
||||
u8 Registers[0x100];
|
||||
u32 CurPos;
|
||||
|
||||
bool Init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
DSi_BPTWL::DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host) : DSi_I2CDevice(dsi, host)
|
||||
{
|
||||
}
|
||||
|
||||
void Reset()
|
||||
DSi_BPTWL::~DSi_BPTWL()
|
||||
{
|
||||
}
|
||||
|
||||
void DSi_BPTWL::Reset()
|
||||
{
|
||||
CurPos = -1;
|
||||
memset(Registers, 0x5A, 0x100);
|
||||
|
@ -115,10 +104,11 @@ void Reset()
|
|||
VolumeSwitchTime = 0.0;
|
||||
VolumeSwitchRepeatTime = 0.0;
|
||||
VolumeSwitchKeysDown = 0;
|
||||
VolumeSwitchDownFlag = false;
|
||||
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
void DSi_BPTWL::DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section("I2BP");
|
||||
|
||||
|
@ -127,24 +117,24 @@ void DoSavestate(Savestate* file)
|
|||
}
|
||||
|
||||
// TODO: Needs more investigation on the other bits
|
||||
inline bool GetIRQMode()
|
||||
inline bool DSi_BPTWL::GetIRQMode() const
|
||||
{
|
||||
return Registers[0x12] & 0x01;
|
||||
}
|
||||
|
||||
u8 GetBootFlag() { return Registers[0x70]; }
|
||||
u8 DSi_BPTWL::GetBootFlag() const { return Registers[0x70]; }
|
||||
|
||||
bool GetBatteryCharging() { return Registers[0x20] >> 7; }
|
||||
void SetBatteryCharging(bool charging)
|
||||
bool DSi_BPTWL::GetBatteryCharging() const { return Registers[0x20] >> 7; }
|
||||
void DSi_BPTWL::SetBatteryCharging(bool charging)
|
||||
{
|
||||
Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F));
|
||||
}
|
||||
|
||||
u8 GetBatteryLevel() { return Registers[0x20] & 0xF; }
|
||||
void SetBatteryLevel(u8 batteryLevel)
|
||||
u8 DSi_BPTWL::GetBatteryLevel() const { return Registers[0x20] & 0xF; }
|
||||
void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel)
|
||||
{
|
||||
Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F));
|
||||
SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false);
|
||||
//SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false);
|
||||
|
||||
if (batteryLevel <= 1)
|
||||
{
|
||||
|
@ -153,20 +143,20 @@ void SetBatteryLevel(u8 batteryLevel)
|
|||
|
||||
}
|
||||
|
||||
u8 GetVolumeLevel() { return Registers[0x40]; }
|
||||
void SetVolumeLevel(u8 volume)
|
||||
u8 DSi_BPTWL::GetVolumeLevel() const { return Registers[0x40]; }
|
||||
void DSi_BPTWL::SetVolumeLevel(u8 volume)
|
||||
{
|
||||
Registers[0x40] = volume & 0x1F;
|
||||
}
|
||||
|
||||
u8 GetBacklightLevel() { return Registers[0x41]; }
|
||||
void SetBacklightLevel(u8 backlight)
|
||||
u8 DSi_BPTWL::GetBacklightLevel() const { return Registers[0x41]; }
|
||||
void DSi_BPTWL::SetBacklightLevel(u8 backlight)
|
||||
{
|
||||
Registers[0x41] = backlight > 4 ? 4 : backlight;
|
||||
}
|
||||
|
||||
|
||||
void ResetButtonState()
|
||||
void DSi_BPTWL::ResetButtonState()
|
||||
{
|
||||
PowerButtonTime = 0.0;
|
||||
PowerButtonDownFlag = false;
|
||||
|
@ -178,7 +168,7 @@ void ResetButtonState()
|
|||
VolumeSwitchRepeatTime = 0.0;
|
||||
}
|
||||
|
||||
void DoHardwareReset(bool direct)
|
||||
void DSi_BPTWL::DoHardwareReset(bool direct)
|
||||
{
|
||||
ResetButtonState();
|
||||
|
||||
|
@ -187,23 +177,23 @@ void DoHardwareReset(bool direct)
|
|||
if (direct)
|
||||
{
|
||||
// TODO: This doesn't seem to stop the SPU
|
||||
DSi::SoftReset();
|
||||
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
|
||||
NDS::ARM7->Halt(4);
|
||||
DSi.ARM7.Halt(4);
|
||||
}
|
||||
|
||||
void DoShutdown()
|
||||
void DSi_BPTWL::DoShutdown()
|
||||
{
|
||||
ResetButtonState();
|
||||
NDS::Stop(Platform::StopReason::PowerOff);
|
||||
DSi.Stop(Platform::StopReason::PowerOff);
|
||||
}
|
||||
|
||||
|
||||
void SetPowerButtonHeld(double time)
|
||||
void DSi_BPTWL::SetPowerButtonHeld(double time)
|
||||
{
|
||||
if (!PowerButtonDownFlag)
|
||||
{
|
||||
|
@ -230,7 +220,7 @@ void SetPowerButtonHeld(double time)
|
|||
}
|
||||
}
|
||||
|
||||
void SetPowerButtonReleased(double time)
|
||||
void DSi_BPTWL::SetPowerButtonReleased(double time)
|
||||
{
|
||||
double elapsed = time - PowerButtonTime;
|
||||
if (elapsed >= 0 && elapsed < PowerButtonShutdownTime)
|
||||
|
@ -243,12 +233,12 @@ void SetPowerButtonReleased(double time)
|
|||
PowerButtonShutdownFlag = false;
|
||||
}
|
||||
|
||||
void SetVolumeSwitchHeld(u32 key)
|
||||
void DSi_BPTWL::SetVolumeSwitchHeld(u32 key)
|
||||
{
|
||||
VolumeSwitchKeysDown |= (1 << key);
|
||||
}
|
||||
|
||||
void SetVolumeSwitchReleased(u32 key)
|
||||
void DSi_BPTWL::SetVolumeSwitchReleased(u32 key)
|
||||
{
|
||||
VolumeSwitchKeysDown &= ~(1 << key);
|
||||
VolumeSwitchDownFlag = false;
|
||||
|
@ -256,7 +246,7 @@ void SetVolumeSwitchReleased(u32 key)
|
|||
VolumeSwitchRepeatTime = 0.0;
|
||||
}
|
||||
|
||||
inline bool CheckVolumeSwitchKeysValid()
|
||||
inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() const
|
||||
{
|
||||
bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up);
|
||||
bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down);
|
||||
|
@ -264,7 +254,7 @@ inline bool CheckVolumeSwitchKeysValid()
|
|||
return up != down;
|
||||
}
|
||||
|
||||
s32 ProcessVolumeSwitchInput(double time)
|
||||
s32 DSi_BPTWL::ProcessVolumeSwitchInput(double time)
|
||||
{
|
||||
if (!CheckVolumeSwitchKeysValid())
|
||||
return -1;
|
||||
|
@ -303,7 +293,7 @@ s32 ProcessVolumeSwitchInput(double time)
|
|||
}
|
||||
|
||||
|
||||
void DoPowerButtonPress()
|
||||
void DSi_BPTWL::DoPowerButtonPress()
|
||||
{
|
||||
// Set button pressed IRQ
|
||||
SetIRQ(IRQ_PowerButtonPressed);
|
||||
|
@ -311,7 +301,7 @@ void DoPowerButtonPress()
|
|||
// There is no default hardware behavior for pressing the power button
|
||||
}
|
||||
|
||||
void DoPowerButtonReset()
|
||||
void DSi_BPTWL::DoPowerButtonReset()
|
||||
{
|
||||
// Reset via IRQ, handled by software
|
||||
SetIRQ(IRQ_PowerButtonReset);
|
||||
|
@ -324,7 +314,7 @@ void DoPowerButtonReset()
|
|||
}
|
||||
}
|
||||
|
||||
void DoPowerButtonShutdown()
|
||||
void DSi_BPTWL::DoPowerButtonShutdown()
|
||||
{
|
||||
// Shutdown via IRQ, handled by software
|
||||
if (!PowerButtonShutdownFlag)
|
||||
|
@ -346,12 +336,12 @@ void DoPowerButtonShutdown()
|
|||
// down the power button, the DSi will still shut down
|
||||
}
|
||||
|
||||
void DoPowerButtonForceShutdown()
|
||||
void DSi_BPTWL::DoPowerButtonForceShutdown()
|
||||
{
|
||||
DoShutdown();
|
||||
}
|
||||
|
||||
void DoVolumeSwitchPress(u32 key)
|
||||
void DSi_BPTWL::DoVolumeSwitchPress(u32 key)
|
||||
{
|
||||
u8 volume = Registers[0x40];
|
||||
|
||||
|
@ -373,22 +363,22 @@ void DoVolumeSwitchPress(u32 key)
|
|||
SetIRQ(IRQ_VolumeSwitchPressed);
|
||||
}
|
||||
|
||||
void SetIRQ(u8 irqFlag)
|
||||
void DSi_BPTWL::SetIRQ(u8 irqFlag)
|
||||
{
|
||||
Registers[0x10] |= irqFlag & IRQ_ValidMask;
|
||||
|
||||
if (GetIRQMode())
|
||||
{
|
||||
NDS::SetIRQ2(NDS::IRQ2_DSi_BPTWL);
|
||||
DSi.SetIRQ2(IRQ2_DSi_BPTWL);
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
void DSi_BPTWL::Acquire()
|
||||
{
|
||||
//printf("BPTWL: start\n");
|
||||
}
|
||||
|
||||
u8 Read(bool last)
|
||||
u8 DSi_BPTWL::Read(bool last)
|
||||
{
|
||||
//printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1));
|
||||
u8 ret = Registers[CurPos];
|
||||
|
@ -409,7 +399,7 @@ u8 Read(bool last)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void Write(u8 val, bool last)
|
||||
void DSi_BPTWL::Write(u8 val, bool last)
|
||||
{
|
||||
if (last)
|
||||
{
|
||||
|
@ -460,51 +450,69 @@ void 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);
|
||||
}
|
||||
|
||||
|
||||
namespace DSi_I2C
|
||||
DSi_I2CHost::~DSi_I2CHost()
|
||||
{
|
||||
|
||||
u8 Cnt;
|
||||
u8 Data;
|
||||
|
||||
u32 Device;
|
||||
|
||||
bool Init()
|
||||
{
|
||||
if (!DSi_BPTWL::Init()) return false;
|
||||
|
||||
return true;
|
||||
delete BPTWL; BPTWL = nullptr;
|
||||
delete Camera0; Camera0 = nullptr;
|
||||
delete Camera1; Camera1 = nullptr;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
DSi_BPTWL::DeInit();
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void DSi_I2CHost::Reset()
|
||||
{
|
||||
Cnt = 0;
|
||||
Data = 0;
|
||||
|
||||
Device = -1;
|
||||
CurDeviceID = 0;
|
||||
CurDevice = nullptr;
|
||||
|
||||
DSi_BPTWL::Reset();
|
||||
BPTWL->Reset();
|
||||
Camera0->Reset();
|
||||
Camera1->Reset();
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
void DSi_I2CHost::DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section("I2Ci");
|
||||
|
||||
file->Var8(&Cnt);
|
||||
file->Var8(&Data);
|
||||
file->Var32(&Device);
|
||||
file->Var8(&CurDeviceID);
|
||||
|
||||
DSi_BPTWL::DoSavestate(file);
|
||||
if (!file->Saving)
|
||||
{
|
||||
GetCurDevice();
|
||||
}
|
||||
|
||||
BPTWL->DoSavestate(file);
|
||||
Camera0->DoSavestate(file);
|
||||
Camera1->DoSavestate(file);
|
||||
}
|
||||
|
||||
void WriteCnt(u8 val)
|
||||
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)
|
||||
{
|
||||
//printf("I2C: write CNT %02X, %02X, %08X\n", val, Data, NDS::GetPC(1));
|
||||
|
||||
|
@ -522,17 +530,13 @@ void WriteCnt(u8 val)
|
|||
// read
|
||||
val &= 0xF7;
|
||||
|
||||
switch (Device)
|
||||
if (CurDevice)
|
||||
{
|
||||
Data = CurDevice->Read(islast);
|
||||
}
|
||||
else
|
||||
{
|
||||
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:
|
||||
Log(LogLevel::Warn, "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);
|
||||
|
@ -545,37 +549,30 @@ void WriteCnt(u8 val)
|
|||
|
||||
if (val & (1<<1))
|
||||
{
|
||||
Device = Data & 0xFE;
|
||||
CurDeviceID = Data & 0xFE;
|
||||
//printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device);
|
||||
|
||||
switch (Device)
|
||||
GetCurDevice();
|
||||
if (CurDevice)
|
||||
{
|
||||
CurDevice->Acquire();
|
||||
}
|
||||
else
|
||||
{
|
||||
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:
|
||||
Log(LogLevel::Warn, "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);
|
||||
|
||||
switch (Device)
|
||||
if (CurDevice)
|
||||
{
|
||||
CurDevice->Write(Data, islast);
|
||||
}
|
||||
else
|
||||
{
|
||||
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:
|
||||
Log(LogLevel::Warn, "I2C: write on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
|
||||
ack = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -588,14 +585,14 @@ void WriteCnt(u8 val)
|
|||
Cnt = val;
|
||||
}
|
||||
|
||||
u8 ReadData()
|
||||
u8 DSi_I2CHost::ReadData()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
void WriteData(u8 val)
|
||||
void DSi_I2CHost::WriteData(u8 val)
|
||||
{
|
||||
Data = val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
232
src/DSi_I2C.h
232
src/DSi_I2C.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,95 +22,167 @@
|
|||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace DSi_BPTWL
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
u8 GetBootFlag();
|
||||
|
||||
bool GetBatteryCharging();
|
||||
void SetBatteryCharging(bool charging);
|
||||
|
||||
enum
|
||||
class DSi_I2CHost;
|
||||
class DSi_Camera;
|
||||
class DSi;
|
||||
class DSi_I2CDevice
|
||||
{
|
||||
batteryLevel_Critical = 0x0,
|
||||
batteryLevel_AlmostEmpty = 0x1,
|
||||
batteryLevel_Low = 0x3,
|
||||
batteryLevel_Half = 0x7,
|
||||
batteryLevel_ThreeQuarters = 0xB,
|
||||
batteryLevel_Full = 0xF
|
||||
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;
|
||||
};
|
||||
|
||||
u8 GetBatteryLevel();
|
||||
void SetBatteryLevel(u8 batteryLevel);
|
||||
|
||||
// 0-31
|
||||
u8 GetVolumeLevel();
|
||||
void SetVolumeLevel(u8 volume);
|
||||
|
||||
// 0-4
|
||||
u8 GetBacklightLevel();
|
||||
void SetBacklightLevel(u8 backlight);
|
||||
|
||||
void DoHardwareReset(bool direct);
|
||||
void DoShutdown();
|
||||
|
||||
enum
|
||||
class DSi_BPTWL : public DSi_I2CDevice
|
||||
{
|
||||
volumeKey_Up,
|
||||
volumeKey_Down,
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
batteryLevel_Critical = 0x0,
|
||||
batteryLevel_AlmostEmpty = 0x1,
|
||||
batteryLevel_Low = 0x3,
|
||||
batteryLevel_Half = 0x7,
|
||||
batteryLevel_ThreeQuarters = 0xB,
|
||||
batteryLevel_Full = 0xF
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
volumeKey_Up,
|
||||
volumeKey_Down,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host);
|
||||
~DSi_BPTWL() override;
|
||||
void Reset() override;
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
u8 GetBootFlag() const;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
enum
|
||||
class DSi_I2CHost
|
||||
{
|
||||
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,
|
||||
};
|
||||
public:
|
||||
DSi_I2CHost(melonDS::DSi& dsi);
|
||||
~DSi_I2CHost();
|
||||
void Reset();
|
||||
void DoSavestate(Savestate* file);
|
||||
|
||||
void SetIRQ(u8 irqFlag);
|
||||
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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DSi_I2C
|
||||
{
|
||||
|
||||
extern u8 Cnt;
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
void Reset();
|
||||
void DoSavestate(Savestate* file);
|
||||
|
||||
void WriteCnt(u8 val);
|
||||
|
||||
u8 ReadData();
|
||||
void WriteData(u8 val);
|
||||
|
||||
//void TransferDone(u32 param);
|
||||
|
||||
}
|
||||
|
||||
#endif // DSI_I2C_H
|
||||
|
|
472
src/DSi_NAND.cpp
472
src/DSi_NAND.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
|||
#include "DSi.h"
|
||||
#include "DSi_AES.h"
|
||||
#include "DSi_NAND.h"
|
||||
#include "FATIO.h"
|
||||
#include "Platform.h"
|
||||
|
||||
#include "sha1/sha1.hpp"
|
||||
|
@ -29,83 +30,21 @@
|
|||
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace melonDS::Platform;
|
||||
|
||||
namespace DSi_NAND
|
||||
namespace melonDS::DSi_NAND
|
||||
{
|
||||
|
||||
FileHandle* CurFile;
|
||||
FATFS CurFS;
|
||||
|
||||
u8 eMMC_CID[16];
|
||||
u64 ConsoleID;
|
||||
|
||||
u8 FATIV[16];
|
||||
u8 FATKey[16];
|
||||
|
||||
u8 ESKey[16];
|
||||
|
||||
|
||||
UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num);
|
||||
UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num);
|
||||
|
||||
|
||||
bool Init(u8* es_keyY)
|
||||
NANDImage::NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept : NANDImage(nandfile, es_keyY.data())
|
||||
{
|
||||
CurFile = nullptr;
|
||||
|
||||
std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath);
|
||||
std::string instnand = nandpath + Platform::InstanceFileSuffix();
|
||||
|
||||
FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting);
|
||||
if ((!nandfile) && (Platform::InstanceID() > 0))
|
||||
{
|
||||
FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read);
|
||||
if (!orig)
|
||||
{
|
||||
Log(LogLevel::Error, "Failed to open DSi NAND\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
long len = FileLength(orig);
|
||||
|
||||
nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWrite);
|
||||
if (nandfile)
|
||||
{
|
||||
u8* tmpbuf = new u8[0x10000];
|
||||
for (long i = 0; i < len; i+=0x10000)
|
||||
{
|
||||
long blklen = 0x10000;
|
||||
if ((i+blklen) > len) blklen = len-i;
|
||||
|
||||
FileRead(tmpbuf, blklen, 1, orig);
|
||||
FileWrite(tmpbuf, blklen, 1, nandfile);
|
||||
}
|
||||
delete[] tmpbuf;
|
||||
}
|
||||
|
||||
Platform::CloseFile(orig);
|
||||
Platform::CloseFile(nandfile);
|
||||
|
||||
nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting);
|
||||
}
|
||||
}
|
||||
|
||||
NANDImage::NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept
|
||||
{
|
||||
if (!nandfile)
|
||||
return false;
|
||||
return;
|
||||
|
||||
u64 nandlen = FileLength(nandfile);
|
||||
|
||||
ff_disk_open(FF_ReadNAND, FF_WriteNAND, (LBA_t)(nandlen>>9));
|
||||
|
||||
FRESULT res;
|
||||
res = f_mount(&CurFS, "0:", 0);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
Log(LogLevel::Error, "NAND mounting failed: %d\n", res);
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
return false;
|
||||
}
|
||||
Length = FileLength(nandfile);
|
||||
|
||||
// read the nocash footer
|
||||
|
||||
|
@ -113,26 +52,24 @@ bool Init(u8* es_keyY)
|
|||
|
||||
char nand_footer[16];
|
||||
const char* nand_footer_ref = "DSi eMMC CID/CPU";
|
||||
FileRead(nand_footer, 1, 16, nandfile);
|
||||
if (memcmp(nand_footer, nand_footer_ref, 16))
|
||||
FileRead(nand_footer, 1, sizeof(nand_footer), nandfile);
|
||||
if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer)))
|
||||
{
|
||||
// There is another copy of the footer at 000FF800h for the case
|
||||
// that by external tools the image was cut off
|
||||
// See https://problemkaputt.de/gbatek.htm#dsisdmmcimages
|
||||
FileSeek(nandfile, 0x000FF800, FileSeekOrigin::Start);
|
||||
FileRead(nand_footer, 1, 16, nandfile);
|
||||
if (memcmp(nand_footer, nand_footer_ref, 16))
|
||||
FileRead(nand_footer, 1, sizeof(nand_footer), nandfile);
|
||||
if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer)))
|
||||
{
|
||||
Log(LogLevel::Error, "ERROR: NAND missing nocash footer\n");
|
||||
CloseFile(nandfile);
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FileRead(eMMC_CID, 1, 16, nandfile);
|
||||
FileRead(&ConsoleID, 1, 8, nandfile);
|
||||
FileRead(eMMC_CID.data(), 1, sizeof(eMMC_CID), nandfile);
|
||||
FileRead(&ConsoleID, 1, sizeof(ConsoleID), nandfile);
|
||||
|
||||
// init NAND crypto
|
||||
|
||||
|
@ -141,10 +78,10 @@ bool Init(u8* es_keyY)
|
|||
u8 keyX[16], keyY[16];
|
||||
|
||||
SHA1Init(&sha);
|
||||
SHA1Update(&sha, eMMC_CID, 16);
|
||||
SHA1Update(&sha, eMMC_CID.data(), sizeof(eMMC_CID));
|
||||
SHA1Final(tmp, &sha);
|
||||
|
||||
Bswap128(FATIV, tmp);
|
||||
Bswap128(FATIV.data(), tmp);
|
||||
|
||||
*(u32*)&keyX[0] = (u32)ConsoleID;
|
||||
*(u32*)&keyX[4] = (u32)ConsoleID ^ 0x24EE6906;
|
||||
|
@ -157,7 +94,7 @@ bool Init(u8* es_keyY)
|
|||
*(u32*)&keyY[12] = 0xE1A00005;
|
||||
|
||||
DSi_AES::DeriveNormalKey(keyX, keyY, tmp);
|
||||
Bswap128(FATKey, tmp);
|
||||
Bswap128(FATKey.data(), tmp);
|
||||
|
||||
|
||||
*(u32*)&keyX[0] = 0x4E00004A;
|
||||
|
@ -165,42 +102,92 @@ bool Init(u8* es_keyY)
|
|||
*(u32*)&keyX[8] = (u32)(ConsoleID >> 32) ^ 0xC80C4B72;
|
||||
*(u32*)&keyX[12] = (u32)ConsoleID;
|
||||
|
||||
memcpy(keyY, es_keyY, 16);
|
||||
memcpy(keyY, es_keyY, sizeof(keyY));
|
||||
|
||||
DSi_AES::DeriveNormalKey(keyX, keyY, tmp);
|
||||
Bswap128(ESKey, tmp);
|
||||
Bswap128(ESKey.data(), tmp);
|
||||
|
||||
CurFile = nandfile;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
NANDImage::~NANDImage()
|
||||
{
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
|
||||
if (CurFile) CloseFile(CurFile);
|
||||
CurFile = nullptr;
|
||||
}
|
||||
|
||||
|
||||
FileHandle* GetFile()
|
||||
NANDImage::NANDImage(NANDImage&& other) noexcept :
|
||||
CurFile(other.CurFile),
|
||||
eMMC_CID(other.eMMC_CID),
|
||||
ConsoleID(other.ConsoleID),
|
||||
FATIV(other.FATIV),
|
||||
FATKey(other.FATKey),
|
||||
ESKey(other.ESKey),
|
||||
Length(other.Length)
|
||||
{
|
||||
return CurFile;
|
||||
other.CurFile = nullptr;
|
||||
}
|
||||
|
||||
NANDImage& NANDImage::operator=(NANDImage&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (CurFile)
|
||||
CloseFile(CurFile);
|
||||
|
||||
CurFile = other.CurFile;
|
||||
eMMC_CID = other.eMMC_CID;
|
||||
ConsoleID = other.ConsoleID;
|
||||
FATIV = other.FATIV;
|
||||
FATKey = other.FATKey;
|
||||
ESKey = other.ESKey;
|
||||
Length = other.Length;
|
||||
|
||||
other.CurFile = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NANDMount::NANDMount(NANDImage& nand) noexcept : Image(&nand)
|
||||
{
|
||||
if (!nand)
|
||||
return;
|
||||
|
||||
CurFS = std::make_unique<FATFS>();
|
||||
ff_disk_open(
|
||||
[this](BYTE* buf, LBA_t sector, UINT num) {
|
||||
return this->FF_ReadNAND(buf, sector, num);
|
||||
},
|
||||
[this](const BYTE* buf, LBA_t sector, UINT num) {
|
||||
return this->FF_WriteNAND(buf, sector, num);
|
||||
},
|
||||
(LBA_t)(nand.GetLength()>>9)
|
||||
);
|
||||
|
||||
FRESULT res;
|
||||
res = f_mount(CurFS.get(), "0:", 0);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
Log(LogLevel::Error, "NAND mounting failed: %d\n", res);
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GetIDs(u8* emmc_cid, u64& consoleid)
|
||||
NANDMount::~NANDMount() noexcept
|
||||
{
|
||||
memcpy(emmc_cid, eMMC_CID, 16);
|
||||
consoleid = ConsoleID;
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
}
|
||||
|
||||
|
||||
void SetupFATCrypto(AES_ctx* ctx, u32 ctr)
|
||||
void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr)
|
||||
{
|
||||
u8 iv[16];
|
||||
memcpy(iv, FATIV, sizeof(iv));
|
||||
memcpy(iv, FATIV.data(), sizeof(iv));
|
||||
|
||||
u32 res;
|
||||
res = iv[15] + (ctr & 0xFF);
|
||||
|
@ -218,10 +205,10 @@ void SetupFATCrypto(AES_ctx* ctx, u32 ctr)
|
|||
else break;
|
||||
}
|
||||
|
||||
AES_init_ctx_iv(ctx, FATKey, iv);
|
||||
AES_init_ctx_iv(ctx, FATKey.data(), iv);
|
||||
}
|
||||
|
||||
u32 ReadFATBlock(u64 addr, u32 len, u8* buf)
|
||||
u32 NANDImage::ReadFATBlock(u64 addr, u32 len, u8* buf)
|
||||
{
|
||||
u32 ctr = (u32)(addr >> 4);
|
||||
|
||||
|
@ -243,7 +230,7 @@ u32 ReadFATBlock(u64 addr, u32 len, u8* buf)
|
|||
return len;
|
||||
}
|
||||
|
||||
u32 WriteFATBlock(u64 addr, u32 len, u8* buf)
|
||||
u32 NANDImage::WriteFATBlock(u64 addr, u32 len, const u8* buf)
|
||||
{
|
||||
u32 ctr = (u32)(addr >> 4);
|
||||
|
||||
|
@ -272,30 +259,30 @@ u32 WriteFATBlock(u64 addr, u32 len, u8* buf)
|
|||
}
|
||||
|
||||
|
||||
UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num)
|
||||
UINT NANDMount::FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num)
|
||||
{
|
||||
// TODO: allow selecting other partitions?
|
||||
u64 baseaddr = 0x10EE00;
|
||||
|
||||
u64 blockaddr = baseaddr + (sector * 0x200ULL);
|
||||
|
||||
u32 res = ReadFATBlock(blockaddr, num*0x200, buf);
|
||||
u32 res = Image->ReadFATBlock(blockaddr, num*0x200, buf);
|
||||
return res >> 9;
|
||||
}
|
||||
|
||||
UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num)
|
||||
UINT NANDMount::FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num)
|
||||
{
|
||||
// TODO: allow selecting other partitions?
|
||||
u64 baseaddr = 0x10EE00;
|
||||
|
||||
u64 blockaddr = baseaddr + (sector * 0x200ULL);
|
||||
|
||||
u32 res = WriteFATBlock(blockaddr, num*0x200, buf);
|
||||
u32 res = Image->WriteFATBlock(blockaddr, num*0x200, buf);
|
||||
return res >> 9;
|
||||
}
|
||||
|
||||
|
||||
bool ESEncrypt(u8* data, u32 len)
|
||||
bool NANDImage::ESEncrypt(u8* data, u32 len) const
|
||||
{
|
||||
AES_ctx ctx;
|
||||
u8 iv[16];
|
||||
|
@ -307,7 +294,7 @@ bool ESEncrypt(u8* data, u32 len)
|
|||
iv[14] = 0x00;
|
||||
iv[15] = 0x01;
|
||||
|
||||
AES_init_ctx_iv(&ctx, ESKey, iv);
|
||||
AES_init_ctx_iv(&ctx, ESKey.data(), iv);
|
||||
|
||||
u32 blklen = (len + 0xF) & ~0xF;
|
||||
mac[0] = 0x3A;
|
||||
|
@ -380,7 +367,7 @@ bool ESEncrypt(u8* data, u32 len)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ESDecrypt(u8* data, u32 len)
|
||||
bool NANDImage::ESDecrypt(u8* data, u32 len) const
|
||||
{
|
||||
AES_ctx ctx;
|
||||
u8 iv[16];
|
||||
|
@ -392,7 +379,7 @@ bool ESDecrypt(u8* data, u32 len)
|
|||
iv[14] = 0x00;
|
||||
iv[15] = 0x01;
|
||||
|
||||
AES_init_ctx_iv(&ctx, ESKey, iv);
|
||||
AES_init_ctx_iv(&ctx, ESKey.data(), iv);
|
||||
|
||||
u32 blklen = (len + 0xF) & ~0xF;
|
||||
mac[0] = 0x3A;
|
||||
|
@ -485,30 +472,43 @@ bool ESDecrypt(u8* data, u32 len)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ReadHardwareInfo(u8* dataS, u8* dataN)
|
||||
bool NANDMount::ReadSerialData(DSiSerialData& dataS)
|
||||
{
|
||||
FF_FIL file;
|
||||
FRESULT res;
|
||||
u32 nread;
|
||||
FRESULT res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ);
|
||||
|
||||
res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ);
|
||||
if (res == FR_OK)
|
||||
{
|
||||
f_read(&file, dataS, 0xA4, &nread);
|
||||
u32 nread;
|
||||
f_read(&file, &dataS, sizeof(DSiSerialData), &nread);
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ);
|
||||
if (res == FR_OK)
|
||||
{
|
||||
f_read(&file, dataN, 0x9C, &nread);
|
||||
f_close(&file);
|
||||
}
|
||||
return res == FR_OK;
|
||||
}
|
||||
|
||||
bool NANDMount::ReadHardwareInfoN(DSiHardwareInfoN& dataN)
|
||||
{
|
||||
FF_FIL file;
|
||||
FRESULT res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ);
|
||||
|
||||
void ReadUserData(u8* data)
|
||||
if (res == FR_OK)
|
||||
{
|
||||
u32 nread;
|
||||
f_read(&file, dataN.data(), sizeof(dataN), &nread);
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
return res == FR_OK;
|
||||
}
|
||||
|
||||
void NANDMount::ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN)
|
||||
{
|
||||
ReadSerialData(dataS);
|
||||
ReadHardwareInfoN(dataN);
|
||||
}
|
||||
|
||||
bool NANDMount::ReadUserData(DSiFirmwareSystemSettings& data)
|
||||
{
|
||||
FF_FIL file;
|
||||
FRESULT res;
|
||||
|
@ -539,7 +539,7 @@ void ReadUserData(u8* data)
|
|||
v2 = tmp;
|
||||
}
|
||||
|
||||
if (v1 < 0 && v2 < 0) return;
|
||||
if (v1 < 0 && v2 < 0) return false;
|
||||
|
||||
if (v2 > v1)
|
||||
{
|
||||
|
@ -553,82 +553,42 @@ void ReadUserData(u8* data)
|
|||
}
|
||||
|
||||
f_lseek(&file, 0);
|
||||
f_read(&file, data, 0x1B0, &nread);
|
||||
f_read(&file, &data, sizeof(DSiFirmwareSystemSettings), &nread);
|
||||
f_close(&file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PatchUserData()
|
||||
static bool SaveUserData(const char* filename, const DSiFirmwareSystemSettings& data)
|
||||
{
|
||||
FRESULT res;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
FF_FIL file;
|
||||
if (FRESULT res = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ | FA_WRITE); res != FR_OK)
|
||||
{
|
||||
char filename[64];
|
||||
sprintf(filename, "0:/shared1/TWLCFG%d.dat", i);
|
||||
|
||||
FF_FIL file;
|
||||
res = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ | FA_WRITE);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res);
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 contents[0x1B0];
|
||||
u32 nres;
|
||||
f_lseek(&file, 0);
|
||||
f_read(&file, contents, 0x1B0, &nres);
|
||||
|
||||
// override user settings, if needed
|
||||
if (Platform::GetConfigBool(Platform::Firm_OverrideSettings))
|
||||
{
|
||||
// setting up username
|
||||
std::string orig_username = Platform::GetConfigString(Platform::Firm_Username);
|
||||
std::u16string username = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_username);
|
||||
size_t usernameLength = std::min(username.length(), (size_t) 10);
|
||||
memset(contents + 0xD0, 0, 11 * sizeof(char16_t));
|
||||
memcpy(contents + 0xD0, username.data(), usernameLength * sizeof(char16_t));
|
||||
|
||||
// setting language
|
||||
contents[0x8E] = Platform::GetConfigInt(Platform::Firm_Language);
|
||||
|
||||
// setting up color
|
||||
contents[0xCC] = Platform::GetConfigInt(Platform::Firm_Color);
|
||||
|
||||
// setting up birthday
|
||||
contents[0xCE] = Platform::GetConfigInt(Platform::Firm_BirthdayMonth);
|
||||
contents[0xCF] = Platform::GetConfigInt(Platform::Firm_BirthdayDay);
|
||||
|
||||
// setup message
|
||||
std::string orig_message = Platform::GetConfigString(Platform::Firm_Message);
|
||||
std::u16string message = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_message);
|
||||
size_t messageLength = std::min(message.length(), (size_t) 26);
|
||||
memset(contents + 0xE6, 0, 27 * sizeof(char16_t));
|
||||
memcpy(contents + 0xE6, message.data(), messageLength * sizeof(char16_t));
|
||||
|
||||
// TODO: make other items configurable?
|
||||
}
|
||||
|
||||
// fix touchscreen coords
|
||||
*(u16*)&contents[0xB8] = 0;
|
||||
*(u16*)&contents[0xBA] = 0;
|
||||
contents[0xBC] = 0;
|
||||
contents[0xBD] = 0;
|
||||
*(u16*)&contents[0xBE] = 255<<4;
|
||||
*(u16*)&contents[0xC0] = 191<<4;
|
||||
contents[0xC2] = 255;
|
||||
contents[0xC3] = 191;
|
||||
|
||||
SHA1_CTX sha;
|
||||
SHA1Init(&sha);
|
||||
SHA1Update(&sha, &contents[0x88], 0x128);
|
||||
SHA1Final(&contents[0], &sha);
|
||||
|
||||
f_lseek(&file, 0);
|
||||
f_write(&file, contents, 0x1B0, &nres);
|
||||
|
||||
f_close(&file);
|
||||
Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res);
|
||||
return false;
|
||||
}
|
||||
// TODO: If the file couldn't be opened, try creating a new one in its place
|
||||
// (after all, we have the data for that)
|
||||
|
||||
u32 bytes_written = 0;
|
||||
FRESULT res = f_write(&file, &data, sizeof(DSiFirmwareSystemSettings), &bytes_written);
|
||||
f_close(&file);
|
||||
|
||||
if (res != FR_OK || bytes_written != sizeof(DSiFirmwareSystemSettings))
|
||||
{
|
||||
Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NANDMount::ApplyUserData(const DSiFirmwareSystemSettings& data)
|
||||
{
|
||||
bool ok0 = SaveUserData("0:/shared1/TWLCFG0.dat", data);
|
||||
bool ok1 = SaveUserData("0:/shared1/TWLCFG1.dat", data);
|
||||
|
||||
return ok0 && ok1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -648,7 +608,7 @@ void debug_listfiles(const char* path)
|
|||
if (!info.fname[0]) break;
|
||||
|
||||
char fullname[512];
|
||||
sprintf(fullname, "%s/%s", path, info.fname);
|
||||
snprintf(fullname, sizeof(fullname), "%s/%s", path, info.fname);
|
||||
Log(LogLevel::Debug, "[%c] %s\n", (info.fattrib&AM_DIR)?'D':'F', fullname);
|
||||
|
||||
if (info.fattrib & AM_DIR)
|
||||
|
@ -660,7 +620,7 @@ void debug_listfiles(const char* path)
|
|||
f_closedir(&dir);
|
||||
}
|
||||
|
||||
bool ImportFile(const char* path, const u8* data, size_t len)
|
||||
bool NANDMount::ImportFile(const char* path, const u8* data, size_t len)
|
||||
{
|
||||
if (!data || !len || !path)
|
||||
return false;
|
||||
|
@ -694,24 +654,21 @@ bool ImportFile(const char* path, const u8* data, size_t len)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ImportFile(const char* path, const char* in)
|
||||
bool NANDMount::ImportFile(const char* path, const char* in)
|
||||
{
|
||||
FF_FIL file;
|
||||
FILE* fin;
|
||||
FRESULT res;
|
||||
|
||||
fin = fopen(in, "rb");
|
||||
Platform::FileHandle* fin = OpenLocalFile(in, FileMode::Read);
|
||||
if (!fin)
|
||||
return false;
|
||||
|
||||
fseek(fin, 0, SEEK_END);
|
||||
u32 len = (u32)ftell(fin);
|
||||
fseek(fin, 0, SEEK_SET);
|
||||
u32 len = FileLength(fin);
|
||||
|
||||
res = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
fclose(fin);
|
||||
CloseFile(fin);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -725,11 +682,11 @@ bool ImportFile(const char* path, const char* in)
|
|||
blocklen = sizeof(buf);
|
||||
|
||||
u32 nwrite;
|
||||
fread(buf, blocklen, 1, fin);
|
||||
FileRead(buf, blocklen, 1, fin);
|
||||
f_write(&file, buf, blocklen, &nwrite);
|
||||
}
|
||||
|
||||
fclose(fin);
|
||||
CloseFile(fin);
|
||||
f_close(&file);
|
||||
|
||||
Log(LogLevel::Debug, "Imported file from %s to %s\n", in, path);
|
||||
|
@ -737,10 +694,9 @@ bool ImportFile(const char* path, const char* in)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ExportFile(const char* path, const char* out)
|
||||
bool NANDMount::ExportFile(const char* path, const char* out)
|
||||
{
|
||||
FF_FIL file;
|
||||
FILE* fout;
|
||||
FRESULT res;
|
||||
|
||||
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
|
||||
|
@ -749,7 +705,7 @@ bool ExportFile(const char* path, const char* out)
|
|||
|
||||
u32 len = f_size(&file);
|
||||
|
||||
fout = fopen(out, "wb");
|
||||
Platform::FileHandle* fout = OpenLocalFile(out, FileMode::Write);
|
||||
if (!fout)
|
||||
{
|
||||
f_close(&file);
|
||||
|
@ -767,10 +723,10 @@ bool ExportFile(const char* path, const char* out)
|
|||
|
||||
u32 nread;
|
||||
f_read(&file, buf, blocklen, &nread);
|
||||
fwrite(buf, blocklen, 1, fout);
|
||||
FileWrite(buf, blocklen, 1, fout);
|
||||
}
|
||||
|
||||
fclose(fout);
|
||||
CloseFile(fout);
|
||||
f_close(&file);
|
||||
|
||||
Log(LogLevel::Debug, "Exported file from %s to %s\n", path, out);
|
||||
|
@ -778,7 +734,7 @@ bool ExportFile(const char* path, const char* out)
|
|||
return true;
|
||||
}
|
||||
|
||||
void RemoveFile(const char* path)
|
||||
void NANDMount::RemoveFile(const char* path)
|
||||
{
|
||||
FF_FILINFO info;
|
||||
FRESULT res = f_stat(path, &info);
|
||||
|
@ -791,7 +747,7 @@ void RemoveFile(const char* path)
|
|||
Log(LogLevel::Debug, "Removed file at %s\n", path);
|
||||
}
|
||||
|
||||
void RemoveDir(const char* path)
|
||||
void NANDMount::RemoveDir(const char* path)
|
||||
{
|
||||
FF_DIR dir;
|
||||
FF_FILINFO info;
|
||||
|
@ -816,7 +772,7 @@ void RemoveDir(const char* path)
|
|||
if (!info.fname[0]) break;
|
||||
|
||||
char fullname[512];
|
||||
sprintf(fullname, "%s/%s", path, info.fname);
|
||||
snprintf(fullname, sizeof(fullname), "%s/%s", path, info.fname);
|
||||
|
||||
if (info.fattrib & AM_RDO)
|
||||
f_chmod(path, 0, AM_RDO);
|
||||
|
@ -846,11 +802,11 @@ void RemoveDir(const char* path)
|
|||
}
|
||||
|
||||
|
||||
u32 GetTitleVersion(u32 category, u32 titleid)
|
||||
u32 NANDMount::GetTitleVersion(u32 category, u32 titleid)
|
||||
{
|
||||
FRESULT res;
|
||||
char path[256];
|
||||
sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid);
|
||||
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid);
|
||||
FF_FIL file;
|
||||
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
|
||||
if (res != FR_OK)
|
||||
|
@ -866,13 +822,13 @@ u32 GetTitleVersion(u32 category, u32 titleid)
|
|||
return version;
|
||||
}
|
||||
|
||||
void ListTitles(u32 category, std::vector<u32>& titlelist)
|
||||
void NANDMount::ListTitles(u32 category, std::vector<u32>& titlelist)
|
||||
{
|
||||
FRESULT res;
|
||||
FF_DIR titledir;
|
||||
char path[256];
|
||||
|
||||
sprintf(path, "0:/title/%08x", category);
|
||||
snprintf(path, sizeof(path), "0:/title/%08x", category);
|
||||
res = f_opendir(&titledir, path);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
|
@ -898,7 +854,7 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
|
|||
if (version == 0xFFFFFFFF)
|
||||
continue;
|
||||
|
||||
sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
|
||||
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
|
||||
FF_FILINFO appinfo;
|
||||
res = f_stat(path, &appinfo);
|
||||
if (res != FR_OK)
|
||||
|
@ -915,16 +871,16 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
|
|||
f_closedir(&titledir);
|
||||
}
|
||||
|
||||
bool TitleExists(u32 category, u32 titleid)
|
||||
bool NANDMount::TitleExists(u32 category, u32 titleid)
|
||||
{
|
||||
char path[256];
|
||||
sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid);
|
||||
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid);
|
||||
|
||||
FRESULT res = f_stat(path, nullptr);
|
||||
return (res == FR_OK);
|
||||
}
|
||||
|
||||
void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner)
|
||||
void NANDMount::GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner)
|
||||
{
|
||||
version = GetTitleVersion(category, titleid);
|
||||
if (version == 0xFFFFFFFF)
|
||||
|
@ -933,7 +889,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND
|
|||
FRESULT res;
|
||||
|
||||
char path[256];
|
||||
sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
|
||||
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
|
||||
FF_FIL file;
|
||||
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
|
||||
if (res != FR_OK)
|
||||
|
@ -960,7 +916,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND
|
|||
}
|
||||
|
||||
|
||||
bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version)
|
||||
bool NANDMount::CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version)
|
||||
{
|
||||
FF_FIL file;
|
||||
FRESULT res;
|
||||
|
@ -986,7 +942,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version)
|
|||
|
||||
memset(&ticket[0x222], 0xFF, 0x20);
|
||||
|
||||
ESEncrypt(ticket, 0x2A4);
|
||||
Image->ESEncrypt(ticket, 0x2A4);
|
||||
|
||||
f_write(&file, ticket, 0x2C4, &nwrite);
|
||||
|
||||
|
@ -995,7 +951,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CreateSaveFile(const char* path, u32 len)
|
||||
bool NANDMount::CreateSaveFile(const char* path, u32 len)
|
||||
{
|
||||
if (len == 0) return true;
|
||||
if (len < 0x200) return false;
|
||||
|
@ -1085,7 +1041,7 @@ bool CreateSaveFile(const char* path, u32 len)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly)
|
||||
bool NANDMount::InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly)
|
||||
{
|
||||
u32 titleid0 = tmd.GetCategory();
|
||||
u32 titleid1 = tmd.GetID();
|
||||
|
@ -1098,10 +1054,10 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat
|
|||
u32 nwrite;
|
||||
|
||||
// ticket
|
||||
sprintf(fname, "0:/ticket/%08x", titleid0);
|
||||
snprintf(fname, sizeof(fname), "0:/ticket/%08x", titleid0);
|
||||
f_mkdir(fname);
|
||||
|
||||
sprintf(fname, "0:/ticket/%08x/%08x.tik", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/ticket/%08x/%08x.tik", titleid0, titleid1);
|
||||
if (!CreateTicket(fname, tmd.GetCategoryNoByteswap(), tmd.GetIDNoByteswap(), header.ROMVersion))
|
||||
return false;
|
||||
|
||||
|
@ -1109,29 +1065,29 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat
|
|||
|
||||
// folder
|
||||
|
||||
sprintf(fname, "0:/title/%08x", titleid0);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x", titleid0);
|
||||
f_mkdir(fname);
|
||||
sprintf(fname, "0:/title/%08x/%08x", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x", titleid0, titleid1);
|
||||
f_mkdir(fname);
|
||||
sprintf(fname, "0:/title/%08x/%08x/content", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content", titleid0, titleid1);
|
||||
f_mkdir(fname);
|
||||
sprintf(fname, "0:/title/%08x/%08x/data", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data", titleid0, titleid1);
|
||||
f_mkdir(fname);
|
||||
|
||||
// data
|
||||
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1);
|
||||
if (!CreateSaveFile(fname, header.DSiPublicSavSize))
|
||||
return false;
|
||||
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1);
|
||||
if (!CreateSaveFile(fname, header.DSiPrivateSavSize))
|
||||
return false;
|
||||
|
||||
if (header.AppFlags & 0x04)
|
||||
{
|
||||
// custom banner file
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1);
|
||||
res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
|
@ -1148,7 +1104,7 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat
|
|||
|
||||
// TMD
|
||||
|
||||
sprintf(fname, "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1);
|
||||
res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
|
@ -1165,14 +1121,14 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly)
|
||||
bool NANDMount::ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly)
|
||||
{
|
||||
NDSHeader header {};
|
||||
{
|
||||
FILE* f = fopen(appfile, "rb");
|
||||
Platform::FileHandle* f = OpenLocalFile(appfile, FileMode::Read);
|
||||
if (!f) return false;
|
||||
fread(&header, sizeof(header), 1, f);
|
||||
fclose(f);
|
||||
FileRead(&header, sizeof(header), 1, f);
|
||||
CloseFile(f);
|
||||
}
|
||||
|
||||
u32 version = tmd.Contents.GetVersion();
|
||||
|
@ -1191,7 +1147,7 @@ bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool re
|
|||
// executable
|
||||
|
||||
char fname[128];
|
||||
sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
|
||||
if (!ImportFile(fname, appfile))
|
||||
{
|
||||
Log(LogLevel::Error, "ImportTitle: failed to create executable\n");
|
||||
|
@ -1203,7 +1159,7 @@ bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool re
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly)
|
||||
bool NANDMount::ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly)
|
||||
{
|
||||
if (!app || appLength < sizeof(NDSHeader))
|
||||
return false;
|
||||
|
@ -1227,7 +1183,7 @@ bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata&
|
|||
// executable
|
||||
|
||||
char fname[128];
|
||||
sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
|
||||
if (!ImportFile(fname, app, appLength))
|
||||
{
|
||||
Log(LogLevel::Error, "ImportTitle: failed to create executable\n");
|
||||
|
@ -1239,18 +1195,18 @@ bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata&
|
|||
return true;
|
||||
}
|
||||
|
||||
void DeleteTitle(u32 category, u32 titleid)
|
||||
void NANDMount::DeleteTitle(u32 category, u32 titleid)
|
||||
{
|
||||
char fname[128];
|
||||
|
||||
sprintf(fname, "0:/ticket/%08x/%08x.tik", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/ticket/%08x/%08x.tik", category, titleid);
|
||||
RemoveFile(fname);
|
||||
|
||||
sprintf(fname, "0:/title/%08x/%08x", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x", category, titleid);
|
||||
RemoveDir(fname);
|
||||
}
|
||||
|
||||
u32 GetTitleDataMask(u32 category, u32 titleid)
|
||||
u32 NANDMount::GetTitleDataMask(u32 category, u32 titleid)
|
||||
{
|
||||
u32 version;
|
||||
NDSHeader header;
|
||||
|
@ -1267,22 +1223,22 @@ u32 GetTitleDataMask(u32 category, u32 titleid)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool ImportTitleData(u32 category, u32 titleid, int type, const char* file)
|
||||
bool NANDMount::ImportTitleData(u32 category, u32 titleid, int type, const char* file)
|
||||
{
|
||||
char fname[128];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TitleData_PublicSav:
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", category, titleid);
|
||||
break;
|
||||
|
||||
case TitleData_PrivateSav:
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", category, titleid);
|
||||
break;
|
||||
|
||||
case TitleData_BannerSav:
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", category, titleid);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1293,22 +1249,22 @@ bool ImportTitleData(u32 category, u32 titleid, int type, const char* file)
|
|||
return ImportFile(fname, file);
|
||||
}
|
||||
|
||||
bool ExportTitleData(u32 category, u32 titleid, int type, const char* file)
|
||||
bool NANDMount::ExportTitleData(u32 category, u32 titleid, int type, const char* file)
|
||||
{
|
||||
char fname[128];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TitleData_PublicSav:
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", category, titleid);
|
||||
break;
|
||||
|
||||
case TitleData_PrivateSav:
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", category, titleid);
|
||||
break;
|
||||
|
||||
case TitleData_BannerSav:
|
||||
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", category, titleid);
|
||||
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", category, titleid);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1318,4 +1274,12 @@ bool ExportTitleData(u32 category, u32 titleid, int type, const char* file)
|
|||
return ExportFile(fname, file);
|
||||
}
|
||||
|
||||
void DSiFirmwareSystemSettings::UpdateHash()
|
||||
{
|
||||
SHA1_CTX sha;
|
||||
SHA1Init(&sha);
|
||||
SHA1Update(&sha, &Bytes[0x88], 0x128);
|
||||
SHA1Final(Hash.data(), &sha);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
242
src/DSi_NAND.h
242
src/DSi_NAND.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,12 +20,18 @@
|
|||
#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>
|
||||
|
||||
namespace DSi_NAND
|
||||
struct AES_ctx;
|
||||
|
||||
namespace melonDS::DSi_NAND
|
||||
{
|
||||
|
||||
enum
|
||||
|
@ -35,28 +41,228 @@ enum
|
|||
TitleData_BannerSav,
|
||||
};
|
||||
|
||||
bool Init(u8* es_keyY);
|
||||
void DeInit();
|
||||
union DSiFirmwareSystemSettings;
|
||||
union DSiSerialData;
|
||||
using DSiHardwareInfoN = std::array<u8, 0x9C>;
|
||||
using DSiKey = std::array<u8, 16>;
|
||||
|
||||
Platform::FileHandle* GetFile();
|
||||
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;
|
||||
|
||||
void GetIDs(u8* emmc_cid, u64& consoleid);
|
||||
NANDImage(NANDImage&& other) noexcept;
|
||||
NANDImage& operator=(NANDImage&& other) noexcept;
|
||||
|
||||
void ReadHardwareInfo(u8* dataS, u8* dataN);
|
||||
Platform::FileHandle* GetFile() { return CurFile; }
|
||||
|
||||
void ReadUserData(u8* data);
|
||||
void PatchUserData();
|
||||
[[nodiscard]] const DSiKey& GetEMMCID() const noexcept { return eMMC_CID; }
|
||||
[[nodiscard]] u64 GetConsoleID() const noexcept { return ConsoleID; }
|
||||
[[nodiscard]] u64 GetLength() const noexcept { return 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, 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);
|
||||
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;
|
||||
};
|
||||
|
||||
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);
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -24,10 +24,12 @@
|
|||
#include "DSi_AES.h"
|
||||
#include "GPU3D.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
DSi_NDMA::DSi_NDMA(u32 cpu, u32 num)
|
||||
DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi) : DSi(dsi), CPU(cpu), Num(num)
|
||||
{
|
||||
CPU = cpu;
|
||||
Num = num;
|
||||
|
@ -126,7 +128,7 @@ void DSi_NDMA::WriteCnt(u32 val)
|
|||
if ((StartMode & 0x1F) == 0x10)
|
||||
Start();
|
||||
else if (StartMode == 0x0A)
|
||||
GPU3D::CheckFIFODMA();
|
||||
DSi.GPU.GPU3D.CheckFIFODMA();
|
||||
|
||||
// TODO: unsupported start modes:
|
||||
// * timers (00-03)
|
||||
|
@ -180,13 +182,13 @@ void DSi_NDMA::Start()
|
|||
//if (SubblockTimer & 0xFFFF)
|
||||
// printf("TODO! NDMA SUBBLOCK TIMER: %08X\n", SubblockTimer);
|
||||
|
||||
if (NDS::DMAsRunning(CPU))
|
||||
if (DSi.DMAsRunning(CPU))
|
||||
Running = 1;
|
||||
else
|
||||
Running = 2;
|
||||
|
||||
InProgress = true;
|
||||
NDS::StopCPU(CPU, 1<<(Num+4));
|
||||
DSi.StopCPU(CPU, 1<<(Num+4));
|
||||
}
|
||||
|
||||
void DSi_NDMA::Run()
|
||||
|
@ -198,7 +200,7 @@ void DSi_NDMA::Run()
|
|||
|
||||
void DSi_NDMA::Run9()
|
||||
{
|
||||
if (NDS::ARM9Timestamp >= NDS::ARM9Target) return;
|
||||
if (DSi.ARM9Timestamp >= DSi.ARM9Target) return;
|
||||
|
||||
Executing = true;
|
||||
|
||||
|
@ -213,11 +215,11 @@ void DSi_NDMA::Run9()
|
|||
|
||||
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
|
||||
{
|
||||
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2];
|
||||
unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][2] + DSi.ARM9MemTimings[CurDstAddr >> 14][2];
|
||||
}
|
||||
else
|
||||
{
|
||||
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3];
|
||||
unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][3] + DSi.ARM9MemTimings[CurDstAddr >> 14][3];
|
||||
if ((CurSrcAddr >> 24) == (CurDstAddr >> 24))
|
||||
unitcycles++;
|
||||
else if ((CurSrcAddr >> 24) == 0x02)
|
||||
|
@ -233,12 +235,12 @@ void DSi_NDMA::Run9()
|
|||
|
||||
while (IterCount > 0 && !Stall)
|
||||
{
|
||||
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
|
||||
DSi.ARM9Timestamp += (unitcycles << DSi.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;
|
||||
|
@ -246,7 +248,7 @@ void DSi_NDMA::Run9()
|
|||
RemCount--;
|
||||
TotalRemCount--;
|
||||
|
||||
if (NDS::ARM9Timestamp >= NDS::ARM9Target) break;
|
||||
if (DSi.ARM9Timestamp >= DSi.ARM9Target) break;
|
||||
}
|
||||
|
||||
Executing = false;
|
||||
|
@ -257,10 +259,10 @@ void DSi_NDMA::Run9()
|
|||
if (IterCount == 0)
|
||||
{
|
||||
Running = 0;
|
||||
NDS::ResumeCPU(0, 1<<(Num+4));
|
||||
DSi.ResumeCPU(0, 1<<(Num+4));
|
||||
|
||||
if (StartMode == 0x0A)
|
||||
GPU3D::CheckFIFODMA();
|
||||
DSi.GPU.GPU3D.CheckFIFODMA();
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -269,25 +271,25 @@ void DSi_NDMA::Run9()
|
|||
if ((StartMode & 0x1F) == 0x10) // CHECKME
|
||||
{
|
||||
Cnt &= ~(1<<31);
|
||||
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
else if (!(Cnt & (1<<29)))
|
||||
{
|
||||
if (TotalRemCount == 0)
|
||||
{
|
||||
Cnt &= ~(1<<31);
|
||||
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
}
|
||||
|
||||
Running = 0;
|
||||
InProgress = false;
|
||||
NDS::ResumeCPU(0, 1<<(Num+4));
|
||||
DSi.ResumeCPU(0, 1<<(Num+4));
|
||||
}
|
||||
|
||||
void DSi_NDMA::Run7()
|
||||
{
|
||||
if (NDS::ARM7Timestamp >= NDS::ARM7Target) return;
|
||||
if (DSi.ARM7Timestamp >= DSi.ARM7Target) return;
|
||||
|
||||
Executing = true;
|
||||
|
||||
|
@ -302,11 +304,11 @@ void DSi_NDMA::Run7()
|
|||
|
||||
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
|
||||
{
|
||||
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2];
|
||||
unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][2] + DSi.ARM7MemTimings[CurDstAddr >> 15][2];
|
||||
}
|
||||
else
|
||||
{
|
||||
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3];
|
||||
unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][3] + DSi.ARM7MemTimings[CurDstAddr >> 15][3];
|
||||
if ((CurSrcAddr >> 23) == (CurDstAddr >> 23))
|
||||
unitcycles++;
|
||||
else if ((CurSrcAddr >> 24) == 0x02)
|
||||
|
@ -322,12 +324,12 @@ void DSi_NDMA::Run7()
|
|||
|
||||
while (IterCount > 0 && !Stall)
|
||||
{
|
||||
NDS::ARM7Timestamp += unitcycles;
|
||||
DSi.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;
|
||||
|
@ -335,7 +337,7 @@ void DSi_NDMA::Run7()
|
|||
RemCount--;
|
||||
TotalRemCount--;
|
||||
|
||||
if (NDS::ARM7Timestamp >= NDS::ARM7Target) break;
|
||||
if (DSi.ARM7Timestamp >= DSi.ARM7Target) break;
|
||||
}
|
||||
|
||||
Executing = false;
|
||||
|
@ -346,10 +348,10 @@ void DSi_NDMA::Run7()
|
|||
if (IterCount == 0)
|
||||
{
|
||||
Running = 0;
|
||||
NDS::ResumeCPU(1, 1<<(Num+4));
|
||||
DSi.ResumeCPU(1, 1<<(Num+4));
|
||||
|
||||
DSi_AES::CheckInputDMA();
|
||||
DSi_AES::CheckOutputDMA();
|
||||
DSi.AES.CheckInputDMA();
|
||||
DSi.AES.CheckOutputDMA();
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -358,21 +360,23 @@ void DSi_NDMA::Run7()
|
|||
if ((StartMode & 0x1F) == 0x10) // CHECKME
|
||||
{
|
||||
Cnt &= ~(1<<31);
|
||||
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
else if (!(Cnt & (1<<29)))
|
||||
{
|
||||
if (TotalRemCount == 0)
|
||||
{
|
||||
Cnt &= ~(1<<31);
|
||||
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
|
||||
if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num);
|
||||
}
|
||||
}
|
||||
|
||||
Running = 0;
|
||||
InProgress = false;
|
||||
NDS::ResumeCPU(1, 1<<(Num+4));
|
||||
DSi.ResumeCPU(1, 1<<(Num+4));
|
||||
|
||||
DSi_AES::CheckInputDMA();
|
||||
DSi_AES::CheckOutputDMA();
|
||||
DSi.AES.CheckInputDMA();
|
||||
DSi.AES.CheckOutputDMA();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,10 +22,14 @@
|
|||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class DSi;
|
||||
|
||||
class DSi_NDMA
|
||||
{
|
||||
public:
|
||||
DSi_NDMA(u32 cpu, u32 num);
|
||||
DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi);
|
||||
~DSi_NDMA();
|
||||
|
||||
void Reset();
|
||||
|
@ -40,12 +44,12 @@ public:
|
|||
void Run9();
|
||||
void Run7();
|
||||
|
||||
bool IsInMode(u32 mode)
|
||||
bool IsInMode(u32 mode) const
|
||||
{
|
||||
return ((mode == StartMode) && (Cnt & 0x80000000));
|
||||
}
|
||||
|
||||
bool IsRunning() { return Running!=0; }
|
||||
bool IsRunning() const { return Running!=0; }
|
||||
|
||||
void StartIfNeeded(u32 mode)
|
||||
{
|
||||
|
@ -73,6 +77,7 @@ public:
|
|||
u32 Cnt;
|
||||
|
||||
private:
|
||||
melonDS::DSi& DSi;
|
||||
u32 CPU, Num;
|
||||
|
||||
u32 StartMode;
|
||||
|
@ -94,4 +99,5 @@ private:
|
|||
bool IsGXFIFODMA;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // DSI_NDMA_H
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -24,6 +24,9 @@
|
|||
#include "WifiAP.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
|
@ -116,38 +119,36 @@ const u8 CIS1[256] =
|
|||
};
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
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.RegisterEventFunc(Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer));
|
||||
|
||||
// this seems to control whether the firmware upload is done
|
||||
EEPROMReady = 0;
|
||||
|
||||
Ctx = this;
|
||||
}
|
||||
|
||||
DSi_NWifi::~DSi_NWifi()
|
||||
{
|
||||
NDS::CancelEvent(NDS::Event_DSi_NWifi);
|
||||
Ctx = nullptr;
|
||||
DSi.CancelEvent(Event_DSi_NWifi);
|
||||
|
||||
DSi.UnregisterEventFunc(Event_DSi_NWifi, 0);
|
||||
}
|
||||
|
||||
void DSi_NWifi::Reset()
|
||||
{
|
||||
using namespace SPI_Firmware;
|
||||
TransferCmd = 0xFFFFFFFF;
|
||||
RemSize = 0;
|
||||
|
||||
|
@ -164,26 +165,28 @@ void DSi_NWifi::Reset()
|
|||
for (int i = 0; i < 9; i++)
|
||||
Mailbox[i].Clear();
|
||||
|
||||
MacAddress mac = GetFirmware()->Header().MacAddress;
|
||||
const Firmware& fw = DSi.SPI.GetFirmware();
|
||||
|
||||
MacAddress mac = fw.GetHeader().MacAddr;
|
||||
Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
WifiBoard type = GetFirmware()->Header().WifiBoard;
|
||||
Firmware::WifiBoard type = fw.GetHeader().WifiBoard;
|
||||
switch (type)
|
||||
{
|
||||
case WifiBoard::W015: // AR6002
|
||||
case Firmware::WifiBoard::W015: // AR6002
|
||||
ROMID = 0x20000188;
|
||||
ChipID = 0x02000001;
|
||||
HostIntAddr = 0x00500400;
|
||||
break;
|
||||
|
||||
case WifiBoard::W024: // AR6013
|
||||
case Firmware::WifiBoard::W024: // AR6013
|
||||
ROMID = 0x23000024;
|
||||
ChipID = 0x0D000000;
|
||||
HostIntAddr = 0x00520000;
|
||||
break;
|
||||
|
||||
case WifiBoard::W028: // AR6014 (3DS)
|
||||
case Firmware::WifiBoard::W028: // AR6014 (3DS)
|
||||
ROMID = 0x2300006F;
|
||||
ChipID = 0x0D000001;
|
||||
HostIntAddr = 0x00520000;
|
||||
|
@ -224,7 +227,7 @@ void DSi_NWifi::Reset()
|
|||
BeaconTimer = 0x10A2220ULL;
|
||||
ConnectionStatus = 0;
|
||||
|
||||
NDS::CancelEvent(NDS::Event_DSi_NWifi);
|
||||
DSi.CancelEvent(Event_DSi_NWifi);
|
||||
}
|
||||
|
||||
void DSi_NWifi::DoSavestate(Savestate* file)
|
||||
|
@ -895,9 +898,8 @@ void DSi_NWifi::HTC_Command()
|
|||
|
||||
case 0x0004: // setup complete
|
||||
{
|
||||
SPI_Firmware::MacAddress mac = SPI_Firmware::GetFirmware()->Header().MacAddress;
|
||||
u8 ready_evt[12];
|
||||
memcpy(&ready_evt[0], &mac, mac.size());
|
||||
memcpy(&ready_evt[0], &EEPROM[0xA], 6); // MAC address
|
||||
ready_evt[6] = 0x02;
|
||||
ready_evt[7] = 0;
|
||||
*(u32*)&ready_evt[8] = 0x2300006C;
|
||||
|
@ -908,7 +910,7 @@ void DSi_NWifi::HTC_Command()
|
|||
SendWMIEvent(1, 0x1006, regdomain_evt, 4);
|
||||
|
||||
BootPhase = 2;
|
||||
NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, MSTimer, 0);
|
||||
DSi.ScheduleEvent(Event_DSi_NWifi, false, 33611, 0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1549,7 +1551,26 @@ void DSi_NWifi::WindowWrite(u32 addr, u32 val)
|
|||
}
|
||||
|
||||
|
||||
void DSi_NWifi::_MSTimer()
|
||||
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)
|
||||
{
|
||||
BeaconTimer++;
|
||||
|
||||
|
@ -1587,29 +1608,8 @@ void DSi_NWifi::_MSTimer()
|
|||
//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);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -23,10 +23,12 @@
|
|||
#include "FIFO.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class DSi_NWifi : public DSi_SDDevice
|
||||
{
|
||||
public:
|
||||
DSi_NWifi(DSi_SDHost* host);
|
||||
DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host);
|
||||
~DSi_NWifi();
|
||||
|
||||
void Reset();
|
||||
|
@ -40,11 +42,10 @@ public:
|
|||
|
||||
void SetIRQ_F1_Counter(u32 n);
|
||||
|
||||
void _MSTimer();
|
||||
|
||||
static void MSTimer(u32 param);
|
||||
void MSTimer(u32 param);
|
||||
|
||||
private:
|
||||
melonDS::DSi& DSi;
|
||||
u32 TransferCmd;
|
||||
u32 TransferAddr;
|
||||
u32 RemSize;
|
||||
|
@ -148,4 +149,5 @@ private:
|
|||
u8 LANBuffer[2048];
|
||||
};
|
||||
|
||||
}
|
||||
#endif // DSI_NWIFI_H
|
||||
|
|
291
src/DSi_SD.cpp
291
src/DSi_SD.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -18,11 +18,19 @@
|
|||
|
||||
#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
|
||||
|
@ -47,27 +55,45 @@ using namespace Platform;
|
|||
|
||||
#define SD_DESC Num?"SDIO":"SD/MMC"
|
||||
|
||||
|
||||
DSi_SDHost::DSi_SDHost(u32 num)
|
||||
enum
|
||||
{
|
||||
Num = num;
|
||||
Transfer_TX = 0,
|
||||
Transfer_RX,
|
||||
};
|
||||
|
||||
Ports[0] = nullptr;
|
||||
|
||||
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
|
||||
{
|
||||
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
|
||||
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
|
||||
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
|
||||
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
|
||||
|
||||
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.RegisterEventFunc(Event_DSi_SDIOTransfer ,
|
||||
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
|
||||
DSi.RegisterEventFunc(Event_DSi_SDIOTransfer,
|
||||
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
|
||||
|
||||
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
|
||||
Ports[1] = nullptr;
|
||||
}
|
||||
|
||||
DSi_SDHost::~DSi_SDHost()
|
||||
{
|
||||
if (Ports[0]) delete Ports[0];
|
||||
if (Ports[1]) delete Ports[1];
|
||||
}
|
||||
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
||||
Transfer_TX);
|
||||
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
||||
Transfer_RX);
|
||||
|
||||
void DSi_SDHost::CloseHandles()
|
||||
{
|
||||
if (Ports[0]) delete Ports[0];
|
||||
if (Ports[1]) delete Ports[1];
|
||||
Ports[0] = nullptr;
|
||||
Ports[1] = nullptr;
|
||||
// unique_ptr's destructor will clean up the ports
|
||||
}
|
||||
|
||||
void DSi_SDHost::Reset()
|
||||
|
@ -110,51 +136,70 @@ void DSi_SDHost::Reset()
|
|||
|
||||
TXReq = false;
|
||||
|
||||
CloseHandles();
|
||||
if (Ports[0]) Ports[0]->Reset();
|
||||
if (Ports[1]) Ports[1]->Reset();
|
||||
}
|
||||
|
||||
if (Num == 0)
|
||||
FATStorage* DSi_SDHost::GetSDCard() noexcept
|
||||
{
|
||||
if (Num != 0) return nullptr;
|
||||
return static_cast<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)
|
||||
{
|
||||
DSi_MMCStorage* sd;
|
||||
DSi_MMCStorage* mmc;
|
||||
|
||||
if (Platform::GetConfigBool(Platform::DSiSD_Enable))
|
||||
if (!Ports[0])
|
||||
{
|
||||
std::string folderpath;
|
||||
if (Platform::GetConfigBool(Platform::DSiSD_FolderSync))
|
||||
folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath);
|
||||
else
|
||||
folderpath = "";
|
||||
|
||||
sd = new DSi_MMCStorage(this,
|
||||
false,
|
||||
Platform::GetConfigString(Platform::DSiSD_ImagePath),
|
||||
(u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024,
|
||||
Platform::GetConfigBool(Platform::DSiSD_ReadOnly),
|
||||
folderpath);
|
||||
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
|
||||
sd->SetCID(sd_cid);
|
||||
Ports[0] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard));
|
||||
}
|
||||
else
|
||||
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;
|
||||
{
|
||||
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(*sdcard));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DSi_NWifi* nwifi = new DSi_NWifi(this);
|
||||
|
||||
Ports[0] = nwifi;
|
||||
Ports[0] = nullptr;
|
||||
}
|
||||
|
||||
if (Ports[0]) Ports[0]->Reset();
|
||||
if (Ports[1]) Ports[1]->Reset();
|
||||
sdcard = std::nullopt;
|
||||
// a moved-from optional isn't empty, it contains a moved-from object
|
||||
}
|
||||
|
||||
void DSi_SDHost::SetNAND(DSi_NAND::NANDImage&& nand) noexcept
|
||||
{
|
||||
if (Num != 0) return;
|
||||
|
||||
static_cast<DSi_MMCStorage*>(Ports[1].get())->SetNAND(std::move(nand));
|
||||
}
|
||||
|
||||
void DSi_SDHost::DoSavestate(Savestate* file)
|
||||
|
@ -212,7 +257,7 @@ void DSi_SDHost::UpdateData32IRQ()
|
|||
newflags &= (Data32IRQ >> 11);
|
||||
|
||||
if ((oldflags == 0) && (newflags != 0))
|
||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
|
||||
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
|
||||
}
|
||||
|
||||
void DSi_SDHost::ClearIRQ(u32 irq)
|
||||
|
@ -228,7 +273,7 @@ void DSi_SDHost::SetIRQ(u32 irq)
|
|||
u32 newflags = IRQStatus & ~IRQMask;
|
||||
|
||||
if ((oldflags == 0) && (newflags != 0))
|
||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
|
||||
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
|
||||
}
|
||||
|
||||
void DSi_SDHost::UpdateIRQ(u32 oldmask)
|
||||
|
@ -237,7 +282,7 @@ void DSi_SDHost::UpdateIRQ(u32 oldmask)
|
|||
u32 newflags = IRQStatus & ~IRQMask;
|
||||
|
||||
if ((oldflags == 0) && (newflags != 0))
|
||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
|
||||
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
|
||||
}
|
||||
|
||||
void DSi_SDHost::SetCardIRQ()
|
||||
|
@ -245,7 +290,7 @@ void DSi_SDHost::SetCardIRQ()
|
|||
if (!(CardIRQCtl & (1<<0))) return;
|
||||
|
||||
u16 oldflags = CardIRQStatus & ~CardIRQMask;
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
|
||||
|
||||
if (dev->IRQ) CardIRQStatus |= (1<<0);
|
||||
else CardIRQStatus &= ~(1<<0);
|
||||
|
@ -254,8 +299,8 @@ void DSi_SDHost::SetCardIRQ()
|
|||
|
||||
if ((oldflags == 0) && (newflags != 0)) // checkme
|
||||
{
|
||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
|
||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1);
|
||||
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
|
||||
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,8 +311,8 @@ void DSi_SDHost::UpdateCardIRQ(u16 oldmask)
|
|||
|
||||
if ((oldflags == 0) && (newflags != 0)) // checkme
|
||||
{
|
||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
|
||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1);
|
||||
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC);
|
||||
DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,17 +328,15 @@ void DSi_SDHost::SendResponse(u32 val, bool last)
|
|||
|
||||
void DSi_SDHost::FinishRX(u32 param)
|
||||
{
|
||||
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
||||
CheckSwapFIFO();
|
||||
|
||||
host->CheckSwapFIFO();
|
||||
|
||||
if (host->DataMode == 1)
|
||||
host->UpdateFIFO32();
|
||||
if (DataMode == 1)
|
||||
UpdateFIFO32();
|
||||
else
|
||||
host->SetIRQ(24);
|
||||
SetIRQ(24);
|
||||
}
|
||||
|
||||
u32 DSi_SDHost::DataRX(u8* data, u32 len)
|
||||
u32 DSi_SDHost::DataRX(const u8* data, u32 len)
|
||||
{
|
||||
if (len != BlockLen16) { Log(LogLevel::Warn, "!! BAD BLOCKLEN\n"); len = BlockLen16; }
|
||||
|
||||
|
@ -310,21 +353,19 @@ u32 DSi_SDHost::DataRX(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
|
||||
u32 param = Num | (last << 1);
|
||||
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
||||
false, 512, FinishRX, param);
|
||||
DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
||||
false, 512, Transfer_RX, 0);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void DSi_SDHost::FinishTX(u32 param)
|
||||
{
|
||||
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
||||
DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1];
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
|
||||
|
||||
if (host->BlockCountInternal == 0)
|
||||
if (BlockCountInternal == 0)
|
||||
{
|
||||
if (host->StopAction & (1<<8))
|
||||
if (StopAction & (1<<8))
|
||||
{
|
||||
if (dev) dev->SendCMD(12, 0);
|
||||
}
|
||||
|
@ -332,8 +373,8 @@ void DSi_SDHost::FinishTX(u32 param)
|
|||
// CHECKME: presumably IRQ2 should not trigger here, but rather
|
||||
// when the data transfer is done
|
||||
//SetIRQ(0);
|
||||
host->SetIRQ(2);
|
||||
host->TXReq = false;
|
||||
SetIRQ(2);
|
||||
TXReq = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -354,7 +395,7 @@ 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;
|
||||
}
|
||||
|
@ -393,13 +434,13 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len)
|
|||
CurFIFO ^= 1;
|
||||
BlockCountInternal--;
|
||||
|
||||
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
||||
false, 512, FinishTX, Num);
|
||||
DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
||||
false, 512, Transfer_TX, 0);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
u32 DSi_SDHost::GetTransferrableLen(u32 len)
|
||||
u32 DSi_SDHost::GetTransferrableLen(u32 len) const
|
||||
{
|
||||
if (len > BlockLen16) len = BlockLen16; // checkme
|
||||
return len;
|
||||
|
@ -407,7 +448,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len)
|
|||
|
||||
void DSi_SDHost::CheckRX()
|
||||
{
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
|
||||
|
||||
CheckSwapFIFO();
|
||||
|
||||
|
@ -447,7 +488,7 @@ void DSi_SDHost::CheckTX()
|
|||
return;
|
||||
}
|
||||
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
|
||||
if (dev) dev->ContinueTransfer();
|
||||
}
|
||||
|
||||
|
@ -524,7 +565,7 @@ u16 DSi_SDHost::Read(u32 addr)
|
|||
case 0x10A: return 0;
|
||||
}
|
||||
|
||||
Log(LogLevel::Warn, "unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1));
|
||||
Log(LogLevel::Warn, "unknown %s read %08X @ %08X\n", SD_DESC, addr, DSi.GetPC(1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -538,7 +579,6 @@ u16 DSi_SDHost::ReadFIFO16()
|
|||
return 0;
|
||||
}
|
||||
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||
u16 ret = DataFIFO[f].Read();
|
||||
|
||||
if (DataFIFO[f].IsEmpty())
|
||||
|
@ -559,7 +599,6 @@ u32 DSi_SDHost::ReadFIFO32()
|
|||
return 0;
|
||||
}
|
||||
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||
u32 ret = DataFIFO32.Read();
|
||||
|
||||
if (DataFIFO32.IsEmpty())
|
||||
|
@ -581,7 +620,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
|
|||
Command = val;
|
||||
u8 cmd = Command & 0x3F;
|
||||
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
|
||||
if (dev)
|
||||
{
|
||||
// CHECKME
|
||||
|
@ -695,7 +734,6 @@ void DSi_SDHost::Write(u32 addr, u16 val)
|
|||
|
||||
void DSi_SDHost::WriteFIFO16(u16 val)
|
||||
{
|
||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||
u32 f = CurFIFO;
|
||||
if (DataFIFO[f].IsFull())
|
||||
{
|
||||
|
@ -749,7 +787,7 @@ void DSi_SDHost::UpdateFIFO32()
|
|||
|
||||
if ((DataFIFO32.Level() << 2) >= BlockLen32)
|
||||
{
|
||||
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
|
||||
DSi.CheckNDMAs(1, Num ? 0x29 : 0x28);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -768,41 +806,23 @@ void DSi_SDHost::CheckSwapFIFO()
|
|||
|
||||
#define MMC_DESC (Internal?"NAND":"SDcard")
|
||||
|
||||
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename)
|
||||
: DSi_SDDevice(host)
|
||||
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept
|
||||
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand))
|
||||
{
|
||||
Internal = internal;
|
||||
File = Platform::OpenLocalFile(filename, FileMode::ReadWriteExisting);
|
||||
|
||||
SD = nullptr;
|
||||
|
||||
ReadOnly = false;
|
||||
SetCID(get<DSi_NAND::NANDImage>(Storage).GetEMMCID().data());
|
||||
}
|
||||
|
||||
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir)
|
||||
: DSi_SDDevice(host)
|
||||
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept
|
||||
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard))
|
||||
{
|
||||
Internal = internal;
|
||||
File = nullptr;
|
||||
|
||||
SD = new FATStorage(filename, size, readonly, sourcedir);
|
||||
SD->Open();
|
||||
|
||||
ReadOnly = readonly;
|
||||
ReadOnly = get<FATStorage>(Storage).IsReadOnly();
|
||||
SetCID(DSiSDCardCID);
|
||||
}
|
||||
|
||||
DSi_MMCStorage::~DSi_MMCStorage()
|
||||
{
|
||||
if (SD)
|
||||
{
|
||||
SD->Close();
|
||||
delete SD;
|
||||
}
|
||||
if (File)
|
||||
{
|
||||
CloseFile(File);
|
||||
}
|
||||
}
|
||||
// The FATStorage or NANDImage is owned by this object;
|
||||
// std::variant's destructor will clean it up.
|
||||
DSi_MMCStorage::~DSi_MMCStorage() = default;
|
||||
|
||||
void DSi_MMCStorage::Reset()
|
||||
{
|
||||
|
@ -831,7 +851,7 @@ void DSi_MMCStorage::Reset()
|
|||
|
||||
void DSi_MMCStorage::DoSavestate(Savestate* file)
|
||||
{
|
||||
file->Section(Internal ? "NAND" : "SDCR");
|
||||
file->Section(holds_alternative<DSi_NAND::NANDImage>(Storage) ? "NAND" : "SDCR");
|
||||
|
||||
file->VarArray(CID, 16);
|
||||
file->VarArray(CSD, 16);
|
||||
|
@ -866,7 +886,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
|||
case 1: // SEND_OP_COND
|
||||
// CHECKME!!
|
||||
// also TODO: it's different for the SD card
|
||||
if (Internal)
|
||||
if (std::holds_alternative<DSi_NAND::NANDImage>(Storage))
|
||||
{
|
||||
param &= ~(1<<30);
|
||||
OCR &= 0xBF000000;
|
||||
|
@ -890,7 +910,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
|||
return;
|
||||
|
||||
case 3: // get/set RCA
|
||||
if (Internal)
|
||||
if (holds_alternative<DSi_NAND::NANDImage>(Storage))
|
||||
{
|
||||
RCA = param >> 16;
|
||||
Host->SendResponse(CSR|0x10000, true); // huh??
|
||||
|
@ -925,7 +945,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
|||
|
||||
case 12: // stop operation
|
||||
SetState(0x04);
|
||||
if (File) FileFlush(File);
|
||||
if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
|
||||
FileFlush(nand->GetFile());
|
||||
RWCommand = 0;
|
||||
Host->SendResponse(CSR, true);
|
||||
return;
|
||||
|
@ -946,6 +967,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
|||
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;
|
||||
|
@ -954,12 +976,14 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
|||
RWAddress <<= 9;
|
||||
BlockSize = 512;
|
||||
}
|
||||
RWCommand = 18;
|
||||
if (cmd == 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;
|
||||
|
@ -968,7 +992,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
|||
RWAddress <<= 9;
|
||||
BlockSize = 512;
|
||||
}
|
||||
RWCommand = 25;
|
||||
if (cmd == 25)
|
||||
RWCommand = 25;
|
||||
Host->SendResponse(CSR, true);
|
||||
RWAddress += WriteBlock(RWAddress);
|
||||
SetState(0x04);
|
||||
|
@ -1002,7 +1027,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
|
|||
// DSi boot2 sets this to 0x40100000 (hardcoded)
|
||||
// then has two codepaths depending on whether bit30 did get set
|
||||
// is it settable at all on the MMC? probably not.
|
||||
if (Internal) param &= ~(1<<30);
|
||||
if (holds_alternative<DSi_NAND::NANDImage>(Storage)) param &= ~(1<<30);
|
||||
OCR &= 0xBF000000;
|
||||
OCR |= (param & 0x40FFFFFF);
|
||||
Host->SendResponse(OCR, true);
|
||||
|
@ -1048,14 +1073,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
|
|||
len = Host->GetTransferrableLen(len);
|
||||
|
||||
u8 data[0x200];
|
||||
if (SD)
|
||||
if (auto* sd = std::get_if<FATStorage>(&Storage))
|
||||
{
|
||||
SD->ReadSectors((u32)(addr >> 9), 1, data);
|
||||
sd->ReadSectors((u32)(addr >> 9), 1, data);
|
||||
}
|
||||
else if (File)
|
||||
else if (auto* nand = std::get_if<DSi_NAND::NANDImage>(&Storage))
|
||||
{
|
||||
FileSeek(File, addr, FileSeekOrigin::Start);
|
||||
FileRead(&data[addr & 0x1FF], 1, len, File);
|
||||
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
|
||||
FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile());
|
||||
}
|
||||
|
||||
return Host->DataRX(&data[addr & 0x1FF], len);
|
||||
|
@ -1069,26 +1094,28 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
|
|||
u8 data[0x200];
|
||||
if (len < 0x200)
|
||||
{
|
||||
if (SD)
|
||||
if (auto* sd = get_if<FATStorage>(&Storage))
|
||||
{
|
||||
SD->ReadSectors((u32)(addr >> 9), 1, data);
|
||||
sd->ReadSectors((u32)(addr >> 9), 1, data);
|
||||
}
|
||||
}
|
||||
if ((len = Host->DataTX(&data[addr & 0x1FF], len)))
|
||||
{
|
||||
if (!ReadOnly)
|
||||
{
|
||||
if (SD)
|
||||
if (auto* sd = get_if<FATStorage>(&Storage))
|
||||
{
|
||||
SD->WriteSectors((u32)(addr >> 9), 1, data);
|
||||
sd->WriteSectors((u32)(addr >> 9), 1, data);
|
||||
}
|
||||
else if (File)
|
||||
else if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
|
||||
{
|
||||
FileSeek(File, addr, FileSeekOrigin::Start);
|
||||
FileWrite(&data[addr & 0x1FF], 1, len, File);
|
||||
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
|
||||
FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
87
src/DSi_SD.h
87
src/DSi_SD.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,30 +20,40 @@
|
|||
#define DSI_SD_H
|
||||
|
||||
#include <cstring>
|
||||
#include <variant>
|
||||
#include "FIFO.h"
|
||||
#include "FATStorage.h"
|
||||
#include "DSi_NAND.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class DSi_SDDevice;
|
||||
class DSi;
|
||||
|
||||
using Nothing = std::monostate;
|
||||
using DSiStorage = std::variant<std::monostate, FATStorage, DSi_NAND::NANDImage>;
|
||||
|
||||
class DSi_SDHost
|
||||
{
|
||||
public:
|
||||
DSi_SDHost(u32 num);
|
||||
/// Creates an SDMMC host.
|
||||
DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard = std::nullopt) noexcept;
|
||||
|
||||
/// Creates an SDIO host
|
||||
explicit DSi_SDHost(melonDS::DSi& dsi) noexcept;
|
||||
~DSi_SDHost();
|
||||
|
||||
void CloseHandles();
|
||||
void Reset();
|
||||
|
||||
void DoSavestate(Savestate* file);
|
||||
|
||||
static void FinishRX(u32 param);
|
||||
static void FinishTX(u32 param);
|
||||
void FinishRX(u32 param);
|
||||
void FinishTX(u32 param);
|
||||
void SendResponse(u32 val, bool last);
|
||||
u32 DataRX(u8* data, u32 len);
|
||||
u32 DataRX(const u8* data, u32 len);
|
||||
u32 DataTX(u8* data, u32 len);
|
||||
u32 GetTransferrableLen(u32 len);
|
||||
u32 GetTransferrableLen(u32 len) const;
|
||||
|
||||
void CheckRX();
|
||||
void CheckTX();
|
||||
|
@ -51,6 +61,15 @@ public:
|
|||
|
||||
void SetCardIRQ();
|
||||
|
||||
[[nodiscard]] FATStorage* GetSDCard() noexcept;
|
||||
[[nodiscard]] const FATStorage* GetSDCard() const noexcept;
|
||||
[[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept;
|
||||
[[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept;
|
||||
|
||||
void SetSDCard(FATStorage&& sdcard) noexcept;
|
||||
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept;
|
||||
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept;
|
||||
|
||||
u16 Read(u32 addr);
|
||||
void Write(u32 addr, u16 val);
|
||||
u16 ReadFIFO16();
|
||||
|
@ -62,6 +81,7 @@ public:
|
|||
void CheckSwapFIFO();
|
||||
|
||||
private:
|
||||
melonDS::DSi& DSi;
|
||||
u32 Num;
|
||||
|
||||
u16 PortSelect;
|
||||
|
@ -87,7 +107,7 @@ private:
|
|||
u32 Param;
|
||||
u16 ResponseBuffer[8];
|
||||
|
||||
DSi_SDDevice* Ports[2];
|
||||
std::array<std::unique_ptr<DSi_SDDevice>, 2> Ports {};
|
||||
|
||||
u32 CurFIFO; // FIFO accessible for read/write
|
||||
FIFO<u16, 0x100> DataFIFO[2];
|
||||
|
@ -125,25 +145,53 @@ protected:
|
|||
class DSi_MMCStorage : public DSi_SDDevice
|
||||
{
|
||||
public:
|
||||
DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename);
|
||||
DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir);
|
||||
~DSi_MMCStorage();
|
||||
DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept;
|
||||
DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept;
|
||||
~DSi_MMCStorage() override;
|
||||
|
||||
void Reset();
|
||||
[[nodiscard]] FATStorage* GetSDCard() noexcept { return std::get_if<FATStorage>(&Storage); }
|
||||
[[nodiscard]] const FATStorage* GetSDCard() const noexcept { return std::get_if<FATStorage>(&Storage); }
|
||||
[[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept { return std::get_if<DSi_NAND::NANDImage>(&Storage); }
|
||||
[[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept { return std::get_if<DSi_NAND::NANDImage>(&Storage); }
|
||||
|
||||
void DoSavestate(Savestate* file);
|
||||
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { Storage = std::move(nand); }
|
||||
void SetSDCard(FATStorage&& sdcard) noexcept { Storage = std::move(sdcard); }
|
||||
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
|
||||
{
|
||||
if (sdcard)
|
||||
{ // If we're setting a new SD card...
|
||||
Storage = std::move(*sdcard);
|
||||
sdcard = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
Storage = Nothing();
|
||||
}
|
||||
}
|
||||
|
||||
void SetCID(u8* cid) { memcpy(CID, cid, 16); }
|
||||
void SetStorage(DSiStorage&& storage) noexcept
|
||||
{
|
||||
Storage = std::move(storage);
|
||||
storage = Nothing();
|
||||
// not sure if a moved-from variant is empty or contains a moved-from object;
|
||||
// better to be safe than sorry
|
||||
}
|
||||
|
||||
void SendCMD(u8 cmd, u32 param);
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); }
|
||||
|
||||
void SendCMD(u8 cmd, u32 param) override;
|
||||
void SendACMD(u8 cmd, u32 param);
|
||||
|
||||
void ContinueTransfer();
|
||||
void ContinueTransfer() override;
|
||||
|
||||
private:
|
||||
bool Internal;
|
||||
Platform::FileHandle* File;
|
||||
FATStorage* SD;
|
||||
static constexpr u8 DSiSDCardCID[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
|
||||
melonDS::DSi& DSi;
|
||||
DSiStorage Storage;
|
||||
|
||||
u8 CID[16];
|
||||
u8 CSD[16];
|
||||
|
@ -164,4 +212,5 @@ private:
|
|||
u32 WriteBlock(u64 addr);
|
||||
};
|
||||
|
||||
}
|
||||
#endif // DSI_SD_H
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -19,38 +19,27 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "DSi.h"
|
||||
#include "SPI.h"
|
||||
#include "DSi_SPI_TSC.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace DSi_SPI_TSC
|
||||
{
|
||||
|
||||
u32 DataPos;
|
||||
u8 Index;
|
||||
u8 Bank;
|
||||
u8 Data;
|
||||
|
||||
u8 Bank3Regs[0x80];
|
||||
u8 TSCMode;
|
||||
|
||||
u16 TouchX, TouchY;
|
||||
|
||||
|
||||
bool Init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
DSi_TSC::DSi_TSC(melonDS::DSi& dsi) : TSC(dsi)
|
||||
{
|
||||
}
|
||||
|
||||
void Reset()
|
||||
DSi_TSC::~DSi_TSC()
|
||||
{
|
||||
}
|
||||
|
||||
void DSi_TSC::Reset()
|
||||
{
|
||||
TSC::Reset();
|
||||
|
||||
DataPos = 0;
|
||||
|
||||
Bank = 0;
|
||||
|
@ -72,8 +61,10 @@ void Reset()
|
|||
TSCMode = 0x01; // DSi mode
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
void DSi_TSC::DoSavestate(Savestate* file)
|
||||
{
|
||||
TSC::DoSavestate(file);
|
||||
|
||||
file->Section("SPTi");
|
||||
|
||||
file->Var32(&DataPos);
|
||||
|
@ -85,19 +76,14 @@ void DoSavestate(Savestate* file)
|
|||
file->Var8(&TSCMode);
|
||||
}
|
||||
|
||||
void SetMode(u8 mode)
|
||||
void DSi_TSC::SetMode(u8 mode)
|
||||
{
|
||||
TSCMode = mode;
|
||||
}
|
||||
|
||||
void SetTouchCoords(u16 x, u16 y)
|
||||
void DSi_TSC::SetTouchCoords(u16 x, u16 y)
|
||||
{
|
||||
if (TSCMode == 0x00)
|
||||
{
|
||||
if (y == 0xFFF) NDS::KeyInput |= (1 << (16+6));
|
||||
else NDS::KeyInput &= ~(1 << (16+6));
|
||||
return SPI_TSC::SetTouchCoords(x, y);
|
||||
}
|
||||
if (TSCMode == 0x00) return TSC::SetTouchCoords(x, y);
|
||||
|
||||
TouchX = x;
|
||||
TouchY = y;
|
||||
|
@ -135,24 +121,17 @@ void SetTouchCoords(u16 x, u16 y)
|
|||
}
|
||||
}
|
||||
|
||||
void MicInputFrame(s16* data, int samples)
|
||||
void DSi_TSC::MicInputFrame(const s16* data, int samples)
|
||||
{
|
||||
if (TSCMode == 0x00) return SPI_TSC::MicInputFrame(data, samples);
|
||||
if (TSCMode == 0x00) return TSC::MicInputFrame(data, samples);
|
||||
|
||||
// otherwise we don't handle mic input
|
||||
// TODO: handle it where it needs to be
|
||||
}
|
||||
|
||||
u8 Read()
|
||||
void DSi_TSC::Write(u8 val)
|
||||
{
|
||||
if (TSCMode == 0x00) return SPI_TSC::Read();
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
void Write(u8 val, u32 hold)
|
||||
{
|
||||
if (TSCMode == 0x00) return SPI_TSC::Write(val, hold);
|
||||
if (TSCMode == 0x00) return TSC::Write(val);
|
||||
|
||||
#define READWRITE(var) { if (Index & 0x01) Data = var; else var = val; }
|
||||
|
||||
|
@ -219,7 +198,7 @@ void Write(u8 val, u32 hold)
|
|||
{
|
||||
Log(LogLevel::Debug, "DSi_SPI_TSC: DS-compatibility mode\n");
|
||||
DataPos = 0;
|
||||
NDS::KeyInput |= (1 << (16+6));
|
||||
NDS.KeyInput |= (1 << (16+6));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -233,8 +212,14 @@ void Write(u8 val, u32 hold)
|
|||
Index += (1<<1); // increment index
|
||||
}
|
||||
|
||||
if (hold) DataPos++;
|
||||
else DataPos = 0;
|
||||
DataPos++;
|
||||
}
|
||||
|
||||
void DSi_TSC::Release()
|
||||
{
|
||||
if (TSCMode == 0x00) return TSC::Release();
|
||||
|
||||
DataPos = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,26 +21,37 @@
|
|||
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
#include "SPI.h"
|
||||
|
||||
namespace DSi_SPI_TSC
|
||||
namespace melonDS
|
||||
{
|
||||
class DSi;
|
||||
class DSi_TSC : public TSC
|
||||
{
|
||||
public:
|
||||
DSi_TSC(melonDS::DSi& dsi);
|
||||
~DSi_TSC() override;
|
||||
|
||||
extern u32 DataPos;
|
||||
void Reset() override;
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
void Reset();
|
||||
void DoSavestate(Savestate* file);
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
// 00=DS-mode 01=normal
|
||||
void SetMode(u8 mode);
|
||||
// 00=DS-mode 01=normal
|
||||
void SetMode(u8 mode);
|
||||
|
||||
void SetTouchCoords(u16 x, u16 y);
|
||||
void MicInputFrame(s16* data, int samples);
|
||||
void SetTouchCoords(u16 x, u16 y) override;
|
||||
void MicInputFrame(const s16* data, int samples) override;
|
||||
|
||||
u8 Read();
|
||||
void Write(u8 val, u32 hold);
|
||||
void Write(u8 val) override;
|
||||
void Release() override;
|
||||
|
||||
private:
|
||||
u8 Index;
|
||||
u8 Bank;
|
||||
|
||||
u8 Bank3Regs[0x80];
|
||||
u8 TSCMode;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DSI_SPI_TSC
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "types.h"
|
||||
#include <array>
|
||||
|
||||
namespace DSi_TMD
|
||||
namespace melonDS::DSi_TMD
|
||||
{
|
||||
|
||||
struct TitleMetadataContent {
|
||||
|
|
|
@ -1,15 +1,26 @@
|
|||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/*
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
#include "ff.h" /* Obtains integer types */
|
||||
#include "diskio.h" /* Declarations of disk functions */
|
||||
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 "FATIO.h"
|
||||
#include "fatfs/ff.h"
|
||||
#include "fatfs/diskio.h"
|
||||
|
||||
using namespace melonDS;
|
||||
|
||||
static ff_disk_read_cb ReadCb;
|
||||
static ff_disk_write_cb WriteCb;
|
||||
|
@ -17,7 +28,7 @@ static LBA_t SectorCount;
|
|||
static DSTATUS Status = STA_NOINIT | STA_NODISK;
|
||||
|
||||
|
||||
void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt)
|
||||
void melonDS::ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt)
|
||||
{
|
||||
if (!readcb) return;
|
||||
|
||||
|
@ -30,10 +41,10 @@ void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt
|
|||
else Status &= ~STA_PROTECT;
|
||||
}
|
||||
|
||||
void ff_disk_close(void)
|
||||
void melonDS::ff_disk_close()
|
||||
{
|
||||
ReadCb = (void*)0;
|
||||
WriteCb = (void*)0;
|
||||
ReadCb = nullptr;
|
||||
WriteCb = nullptr;
|
||||
SectorCount = 0;
|
||||
|
||||
Status &= ~STA_PROTECT;
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
melonDS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FATIO_H
|
||||
#define FATIO_H
|
||||
|
||||
#include <functional>
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
// extra additions for interfacing with melonDS
|
||||
namespace melonDS
|
||||
{
|
||||
using ff_disk_read_cb = std::function<UINT(BYTE*, LBA_t, UINT)>;
|
||||
using ff_disk_write_cb = std::function<UINT(const BYTE*, LBA_t, UINT)>;
|
||||
|
||||
void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt);
|
||||
void ff_disk_close();
|
||||
}
|
||||
|
||||
#endif // FATIO_H
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -21,52 +21,88 @@
|
|||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
|
||||
#include "FATIO.h"
|
||||
#include "FATStorage.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Platform;
|
||||
using std::string;
|
||||
|
||||
FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir)
|
||||
FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional<string>& sourcedir) :
|
||||
FATStorage(FATStorageArgs { filename, size, readonly, sourcedir })
|
||||
{
|
||||
ReadOnly = readonly;
|
||||
Load(filename, size, sourcedir);
|
||||
}
|
||||
|
||||
File = nullptr;
|
||||
FATStorage::FATStorage(const FATStorageArgs& args) noexcept :
|
||||
FATStorage(args.Filename, args.Size, args.ReadOnly, args.SourceDir)
|
||||
{
|
||||
}
|
||||
|
||||
FATStorage::FATStorage(FATStorageArgs&& args) noexcept :
|
||||
FilePath(std::move(args.Filename)),
|
||||
FileSize(args.Size),
|
||||
ReadOnly(args.ReadOnly),
|
||||
SourceDir(std::move(args.SourceDir))
|
||||
{
|
||||
Load(FilePath, FileSize, SourceDir);
|
||||
}
|
||||
|
||||
FATStorage::FATStorage(FATStorage&& other) noexcept
|
||||
{
|
||||
FilePath = std::move(other.FilePath);
|
||||
IndexPath = std::move(other.IndexPath);
|
||||
SourceDir = std::move(other.SourceDir);
|
||||
ReadOnly = other.ReadOnly;
|
||||
File = other.File;
|
||||
FileSize = other.FileSize;
|
||||
DirIndex = std::move(other.DirIndex);
|
||||
FileIndex = std::move(other.FileIndex);
|
||||
|
||||
other.File = nullptr;
|
||||
}
|
||||
|
||||
FATStorage& FATStorage::operator=(FATStorage&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (File)
|
||||
{ // Sync this file's contents to the host (if applicable) before closing it
|
||||
if (!ReadOnly) Save();
|
||||
CloseFile(File);
|
||||
}
|
||||
|
||||
FilePath = std::move(other.FilePath);
|
||||
IndexPath = std::move(other.IndexPath);
|
||||
SourceDir = std::move(other.SourceDir);
|
||||
ReadOnly = other.ReadOnly;
|
||||
File = other.File;
|
||||
FileSize = other.FileSize;
|
||||
DirIndex = std::move(other.DirIndex);
|
||||
FileIndex = std::move(other.FileIndex);
|
||||
|
||||
other.File = nullptr;
|
||||
other.SourceDir = std::nullopt;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FATStorage::~FATStorage()
|
||||
{
|
||||
if (!ReadOnly) Save();
|
||||
}
|
||||
|
||||
|
||||
bool FATStorage::Open()
|
||||
{
|
||||
File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
|
||||
if (!File)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FATStorage::Close()
|
||||
{
|
||||
if (File) CloseFile(File);
|
||||
File = nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
|
||||
{
|
||||
if (!File) return false;
|
||||
if (FF_File) return false;
|
||||
|
||||
FF_File = File;
|
||||
FF_FileSize = FileSize;
|
||||
ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9));
|
||||
ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
|
||||
|
||||
FRESULT res;
|
||||
FATFS fs;
|
||||
|
@ -74,8 +110,8 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
|
|||
res = f_mount(&fs, "0:", 1);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
FF_File = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -87,7 +123,6 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
|
|||
{
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
FF_File = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -97,34 +132,75 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
|
|||
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
FF_File = nullptr;
|
||||
return nwrite==len;
|
||||
}
|
||||
|
||||
u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data)
|
||||
{
|
||||
if (!File) return false;
|
||||
|
||||
u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data)
|
||||
ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
|
||||
|
||||
FRESULT res;
|
||||
FATFS fs;
|
||||
|
||||
res = f_mount(&fs, "0:", 1);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string prefixedPath("0:/");
|
||||
prefixedPath += path;
|
||||
FF_FIL file;
|
||||
res = f_open(&file, prefixedPath.c_str(), FA_READ);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 nread;
|
||||
f_lseek(&file, start);
|
||||
f_read(&file, data, len, &nread);
|
||||
f_close(&file);
|
||||
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
return nread;
|
||||
}
|
||||
|
||||
u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) const
|
||||
{
|
||||
return ReadSectorsInternal(File, FileSize, start, num, data);
|
||||
}
|
||||
|
||||
u32 FATStorage::WriteSectors(u32 start, u32 num, u8* data)
|
||||
u32 FATStorage::WriteSectors(u32 start, u32 num, const u8* data)
|
||||
{
|
||||
if (ReadOnly) return 0;
|
||||
return WriteSectorsInternal(File, FileSize, start, num, data);
|
||||
}
|
||||
|
||||
|
||||
FileHandle* FATStorage::FF_File;
|
||||
u64 FATStorage::FF_FileSize;
|
||||
|
||||
UINT FATStorage::FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num)
|
||||
u64 FATStorage::GetSectorCount() const
|
||||
{
|
||||
return ReadSectorsInternal(FF_File, FF_FileSize, sector, num, buf);
|
||||
return FileSize / 0x200;
|
||||
}
|
||||
|
||||
UINT FATStorage::FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num)
|
||||
ff_disk_read_cb FATStorage::FF_ReadStorage() const noexcept
|
||||
{
|
||||
return WriteSectorsInternal(FF_File, FF_FileSize, sector, num, buf);
|
||||
return [this](BYTE* buf, LBA_t sector, UINT num) {
|
||||
return ReadSectorsInternal(File, FileSize, sector, num, buf);
|
||||
};
|
||||
}
|
||||
|
||||
ff_disk_write_cb FATStorage::FF_WriteStorage() const noexcept
|
||||
{
|
||||
return [this](const BYTE* buf, LBA_t sector, UINT num) {
|
||||
return WriteSectorsInternal(File, FileSize, sector, num, buf);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -157,7 +233,7 @@ u32 FATStorage::ReadSectorsInternal(FileHandle* file, u64 filelen, u32 start, u3
|
|||
return res;
|
||||
}
|
||||
|
||||
u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, u8* data)
|
||||
u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data)
|
||||
{
|
||||
if (!file) return 0;
|
||||
|
||||
|
@ -318,17 +394,17 @@ void FATStorage::SaveIndex()
|
|||
FileHandle* f = OpenLocalFile(IndexPath, FileMode::WriteText);
|
||||
if (!f) return;
|
||||
|
||||
FileWriteFormatted(f, "SIZE %" PRIu64 "\r\n", FileSize);
|
||||
FileWriteFormatted(f, "SIZE %" PRIu64 "\n", FileSize);
|
||||
|
||||
for (const auto& [key, val] : DirIndex)
|
||||
{
|
||||
FileWriteFormatted(f, "DIR %u %s\r\n",
|
||||
FileWriteFormatted(f, "DIR %u %s\n",
|
||||
val.IsReadOnly?1:0, val.Path.c_str());
|
||||
}
|
||||
|
||||
for (const auto& [key, val] : FileIndex)
|
||||
{
|
||||
FileWriteFormatted(f, "FILE %u %" PRIu64 " %" PRId64 " %u %s\r\n",
|
||||
FileWriteFormatted(f, "FILE %u %" PRIu64 " %" PRId64 " %u %s\n",
|
||||
val.IsReadOnly?1:0, val.Size, val.LastModified, val.LastModifiedInternal, val.Path.c_str());
|
||||
}
|
||||
|
||||
|
@ -904,7 +980,7 @@ bool FATStorage::ImportDirectory(const std::string& sourcedir)
|
|||
return true;
|
||||
}
|
||||
|
||||
u64 FATStorage::GetDirectorySize(fs::path sourcedir)
|
||||
u64 FATStorage::GetDirectorySize(fs::path sourcedir) const
|
||||
{
|
||||
u64 ret = 0;
|
||||
u32 csize = 0x1000; // this is an estimate
|
||||
|
@ -927,19 +1003,15 @@ u64 FATStorage::GetDirectorySize(fs::path sourcedir)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool FATStorage::Load(const std::string& filename, u64 size, const std::string& sourcedir)
|
||||
bool FATStorage::Load(const std::string& filename, u64 size, const std::optional<string>& sourcedir)
|
||||
{
|
||||
FilePath = filename;
|
||||
FileSize = size;
|
||||
SourceDir = sourcedir;
|
||||
|
||||
bool hasdir = !sourcedir.empty();
|
||||
if (hasdir)
|
||||
bool hasdir = sourcedir && !sourcedir->empty();
|
||||
if (sourcedir)
|
||||
{
|
||||
if (!fs::is_directory(fs::u8path(sourcedir)))
|
||||
if (!fs::is_directory(fs::u8path(*sourcedir)))
|
||||
{
|
||||
hasdir = false;
|
||||
SourceDir = "";
|
||||
SourceDir = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -950,8 +1022,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
|
|||
// with a minimum 128MB extra, otherwise size is defaulted to 512MB
|
||||
|
||||
bool isnew = !Platform::LocalFileExists(filename);
|
||||
FF_File = Platform::OpenLocalFile(filename, static_cast<FileMode>(FileMode::ReadWrite | FileMode::Preserve));
|
||||
if (!FF_File)
|
||||
File = Platform::OpenLocalFile(filename, static_cast<FileMode>(FileMode::ReadWrite | FileMode::Preserve));
|
||||
if (!File)
|
||||
return false;
|
||||
|
||||
IndexPath = FilePath + ".idx";
|
||||
|
@ -967,7 +1039,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
|
|||
|
||||
if (FileSize == 0)
|
||||
{
|
||||
FileSize = FileLength(FF_File);
|
||||
FileSize = FileLength(File);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -981,8 +1053,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
|
|||
}
|
||||
else
|
||||
{
|
||||
FF_FileSize = FileSize;
|
||||
ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9));
|
||||
ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
|
||||
|
||||
res = f_mount(&fs, "0:", 1);
|
||||
if (res != FR_OK)
|
||||
|
@ -1002,7 +1073,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
|
|||
{
|
||||
if (hasdir)
|
||||
{
|
||||
FileSize = GetDirectorySize(fs::u8path(sourcedir));
|
||||
FileSize = GetDirectorySize(fs::u8path(*sourcedir));
|
||||
FileSize += 0x8000000ULL; // 128MB leeway
|
||||
|
||||
// make it a power of two
|
||||
|
@ -1018,9 +1089,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
|
|||
FileSize = 0x20000000ULL; // 512MB
|
||||
}
|
||||
|
||||
FF_FileSize = FileSize;
|
||||
ff_disk_close();
|
||||
ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9));
|
||||
ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
|
||||
|
||||
DirIndex.clear();
|
||||
FileIndex.clear();
|
||||
|
@ -1051,33 +1121,24 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
|
|||
if (res == FR_OK)
|
||||
{
|
||||
if (hasdir)
|
||||
ImportDirectory(sourcedir);
|
||||
ImportDirectory(*sourcedir);
|
||||
}
|
||||
|
||||
f_unmount("0:");
|
||||
|
||||
ff_disk_close();
|
||||
CloseFile(FF_File);
|
||||
FF_File = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FATStorage::Save()
|
||||
{
|
||||
if (SourceDir.empty())
|
||||
{
|
||||
return true;
|
||||
if (!SourceDir)
|
||||
{ // If we're not syncing the SD card image to a host directory...
|
||||
return true; // Not an error.
|
||||
}
|
||||
|
||||
FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
|
||||
if (!FF_File)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_FileSize = FileSize;
|
||||
ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9));
|
||||
ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9));
|
||||
|
||||
FRESULT res;
|
||||
FATFS fs;
|
||||
|
@ -1085,21 +1146,20 @@ bool FATStorage::Save()
|
|||
res = f_mount(&fs, "0:", 1);
|
||||
if (res != FR_OK)
|
||||
{
|
||||
f_unmount("0:");
|
||||
ff_disk_close();
|
||||
CloseFile(FF_File);
|
||||
FF_File = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
ExportChanges(SourceDir);
|
||||
ExportChanges(*SourceDir);
|
||||
|
||||
SaveIndex();
|
||||
|
||||
f_unmount("0:");
|
||||
|
||||
ff_disk_close();
|
||||
CloseFile(FF_File);
|
||||
FF_File = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,43 +22,66 @@
|
|||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
|
||||
#include "Platform.h"
|
||||
#include "types.h"
|
||||
#include "fatfs/ff.h"
|
||||
#include "FATIO.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
/// Contains information necessary to load an SD card image.
|
||||
/// The intended use case is for loading homebrew NDS ROMs;
|
||||
/// you won't know that a ROM is homebrew until you parse it,
|
||||
/// so if you load the SD card before the ROM
|
||||
/// then you might end up discarding it.
|
||||
struct FATStorageArgs
|
||||
{
|
||||
std::string Filename;
|
||||
|
||||
/// Size of the desired SD card in bytes, or 0 for auto-detect.
|
||||
u64 Size;
|
||||
bool ReadOnly;
|
||||
std::optional<std::string> SourceDir;
|
||||
};
|
||||
|
||||
class FATStorage
|
||||
{
|
||||
public:
|
||||
FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir);
|
||||
FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional<std::string>& sourcedir = std::nullopt);
|
||||
explicit FATStorage(const FATStorageArgs& args) noexcept;
|
||||
explicit FATStorage(FATStorageArgs&& args) noexcept;
|
||||
FATStorage(FATStorage&& other) noexcept;
|
||||
FATStorage(const FATStorage& other) = delete;
|
||||
FATStorage& operator=(const FATStorage& other) = delete;
|
||||
FATStorage& operator=(FATStorage&& other) noexcept;
|
||||
~FATStorage();
|
||||
|
||||
bool Open();
|
||||
void Close();
|
||||
|
||||
bool InjectFile(const std::string& path, u8* data, u32 len);
|
||||
u32 ReadFile(const std::string& path, u32 start, u32 len, u8* data);
|
||||
|
||||
u32 ReadSectors(u32 start, u32 num, u8* data);
|
||||
u32 WriteSectors(u32 start, u32 num, u8* data);
|
||||
u32 ReadSectors(u32 start, u32 num, u8* data) const;
|
||||
u32 WriteSectors(u32 start, u32 num, const u8* data);
|
||||
|
||||
[[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; }
|
||||
u64 GetSectorCount() const;
|
||||
|
||||
private:
|
||||
std::string FilePath;
|
||||
std::string IndexPath;
|
||||
std::string SourceDir;
|
||||
std::optional<std::string> SourceDir;
|
||||
bool ReadOnly;
|
||||
|
||||
Platform::FileHandle* File;
|
||||
u64 FileSize;
|
||||
|
||||
static Platform::FileHandle* FF_File;
|
||||
static u64 FF_FileSize;
|
||||
static UINT FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num);
|
||||
static UINT FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num);
|
||||
[[nodiscard]] ff_disk_read_cb FF_ReadStorage() const noexcept;
|
||||
[[nodiscard]] ff_disk_write_cb FF_WriteStorage() const noexcept;
|
||||
|
||||
static u32 ReadSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data);
|
||||
static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data);
|
||||
static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data);
|
||||
|
||||
void LoadIndex();
|
||||
void SaveIndex();
|
||||
|
@ -73,9 +96,9 @@ private:
|
|||
void CleanupDirectory(const std::string& sourcedir, const std::string& path, int level);
|
||||
bool ImportFile(const std::string& path, std::filesystem::path in);
|
||||
bool ImportDirectory(const std::string& sourcedir);
|
||||
u64 GetDirectorySize(std::filesystem::path sourcedir);
|
||||
u64 GetDirectorySize(std::filesystem::path sourcedir) const;
|
||||
|
||||
bool Load(const std::string& filename, u64 size, const std::string& sourcedir);
|
||||
bool Load(const std::string& filename, u64 size, const std::optional<std::string>& sourcedir);
|
||||
bool Save();
|
||||
|
||||
typedef struct
|
||||
|
@ -99,4 +122,5 @@ private:
|
|||
std::map<std::string, FileIndexEntry> FileIndex;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // FATSTORAGE_H
|
||||
|
|
29
src/FIFO.h
29
src/FIFO.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
|||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
template<typename T, u32 NumEntries>
|
||||
class FIFO
|
||||
{
|
||||
|
@ -72,12 +74,12 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
T Peek()
|
||||
T Peek() const
|
||||
{
|
||||
return Entries[ReadPos];
|
||||
}
|
||||
|
||||
T Peek(u32 offset)
|
||||
T Peek(u32 offset) const
|
||||
{
|
||||
u32 pos = ReadPos + offset;
|
||||
if (pos >= NumEntries)
|
||||
|
@ -86,11 +88,11 @@ public:
|
|||
return Entries[pos];
|
||||
}
|
||||
|
||||
u32 Level() { return NumOccupied; }
|
||||
bool IsEmpty() { return NumOccupied == 0; }
|
||||
bool IsFull() { return NumOccupied >= NumEntries; }
|
||||
u32 Level() const { return NumOccupied; }
|
||||
bool IsEmpty() const { return NumOccupied == 0; }
|
||||
bool IsFull() const { return NumOccupied >= NumEntries; }
|
||||
|
||||
bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); }
|
||||
bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); }
|
||||
|
||||
private:
|
||||
T Entries[NumEntries] = {0};
|
||||
|
@ -162,12 +164,12 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
T Peek()
|
||||
T Peek() const
|
||||
{
|
||||
return Entries[ReadPos];
|
||||
}
|
||||
|
||||
T Peek(u32 offset)
|
||||
T Peek(u32 offset) const
|
||||
{
|
||||
u32 pos = ReadPos + offset;
|
||||
if (pos >= NumEntries)
|
||||
|
@ -176,11 +178,11 @@ public:
|
|||
return Entries[pos];
|
||||
}
|
||||
|
||||
u32 Level() { return NumOccupied; }
|
||||
bool IsEmpty() { return NumOccupied == 0; }
|
||||
bool IsFull() { return NumOccupied >= NumEntries; }
|
||||
u32 Level() const { return NumOccupied; }
|
||||
bool IsEmpty() const { return NumOccupied == 0; }
|
||||
bool IsFull() const { return NumOccupied >= NumEntries; }
|
||||
|
||||
bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); }
|
||||
bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); }
|
||||
|
||||
private:
|
||||
u32 NumEntries;
|
||||
|
@ -189,4 +191,5 @@ private:
|
|||
u32 ReadPos, WritePos;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
697
src/FreeBIOS.cpp
697
src/FreeBIOS.cpp
|
@ -26,10 +26,12 @@
|
|||
|
||||
#include "FreeBIOS.h"
|
||||
|
||||
unsigned char bios_arm7_bin[] = {
|
||||
namespace melonDS
|
||||
{
|
||||
std::array<u8, ARM7BIOSSize> bios_arm7_bin = {
|
||||
0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea,
|
||||
0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea,
|
||||
0xe3, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
|
||||
0xe5, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
@ -383,17 +385,17 @@ unsigned char bios_arm7_bin[] = {
|
|||
0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5,
|
||||
0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3,
|
||||
0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1,
|
||||
0xd4, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
|
||||
0xdc, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
|
||||
0x5c, 0x11, 0x00, 0x00, 0x90, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00,
|
||||
0x4c, 0x11, 0x00, 0x00, 0xbc, 0x11, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x00,
|
||||
0xe8, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x44, 0x12, 0x00, 0x00,
|
||||
0xc4, 0x12, 0x00, 0x00, 0x04, 0x13, 0x00, 0x00, 0x50, 0x13, 0x00, 0x00,
|
||||
0xe8, 0x13, 0x00, 0x00, 0xf0, 0x13, 0x00, 0x00, 0x84, 0x14, 0x00, 0x00,
|
||||
0x00, 0x15, 0x00, 0x00, 0xac, 0x15, 0x00, 0x00, 0xb0, 0x15, 0x00, 0x00,
|
||||
0xb0, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
|
||||
0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x88, 0x16, 0x00, 0x00,
|
||||
0x9c, 0x1c, 0x00, 0x00, 0xc4, 0x1f, 0x00, 0x00, 0x84, 0x1f, 0x00, 0x00,
|
||||
0xa0, 0x1f, 0x00, 0x00, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3,
|
||||
0xf0, 0x13, 0x00, 0x00, 0xf8, 0x13, 0x00, 0x00, 0x8c, 0x14, 0x00, 0x00,
|
||||
0x08, 0x15, 0x00, 0x00, 0xb4, 0x15, 0x00, 0x00, 0xb8, 0x15, 0x00, 0x00,
|
||||
0xb8, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00,
|
||||
0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x90, 0x16, 0x00, 0x00,
|
||||
0xa4, 0x1c, 0x00, 0x00, 0xcc, 0x1f, 0x00, 0x00, 0x8c, 0x1f, 0x00, 0x00,
|
||||
0xa8, 0x1f, 0x00, 0x00, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3,
|
||||
0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1,
|
||||
0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x03, 0xa0, 0xe3, 0x80, 0x20, 0xa0, 0xe3, 0x01, 0x23, 0xc0, 0xe5,
|
||||
|
@ -440,205 +442,206 @@ unsigned char bios_arm7_bin[] = {
|
|||
0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c,
|
||||
0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44,
|
||||
0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2,
|
||||
0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0,
|
||||
0xff, 0x04, 0xc0, 0xe3, 0xff, 0x08, 0xc0, 0xe3, 0xa2, 0x20, 0xb0, 0xe1,
|
||||
0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, 0x80, 0x50, 0x04, 0xe0,
|
||||
0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0,
|
||||
0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x53, 0x04, 0xe0,
|
||||
0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0,
|
||||
0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a,
|
||||
0x20, 0x00, 0xbd, 0xe8, 0x50, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3,
|
||||
0x4e, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1,
|
||||
0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5,
|
||||
0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3,
|
||||
0x15, 0x5c, 0xa0, 0xe1, 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3,
|
||||
0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3,
|
||||
0x83, 0x31, 0xa0, 0x01, 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01,
|
||||
0x04, 0x00, 0x5c, 0xe3, 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3,
|
||||
0x01, 0x60, 0xd0, 0x04, 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0,
|
||||
0x36, 0x6c, 0xa0, 0xe1, 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03,
|
||||
0x04, 0x90, 0x89, 0x10, 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0,
|
||||
0x20, 0x00, 0x58, 0xe3, 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03,
|
||||
0x00, 0x80, 0xa0, 0x03, 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a,
|
||||
0xe0, 0x03, 0xbd, 0xe8, 0x29, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0x23, 0x24, 0xb0, 0xe1, 0x26, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4,
|
||||
0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a,
|
||||
0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1,
|
||||
0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2,
|
||||
0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x16, 0xff, 0xff, 0x0a,
|
||||
0x01, 0xe0, 0x5e, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1,
|
||||
0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x0d, 0xff, 0xff, 0x0a,
|
||||
0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea,
|
||||
0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x07, 0xff, 0xff, 0x0a,
|
||||
0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4,
|
||||
0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a,
|
||||
0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1,
|
||||
0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2,
|
||||
0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3,
|
||||
0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11,
|
||||
0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x20, 0x00, 0xbd, 0x08, 0xf0, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2,
|
||||
0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a,
|
||||
0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04,
|
||||
0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11,
|
||||
0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, 0x20, 0x00, 0xbd, 0xe8,
|
||||
0x4e, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, 0x4c, 0xff, 0xff, 0xea,
|
||||
0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, 0x02, 0xc0, 0xd2, 0xe5,
|
||||
0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, 0xa4, 0x2f, 0xa0, 0xe1,
|
||||
0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, 0x15, 0x5c, 0xa0, 0xe1,
|
||||
0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3,
|
||||
0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, 0x83, 0x31, 0xa0, 0x01,
|
||||
0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, 0x04, 0x00, 0x5c, 0xe3,
|
||||
0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, 0x01, 0x60, 0xd0, 0x04,
|
||||
0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, 0x36, 0x6c, 0xa0, 0xe1,
|
||||
0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, 0x04, 0x90, 0x89, 0x10,
|
||||
0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, 0x20, 0x00, 0x58, 0xe3,
|
||||
0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, 0x00, 0x80, 0xa0, 0x03,
|
||||
0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, 0xe0, 0x03, 0xbd, 0xe8,
|
||||
0x27, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1,
|
||||
0x24, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3,
|
||||
0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4,
|
||||
0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1,
|
||||
0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0,
|
||||
0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x01, 0x40, 0xc1, 0xe4, 0x14, 0xff, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2,
|
||||
0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xed, 0xff, 0xff, 0x5a,
|
||||
0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x01, 0x40, 0xc1, 0xe4, 0x0b, 0xff, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1,
|
||||
0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0x23, 0x24, 0xb0, 0xe1, 0x05, 0xff, 0xff, 0x0a, 0x20, 0x00, 0x2d, 0xe9,
|
||||
0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3,
|
||||
0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4,
|
||||
0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1,
|
||||
0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0,
|
||||
0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xdc, 0x04,
|
||||
0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11,
|
||||
0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08,
|
||||
0xe2, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a,
|
||||
0xd9, 0xff, 0xff, 0xea, 0xde, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3,
|
||||
0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4,
|
||||
0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0xd3, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a,
|
||||
0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xcb, 0xfe, 0xff, 0x0a,
|
||||
0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea,
|
||||
0x00, 0x00, 0x24, 0x03, 0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f,
|
||||
0xc8, 0x12, 0xe2, 0x15, 0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22,
|
||||
0x28, 0x25, 0x26, 0x28, 0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33,
|
||||
0xba, 0x36, 0x8c, 0x39, 0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44,
|
||||
0x1c, 0x47, 0xb4, 0x49, 0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53,
|
||||
0xf5, 0x55, 0x42, 0x58, 0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60,
|
||||
0xf1, 0x62, 0xe8, 0x64, 0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c,
|
||||
0xc9, 0x6d, 0x5e, 0x6f, 0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75,
|
||||
0x41, 0x76, 0x6b, 0x77, 0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b,
|
||||
0x29, 0x7c, 0xe3, 0x7c, 0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f,
|
||||
0x61, 0x7f, 0xa6, 0x7f, 0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0,
|
||||
0x8c, 0x10, 0x4f, 0xe2, 0xb0, 0x00, 0x91, 0xe1, 0xa4, 0xfe, 0xff, 0xea,
|
||||
0x00, 0x00, 0x3b, 0x00, 0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01,
|
||||
0x64, 0x01, 0x9f, 0x01, 0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02,
|
||||
0xca, 0x02, 0x05, 0x03, 0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03,
|
||||
0x31, 0x04, 0x6e, 0x04, 0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05,
|
||||
0x9b, 0x05, 0xd8, 0x05, 0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06,
|
||||
0x07, 0x07, 0x43, 0x07, 0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08,
|
||||
0x74, 0x08, 0xb1, 0x08, 0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09,
|
||||
0xe4, 0x09, 0x21, 0x0a, 0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b,
|
||||
0x56, 0x0b, 0x93, 0x0b, 0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c,
|
||||
0xc9, 0x0c, 0x07, 0x0d, 0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e,
|
||||
0x3f, 0x0e, 0x7d, 0x0e, 0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f,
|
||||
0xb6, 0x0f, 0xf5, 0x0f, 0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10,
|
||||
0x30, 0x11, 0x6f, 0x11, 0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12,
|
||||
0xac, 0x12, 0xeb, 0x12, 0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13,
|
||||
0x2a, 0x14, 0x6a, 0x14, 0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15,
|
||||
0xaa, 0x15, 0xea, 0x15, 0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16,
|
||||
0x2c, 0x17, 0x6c, 0x17, 0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18,
|
||||
0xb0, 0x18, 0xf0, 0x18, 0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19,
|
||||
0x36, 0x1a, 0x77, 0x1a, 0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b,
|
||||
0xbe, 0x1b, 0x00, 0x1c, 0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d,
|
||||
0x48, 0x1d, 0x8a, 0x1d, 0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e,
|
||||
0xd5, 0x1e, 0x17, 0x1f, 0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20,
|
||||
0x64, 0x20, 0xa6, 0x20, 0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21,
|
||||
0xf5, 0x21, 0x38, 0x22, 0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23,
|
||||
0x88, 0x23, 0xcb, 0x23, 0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24,
|
||||
0x1d, 0x25, 0x61, 0x25, 0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26,
|
||||
0xb4, 0x26, 0xf8, 0x26, 0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28,
|
||||
0x4e, 0x28, 0x92, 0x28, 0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29,
|
||||
0xea, 0x29, 0x2f, 0x2a, 0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b,
|
||||
0x88, 0x2b, 0xcd, 0x2b, 0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c,
|
||||
0x28, 0x2d, 0x6e, 0x2d, 0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e,
|
||||
0xcb, 0x2e, 0x11, 0x2f, 0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30,
|
||||
0x70, 0x30, 0xb6, 0x30, 0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31,
|
||||
0x17, 0x32, 0x5e, 0x32, 0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33,
|
||||
0xc1, 0x33, 0x08, 0x34, 0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35,
|
||||
0x6c, 0x35, 0xb4, 0x35, 0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36,
|
||||
0x1a, 0x37, 0x62, 0x37, 0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38,
|
||||
0xcb, 0x38, 0x13, 0x39, 0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a,
|
||||
0x7e, 0x3a, 0xc6, 0x3a, 0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b,
|
||||
0x33, 0x3c, 0x7c, 0x3c, 0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d,
|
||||
0xea, 0x3d, 0x34, 0x3e, 0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f,
|
||||
0xa4, 0x3f, 0xee, 0x3f, 0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41,
|
||||
0x61, 0x41, 0xab, 0x41, 0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42,
|
||||
0x1f, 0x43, 0x6a, 0x43, 0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44,
|
||||
0xe1, 0x44, 0x2c, 0x45, 0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46,
|
||||
0xa4, 0x46, 0xf0, 0x46, 0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48,
|
||||
0x6a, 0x48, 0xb6, 0x48, 0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49,
|
||||
0x33, 0x4a, 0x7f, 0x4a, 0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b,
|
||||
0xfe, 0x4b, 0x4a, 0x4c, 0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d,
|
||||
0xcb, 0x4d, 0x18, 0x4e, 0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f,
|
||||
0x9b, 0x4f, 0xe9, 0x4f, 0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51,
|
||||
0x6e, 0x51, 0xbc, 0x51, 0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52,
|
||||
0x43, 0x53, 0x91, 0x53, 0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54,
|
||||
0x1a, 0x55, 0x69, 0x55, 0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56,
|
||||
0xf4, 0x56, 0x44, 0x57, 0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58,
|
||||
0xd1, 0x58, 0x21, 0x59, 0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a,
|
||||
0xb0, 0x5a, 0x01, 0x5b, 0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c,
|
||||
0x92, 0x5c, 0xe3, 0x5c, 0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e,
|
||||
0x77, 0x5e, 0xc8, 0x5e, 0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60,
|
||||
0x5e, 0x60, 0xb0, 0x60, 0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61,
|
||||
0x48, 0x62, 0x9a, 0x62, 0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63,
|
||||
0x34, 0x64, 0x87, 0x64, 0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65,
|
||||
0x24, 0x66, 0x76, 0x66, 0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67,
|
||||
0x15, 0x68, 0x69, 0x68, 0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69,
|
||||
0x0a, 0x6a, 0x5e, 0x6a, 0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b,
|
||||
0x01, 0x6c, 0x55, 0x6c, 0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d,
|
||||
0xfb, 0x6d, 0x50, 0x6e, 0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f,
|
||||
0xf8, 0x6f, 0x4d, 0x70, 0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71,
|
||||
0xf7, 0x71, 0x4d, 0x72, 0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73,
|
||||
0xfa, 0x73, 0x50, 0x74, 0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75,
|
||||
0xff, 0x75, 0x55, 0x76, 0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77,
|
||||
0x07, 0x78, 0x5e, 0x78, 0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79,
|
||||
0x11, 0x7a, 0x69, 0x7a, 0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b,
|
||||
0x1f, 0x7c, 0x77, 0x7c, 0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d,
|
||||
0x2f, 0x7e, 0x88, 0x7e, 0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f,
|
||||
0x42, 0x80, 0x9b, 0x80, 0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81,
|
||||
0x59, 0x82, 0xb2, 0x82, 0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84,
|
||||
0x72, 0x84, 0xcb, 0x84, 0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86,
|
||||
0x8e, 0x86, 0xe8, 0x86, 0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88,
|
||||
0xac, 0x88, 0x07, 0x89, 0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a,
|
||||
0xce, 0x8a, 0x2a, 0x8b, 0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c,
|
||||
0xf3, 0x8c, 0x4f, 0x8d, 0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e,
|
||||
0x1b, 0x8f, 0x77, 0x8f, 0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90,
|
||||
0x46, 0x91, 0xa2, 0x91, 0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93,
|
||||
0x73, 0x93, 0xd1, 0x93, 0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95,
|
||||
0xa4, 0x95, 0x02, 0x96, 0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97,
|
||||
0xd8, 0x97, 0x36, 0x98, 0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99,
|
||||
0x0f, 0x9a, 0x6e, 0x9a, 0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b,
|
||||
0x49, 0x9c, 0xa8, 0x9c, 0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e,
|
||||
0x86, 0x9e, 0xe6, 0x9e, 0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0,
|
||||
0xc6, 0xa0, 0x27, 0xa1, 0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2,
|
||||
0x0a, 0xa3, 0x6b, 0xa3, 0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4,
|
||||
0x50, 0xa5, 0xb2, 0xa5, 0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7,
|
||||
0x9a, 0xa7, 0xfc, 0xa7, 0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9,
|
||||
0xe7, 0xa9, 0x49, 0xaa, 0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab,
|
||||
0x37, 0xac, 0x9a, 0xac, 0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae,
|
||||
0x8a, 0xae, 0xed, 0xae, 0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0,
|
||||
0xe0, 0xb0, 0x45, 0xb1, 0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2,
|
||||
0x3a, 0xb3, 0x9f, 0xb3, 0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5,
|
||||
0x97, 0xb5, 0xfc, 0xb5, 0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7,
|
||||
0xf7, 0xb7, 0x5d, 0xb8, 0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9,
|
||||
0x5b, 0xba, 0xc1, 0xba, 0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc,
|
||||
0xc2, 0xbc, 0x29, 0xbd, 0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe,
|
||||
0x2c, 0xbf, 0x94, 0xbf, 0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1,
|
||||
0x9a, 0xc1, 0x02, 0xc2, 0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3,
|
||||
0x0b, 0xc4, 0x73, 0xc4, 0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6,
|
||||
0x7f, 0xc6, 0xe8, 0xc6, 0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8,
|
||||
0xf7, 0xc8, 0x60, 0xc9, 0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb,
|
||||
0x72, 0xcb, 0xdc, 0xcb, 0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd,
|
||||
0xf1, 0xcd, 0x5b, 0xce, 0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0,
|
||||
0x73, 0xd0, 0xde, 0xd0, 0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2,
|
||||
0xf8, 0xd2, 0x64, 0xd3, 0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5,
|
||||
0x82, 0xd5, 0xee, 0xd5, 0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7,
|
||||
0x0e, 0xd8, 0x7b, 0xd8, 0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda,
|
||||
0x9e, 0xda, 0x0c, 0xdb, 0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc,
|
||||
0x32, 0xdd, 0xa0, 0xdd, 0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf,
|
||||
0xc9, 0xdf, 0x38, 0xe0, 0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1,
|
||||
0x64, 0xe2, 0xd4, 0xe2, 0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4,
|
||||
0x03, 0xe5, 0x73, 0xe5, 0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7,
|
||||
0xa5, 0xe7, 0x16, 0xe8, 0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9,
|
||||
0x4b, 0xea, 0xbc, 0xea, 0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec,
|
||||
0xf5, 0xec, 0x66, 0xed, 0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef,
|
||||
0xa2, 0xef, 0x14, 0xf0, 0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1,
|
||||
0x53, 0xf2, 0xc6, 0xf2, 0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4,
|
||||
0x07, 0xf5, 0x7b, 0xf5, 0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7,
|
||||
0xc0, 0xf7, 0x34, 0xf8, 0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa,
|
||||
0x7c, 0xfa, 0xf1, 0xfa, 0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc,
|
||||
0x3c, 0xfd, 0xb2, 0xfd, 0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff,
|
||||
0x98, 0x16, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5,
|
||||
0xb0, 0x00, 0x91, 0xe1, 0x1f, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01,
|
||||
0xee, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, 0xf4, 0xff, 0xff, 0x1a,
|
||||
0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, 0xe5, 0xff, 0xff, 0xea,
|
||||
0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, 0x01, 0x40, 0xd0, 0x14,
|
||||
0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, 0xe0, 0xfe, 0xff, 0x0a,
|
||||
0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, 0xd9, 0xff, 0xff, 0xea,
|
||||
0xdc, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1,
|
||||
0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2,
|
||||
0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2,
|
||||
0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xd1, 0xfe, 0xff, 0x0a,
|
||||
0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea,
|
||||
0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x01, 0xc0, 0xc1, 0xe4, 0xc9, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2,
|
||||
0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x00, 0x00, 0x24, 0x03,
|
||||
0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f, 0xc8, 0x12, 0xe2, 0x15,
|
||||
0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22, 0x28, 0x25, 0x26, 0x28,
|
||||
0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33, 0xba, 0x36, 0x8c, 0x39,
|
||||
0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44, 0x1c, 0x47, 0xb4, 0x49,
|
||||
0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53, 0xf5, 0x55, 0x42, 0x58,
|
||||
0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60, 0xf1, 0x62, 0xe8, 0x64,
|
||||
0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c, 0xc9, 0x6d, 0x5e, 0x6f,
|
||||
0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75, 0x41, 0x76, 0x6b, 0x77,
|
||||
0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b, 0x29, 0x7c, 0xe3, 0x7c,
|
||||
0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f, 0x61, 0x7f, 0xa6, 0x7f,
|
||||
0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0, 0x8c, 0x10, 0x4f, 0xe2,
|
||||
0xb0, 0x00, 0x91, 0xe1, 0xa2, 0xfe, 0xff, 0xea, 0x00, 0x00, 0x3b, 0x00,
|
||||
0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01, 0x64, 0x01, 0x9f, 0x01,
|
||||
0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02, 0xca, 0x02, 0x05, 0x03,
|
||||
0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03, 0x31, 0x04, 0x6e, 0x04,
|
||||
0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05, 0x9b, 0x05, 0xd8, 0x05,
|
||||
0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06, 0x07, 0x07, 0x43, 0x07,
|
||||
0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08, 0x74, 0x08, 0xb1, 0x08,
|
||||
0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09, 0xe4, 0x09, 0x21, 0x0a,
|
||||
0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b, 0x56, 0x0b, 0x93, 0x0b,
|
||||
0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c, 0xc9, 0x0c, 0x07, 0x0d,
|
||||
0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e, 0x3f, 0x0e, 0x7d, 0x0e,
|
||||
0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f, 0xb6, 0x0f, 0xf5, 0x0f,
|
||||
0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10, 0x30, 0x11, 0x6f, 0x11,
|
||||
0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12, 0xac, 0x12, 0xeb, 0x12,
|
||||
0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13, 0x2a, 0x14, 0x6a, 0x14,
|
||||
0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15, 0xaa, 0x15, 0xea, 0x15,
|
||||
0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16, 0x2c, 0x17, 0x6c, 0x17,
|
||||
0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18, 0xb0, 0x18, 0xf0, 0x18,
|
||||
0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19, 0x36, 0x1a, 0x77, 0x1a,
|
||||
0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b, 0xbe, 0x1b, 0x00, 0x1c,
|
||||
0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d, 0x48, 0x1d, 0x8a, 0x1d,
|
||||
0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e, 0xd5, 0x1e, 0x17, 0x1f,
|
||||
0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20, 0x64, 0x20, 0xa6, 0x20,
|
||||
0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21, 0xf5, 0x21, 0x38, 0x22,
|
||||
0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23, 0x88, 0x23, 0xcb, 0x23,
|
||||
0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24, 0x1d, 0x25, 0x61, 0x25,
|
||||
0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26, 0xb4, 0x26, 0xf8, 0x26,
|
||||
0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28, 0x4e, 0x28, 0x92, 0x28,
|
||||
0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29, 0xea, 0x29, 0x2f, 0x2a,
|
||||
0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b, 0x88, 0x2b, 0xcd, 0x2b,
|
||||
0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c, 0x28, 0x2d, 0x6e, 0x2d,
|
||||
0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e, 0xcb, 0x2e, 0x11, 0x2f,
|
||||
0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30, 0x70, 0x30, 0xb6, 0x30,
|
||||
0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31, 0x17, 0x32, 0x5e, 0x32,
|
||||
0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33, 0xc1, 0x33, 0x08, 0x34,
|
||||
0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35, 0x6c, 0x35, 0xb4, 0x35,
|
||||
0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36, 0x1a, 0x37, 0x62, 0x37,
|
||||
0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38, 0xcb, 0x38, 0x13, 0x39,
|
||||
0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a, 0x7e, 0x3a, 0xc6, 0x3a,
|
||||
0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b, 0x33, 0x3c, 0x7c, 0x3c,
|
||||
0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d, 0xea, 0x3d, 0x34, 0x3e,
|
||||
0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f, 0xa4, 0x3f, 0xee, 0x3f,
|
||||
0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41, 0x61, 0x41, 0xab, 0x41,
|
||||
0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42, 0x1f, 0x43, 0x6a, 0x43,
|
||||
0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44, 0xe1, 0x44, 0x2c, 0x45,
|
||||
0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46, 0xa4, 0x46, 0xf0, 0x46,
|
||||
0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48, 0x6a, 0x48, 0xb6, 0x48,
|
||||
0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49, 0x33, 0x4a, 0x7f, 0x4a,
|
||||
0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b, 0xfe, 0x4b, 0x4a, 0x4c,
|
||||
0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d, 0xcb, 0x4d, 0x18, 0x4e,
|
||||
0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f, 0x9b, 0x4f, 0xe9, 0x4f,
|
||||
0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51, 0x6e, 0x51, 0xbc, 0x51,
|
||||
0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52, 0x43, 0x53, 0x91, 0x53,
|
||||
0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54, 0x1a, 0x55, 0x69, 0x55,
|
||||
0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56, 0xf4, 0x56, 0x44, 0x57,
|
||||
0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58, 0xd1, 0x58, 0x21, 0x59,
|
||||
0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a, 0xb0, 0x5a, 0x01, 0x5b,
|
||||
0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c, 0x92, 0x5c, 0xe3, 0x5c,
|
||||
0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e, 0x77, 0x5e, 0xc8, 0x5e,
|
||||
0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60, 0x5e, 0x60, 0xb0, 0x60,
|
||||
0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61, 0x48, 0x62, 0x9a, 0x62,
|
||||
0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63, 0x34, 0x64, 0x87, 0x64,
|
||||
0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65, 0x24, 0x66, 0x76, 0x66,
|
||||
0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67, 0x15, 0x68, 0x69, 0x68,
|
||||
0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69, 0x0a, 0x6a, 0x5e, 0x6a,
|
||||
0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b, 0x01, 0x6c, 0x55, 0x6c,
|
||||
0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d, 0xfb, 0x6d, 0x50, 0x6e,
|
||||
0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f, 0xf8, 0x6f, 0x4d, 0x70,
|
||||
0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71, 0xf7, 0x71, 0x4d, 0x72,
|
||||
0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73, 0xfa, 0x73, 0x50, 0x74,
|
||||
0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75, 0xff, 0x75, 0x55, 0x76,
|
||||
0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77, 0x07, 0x78, 0x5e, 0x78,
|
||||
0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79, 0x11, 0x7a, 0x69, 0x7a,
|
||||
0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b, 0x1f, 0x7c, 0x77, 0x7c,
|
||||
0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d, 0x2f, 0x7e, 0x88, 0x7e,
|
||||
0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f, 0x42, 0x80, 0x9b, 0x80,
|
||||
0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81, 0x59, 0x82, 0xb2, 0x82,
|
||||
0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84, 0x72, 0x84, 0xcb, 0x84,
|
||||
0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86, 0x8e, 0x86, 0xe8, 0x86,
|
||||
0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88, 0xac, 0x88, 0x07, 0x89,
|
||||
0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a, 0xce, 0x8a, 0x2a, 0x8b,
|
||||
0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c, 0xf3, 0x8c, 0x4f, 0x8d,
|
||||
0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e, 0x1b, 0x8f, 0x77, 0x8f,
|
||||
0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90, 0x46, 0x91, 0xa2, 0x91,
|
||||
0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93, 0x73, 0x93, 0xd1, 0x93,
|
||||
0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95, 0xa4, 0x95, 0x02, 0x96,
|
||||
0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97, 0xd8, 0x97, 0x36, 0x98,
|
||||
0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99, 0x0f, 0x9a, 0x6e, 0x9a,
|
||||
0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b, 0x49, 0x9c, 0xa8, 0x9c,
|
||||
0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e, 0x86, 0x9e, 0xe6, 0x9e,
|
||||
0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0, 0xc6, 0xa0, 0x27, 0xa1,
|
||||
0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2, 0x0a, 0xa3, 0x6b, 0xa3,
|
||||
0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4, 0x50, 0xa5, 0xb2, 0xa5,
|
||||
0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7, 0x9a, 0xa7, 0xfc, 0xa7,
|
||||
0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9, 0xe7, 0xa9, 0x49, 0xaa,
|
||||
0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab, 0x37, 0xac, 0x9a, 0xac,
|
||||
0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae, 0x8a, 0xae, 0xed, 0xae,
|
||||
0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0, 0xe0, 0xb0, 0x45, 0xb1,
|
||||
0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2, 0x3a, 0xb3, 0x9f, 0xb3,
|
||||
0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5, 0x97, 0xb5, 0xfc, 0xb5,
|
||||
0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7, 0xf7, 0xb7, 0x5d, 0xb8,
|
||||
0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9, 0x5b, 0xba, 0xc1, 0xba,
|
||||
0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc, 0xc2, 0xbc, 0x29, 0xbd,
|
||||
0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe, 0x2c, 0xbf, 0x94, 0xbf,
|
||||
0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1, 0x9a, 0xc1, 0x02, 0xc2,
|
||||
0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3, 0x0b, 0xc4, 0x73, 0xc4,
|
||||
0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6, 0x7f, 0xc6, 0xe8, 0xc6,
|
||||
0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8, 0xf7, 0xc8, 0x60, 0xc9,
|
||||
0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb, 0x72, 0xcb, 0xdc, 0xcb,
|
||||
0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd, 0xf1, 0xcd, 0x5b, 0xce,
|
||||
0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0, 0x73, 0xd0, 0xde, 0xd0,
|
||||
0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2, 0xf8, 0xd2, 0x64, 0xd3,
|
||||
0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5, 0x82, 0xd5, 0xee, 0xd5,
|
||||
0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7, 0x0e, 0xd8, 0x7b, 0xd8,
|
||||
0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda, 0x9e, 0xda, 0x0c, 0xdb,
|
||||
0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc, 0x32, 0xdd, 0xa0, 0xdd,
|
||||
0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf, 0xc9, 0xdf, 0x38, 0xe0,
|
||||
0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1, 0x64, 0xe2, 0xd4, 0xe2,
|
||||
0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4, 0x03, 0xe5, 0x73, 0xe5,
|
||||
0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7, 0xa5, 0xe7, 0x16, 0xe8,
|
||||
0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9, 0x4b, 0xea, 0xbc, 0xea,
|
||||
0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec, 0xf5, 0xec, 0x66, 0xed,
|
||||
0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef, 0xa2, 0xef, 0x14, 0xf0,
|
||||
0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1, 0x53, 0xf2, 0xc6, 0xf2,
|
||||
0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4, 0x07, 0xf5, 0x7b, 0xf5,
|
||||
0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7, 0xc0, 0xf7, 0x34, 0xf8,
|
||||
0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa, 0x7c, 0xfa, 0xf1, 0xfa,
|
||||
0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc, 0x3c, 0xfd, 0xb2, 0xfd,
|
||||
0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff, 0xa0, 0x16, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5, 0xb0, 0x00, 0x91, 0xe1,
|
||||
0x1d, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
|
@ -646,74 +649,73 @@ unsigned char bios_arm7_bin[] = {
|
|||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
|
||||
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
||||
0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
|
||||
0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
|
||||
0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
|
||||
0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13,
|
||||
0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
|
||||
0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19,
|
||||
0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c,
|
||||
0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20,
|
||||
0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25,
|
||||
0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b,
|
||||
0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31,
|
||||
0x32, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38,
|
||||
0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x42, 0x43, 0x44, 0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a,
|
||||
0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55,
|
||||
0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
|
||||
0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71,
|
||||
0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20,
|
||||
0x21, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25,
|
||||
0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b,
|
||||
0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31,
|
||||
0x31, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38,
|
||||
0x39, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40,
|
||||
0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a,
|
||||
0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
|
||||
0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62,
|
||||
0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70,
|
||||
0x71, 0x73, 0x74, 0x75, 0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40,
|
||||
0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a,
|
||||
0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
|
||||
0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
|
||||
0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70,
|
||||
0x71, 0x72, 0x74, 0x75, 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40,
|
||||
0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a,
|
||||
0x4b, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
|
||||
0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
|
||||
0x62, 0x63, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f,
|
||||
0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3,
|
||||
0xff, 0x20, 0xa0, 0xe3, 0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3,
|
||||
0x05, 0x2c, 0x82, 0xe3, 0x62, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3,
|
||||
0x01, 0x03, 0xc1, 0xe5, 0x5f, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9,
|
||||
0x01, 0x03, 0xa0, 0xe3, 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5,
|
||||
0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2,
|
||||
0x00, 0x00, 0xd1, 0xe7, 0x56, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03,
|
||||
0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3,
|
||||
0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a,
|
||||
0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
|
||||
0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1,
|
||||
0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2,
|
||||
0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3,
|
||||
0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8,
|
||||
0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06,
|
||||
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09,
|
||||
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a,
|
||||
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d,
|
||||
0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11,
|
||||
0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14,
|
||||
0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16,
|
||||
0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a,
|
||||
0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e,
|
||||
0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22,
|
||||
0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
|
||||
0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d,
|
||||
0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33,
|
||||
0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b,
|
||||
0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44,
|
||||
0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67,
|
||||
0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75, 0x76,
|
||||
0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20, 0x21, 0x21, 0x21, 0x22,
|
||||
0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27,
|
||||
0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d,
|
||||
0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33,
|
||||
0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 0x39, 0x39, 0x3a, 0x3b,
|
||||
0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40, 0x41, 0x42, 0x43, 0x43,
|
||||
0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d,
|
||||
0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x65, 0x66,
|
||||
0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x73, 0x74, 0x75,
|
||||
0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40, 0x41, 0x42, 0x43, 0x43,
|
||||
0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4c, 0x4d,
|
||||
0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x64, 0x65, 0x66,
|
||||
0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70, 0x71, 0x72, 0x74, 0x75,
|
||||
0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40, 0x41, 0x42, 0x42, 0x43,
|
||||
0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66,
|
||||
0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75,
|
||||
0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x00, 0x00, 0x00, 0x00,
|
||||
0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3, 0xff, 0x20, 0xa0, 0xe3,
|
||||
0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3, 0x05, 0x2c, 0x82, 0xe3,
|
||||
0x60, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x01, 0x03, 0xc1, 0xe5,
|
||||
0x5d, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3,
|
||||
0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
|
||||
0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2, 0x00, 0x00, 0xd1, 0xe7,
|
||||
0x54, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3,
|
||||
0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5,
|
||||
0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5,
|
||||
0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1,
|
||||
0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3,
|
||||
0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1,
|
||||
0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
|
||||
0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
@ -1395,10 +1397,10 @@ unsigned char bios_arm7_bin[] = {
|
|||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
unsigned char bios_arm9_bin[] = {
|
||||
std::array<u8, ARM9BIOSSize> bios_arm9_bin = {
|
||||
0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea,
|
||||
0x3c, 0x00, 0x00, 0xea, 0x3b, 0x00, 0x00, 0xea, 0x3a, 0x00, 0x00, 0xea,
|
||||
0xad, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
|
||||
0xaf, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
@ -1422,17 +1424,17 @@ unsigned char bios_arm9_bin[] = {
|
|||
0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5,
|
||||
0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3,
|
||||
0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1,
|
||||
0xf8, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0x00, 0x07, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0xe0, 0x01, 0xff, 0xff, 0x20, 0x02, 0xff, 0xff, 0x18, 0x02, 0xff, 0xff,
|
||||
0xd4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0x4c, 0x02, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xa8, 0x02, 0xff, 0xff,
|
||||
0x28, 0x03, 0xff, 0xff, 0x68, 0x03, 0xff, 0xff, 0xb4, 0x03, 0xff, 0xff,
|
||||
0x4c, 0x04, 0xff, 0xff, 0x54, 0x04, 0xff, 0xff, 0xe8, 0x04, 0xff, 0xff,
|
||||
0x64, 0x05, 0xff, 0xff, 0x10, 0x06, 0xff, 0xff, 0x14, 0x06, 0xff, 0xff,
|
||||
0x14, 0x06, 0xff, 0xff, 0x6c, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0x98, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0x54, 0x04, 0xff, 0xff, 0x5c, 0x04, 0xff, 0xff, 0xf0, 0x04, 0xff, 0xff,
|
||||
0x6c, 0x05, 0xff, 0xff, 0x18, 0x06, 0xff, 0xff, 0x1c, 0x06, 0xff, 0xff,
|
||||
0x1c, 0x06, 0xff, 0xff, 0x74, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0xa0, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff,
|
||||
0xc8, 0x06, 0xff, 0xff, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3,
|
||||
0xd0, 0x06, 0xff, 0xff, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3,
|
||||
0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1,
|
||||
0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xa0, 0xe3, 0x90, 0x0f, 0x07, 0xee, 0xf4, 0xff, 0xff, 0xea,
|
||||
|
@ -1476,83 +1478,83 @@ unsigned char bios_arm9_bin[] = {
|
|||
0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c,
|
||||
0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44,
|
||||
0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2,
|
||||
0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0,
|
||||
0xff, 0x04, 0xc0, 0xe3, 0xff, 0x08, 0xc0, 0xe3, 0xa2, 0x20, 0xb0, 0xe1,
|
||||
0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, 0x80, 0x50, 0x04, 0xe0,
|
||||
0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0,
|
||||
0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x53, 0x04, 0xe0,
|
||||
0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0,
|
||||
0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1,
|
||||
0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a,
|
||||
0x20, 0x00, 0xbd, 0xe8, 0x59, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3,
|
||||
0x57, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1,
|
||||
0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5,
|
||||
0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3,
|
||||
0x15, 0x5c, 0xa0, 0xe1, 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3,
|
||||
0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3,
|
||||
0x83, 0x31, 0xa0, 0x01, 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01,
|
||||
0x04, 0x00, 0x5c, 0xe3, 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3,
|
||||
0x01, 0x60, 0xd0, 0x04, 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0,
|
||||
0x36, 0x6c, 0xa0, 0xe1, 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03,
|
||||
0x04, 0x90, 0x89, 0x10, 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0,
|
||||
0x20, 0x00, 0x58, 0xe3, 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03,
|
||||
0x00, 0x80, 0xa0, 0x03, 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a,
|
||||
0xe0, 0x03, 0xbd, 0xe8, 0x32, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0x23, 0x24, 0xb0, 0xe1, 0x2f, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4,
|
||||
0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a,
|
||||
0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1,
|
||||
0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2,
|
||||
0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x1f, 0xff, 0xff, 0x0a,
|
||||
0x01, 0xe0, 0x5e, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1,
|
||||
0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x16, 0xff, 0xff, 0x0a,
|
||||
0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea,
|
||||
0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x10, 0xff, 0xff, 0x0a,
|
||||
0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4,
|
||||
0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a,
|
||||
0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1,
|
||||
0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2,
|
||||
0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3,
|
||||
0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11,
|
||||
0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x20, 0x00, 0xbd, 0x08, 0xf9, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2,
|
||||
0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a,
|
||||
0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04,
|
||||
0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11,
|
||||
0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0,
|
||||
0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, 0x20, 0x00, 0xbd, 0xe8,
|
||||
0x57, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, 0x55, 0xff, 0xff, 0xea,
|
||||
0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, 0x02, 0xc0, 0xd2, 0xe5,
|
||||
0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, 0xa4, 0x2f, 0xa0, 0xe1,
|
||||
0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, 0x15, 0x5c, 0xa0, 0xe1,
|
||||
0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3,
|
||||
0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, 0x83, 0x31, 0xa0, 0x01,
|
||||
0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, 0x04, 0x00, 0x5c, 0xe3,
|
||||
0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, 0x01, 0x60, 0xd0, 0x04,
|
||||
0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, 0x36, 0x6c, 0xa0, 0xe1,
|
||||
0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, 0x04, 0x90, 0x89, 0x10,
|
||||
0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, 0x20, 0x00, 0x58, 0xe3,
|
||||
0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, 0x00, 0x80, 0xa0, 0x03,
|
||||
0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, 0xe0, 0x03, 0xbd, 0xe8,
|
||||
0x30, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1,
|
||||
0x2d, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3,
|
||||
0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4,
|
||||
0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1,
|
||||
0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0,
|
||||
0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x01, 0x40, 0xc1, 0xe4, 0x1d, 0xff, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2,
|
||||
0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xed, 0xff, 0xff, 0x5a,
|
||||
0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x01, 0x40, 0xc1, 0xe4, 0x14, 0xff, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1,
|
||||
0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0x23, 0x24, 0xb0, 0xe1, 0x0e, 0xff, 0xff, 0x0a, 0x20, 0x00, 0x2d, 0xe9,
|
||||
0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3,
|
||||
0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4,
|
||||
0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1,
|
||||
0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0,
|
||||
0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xdc, 0x04,
|
||||
0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11,
|
||||
0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08,
|
||||
0xeb, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a,
|
||||
0xd9, 0xff, 0xff, 0xea, 0xe7, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3,
|
||||
0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4,
|
||||
0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0xdc, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a,
|
||||
0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xd4, 0xfe, 0xff, 0x0a,
|
||||
0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea,
|
||||
0x04, 0x30, 0x90, 0xe4, 0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1,
|
||||
0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4,
|
||||
0xfa, 0xff, 0xff, 0x1a, 0xc6, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0xb2, 0xc0, 0xd0, 0xe0, 0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0,
|
||||
0x01, 0x20, 0xc2, 0xe3, 0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0,
|
||||
0x02, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0,
|
||||
0xfa, 0xff, 0xff, 0x1a, 0xba, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3,
|
||||
0x00, 0x03, 0xc1, 0xe5, 0xb7, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9,
|
||||
0x11, 0x0f, 0x19, 0xee, 0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2,
|
||||
0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
|
||||
0x04, 0xf0, 0x5e, 0xe2, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3,
|
||||
0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5,
|
||||
0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5,
|
||||
0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1,
|
||||
0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3,
|
||||
0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1,
|
||||
0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
|
||||
0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xf7, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, 0xf4, 0xff, 0xff, 0x1a,
|
||||
0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, 0xe5, 0xff, 0xff, 0xea,
|
||||
0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, 0x01, 0x40, 0xd0, 0x14,
|
||||
0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2,
|
||||
0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, 0xe9, 0xfe, 0xff, 0x0a,
|
||||
0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, 0xd9, 0xff, 0xff, 0xea,
|
||||
0xe5, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1,
|
||||
0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2,
|
||||
0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2,
|
||||
0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xda, 0xfe, 0xff, 0x0a,
|
||||
0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea,
|
||||
0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x01, 0xc0, 0xc1, 0xe4, 0xd2, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2,
|
||||
0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4,
|
||||
0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1, 0x01, 0xc0, 0xc1, 0xe4,
|
||||
0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2,
|
||||
0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4, 0xfa, 0xff, 0xff, 0x1a,
|
||||
0xc4, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0xb2, 0xc0, 0xd0, 0xe0,
|
||||
0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0, 0x01, 0x20, 0xc2, 0xe3,
|
||||
0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0, 0x02, 0x20, 0x52, 0xe2,
|
||||
0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0, 0xfa, 0xff, 0xff, 0x1a,
|
||||
0xb8, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x00, 0x03, 0xc1, 0xe5,
|
||||
0xb5, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x11, 0x0f, 0x19, 0xee,
|
||||
0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2, 0x0f, 0xe0, 0xa0, 0xe1,
|
||||
0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2,
|
||||
0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3,
|
||||
0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2,
|
||||
0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3,
|
||||
0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1,
|
||||
0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1,
|
||||
0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1,
|
||||
0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2,
|
||||
0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
@ -1738,4 +1740,5 @@ unsigned char bios_arm9_bin[] = {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
};
|
||||
}
|
|
@ -28,7 +28,13 @@
|
|||
#ifndef FREEBIOS_H
|
||||
#define FREEBIOS_H
|
||||
|
||||
extern unsigned char bios_arm7_bin[16384];
|
||||
extern unsigned char bios_arm9_bin[4096];
|
||||
#include <array>
|
||||
#include "MemConstants.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
extern std::array<u8, ARM7BIOSSize> bios_arm7_bin;
|
||||
extern std::array<u8, ARM9BIOSSize> bios_arm9_bin;
|
||||
}
|
||||
|
||||
#endif // FREEBIOS_H
|
||||
|
|
1811
src/GBACart.cpp
1811
src/GBACart.cpp
File diff suppressed because it is too large
Load Diff
546
src/GBACart.h
546
src/GBACart.h
|
@ -1,257 +1,289 @@
|
|||
/*
|
||||
Copyright 2016-2022 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 GBACART_H
|
||||
#define GBACART_H
|
||||
|
||||
#include <memory>
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace GBACart
|
||||
{
|
||||
|
||||
enum CartType
|
||||
{
|
||||
Default = 0x001,
|
||||
Game = 0x101,
|
||||
GameSolarSensor = 0x102,
|
||||
RAMExpansion = 0x201,
|
||||
};
|
||||
|
||||
// CartCommon -- base code shared by all cart types
|
||||
class CartCommon
|
||||
{
|
||||
public:
|
||||
CartCommon();
|
||||
virtual ~CartCommon();
|
||||
|
||||
virtual u32 Type() const = 0;
|
||||
virtual u32 Checksum() const { return 0; }
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual void DoSavestate(Savestate* file);
|
||||
|
||||
virtual void SetupSave(u32 type);
|
||||
virtual void LoadSave(const u8* savedata, u32 savelen);
|
||||
|
||||
virtual int SetInput(int num, bool pressed);
|
||||
|
||||
virtual u16 ROMRead(u32 addr);
|
||||
virtual void ROMWrite(u32 addr, u16 val);
|
||||
|
||||
virtual u8 SRAMRead(u32 addr);
|
||||
virtual void SRAMWrite(u32 addr, u8 val);
|
||||
|
||||
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
|
||||
[[nodiscard]] virtual u32 GetROMLength() const { return 0; }
|
||||
|
||||
virtual u8* GetSaveMemory() const;
|
||||
virtual u32 GetSaveMemoryLength() const;
|
||||
};
|
||||
|
||||
// CartGame -- regular retail game cart (ROM, SRAM)
|
||||
class CartGame : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartGame(u8* rom, u32 len);
|
||||
virtual ~CartGame() override;
|
||||
|
||||
virtual u32 Type() const override { return CartType::Game; }
|
||||
virtual u32 Checksum() const override;
|
||||
|
||||
virtual void Reset() override;
|
||||
|
||||
virtual void DoSavestate(Savestate* file) override;
|
||||
|
||||
virtual void SetupSave(u32 type) override;
|
||||
virtual void LoadSave(const u8* savedata, u32 savelen) override;
|
||||
|
||||
virtual u16 ROMRead(u32 addr) override;
|
||||
virtual void ROMWrite(u32 addr, u16 val) override;
|
||||
|
||||
virtual u8 SRAMRead(u32 addr) override;
|
||||
virtual void SRAMWrite(u32 addr, u8 val) override;
|
||||
|
||||
[[nodiscard]] const u8* GetROM() const override { return ROM; }
|
||||
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
|
||||
|
||||
virtual u8* GetSaveMemory() const override;
|
||||
virtual u32 GetSaveMemoryLength() const override;
|
||||
protected:
|
||||
virtual void ProcessGPIO();
|
||||
|
||||
u8 SRAMRead_EEPROM(u32 addr);
|
||||
void SRAMWrite_EEPROM(u32 addr, u8 val);
|
||||
u8 SRAMRead_FLASH(u32 addr);
|
||||
void SRAMWrite_FLASH(u32 addr, u8 val);
|
||||
u8 SRAMRead_SRAM(u32 addr);
|
||||
void SRAMWrite_SRAM(u32 addr, u8 val);
|
||||
|
||||
u8* ROM;
|
||||
u32 ROMLength;
|
||||
|
||||
struct
|
||||
{
|
||||
u16 data;
|
||||
u16 direction;
|
||||
u16 control;
|
||||
|
||||
} GPIO;
|
||||
|
||||
enum SaveType
|
||||
{
|
||||
S_NULL,
|
||||
S_EEPROM4K,
|
||||
S_EEPROM64K,
|
||||
S_SRAM256K,
|
||||
S_FLASH512K,
|
||||
S_FLASH1M
|
||||
};
|
||||
|
||||
// from DeSmuME
|
||||
struct
|
||||
{
|
||||
u8 state;
|
||||
u8 cmd;
|
||||
u8 device;
|
||||
u8 manufacturer;
|
||||
u8 bank;
|
||||
|
||||
} SRAMFlashState;
|
||||
|
||||
u8* SRAM;
|
||||
u32 SRAMLength;
|
||||
SaveType SRAMType;
|
||||
};
|
||||
|
||||
// CartGameSolarSensor -- Boktai game cart
|
||||
class CartGameSolarSensor : public CartGame
|
||||
{
|
||||
public:
|
||||
CartGameSolarSensor(u8* rom, u32 len);
|
||||
virtual ~CartGameSolarSensor() override;
|
||||
|
||||
virtual u32 Type() const override { return CartType::GameSolarSensor; }
|
||||
|
||||
virtual void Reset() override;
|
||||
|
||||
virtual void DoSavestate(Savestate* file) override;
|
||||
|
||||
virtual int SetInput(int num, bool pressed) override;
|
||||
|
||||
private:
|
||||
virtual void ProcessGPIO() override;
|
||||
|
||||
static const int kLuxLevels[11];
|
||||
|
||||
bool LightEdge;
|
||||
u8 LightCounter;
|
||||
u8 LightSample;
|
||||
u8 LightLevel;
|
||||
};
|
||||
|
||||
// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
|
||||
class CartRAMExpansion : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartRAMExpansion();
|
||||
~CartRAMExpansion() override;
|
||||
|
||||
virtual u32 Type() const override { return CartType::RAMExpansion; }
|
||||
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
u16 ROMRead(u32 addr) override;
|
||||
void ROMWrite(u32 addr, u16 val) override;
|
||||
|
||||
private:
|
||||
u8 RAM[0x800000];
|
||||
u16 RAMEnable;
|
||||
};
|
||||
|
||||
// possible inputs for GBA carts that might accept user input
|
||||
enum
|
||||
{
|
||||
Input_SolarSensorDown = 0,
|
||||
Input_SolarSensorUp,
|
||||
};
|
||||
|
||||
extern std::unique_ptr<CartCommon> Cart;
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
void Reset();
|
||||
|
||||
void DoSavestate(Savestate* file);
|
||||
|
||||
/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass
|
||||
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
|
||||
/// @param romdata The ROM data to parse.
|
||||
/// The returned cartridge will contain a copy of this data,
|
||||
/// so the caller may deallocate \c romdata after this function returns.
|
||||
/// @param romlen The length of the ROM data in bytes.
|
||||
/// @returns A \c GBACart::CartCommon object representing the parsed ROM,
|
||||
/// or \c nullptr if the ROM data couldn't be parsed.
|
||||
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
|
||||
|
||||
/// Applies the GBACartData to the emulator state and unloads an existing ROM if any.
|
||||
/// Upon successful insertion, \c cart will be nullptr and the global GBACart state
|
||||
/// (\c CartROM, CartInserted, etc.) will be updated.
|
||||
bool InsertROM(std::unique_ptr<CartCommon>&& cart);
|
||||
bool LoadROM(const u8* romdata, u32 romlen);
|
||||
void LoadSave(const u8* savedata, u32 savelen);
|
||||
|
||||
void LoadAddon(int type);
|
||||
|
||||
void EjectCart();
|
||||
|
||||
//bool LoadROM(const char* path, const char* sram);
|
||||
//bool LoadROM(const u8* romdata, u32 filelength, const char *sram);
|
||||
//void RelocateSave(const char* path, bool write);
|
||||
|
||||
// TODO: make more flexible, support nonbinary inputs
|
||||
int SetInput(int num, bool pressed);
|
||||
|
||||
void SetOpenBusDecay(u16 val);
|
||||
|
||||
u16 ROMRead(u32 addr);
|
||||
void ROMWrite(u32 addr, u16 val);
|
||||
|
||||
u8 SRAMRead(u32 addr);
|
||||
void SRAMWrite(u32 addr, u8 val);
|
||||
|
||||
/// This function is intended to allow frontends to save and load SRAM
|
||||
/// without using melonDS APIs.
|
||||
/// Modifying the emulated SRAM for any other reason is strongly discouraged.
|
||||
/// The returned pointer may be invalidated if the emulator is reset,
|
||||
/// or when a new game is loaded.
|
||||
/// Consequently, don't store the returned pointer for any longer than necessary.
|
||||
/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
|
||||
u8* GetSaveMemory();
|
||||
|
||||
/// @returns The length of the buffer returned by ::GetSaveMemory()
|
||||
/// if a cart is loaded and supports SRAM, otherwise zero.
|
||||
u32 GetSaveMemoryLength();
|
||||
|
||||
}
|
||||
|
||||
#endif // GBACART_H
|
||||
/*
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
melonDS is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#ifndef GBACART_H
|
||||
#define GBACART_H
|
||||
|
||||
#include <memory>
|
||||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace melonDS::GBACart
|
||||
{
|
||||
|
||||
enum CartType
|
||||
{
|
||||
Default = 0x001,
|
||||
Game = 0x101,
|
||||
GameSolarSensor = 0x102,
|
||||
RAMExpansion = 0x201,
|
||||
};
|
||||
|
||||
// CartCommon -- base code shared by all cart types
|
||||
class CartCommon
|
||||
{
|
||||
public:
|
||||
virtual ~CartCommon() = default;
|
||||
|
||||
[[nodiscard]] u32 Type() const { return CartType; }
|
||||
virtual u32 Checksum() const { return 0; }
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
virtual void DoSavestate(Savestate* file);
|
||||
|
||||
virtual int SetInput(int num, bool pressed);
|
||||
|
||||
virtual u16 ROMRead(u32 addr) const;
|
||||
virtual void ROMWrite(u32 addr, u16 val);
|
||||
|
||||
virtual u8 SRAMRead(u32 addr);
|
||||
virtual void SRAMWrite(u32 addr, u8 val);
|
||||
|
||||
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
|
||||
[[nodiscard]] virtual u32 GetROMLength() const { return 0; }
|
||||
|
||||
virtual u8* GetSaveMemory() const;
|
||||
virtual u32 GetSaveMemoryLength() const;
|
||||
virtual void SetSaveMemory(const u8* savedata, u32 savelen);
|
||||
protected:
|
||||
CartCommon(GBACart::CartType type);
|
||||
friend class GBACartSlot;
|
||||
private:
|
||||
GBACart::CartType CartType;
|
||||
};
|
||||
|
||||
// CartGame -- regular retail game cart (ROM, SRAM)
|
||||
class CartGame : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
|
||||
CartGame(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
|
||||
~CartGame() override;
|
||||
|
||||
u32 Checksum() const override;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
u16 ROMRead(u32 addr) const override;
|
||||
void ROMWrite(u32 addr, u16 val) override;
|
||||
|
||||
u8 SRAMRead(u32 addr) override;
|
||||
void SRAMWrite(u32 addr, u8 val) override;
|
||||
|
||||
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
|
||||
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
|
||||
|
||||
u8* GetSaveMemory() const override;
|
||||
u32 GetSaveMemoryLength() const override;
|
||||
void SetSaveMemory(const u8* savedata, u32 savelen) override;
|
||||
protected:
|
||||
virtual void ProcessGPIO();
|
||||
|
||||
u8 SRAMRead_EEPROM(u32 addr);
|
||||
void SRAMWrite_EEPROM(u32 addr, u8 val);
|
||||
u8 SRAMRead_FLASH(u32 addr);
|
||||
void SRAMWrite_FLASH(u32 addr, u8 val);
|
||||
u8 SRAMRead_SRAM(u32 addr);
|
||||
void SRAMWrite_SRAM(u32 addr, u8 val);
|
||||
|
||||
std::unique_ptr<u8[]> ROM;
|
||||
u32 ROMLength;
|
||||
|
||||
struct
|
||||
{
|
||||
u16 data;
|
||||
u16 direction;
|
||||
u16 control;
|
||||
|
||||
} GPIO {};
|
||||
|
||||
enum SaveType
|
||||
{
|
||||
S_NULL,
|
||||
S_EEPROM4K,
|
||||
S_EEPROM64K,
|
||||
S_SRAM256K,
|
||||
S_FLASH512K,
|
||||
S_FLASH1M
|
||||
};
|
||||
|
||||
// from DeSmuME
|
||||
struct
|
||||
{
|
||||
u8 state;
|
||||
u8 cmd;
|
||||
u8 device;
|
||||
u8 manufacturer;
|
||||
u8 bank;
|
||||
|
||||
} SRAMFlashState {};
|
||||
|
||||
std::unique_ptr<u8[]> SRAM = nullptr;
|
||||
u32 SRAMLength = 0;
|
||||
SaveType SRAMType = S_NULL;
|
||||
private:
|
||||
void SetupSave(u32 type);
|
||||
};
|
||||
|
||||
// CartGameSolarSensor -- Boktai game cart
|
||||
class CartGameSolarSensor : public CartGame
|
||||
{
|
||||
public:
|
||||
CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen);
|
||||
CartGameSolarSensor(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen);
|
||||
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
int SetInput(int num, bool pressed) override;
|
||||
|
||||
protected:
|
||||
void ProcessGPIO() override;
|
||||
|
||||
private:
|
||||
static const int kLuxLevels[11];
|
||||
|
||||
bool LightEdge = false;
|
||||
u8 LightCounter = 0;
|
||||
u8 LightSample = 0;
|
||||
u8 LightLevel = 0;
|
||||
};
|
||||
|
||||
// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
|
||||
class CartRAMExpansion : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartRAMExpansion();
|
||||
~CartRAMExpansion() override;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
u16 ROMRead(u32 addr) const override;
|
||||
void ROMWrite(u32 addr, u16 val) override;
|
||||
|
||||
private:
|
||||
u8 RAM[0x800000] {};
|
||||
u16 RAMEnable = 0;
|
||||
};
|
||||
|
||||
// possible inputs for GBA carts that might accept user input
|
||||
enum
|
||||
{
|
||||
Input_SolarSensorDown = 0,
|
||||
Input_SolarSensorUp,
|
||||
};
|
||||
|
||||
class GBACartSlot
|
||||
{
|
||||
public:
|
||||
GBACartSlot(std::unique_ptr<CartCommon>&& cart = nullptr) noexcept;
|
||||
~GBACartSlot() noexcept = default;
|
||||
void Reset() noexcept;
|
||||
void DoSavestate(Savestate* file) noexcept;
|
||||
|
||||
/// Ejects the cart in the GBA slot (if any)
|
||||
/// and inserts the given one.
|
||||
///
|
||||
/// To insert a cart that does not require ROM data
|
||||
/// (such as the RAM expansion pack),
|
||||
/// create it manually with std::make_unique and pass it here.
|
||||
///
|
||||
/// @param cart Movable \c unique_ptr to the GBA cart object.
|
||||
/// May be \c nullptr, in which case the cart slot remains empty.
|
||||
/// @post \c cart is \c nullptr and the underlying object
|
||||
/// is moved into the cart slot.
|
||||
void SetCart(std::unique_ptr<CartCommon>&& cart) noexcept;
|
||||
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
|
||||
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
|
||||
|
||||
void LoadAddon(int type) noexcept;
|
||||
|
||||
/// @return The cart that was in the cart slot if any,
|
||||
/// or \c nullptr if the cart slot was empty.
|
||||
std::unique_ptr<CartCommon> EjectCart() noexcept;
|
||||
|
||||
// TODO: make more flexible, support nonbinary inputs
|
||||
int SetInput(int num, bool pressed) noexcept;
|
||||
|
||||
void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; }
|
||||
|
||||
u16 ROMRead(u32 addr) const noexcept;
|
||||
void ROMWrite(u32 addr, u16 val) noexcept;
|
||||
|
||||
u8 SRAMRead(u32 addr) noexcept;
|
||||
void SRAMWrite(u32 addr, u8 val) noexcept;
|
||||
|
||||
/// This function is intended to allow frontends to save and load SRAM
|
||||
/// without using melonDS APIs.
|
||||
/// Modifying the emulated SRAM for any other reason is strongly discouraged.
|
||||
/// The returned pointer may be invalidated if the emulator is reset,
|
||||
/// or when a new game is loaded.
|
||||
/// Consequently, don't store the returned pointer for any longer than necessary.
|
||||
/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
|
||||
[[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
|
||||
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
|
||||
|
||||
/// Sets the loaded cart's SRAM.
|
||||
/// Does nothing if no cart is inserted
|
||||
/// or the inserted cart doesn't support SRAM.
|
||||
///
|
||||
/// @param savedata Buffer containing the raw contents of the SRAM.
|
||||
/// The contents of this buffer are copied into the cart slot,
|
||||
/// so the caller may dispose of it after this method returns.
|
||||
/// @param savelen The length of the buffer in \c savedata, in bytes.
|
||||
void SetSaveMemory(const u8* savedata, u32 savelen) noexcept;
|
||||
|
||||
/// @returns The length of the buffer returned by ::GetSaveMemory()
|
||||
/// if a cart is loaded and supports SRAM, otherwise zero.
|
||||
[[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; }
|
||||
private:
|
||||
std::unique_ptr<CartCommon> Cart = nullptr;
|
||||
u16 OpenBusDecay = 0;
|
||||
};
|
||||
|
||||
/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass
|
||||
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
|
||||
/// @param romdata The ROM data to parse.
|
||||
/// The returned cartridge will contain a copy of this data,
|
||||
/// so the caller may deallocate \c romdata after this function returns.
|
||||
/// @param romlen The length of the ROM data in bytes.
|
||||
/// @returns A \c GBACart::CartCommon object representing the parsed ROM,
|
||||
/// or \c nullptr if the ROM data couldn't be parsed.
|
||||
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
|
||||
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen);
|
||||
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, const u8* sramdata, u32 sramlen);
|
||||
|
||||
/// @param romdata The ROM data to parse. Will be moved-from.
|
||||
/// @param romlen Length of romdata in bytes.
|
||||
/// @param sramdata The save data to add to the cart.
|
||||
/// May be \c nullptr, in which case the cart will have no save data.
|
||||
/// @param sramlen Length of sramdata in bytes.
|
||||
/// May be zero, in which case the cart will have no save data.
|
||||
/// @return Unique pointer to the parsed GBA cart,
|
||||
/// or \c nullptr if there was an error.
|
||||
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen);
|
||||
|
||||
}
|
||||
|
||||
#endif // GBACART_H
|
||||
|
|
567
src/GPU.cpp
567
src/GPU.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,79 +20,27 @@
|
|||
#include "NDS.h"
|
||||
#include "GPU.h"
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
#include "ARMJIT.h"
|
||||
#endif
|
||||
|
||||
#include "GPU2D_Soft.h"
|
||||
#include "GPU3D.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace GPU
|
||||
{
|
||||
|
||||
#define LINE_CYCLES (355*6)
|
||||
#define HBLANK_CYCLES (48+(256*6))
|
||||
#define FRAME_CYCLES (LINE_CYCLES * 263)
|
||||
|
||||
u16 VCount;
|
||||
u32 NextVCount;
|
||||
u16 TotalScanlines;
|
||||
enum
|
||||
{
|
||||
LCD_StartHBlank = 0,
|
||||
LCD_StartScanline,
|
||||
LCD_FinishFrame,
|
||||
};
|
||||
|
||||
bool RunFIFO;
|
||||
|
||||
u16 DispStat[2], VMatch[2];
|
||||
|
||||
u8 Palette[2*1024];
|
||||
u8 OAM[2*1024];
|
||||
|
||||
u8 VRAM_A[128*1024];
|
||||
u8 VRAM_B[128*1024];
|
||||
u8 VRAM_C[128*1024];
|
||||
u8 VRAM_D[128*1024];
|
||||
u8 VRAM_E[ 64*1024];
|
||||
u8 VRAM_F[ 16*1024];
|
||||
u8 VRAM_G[ 16*1024];
|
||||
u8 VRAM_H[ 32*1024];
|
||||
u8 VRAM_I[ 16*1024];
|
||||
u8* const VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I};
|
||||
u32 const VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF};
|
||||
|
||||
u8 VRAMCNT[9];
|
||||
u8 VRAMSTAT;
|
||||
|
||||
u32 VRAMMap_LCDC;
|
||||
|
||||
u32 VRAMMap_ABG[0x20];
|
||||
u32 VRAMMap_AOBJ[0x10];
|
||||
u32 VRAMMap_BBG[0x8];
|
||||
u32 VRAMMap_BOBJ[0x8];
|
||||
|
||||
u32 VRAMMap_ABGExtPal[4];
|
||||
u32 VRAMMap_AOBJExtPal;
|
||||
u32 VRAMMap_BBGExtPal[4];
|
||||
u32 VRAMMap_BOBJExtPal;
|
||||
|
||||
u32 VRAMMap_Texture[4];
|
||||
u32 VRAMMap_TexPal[8];
|
||||
|
||||
u32 VRAMMap_ARM7[2];
|
||||
|
||||
u8* VRAMPtr_ABG[0x20];
|
||||
u8* VRAMPtr_AOBJ[0x10];
|
||||
u8* VRAMPtr_BBG[0x8];
|
||||
u8* VRAMPtr_BOBJ[0x8];
|
||||
|
||||
int FrontBuffer;
|
||||
u32* Framebuffer[2][2];
|
||||
int Renderer = 0;
|
||||
|
||||
GPU2D::Unit GPU2D_A(0);
|
||||
GPU2D::Unit GPU2D_B(1);
|
||||
|
||||
std::unique_ptr<GPU2D::Renderer2D> GPU2D_Renderer = {};
|
||||
|
||||
/*
|
||||
VRAM invalidation tracking
|
||||
|
@ -115,75 +63,32 @@ std::unique_ptr<GPU2D::Renderer2D> GPU2D_Renderer = {};
|
|||
VRAMDirty need to be reset for the respective VRAM bank.
|
||||
*/
|
||||
|
||||
VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG;
|
||||
VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ;
|
||||
VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG;
|
||||
VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ;
|
||||
|
||||
VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal;
|
||||
VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal;
|
||||
VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal;
|
||||
VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal;
|
||||
|
||||
VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture;
|
||||
VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal;
|
||||
|
||||
NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9];
|
||||
|
||||
u8 VRAMFlat_ABG[512*1024];
|
||||
u8 VRAMFlat_BBG[128*1024];
|
||||
u8 VRAMFlat_AOBJ[256*1024];
|
||||
u8 VRAMFlat_BOBJ[128*1024];
|
||||
|
||||
u8 VRAMFlat_ABGExtPal[32*1024];
|
||||
u8 VRAMFlat_BBGExtPal[32*1024];
|
||||
u8 VRAMFlat_AOBJExtPal[8*1024];
|
||||
u8 VRAMFlat_BOBJExtPal[8*1024];
|
||||
|
||||
u8 VRAMFlat_Texture[512*1024];
|
||||
u8 VRAMFlat_TexPal[128*1024];
|
||||
|
||||
u32 OAMDirty;
|
||||
u32 PaletteDirty;
|
||||
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
std::unique_ptr<GLCompositor> CurGLCompositor = {};
|
||||
#endif
|
||||
|
||||
bool Init()
|
||||
GPU::GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d, std::unique_ptr<GPU2D::Renderer2D>&& renderer2d) noexcept :
|
||||
NDS(nds),
|
||||
GPU2D_A(0, *this),
|
||||
GPU2D_B(1, *this),
|
||||
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>()),
|
||||
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this))
|
||||
{
|
||||
GPU2D_Renderer = std::make_unique<GPU2D::SoftRenderer>();
|
||||
if (!GPU3D::Init()) return false;
|
||||
NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank));
|
||||
NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline));
|
||||
NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame));
|
||||
NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO));
|
||||
|
||||
FrontBuffer = 0;
|
||||
Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL;
|
||||
Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL;
|
||||
Renderer = 0;
|
||||
|
||||
return true;
|
||||
InitFramebuffers();
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
GPU::~GPU() noexcept
|
||||
{
|
||||
GPU2D_Renderer.reset();
|
||||
GPU3D::DeInit();
|
||||
// All unique_ptr fields are automatically cleaned up
|
||||
|
||||
if (Framebuffer[0][0]) delete[] Framebuffer[0][0];
|
||||
if (Framebuffer[0][1]) delete[] Framebuffer[0][1];
|
||||
if (Framebuffer[1][0]) delete[] Framebuffer[1][0];
|
||||
if (Framebuffer[1][1]) delete[] Framebuffer[1][1];
|
||||
|
||||
Framebuffer[0][0] = nullptr;
|
||||
Framebuffer[0][1] = nullptr;
|
||||
Framebuffer[1][0] = nullptr;
|
||||
Framebuffer[1][1] = nullptr;
|
||||
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
CurGLCompositor = nullptr;
|
||||
#endif
|
||||
NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank);
|
||||
NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline);
|
||||
NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame);
|
||||
NDS.UnregisterEventFunc(Event_DisplayFIFO, 0);
|
||||
}
|
||||
|
||||
void ResetVRAMCache()
|
||||
void GPU::ResetVRAMCache() noexcept
|
||||
{
|
||||
for (int i = 0; i < 9; i++)
|
||||
VRAMDirty[i] = NonStupidBitField<128*1024/VRAMDirtyGranularity>();
|
||||
|
@ -211,7 +116,7 @@ void ResetVRAMCache()
|
|||
memset(VRAMFlat_TexPal, 0, sizeof(VRAMFlat_TexPal));
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void GPU::Reset() noexcept
|
||||
{
|
||||
VCount = 0;
|
||||
NextVCount = -1;
|
||||
|
@ -262,7 +167,7 @@ void Reset()
|
|||
memset(VRAMPtr_BOBJ, 0, sizeof(VRAMPtr_BOBJ));
|
||||
|
||||
size_t fbsize;
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (GPU3D.IsRendererAccelerated())
|
||||
fbsize = (256*3 + 1) * 192;
|
||||
else
|
||||
fbsize = 256 * 192;
|
||||
|
@ -280,12 +185,10 @@ void Reset()
|
|||
|
||||
GPU2D_A.Reset();
|
||||
GPU2D_B.Reset();
|
||||
GPU3D::Reset();
|
||||
GPU3D.Reset();
|
||||
|
||||
int backbuf = FrontBuffer ? 0 : 1;
|
||||
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]);
|
||||
|
||||
ResetRenderer();
|
||||
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get());
|
||||
|
||||
ResetVRAMCache();
|
||||
|
||||
|
@ -293,28 +196,23 @@ void Reset()
|
|||
PaletteDirty = 0xF;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
void GPU::Stop() noexcept
|
||||
{
|
||||
int fbsize;
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (GPU3D.IsRendererAccelerated())
|
||||
fbsize = (256*3 + 1) * 192;
|
||||
else
|
||||
fbsize = 256 * 192;
|
||||
|
||||
memset(Framebuffer[0][0], 0, fbsize*4);
|
||||
memset(Framebuffer[0][1], 0, fbsize*4);
|
||||
memset(Framebuffer[1][0], 0, fbsize*4);
|
||||
memset(Framebuffer[1][1], 0, fbsize*4);
|
||||
memset(Framebuffer[0][0].get(), 0, fbsize*4);
|
||||
memset(Framebuffer[0][1].get(), 0, fbsize*4);
|
||||
memset(Framebuffer[1][0].get(), 0, fbsize*4);
|
||||
memset(Framebuffer[1][1].get(), 0, fbsize*4);
|
||||
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
// This needs a better way to know that we're
|
||||
// using the OpenGL renderer specifically
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
CurGLCompositor->Stop();
|
||||
#endif
|
||||
GPU3D.Stop(*this);
|
||||
}
|
||||
|
||||
void DoSavestate(Savestate* file)
|
||||
void GPU::DoSavestate(Savestate* file) noexcept
|
||||
{
|
||||
file->Section("GPUG");
|
||||
|
||||
|
@ -375,126 +273,54 @@ void DoSavestate(Savestate* file)
|
|||
|
||||
GPU2D_A.DoSavestate(file);
|
||||
GPU2D_B.DoSavestate(file);
|
||||
GPU3D::DoSavestate(file);
|
||||
GPU3D.DoSavestate(file);
|
||||
|
||||
ResetVRAMCache();
|
||||
if (!file->Saving)
|
||||
ResetVRAMCache();
|
||||
}
|
||||
|
||||
void AssignFramebuffers()
|
||||
void GPU::AssignFramebuffers() noexcept
|
||||
{
|
||||
int backbuf = FrontBuffer ? 0 : 1;
|
||||
if (NDS::PowerControl9 & (1<<15))
|
||||
if (NDS.PowerControl9 & (1<<15))
|
||||
{
|
||||
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0], Framebuffer[backbuf][1]);
|
||||
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0].get(), Framebuffer[backbuf][1].get());
|
||||
}
|
||||
else
|
||||
{
|
||||
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]);
|
||||
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get());
|
||||
}
|
||||
}
|
||||
|
||||
void InitRenderer(int renderer)
|
||||
void GPU::SetRenderer3D(std::unique_ptr<Renderer3D>&& renderer) noexcept
|
||||
{
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
if (renderer != renderer3D_Software)
|
||||
{
|
||||
CurGLCompositor = GLCompositor::New();
|
||||
// Create opengl renderer
|
||||
if (!CurGLCompositor)
|
||||
{
|
||||
// Fallback on software renderer
|
||||
renderer = 0;
|
||||
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
|
||||
}
|
||||
if (renderer == renderer3D_OpenGL)
|
||||
GPU3D::CurrentRenderer = GPU3D::GLRenderer::New();
|
||||
else if (renderer == renderer3D_OpenGLCompute)
|
||||
GPU3D::CurrentRenderer = GPU3D::ComputeRenderer::New();
|
||||
if (!GPU3D::CurrentRenderer)
|
||||
{
|
||||
// Fallback on software renderer
|
||||
CurGLCompositor.reset();
|
||||
renderer = 0;
|
||||
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
|
||||
}
|
||||
}
|
||||
if (renderer == nullptr)
|
||||
GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>());
|
||||
else
|
||||
#endif
|
||||
{
|
||||
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
|
||||
}
|
||||
GPU3D.SetCurrentRenderer(std::move(renderer));
|
||||
|
||||
Renderer = renderer;
|
||||
InitFramebuffers();
|
||||
}
|
||||
|
||||
void DeInitRenderer()
|
||||
void GPU::InitFramebuffers() noexcept
|
||||
{
|
||||
// Delete the 3D renderer, if it exists
|
||||
GPU3D::CurrentRenderer.reset();
|
||||
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
// Delete the compositor, if one exists
|
||||
CurGLCompositor.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResetRenderer()
|
||||
{
|
||||
if (Renderer == 0)
|
||||
{
|
||||
GPU3D::CurrentRenderer->Reset();
|
||||
}
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
else
|
||||
{
|
||||
CurGLCompositor->Reset();
|
||||
GPU3D::CurrentRenderer->Reset();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetRenderSettings(int renderer, RenderSettings& settings)
|
||||
{
|
||||
if (renderer != Renderer)
|
||||
{
|
||||
DeInitRenderer();
|
||||
InitRenderer(renderer);
|
||||
}
|
||||
|
||||
int fbsize;
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (GPU3D.IsRendererAccelerated())
|
||||
fbsize = (256*3 + 1) * 192;
|
||||
else
|
||||
fbsize = 256 * 192;
|
||||
|
||||
if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; }
|
||||
if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; }
|
||||
if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; }
|
||||
if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; }
|
||||
Framebuffer[0][0] = std::make_unique<u32[]>(fbsize);
|
||||
Framebuffer[1][0] = std::make_unique<u32[]>(fbsize);
|
||||
Framebuffer[0][1] = std::make_unique<u32[]>(fbsize);
|
||||
Framebuffer[1][1] = std::make_unique<u32[]>(fbsize);
|
||||
|
||||
Framebuffer[0][0] = new u32[fbsize];
|
||||
Framebuffer[1][0] = new u32[fbsize];
|
||||
Framebuffer[0][1] = new u32[fbsize];
|
||||
Framebuffer[1][1] = new u32[fbsize];
|
||||
|
||||
memset(Framebuffer[0][0], 0, fbsize*4);
|
||||
memset(Framebuffer[1][0], 0, fbsize*4);
|
||||
memset(Framebuffer[0][1], 0, fbsize*4);
|
||||
memset(Framebuffer[1][1], 0, fbsize*4);
|
||||
memset(Framebuffer[0][0].get(), 0, fbsize*4);
|
||||
memset(Framebuffer[1][0].get(), 0, fbsize*4);
|
||||
memset(Framebuffer[0][1].get(), 0, fbsize*4);
|
||||
memset(Framebuffer[1][1].get(), 0, fbsize*4);
|
||||
|
||||
AssignFramebuffers();
|
||||
|
||||
if (Renderer == 0)
|
||||
{
|
||||
GPU3D::CurrentRenderer->SetRenderSettings(settings);
|
||||
}
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
else
|
||||
{
|
||||
CurGLCompositor->SetRenderSettings(settings);
|
||||
GPU3D::CurrentRenderer->SetRenderSettings(settings);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -528,7 +354,14 @@ void SetRenderSettings(int renderer, RenderSettings& settings)
|
|||
// when reading: values are read from each bank and ORed together
|
||||
// when writing: value is written to each bank
|
||||
|
||||
u8* GetUniqueBankPtr(u32 mask, u32 offset)
|
||||
u8* GPU::GetUniqueBankPtr(u32 mask, u32 offset) noexcept
|
||||
{
|
||||
if (!mask || (mask & (mask - 1)) != 0) return NULL;
|
||||
int num = __builtin_ctz(mask);
|
||||
return &VRAM[num][offset & VRAMMask[num]];
|
||||
}
|
||||
|
||||
const u8* GPU::GetUniqueBankPtr(u32 mask, u32 offset) const noexcept
|
||||
{
|
||||
if (!mask || (mask & (mask - 1)) != 0) return NULL;
|
||||
int num = __builtin_ctz(mask);
|
||||
|
@ -543,7 +376,7 @@ u8* GetUniqueBankPtr(u32 mask, u32 offset)
|
|||
#define UNMAP_RANGE_PTR(map, base, n) \
|
||||
for (int i = 0; i < n; i++) { VRAMMap_##map[(base)+i] &= ~bankmask; VRAMPtr_##map[(base)+i] = GetUniqueBankPtr(VRAMMap_##map[(base)+i], ((base)+i)<<14); }
|
||||
|
||||
void MapVRAM_AB(u32 bank, u8 cnt)
|
||||
void GPU::MapVRAM_AB(u32 bank, u8 cnt) noexcept
|
||||
{
|
||||
cnt &= 0x9B;
|
||||
|
||||
|
@ -603,7 +436,7 @@ void MapVRAM_AB(u32 bank, u8 cnt)
|
|||
}
|
||||
}
|
||||
|
||||
void MapVRAM_CD(u32 bank, u8 cnt)
|
||||
void GPU::MapVRAM_CD(u32 bank, u8 cnt) noexcept
|
||||
{
|
||||
cnt &= 0x9F;
|
||||
|
||||
|
@ -669,9 +502,7 @@ void MapVRAM_CD(u32 bank, u8 cnt)
|
|||
VRAMMap_ARM7[ofs] |= bankmask;
|
||||
memset(VRAMDirty[bank].Data, 0xFF, sizeof(VRAMDirty[bank].Data));
|
||||
VRAMSTAT |= (1 << (bank-2));
|
||||
#ifdef JIT_ENABLED
|
||||
ARMJIT::CheckAndInvalidateWVRAM(ofs);
|
||||
#endif
|
||||
NDS.JIT.CheckAndInvalidateWVRAM(ofs);
|
||||
break;
|
||||
|
||||
case 3: // texture
|
||||
|
@ -692,7 +523,7 @@ void MapVRAM_CD(u32 bank, u8 cnt)
|
|||
}
|
||||
}
|
||||
|
||||
void MapVRAM_E(u32 bank, u8 cnt)
|
||||
void GPU::MapVRAM_E(u32 bank, u8 cnt) noexcept
|
||||
{
|
||||
cnt &= 0x87;
|
||||
|
||||
|
@ -756,7 +587,7 @@ void MapVRAM_E(u32 bank, u8 cnt)
|
|||
}
|
||||
}
|
||||
|
||||
void MapVRAM_FG(u32 bank, u8 cnt)
|
||||
void GPU::MapVRAM_FG(u32 bank, u8 cnt) noexcept
|
||||
{
|
||||
cnt &= 0x9F;
|
||||
|
||||
|
@ -856,7 +687,7 @@ void MapVRAM_FG(u32 bank, u8 cnt)
|
|||
}
|
||||
}
|
||||
|
||||
void MapVRAM_H(u32 bank, u8 cnt)
|
||||
void GPU::MapVRAM_H(u32 bank, u8 cnt) noexcept
|
||||
{
|
||||
cnt &= 0x83;
|
||||
|
||||
|
@ -918,7 +749,7 @@ void MapVRAM_H(u32 bank, u8 cnt)
|
|||
}
|
||||
}
|
||||
|
||||
void MapVRAM_I(u32 bank, u8 cnt)
|
||||
void GPU::MapVRAM_I(u32 bank, u8 cnt) noexcept
|
||||
{
|
||||
cnt &= 0x83;
|
||||
|
||||
|
@ -989,7 +820,7 @@ void MapVRAM_I(u32 bank, u8 cnt)
|
|||
}
|
||||
|
||||
|
||||
void SetPowerCnt(u32 val)
|
||||
void GPU::SetPowerCnt(u32 val) noexcept
|
||||
{
|
||||
// POWCNT1 effects:
|
||||
// * bit0: asplodes hardware??? not tested.
|
||||
|
@ -1003,13 +834,13 @@ void SetPowerCnt(u32 val)
|
|||
|
||||
GPU2D_A.SetEnabled(val & (1<<1));
|
||||
GPU2D_B.SetEnabled(val & (1<<9));
|
||||
GPU3D::SetEnabled(val & (1<<3), val & (1<<2));
|
||||
GPU3D.SetEnabled(val & (1<<3), val & (1<<2));
|
||||
|
||||
AssignFramebuffers();
|
||||
}
|
||||
|
||||
|
||||
void DisplayFIFO(u32 x)
|
||||
void GPU::DisplayFIFO(u32 x) noexcept
|
||||
{
|
||||
// sample the FIFO
|
||||
// as this starts 16 cycles (~3 pixels) before display start,
|
||||
|
@ -1025,25 +856,25 @@ void DisplayFIFO(u32 x)
|
|||
if (x < 256)
|
||||
{
|
||||
// transfer the next 8 pixels
|
||||
NDS::CheckDMAs(0, 0x04);
|
||||
NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 6*8, DisplayFIFO, x+8);
|
||||
NDS.CheckDMAs(0, 0x04);
|
||||
NDS.ScheduleEvent(Event_DisplayFIFO, true, 6*8, 0, x+8);
|
||||
}
|
||||
else
|
||||
GPU2D_A.SampleFIFO(253, 3); // sample the remaining pixels
|
||||
}
|
||||
|
||||
void StartFrame()
|
||||
void GPU::StartFrame() noexcept
|
||||
{
|
||||
// only run the display FIFO if needed:
|
||||
// * if it is used for display or capture
|
||||
// * if we have display FIFO DMA
|
||||
RunFIFO = GPU2D_A.UsesFIFO() || NDS::DMAsInMode(0, 0x04);
|
||||
RunFIFO = GPU2D_A.UsesFIFO() || NDS.DMAsInMode(0, 0x04);
|
||||
|
||||
TotalScanlines = 0;
|
||||
StartScanline(0);
|
||||
}
|
||||
|
||||
void StartHBlank(u32 line)
|
||||
void GPU::StartHBlank(u32 line) noexcept
|
||||
{
|
||||
DispStat[0] |= (1<<1);
|
||||
DispStat[1] |= (1<<1);
|
||||
|
@ -1065,11 +896,11 @@ void StartHBlank(u32 line)
|
|||
GPU2D_Renderer->DrawSprites(line+1, &GPU2D_B);
|
||||
}
|
||||
|
||||
NDS::CheckDMAs(0, 0x02);
|
||||
NDS.CheckDMAs(0, 0x02);
|
||||
}
|
||||
else if (VCount == 215)
|
||||
{
|
||||
GPU3D::VCount215();
|
||||
GPU3D.VCount215(*this);
|
||||
}
|
||||
else if (VCount == 262)
|
||||
{
|
||||
|
@ -1077,30 +908,48 @@ void StartHBlank(u32 line)
|
|||
GPU2D_Renderer->DrawSprites(0, &GPU2D_B);
|
||||
}
|
||||
|
||||
if (DispStat[0] & (1<<4)) NDS::SetIRQ(0, NDS::IRQ_HBlank);
|
||||
if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank);
|
||||
if (DispStat[0] & (1<<4)) NDS.SetIRQ(0, IRQ_HBlank);
|
||||
if (DispStat[1] & (1<<4)) NDS.SetIRQ(1, IRQ_HBlank);
|
||||
|
||||
if (VCount < 262)
|
||||
NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), StartScanline, line+1);
|
||||
NDS.ScheduleEvent(Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_StartScanline, line+1);
|
||||
else
|
||||
NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), FinishFrame, line+1);
|
||||
NDS.ScheduleEvent(Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_FinishFrame, line+1);
|
||||
}
|
||||
|
||||
void FinishFrame(u32 lines)
|
||||
void GPU::FinishFrame(u32 lines) noexcept
|
||||
{
|
||||
FrontBuffer = FrontBuffer ? 0 : 1;
|
||||
AssignFramebuffers();
|
||||
|
||||
TotalScanlines = lines;
|
||||
|
||||
if (GPU3D::AbortFrame)
|
||||
if (GPU3D.AbortFrame)
|
||||
{
|
||||
GPU3D::RestartFrame();
|
||||
GPU3D::AbortFrame = false;
|
||||
GPU3D.RestartFrame(*this);
|
||||
GPU3D.AbortFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
void StartScanline(u32 line)
|
||||
void GPU::BlankFrame() noexcept
|
||||
{
|
||||
int backbuf = FrontBuffer ? 0 : 1;
|
||||
int fbsize;
|
||||
if (GPU3D.IsRendererAccelerated())
|
||||
fbsize = (256*3 + 1) * 192;
|
||||
else
|
||||
fbsize = 256 * 192;
|
||||
|
||||
memset(Framebuffer[backbuf][0].get(), 0, fbsize*4);
|
||||
memset(Framebuffer[backbuf][1].get(), 0, fbsize*4);
|
||||
|
||||
FrontBuffer = backbuf;
|
||||
AssignFramebuffers();
|
||||
|
||||
TotalScanlines = 263;
|
||||
}
|
||||
|
||||
void GPU::StartScanline(u32 line) noexcept
|
||||
{
|
||||
if (line == 0)
|
||||
VCount = 0;
|
||||
|
@ -1118,7 +967,7 @@ void StartScanline(u32 line)
|
|||
{
|
||||
DispStat[0] |= (1<<2);
|
||||
|
||||
if (DispStat[0] & (1<<5)) NDS::SetIRQ(0, NDS::IRQ_VCount);
|
||||
if (DispStat[0] & (1<<5)) NDS.SetIRQ(0, IRQ_VCount);
|
||||
}
|
||||
else
|
||||
DispStat[0] &= ~(1<<2);
|
||||
|
@ -1127,7 +976,7 @@ void StartScanline(u32 line)
|
|||
{
|
||||
DispStat[1] |= (1<<2);
|
||||
|
||||
if (DispStat[1] & (1<<5)) NDS::SetIRQ(1, NDS::IRQ_VCount);
|
||||
if (DispStat[1] & (1<<5)) NDS.SetIRQ(1, IRQ_VCount);
|
||||
}
|
||||
else
|
||||
DispStat[1] &= ~(1<<2);
|
||||
|
@ -1136,9 +985,9 @@ void StartScanline(u32 line)
|
|||
GPU2D_B.CheckWindows(VCount);
|
||||
|
||||
if (VCount >= 2 && VCount < 194)
|
||||
NDS::CheckDMAs(0, 0x03);
|
||||
NDS.CheckDMAs(0, 0x03);
|
||||
else if (VCount == 194)
|
||||
NDS::StopDMAs(0, 0x03);
|
||||
NDS.StopDMAs(0, 0x03);
|
||||
|
||||
if (line < 192)
|
||||
{
|
||||
|
@ -1150,7 +999,7 @@ void StartScanline(u32 line)
|
|||
}
|
||||
|
||||
if (RunFIFO)
|
||||
NDS::ScheduleEvent(NDS::Event_DisplayFIFO, false, 32, DisplayFIFO, 0);
|
||||
NDS.ScheduleEvent(Event_DisplayFIFO, false, 32, 0, 0);
|
||||
}
|
||||
|
||||
if (VCount == 262)
|
||||
|
@ -1170,37 +1019,35 @@ void StartScanline(u32 line)
|
|||
// texture memory anyway and only update it before the start
|
||||
//of the next frame.
|
||||
// So we can give the rasteriser a bit more headroom
|
||||
GPU3D::VCount144();
|
||||
GPU3D.VCount144(*this);
|
||||
|
||||
// VBlank
|
||||
DispStat[0] |= (1<<0);
|
||||
DispStat[1] |= (1<<0);
|
||||
|
||||
NDS::StopDMAs(0, 0x04);
|
||||
NDS.StopDMAs(0, 0x04);
|
||||
|
||||
NDS::CheckDMAs(0, 0x01);
|
||||
NDS::CheckDMAs(1, 0x11);
|
||||
NDS.CheckDMAs(0, 0x01);
|
||||
NDS.CheckDMAs(1, 0x11);
|
||||
|
||||
if (DispStat[0] & (1<<3)) NDS::SetIRQ(0, NDS::IRQ_VBlank);
|
||||
if (DispStat[1] & (1<<3)) NDS::SetIRQ(1, NDS::IRQ_VBlank);
|
||||
if (DispStat[0] & (1<<3)) NDS.SetIRQ(0, IRQ_VBlank);
|
||||
if (DispStat[1] & (1<<3)) NDS.SetIRQ(1, IRQ_VBlank);
|
||||
|
||||
GPU2D_A.VBlank();
|
||||
GPU2D_B.VBlank();
|
||||
GPU3D::VBlank();
|
||||
GPU3D.VBlank();
|
||||
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
// Need a better way to identify the openGL renderer in particular
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
CurGLCompositor->RenderFrame();
|
||||
#endif
|
||||
if (GPU3D.IsRendererAccelerated())
|
||||
GPU3D.Blit(*this);
|
||||
}
|
||||
}
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, StartHBlank, line);
|
||||
NDS.ScheduleEvent(Event_LCD, true, HBLANK_CYCLES, LCD_StartHBlank, line);
|
||||
}
|
||||
|
||||
|
||||
void SetDispStat(u32 cpu, u16 val)
|
||||
void GPU::SetDispStat(u32 cpu, u16 val) noexcept
|
||||
{
|
||||
val &= 0xFFB8;
|
||||
DispStat[cpu] &= 0x0047;
|
||||
|
@ -1209,7 +1056,7 @@ void SetDispStat(u32 cpu, u16 val)
|
|||
VMatch[cpu] = (val >> 8) | ((val & 0x80) << 1);
|
||||
}
|
||||
|
||||
void SetVCount(u16 val)
|
||||
void GPU::SetVCount(u16 val) noexcept
|
||||
{
|
||||
// VCount write is delayed until the next scanline
|
||||
|
||||
|
@ -1217,12 +1064,12 @@ void SetVCount(u16 val)
|
|||
// 3D engine seems to give up on the current frame in that situation, repeating the last two scanlines
|
||||
// TODO: also check the various DMA types that can be involved
|
||||
|
||||
GPU3D::AbortFrame |= NextVCount != val;
|
||||
GPU3D.AbortFrame |= NextVCount != val;
|
||||
NextVCount = val;
|
||||
}
|
||||
|
||||
template <u32 Size, u32 MappingGranularity>
|
||||
NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranularity>::DeriveState(u32* currentMappings)
|
||||
NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranularity>::DeriveState(const u32* currentMappings, GPU& gpu)
|
||||
{
|
||||
NonStupidBitField<Size/VRAMDirtyGranularity> result;
|
||||
u16 banksToBeZeroed = 0;
|
||||
|
@ -1251,20 +1098,20 @@ NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranul
|
|||
static_assert(VRAMDirtyGranularity == 512, "");
|
||||
if (MappingGranularity == 16*1024)
|
||||
{
|
||||
u32 dirty = ((u32*)VRAMDirty[num].Data)[i & (VRAMMask[num] >> 14)];
|
||||
u32 dirty = ((u32*)gpu.VRAMDirty[num].Data)[i & (gpu.VRAMMask[num] >> 14)];
|
||||
result.Data[i / 2] |= (u64)dirty << ((i&1)*32);
|
||||
}
|
||||
else if (MappingGranularity == 8*1024)
|
||||
{
|
||||
u16 dirty = ((u16*)VRAMDirty[num].Data)[i & (VRAMMask[num] >> 13)];
|
||||
u16 dirty = ((u16*)gpu.VRAMDirty[num].Data)[i & (gpu.VRAMMask[num] >> 13)];
|
||||
result.Data[i / 4] |= (u64)dirty << ((i&3)*16);
|
||||
}
|
||||
else if (MappingGranularity == 128*1024)
|
||||
{
|
||||
result.Data[i * 4 + 0] |= VRAMDirty[num].Data[0];
|
||||
result.Data[i * 4 + 1] |= VRAMDirty[num].Data[1];
|
||||
result.Data[i * 4 + 2] |= VRAMDirty[num].Data[2];
|
||||
result.Data[i * 4 + 3] |= VRAMDirty[num].Data[3];
|
||||
result.Data[i * 4 + 0] |= gpu.VRAMDirty[num].Data[0];
|
||||
result.Data[i * 4 + 1] |= gpu.VRAMDirty[num].Data[1];
|
||||
result.Data[i * 4 + 2] |= gpu.VRAMDirty[num].Data[2];
|
||||
result.Data[i * 4 + 3] |= gpu.VRAMDirty[num].Data[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1279,137 +1126,63 @@ NonStupidBitField<Size/VRAMDirtyGranularity> VRAMTrackingSet<Size, MappingGranul
|
|||
{
|
||||
u32 num = __builtin_ctz(banksToBeZeroed);
|
||||
banksToBeZeroed &= ~(1 << num);
|
||||
VRAMDirty[num].Clear();
|
||||
gpu.VRAMDirty[num].Clear();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(u32*);
|
||||
template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(const u32*, GPU& gpu);
|
||||
template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(const u32*, GPU& gpu);
|
||||
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(const u32*, GPU& gpu);
|
||||
template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(const u32*, GPU& gpu);
|
||||
template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(const u32*, GPU& gpu);
|
||||
template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(const u32*, GPU& gpu);
|
||||
|
||||
template <u32 MappingGranularity, u32 Size>
|
||||
inline bool CopyLinearVRAM(u8* flat, u32* mappings, NonStupidBitField<Size>& dirty, u64 (*slowAccess)(u32 addr))
|
||||
|
||||
|
||||
bool GPU::MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
const u32 VRAMBitsPerMapping = MappingGranularity / VRAMDirtyGranularity;
|
||||
|
||||
bool change = false;
|
||||
|
||||
typename NonStupidBitField<Size>::Iterator it = dirty.Begin();
|
||||
while (it != dirty.End())
|
||||
{
|
||||
u32 offset = *it * VRAMDirtyGranularity;
|
||||
u8* dst = flat + offset;
|
||||
u8* fastAccess = GetUniqueBankPtr(mappings[*it / VRAMBitsPerMapping], offset);
|
||||
if (fastAccess)
|
||||
{
|
||||
memcpy(dst, fastAccess, VRAMDirtyGranularity);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < VRAMDirtyGranularity; i += 8)
|
||||
*(u64*)&dst[i] = slowAccess(offset + i);
|
||||
}
|
||||
change = true;
|
||||
it++;
|
||||
}
|
||||
return change;
|
||||
return CopyLinearVRAM<128*1024>(VRAMFlat_Texture, VRAMMap_Texture, dirty, &GPU::ReadVRAM_Texture<u64>);
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty)
|
||||
bool GPU::MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
return CopyLinearVRAM<128*1024>(VRAMFlat_Texture, VRAMMap_Texture, dirty, ReadVRAM_Texture<u64>);
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_TexPal, VRAMMap_TexPal, dirty, &GPU::ReadVRAM_TexPal<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty)
|
||||
|
||||
bool GPU::MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_TexPal, VRAMMap_TexPal, dirty, ReadVRAM_TexPal<u64>);
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_ABG, VRAMMap_ABG, dirty, &GPU::ReadVRAM_ABG<u64>);
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty)
|
||||
bool GPU::MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_ABG, VRAMMap_ABG, dirty, ReadVRAM_ABG<u64>);
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_BBG, VRAMMap_BBG, dirty, &GPU::ReadVRAM_BBG<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty)
|
||||
|
||||
bool GPU::MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_BBG, VRAMMap_BBG, dirty, ReadVRAM_BBG<u64>);
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_AOBJ, VRAMMap_AOBJ, dirty, &GPU::ReadVRAM_AOBJ<u64>);
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty)
|
||||
bool GPU::MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_AOBJ, VRAMMap_AOBJ, dirty, ReadVRAM_AOBJ<u64>);
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_BOBJ, VRAMMap_BOBJ, dirty, &GPU::ReadVRAM_BOBJ<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty)
|
||||
|
||||
bool GPU::MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
return CopyLinearVRAM<16*1024>(VRAMFlat_BOBJ, VRAMMap_BOBJ, dirty, ReadVRAM_BOBJ<u64>);
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_ABGExtPal, VRAMMap_ABGExtPal, dirty, &GPU::ReadVRAM_ABGExtPal<u64>);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_ABGExtPal(u32 addr)
|
||||
bool GPU::MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
u32 mask = VRAMMap_ABGExtPal[(addr >> 13) & 0x3];
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0x7FFF];
|
||||
if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF];
|
||||
if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF];
|
||||
|
||||
return ret;
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_BBGExtPal, VRAMMap_BBGExtPal, dirty, &GPU::ReadVRAM_BBGExtPal<u64>);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_BBGExtPal(u32 addr)
|
||||
bool GPU::MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
u32 mask = VRAMMap_BBGExtPal[(addr >> 13) & 0x3];
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF];
|
||||
|
||||
return ret;
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_AOBJExtPal, &VRAMMap_AOBJExtPal, dirty, &GPU::ReadVRAM_AOBJExtPal<u64>);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_AOBJExtPal(u32 addr)
|
||||
bool GPU::MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept
|
||||
{
|
||||
u32 mask = VRAMMap_AOBJExtPal;
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<4)) ret |= *(T*)&VRAM_F[addr & 0x1FFF];
|
||||
if (mask & (1<<5)) ret |= *(T*)&VRAM_G[addr & 0x1FFF];
|
||||
|
||||
return ret;
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_BOBJExtPal, &VRAMMap_BOBJExtPal, dirty, &GPU::ReadVRAM_BOBJExtPal<u64>);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ReadVRAM_BOBJExtPal(u32 addr)
|
||||
{
|
||||
u32 mask = VRAMMap_BOBJExtPal;
|
||||
|
||||
T ret = 0;
|
||||
if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x1FFF];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_ABGExtPal, VRAMMap_ABGExtPal, dirty, ReadVRAM_ABGExtPal<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_BBGExtPal, VRAMMap_BBGExtPal, dirty, ReadVRAM_BBGExtPal<u64>);
|
||||
}
|
||||
|
||||
bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_AOBJExtPal, &VRAMMap_AOBJExtPal, dirty, ReadVRAM_AOBJExtPal<u64>);
|
||||
}
|
||||
bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty)
|
||||
{
|
||||
return CopyLinearVRAM<8*1024>(VRAMFlat_BOBJExtPal, &VRAMMap_BOBJExtPal, dirty, ReadVRAM_BOBJExtPal<u64>);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
|||
#include "GPU.h"
|
||||
#include "GPU3D.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
|
@ -85,10 +87,8 @@ using Platform::LogLevel;
|
|||
|
||||
namespace GPU2D
|
||||
{
|
||||
|
||||
Unit::Unit(u32 num)
|
||||
Unit::Unit(u32 num, melonDS::GPU& gpu) : Num(num), GPU(gpu)
|
||||
{
|
||||
Num = num;
|
||||
}
|
||||
|
||||
void Unit::Reset()
|
||||
|
@ -288,10 +288,10 @@ void Unit::Write8(u32 addr, u8 val)
|
|||
return;
|
||||
|
||||
case 0x10:
|
||||
if (!Num) GPU3D::SetRenderXPos((GPU3D::RenderXPos & 0xFF00) | val);
|
||||
if (!Num) GPU.GPU3D.SetRenderXPos((GPU.GPU3D.GetRenderXPos() & 0xFF00) | val);
|
||||
break;
|
||||
case 0x11:
|
||||
if (!Num) GPU3D::SetRenderXPos((GPU3D::RenderXPos & 0x00FF) | (val << 8));
|
||||
if (!Num) GPU.GPU3D.SetRenderXPos((GPU.GPU3D.GetRenderXPos() & 0x00FF) | (val << 8));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -384,7 +384,7 @@ void Unit::Write16(u32 addr, u16 val)
|
|||
return;
|
||||
|
||||
case 0x010:
|
||||
if (!Num) GPU3D::SetRenderXPos(val);
|
||||
if (!Num) GPU.GPU3D.SetRenderXPos(val);
|
||||
break;
|
||||
|
||||
case 0x068:
|
||||
|
@ -423,21 +423,21 @@ void Unit::Write16(u32 addr, u16 val)
|
|||
case 0x026: BGRotD[0] = val; return;
|
||||
case 0x028:
|
||||
BGXRef[0] = (BGXRef[0] & 0xFFFF0000) | val;
|
||||
if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0];
|
||||
if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0];
|
||||
return;
|
||||
case 0x02A:
|
||||
if (val & 0x0800) val |= 0xF000;
|
||||
BGXRef[0] = (BGXRef[0] & 0xFFFF) | (val << 16);
|
||||
if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0];
|
||||
if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0];
|
||||
return;
|
||||
case 0x02C:
|
||||
BGYRef[0] = (BGYRef[0] & 0xFFFF0000) | val;
|
||||
if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0];
|
||||
if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0];
|
||||
return;
|
||||
case 0x02E:
|
||||
if (val & 0x0800) val |= 0xF000;
|
||||
BGYRef[0] = (BGYRef[0] & 0xFFFF) | (val << 16);
|
||||
if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0];
|
||||
if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0];
|
||||
return;
|
||||
|
||||
case 0x030: BGRotA[1] = val; return;
|
||||
|
@ -446,21 +446,21 @@ void Unit::Write16(u32 addr, u16 val)
|
|||
case 0x036: BGRotD[1] = val; return;
|
||||
case 0x038:
|
||||
BGXRef[1] = (BGXRef[1] & 0xFFFF0000) | val;
|
||||
if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1];
|
||||
if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1];
|
||||
return;
|
||||
case 0x03A:
|
||||
if (val & 0x0800) val |= 0xF000;
|
||||
BGXRef[1] = (BGXRef[1] & 0xFFFF) | (val << 16);
|
||||
if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1];
|
||||
if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1];
|
||||
return;
|
||||
case 0x03C:
|
||||
BGYRef[1] = (BGYRef[1] & 0xFFFF0000) | val;
|
||||
if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1];
|
||||
if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1];
|
||||
return;
|
||||
case 0x03E:
|
||||
if (val & 0x0800) val |= 0xF000;
|
||||
BGYRef[1] = (BGYRef[1] & 0xFFFF) | (val << 16);
|
||||
if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1];
|
||||
if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1];
|
||||
return;
|
||||
|
||||
case 0x040:
|
||||
|
@ -542,23 +542,23 @@ void Unit::Write32(u32 addr, u32 val)
|
|||
case 0x028:
|
||||
if (val & 0x08000000) val |= 0xF0000000;
|
||||
BGXRef[0] = val;
|
||||
if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0];
|
||||
if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0];
|
||||
return;
|
||||
case 0x02C:
|
||||
if (val & 0x08000000) val |= 0xF0000000;
|
||||
BGYRef[0] = val;
|
||||
if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0];
|
||||
if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0];
|
||||
return;
|
||||
|
||||
case 0x038:
|
||||
if (val & 0x08000000) val |= 0xF0000000;
|
||||
BGXRef[1] = val;
|
||||
if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1];
|
||||
if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1];
|
||||
return;
|
||||
case 0x03C:
|
||||
if (val & 0x08000000) val |= 0xF0000000;
|
||||
BGYRef[1] = val;
|
||||
if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1];
|
||||
if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -629,15 +629,15 @@ u16* Unit::GetBGExtPal(u32 slot, u32 pal)
|
|||
const u32 PaletteSize = 256 * 2;
|
||||
const u32 SlotSize = PaletteSize * 16;
|
||||
return (u16*)&(Num == 0
|
||||
? GPU::VRAMFlat_ABGExtPal
|
||||
: GPU::VRAMFlat_BBGExtPal)[slot * SlotSize + pal * PaletteSize];
|
||||
? GPU.VRAMFlat_ABGExtPal
|
||||
: GPU.VRAMFlat_BBGExtPal)[slot * SlotSize + pal * PaletteSize];
|
||||
}
|
||||
|
||||
u16* Unit::GetOBJExtPal()
|
||||
{
|
||||
return Num == 0
|
||||
? (u16*)GPU::VRAMFlat_AOBJExtPal
|
||||
: (u16*)GPU::VRAMFlat_BOBJExtPal;
|
||||
? (u16*)GPU.VRAMFlat_AOBJExtPal
|
||||
: (u16*)GPU.VRAMFlat_BOBJExtPal;
|
||||
}
|
||||
|
||||
void Unit::CheckWindows(u32 line)
|
||||
|
@ -649,7 +649,7 @@ void Unit::CheckWindows(u32 line)
|
|||
else if (line == Win1Coords[2]) Win1Active |= 0x1;
|
||||
}
|
||||
|
||||
void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow)
|
||||
void Unit::CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow)
|
||||
{
|
||||
for (u32 i = 0; i < 256; i++)
|
||||
windowMask[i] = WinCnt[2]; // window outside
|
||||
|
@ -695,32 +695,33 @@ void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow)
|
|||
}
|
||||
}
|
||||
|
||||
void Unit::GetBGVRAM(u8*& data, u32& mask)
|
||||
void Unit::GetBGVRAM(u8*& data, u32& mask) const
|
||||
{
|
||||
if (Num == 0)
|
||||
{
|
||||
data = GPU::VRAMFlat_ABG;
|
||||
data = GPU.VRAMFlat_ABG;
|
||||
mask = 0x7FFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = GPU::VRAMFlat_BBG;
|
||||
data = GPU.VRAMFlat_BBG;
|
||||
mask = 0x1FFFF;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::GetOBJVRAM(u8*& data, u32& mask)
|
||||
void Unit::GetOBJVRAM(u8*& data, u32& mask) const
|
||||
{
|
||||
if (Num == 0)
|
||||
{
|
||||
data = GPU::VRAMFlat_AOBJ;
|
||||
data = GPU.VRAMFlat_AOBJ;
|
||||
mask = 0x3FFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = GPU::VRAMFlat_BOBJ;
|
||||
data = GPU.VRAMFlat_BOBJ;
|
||||
mask = 0x1FFFF;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
23
src/GPU2D.h
23
src/GPU2D.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -22,14 +22,20 @@
|
|||
#include "types.h"
|
||||
#include "Savestate.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
class GPU;
|
||||
|
||||
namespace GPU2D
|
||||
{
|
||||
|
||||
class Unit
|
||||
{
|
||||
public:
|
||||
Unit(u32 num);
|
||||
|
||||
// take a reference to the GPU so we can access its state
|
||||
// and ensure that it's not null
|
||||
Unit(u32 num, melonDS::GPU& gpu);
|
||||
virtual ~Unit() = default;
|
||||
Unit(const Unit&) = delete;
|
||||
Unit& operator=(const Unit&) = delete;
|
||||
|
||||
|
@ -46,7 +52,7 @@ public:
|
|||
void Write16(u32 addr, u16 val);
|
||||
void Write32(u32 addr, u32 val);
|
||||
|
||||
bool UsesFIFO()
|
||||
bool UsesFIFO() const
|
||||
{
|
||||
if (((DispCnt >> 16) & 0x3) == 3)
|
||||
return true;
|
||||
|
@ -66,11 +72,11 @@ public:
|
|||
u16* GetBGExtPal(u32 slot, u32 pal);
|
||||
u16* GetOBJExtPal();
|
||||
|
||||
void GetBGVRAM(u8*& data, u32& mask);
|
||||
void GetOBJVRAM(u8*& data, u32& mask);
|
||||
void GetBGVRAM(u8*& data, u32& mask) const;
|
||||
void GetOBJVRAM(u8*& data, u32& mask) const;
|
||||
|
||||
void UpdateMosaicCounters(u32 line);
|
||||
void CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow);
|
||||
void CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow);
|
||||
|
||||
u32 Num;
|
||||
bool Enabled;
|
||||
|
@ -116,6 +122,8 @@ public:
|
|||
u32 CaptureCnt;
|
||||
|
||||
u16 MasterBrightness;
|
||||
private:
|
||||
melonDS::GPU& GPU;
|
||||
};
|
||||
|
||||
class Renderer2D
|
||||
|
@ -141,4 +149,5 @@ protected:
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
Copyright 2016-2023 melonDS team
|
||||
|
||||
This file is part of melonDS.
|
||||
|
||||
|
@ -20,77 +20,17 @@
|
|||
#include "GPU.h"
|
||||
#include "GPU3D.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
namespace GPU2D
|
||||
{
|
||||
|
||||
SoftRenderer::SoftRenderer()
|
||||
: Renderer2D()
|
||||
SoftRenderer::SoftRenderer(melonDS::GPU& gpu)
|
||||
: Renderer2D(), GPU(gpu)
|
||||
{
|
||||
// initialize mosaic table
|
||||
for (int m = 0; m < 16; m++)
|
||||
{
|
||||
for (int x = 0; x < 256; x++)
|
||||
{
|
||||
int offset = x % (m+1);
|
||||
MosaicTable[m][x] = offset;
|
||||
}
|
||||
}
|
||||
// mosaic table is initialized at compile-time
|
||||
}
|
||||
|
||||
u32 SoftRenderer::ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb)
|
||||
{
|
||||
u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4;
|
||||
u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00;
|
||||
u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000;
|
||||
|
||||
if (r > 0x00003F) r = 0x00003F;
|
||||
if (g > 0x003F00) g = 0x003F00;
|
||||
if (b > 0x3F0000) b = 0x3F0000;
|
||||
|
||||
return r | g | b | 0xFF000000;
|
||||
}
|
||||
|
||||
u32 SoftRenderer::ColorBlend5(u32 val1, u32 val2)
|
||||
{
|
||||
u32 eva = ((val1 >> 24) & 0x1F) + 1;
|
||||
u32 evb = 32 - eva;
|
||||
|
||||
if (eva == 32) return val1;
|
||||
|
||||
u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5;
|
||||
u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00;
|
||||
u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000;
|
||||
|
||||
if (r > 0x00003F) r = 0x00003F;
|
||||
if (g > 0x003F00) g = 0x003F00;
|
||||
if (b > 0x3F0000) b = 0x3F0000;
|
||||
|
||||
return r | g | b | 0xFF000000;
|
||||
}
|
||||
|
||||
u32 SoftRenderer::ColorBrightnessUp(u32 val, u32 factor, u32 bias)
|
||||
{
|
||||
u32 rb = val & 0x3F003F;
|
||||
u32 g = val & 0x003F00;
|
||||
|
||||
rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
|
||||
g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00);
|
||||
|
||||
return rb | g | 0xFF000000;
|
||||
}
|
||||
|
||||
u32 SoftRenderer::ColorBrightnessDown(u32 val, u32 factor, u32 bias)
|
||||
{
|
||||
u32 rb = val & 0x3F003F;
|
||||
u32 g = val & 0x003F00;
|
||||
|
||||
rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
|
||||
g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00);
|
||||
|
||||
return rb | g | 0xFF000000;
|
||||
}
|
||||
|
||||
u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2)
|
||||
u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) const
|
||||
{
|
||||
u32 coloreffect = 0;
|
||||
u32 eva, evb;
|
||||
|
@ -166,29 +106,29 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
|
|||
{
|
||||
CurUnit = unit;
|
||||
|
||||
int stride = GPU3D::CurrentRenderer->Accelerated ? (256*3 + 1) : 256;
|
||||
int stride = GPU.GPU3D.IsRendererAccelerated() ? (256*3 + 1) : 256;
|
||||
u32* dst = &Framebuffer[CurUnit->Num][stride * line];
|
||||
|
||||
int n3dline = line;
|
||||
line = GPU::VCount;
|
||||
line = GPU.VCount;
|
||||
|
||||
if (CurUnit->Num == 0)
|
||||
{
|
||||
auto bgDirty = GPU::VRAMDirty_ABG.DeriveState(GPU::VRAMMap_ABG);
|
||||
GPU::MakeVRAMFlat_ABGCoherent(bgDirty);
|
||||
auto bgExtPalDirty = GPU::VRAMDirty_ABGExtPal.DeriveState(GPU::VRAMMap_ABGExtPal);
|
||||
GPU::MakeVRAMFlat_ABGExtPalCoherent(bgExtPalDirty);
|
||||
auto objExtPalDirty = GPU::VRAMDirty_AOBJExtPal.DeriveState(&GPU::VRAMMap_AOBJExtPal);
|
||||
GPU::MakeVRAMFlat_AOBJExtPalCoherent(objExtPalDirty);
|
||||
auto bgDirty = GPU.VRAMDirty_ABG.DeriveState(GPU.VRAMMap_ABG, GPU);
|
||||
GPU.MakeVRAMFlat_ABGCoherent(bgDirty);
|
||||
auto bgExtPalDirty = GPU.VRAMDirty_ABGExtPal.DeriveState(GPU.VRAMMap_ABGExtPal, GPU);
|
||||
GPU.MakeVRAMFlat_ABGExtPalCoherent(bgExtPalDirty);
|
||||
auto objExtPalDirty = GPU.VRAMDirty_AOBJExtPal.DeriveState(&GPU.VRAMMap_AOBJExtPal, GPU);
|
||||
GPU.MakeVRAMFlat_AOBJExtPalCoherent(objExtPalDirty);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bgDirty = GPU::VRAMDirty_BBG.DeriveState(GPU::VRAMMap_BBG);
|
||||
GPU::MakeVRAMFlat_BBGCoherent(bgDirty);
|
||||
auto bgExtPalDirty = GPU::VRAMDirty_BBGExtPal.DeriveState(GPU::VRAMMap_BBGExtPal);
|
||||
GPU::MakeVRAMFlat_BBGExtPalCoherent(bgExtPalDirty);
|
||||
auto objExtPalDirty = GPU::VRAMDirty_BOBJExtPal.DeriveState(&GPU::VRAMMap_BOBJExtPal);
|
||||
GPU::MakeVRAMFlat_BOBJExtPalCoherent(objExtPalDirty);
|
||||
auto bgDirty = GPU.VRAMDirty_BBG.DeriveState(GPU.VRAMMap_BBG, GPU);
|
||||
GPU.MakeVRAMFlat_BBGCoherent(bgDirty);
|
||||
auto bgExtPalDirty = GPU.VRAMDirty_BBGExtPal.DeriveState(GPU.VRAMMap_BBGExtPal, GPU);
|
||||
GPU.MakeVRAMFlat_BBGExtPalCoherent(bgExtPalDirty);
|
||||
auto objExtPalDirty = GPU.VRAMDirty_BOBJExtPal.DeriveState(&GPU.VRAMMap_BOBJExtPal, GPU);
|
||||
GPU.MakeVRAMFlat_BOBJExtPalCoherent(objExtPalDirty);
|
||||
}
|
||||
|
||||
bool forceblank = false;
|
||||
|
@ -206,11 +146,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
|
|||
|
||||
if (CurUnit->Num == 0)
|
||||
{
|
||||
if (!GPU3D::CurrentRenderer->Accelerated)
|
||||
_3DLine = GPU3D::GetLine(n3dline);
|
||||
if (!GPU.GPU3D.IsRendererAccelerated())
|
||||
_3DLine = GPU.GPU3D.GetLine(n3dline);
|
||||
else if (CurUnit->CaptureLatch && (((CurUnit->CaptureCnt >> 29) & 0x3) != 1))
|
||||
{
|
||||
_3DLine = GPU3D::GetLine(n3dline);
|
||||
_3DLine = GPU.GPU3D.GetLine(n3dline);
|
||||
//GPU3D::GLRenderer::PrepareCaptureFrame();
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +160,7 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
|
|||
for (int i = 0; i < 256; i++)
|
||||
dst[i] = 0xFFFFFFFF;
|
||||
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (GPU.GPU3D.IsRendererAccelerated())
|
||||
{
|
||||
dst[256*3] = 0;
|
||||
}
|
||||
|
@ -254,9 +194,9 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
|
|||
case 2: // VRAM display
|
||||
{
|
||||
u32 vrambank = (CurUnit->DispCnt >> 18) & 0x3;
|
||||
if (GPU::VRAMMap_LCDC & (1<<vrambank))
|
||||
if (GPU.VRAMMap_LCDC & (1<<vrambank))
|
||||
{
|
||||
u16* vram = (u16*)GPU::VRAM[vrambank];
|
||||
u16* vram = (u16*)GPU.VRAM[vrambank];
|
||||
vram = &vram[line * 256];
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
|
@ -312,7 +252,7 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
|
|||
|
||||
u32 masterBrightness = CurUnit->MasterBrightness;
|
||||
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (GPU.GPU3D.IsRendererAccelerated())
|
||||
{
|
||||
dst[256*3] = masterBrightness | (CurUnit->DispCnt & 0x30000);
|
||||
return;
|
||||
|
@ -364,11 +304,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
|
|||
void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB)
|
||||
{
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (Renderer3D& renderer3d = GPU.GPU3D.GetCurrentRenderer(); renderer3d.Accelerated)
|
||||
{
|
||||
if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1))
|
||||
{
|
||||
GPU3D::CurrentRenderer.get()->PrepareCaptureFrame();
|
||||
renderer3d.PrepareCaptureFrame();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -381,10 +321,10 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
|
|||
|
||||
// TODO: confirm this
|
||||
// it should work like VRAM display mode, which requires VRAM to be mapped to LCDC
|
||||
if (!(GPU::VRAMMap_LCDC & (1<<dstvram)))
|
||||
if (!(GPU.VRAMMap_LCDC & (1<<dstvram)))
|
||||
return;
|
||||
|
||||
u16* dst = (u16*)GPU::VRAM[dstvram];
|
||||
u16* dst = (u16*)GPU.VRAM[dstvram];
|
||||
u32 dstaddr = (((captureCnt >> 18) & 0x3) << 14) + (line * width);
|
||||
|
||||
// TODO: handle 3D in GPU3D::CurrentRenderer->Accelerated mode!!
|
||||
|
@ -397,7 +337,7 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
|
|||
else
|
||||
{
|
||||
srcA = BGOBJLine;
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (GPU.GPU3D.IsRendererAccelerated())
|
||||
{
|
||||
// in GPU3D::CurrentRenderer->Accelerated mode, compositing is normally done on the GPU
|
||||
// but when doing display capture, we do need the composited output
|
||||
|
@ -469,8 +409,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
|
|||
else
|
||||
{
|
||||
u32 srcvram = (CurUnit->DispCnt >> 18) & 0x3;
|
||||
if (GPU::VRAMMap_LCDC & (1<<srcvram))
|
||||
srcB = (u16*)GPU::VRAM[srcvram];
|
||||
if (GPU.VRAMMap_LCDC & (1<<srcvram))
|
||||
srcB = (u16*)GPU.VRAM[srcvram];
|
||||
|
||||
if (((CurUnit->DispCnt >> 16) & 0x3) != 2)
|
||||
srcBaddr += ((captureCnt >> 26) & 0x3) << 14;
|
||||
|
@ -479,8 +419,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
|
|||
dstaddr &= 0xFFFF;
|
||||
srcBaddr &= 0xFFFF;
|
||||
|
||||
static_assert(GPU::VRAMDirtyGranularity == 512, "");
|
||||
GPU::VRAMDirty[dstvram][(dstaddr * 2) / GPU::VRAMDirtyGranularity] = true;
|
||||
static_assert(VRAMDirtyGranularity == 512);
|
||||
GPU.VRAMDirty[dstvram][(dstaddr * 2) / VRAMDirtyGranularity] = true;
|
||||
|
||||
switch ((captureCnt >> 29) & 0x3)
|
||||
{
|
||||
|
@ -601,12 +541,12 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
|
|||
{ \
|
||||
if ((bgCnt[num] & 0x0040) && (CurUnit->BGMosaicSize[0] > 0)) \
|
||||
{ \
|
||||
if (GPU3D::CurrentRenderer->Accelerated) DrawBG_##type<true, DrawPixel_Accel>(line, num); \
|
||||
if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_##type<true, DrawPixel_Accel>(line, num); \
|
||||
else DrawBG_##type<true, DrawPixel_Normal>(line, num); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (GPU3D::CurrentRenderer->Accelerated) DrawBG_##type<false, DrawPixel_Accel>(line, num); \
|
||||
if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_##type<false, DrawPixel_Accel>(line, num); \
|
||||
else DrawBG_##type<false, DrawPixel_Normal>(line, num); \
|
||||
} \
|
||||
} while (false)
|
||||
|
@ -616,18 +556,18 @@ void SoftRenderer::DoCapture(u32 line, u32 width)
|
|||
{ \
|
||||
if ((bgCnt[2] & 0x0040) && (CurUnit->BGMosaicSize[0] > 0)) \
|
||||
{ \
|
||||
if (GPU3D::CurrentRenderer->Accelerated) DrawBG_Large<true, DrawPixel_Accel>(line); \
|
||||
if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_Large<true, DrawPixel_Accel>(line); \
|
||||
else DrawBG_Large<true, DrawPixel_Normal>(line); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (GPU3D::CurrentRenderer->Accelerated) DrawBG_Large<false, DrawPixel_Accel>(line); \
|
||||
if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_Large<false, DrawPixel_Accel>(line); \
|
||||
else DrawBG_Large<false, DrawPixel_Normal>(line); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define DoInterleaveSprites(prio) \
|
||||
if (GPU3D::CurrentRenderer->Accelerated) InterleaveSprites<DrawPixel_Accel>(prio); else InterleaveSprites<DrawPixel_Normal>(prio);
|
||||
if (GPU.GPU3D.IsRendererAccelerated()) InterleaveSprites<DrawPixel_Accel>(prio); else InterleaveSprites<DrawPixel_Normal>(prio);
|
||||
|
||||
template<u32 bgmode>
|
||||
void SoftRenderer::DrawScanlineBGMode(u32 line)
|
||||
|
@ -757,8 +697,8 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line)
|
|||
}
|
||||
|
||||
u64 backdrop;
|
||||
if (CurUnit->Num) backdrop = *(u16*)&GPU::Palette[0x400];
|
||||
else backdrop = *(u16*)&GPU::Palette[0];
|
||||
if (CurUnit->Num) backdrop = *(u16*)&GPU.Palette[0x400];
|
||||
else backdrop = *(u16*)&GPU.Palette[0];
|
||||
|
||||
{
|
||||
u8 r = (backdrop & 0x001F) << 1;
|
||||
|
@ -778,7 +718,7 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line)
|
|||
memset(WindowMask, 0xFF, 256);
|
||||
|
||||
ApplySpriteMosaicX();
|
||||
CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]];
|
||||
CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]].data();
|
||||
|
||||
switch (CurUnit->DispCnt & 0x7)
|
||||
{
|
||||
|
@ -795,7 +735,7 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line)
|
|||
// color special effects
|
||||
// can likely be optimized
|
||||
|
||||
if (!GPU3D::CurrentRenderer->Accelerated)
|
||||
if (!GPU.GPU3D.IsRendererAccelerated())
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
|
@ -941,7 +881,7 @@ void SoftRenderer::DrawBG_3D()
|
|||
{
|
||||
int i = 0;
|
||||
|
||||
if (GPU3D::CurrentRenderer->Accelerated)
|
||||
if (GPU.GPU3D.IsRendererAccelerated())
|
||||
{
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
|
@ -998,14 +938,14 @@ void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum)
|
|||
tilesetaddr = ((bgcnt & 0x003C) << 12);
|
||||
tilemapaddr = ((bgcnt & 0x1F00) << 3);
|
||||
|
||||
pal = (u16*)&GPU::Palette[0x400];
|
||||
pal = (u16*)&GPU.Palette[0x400];
|
||||
}
|
||||
else
|
||||
{
|
||||
tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12);
|
||||
tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3);
|
||||
|
||||
pal = (u16*)&GPU::Palette[0];
|
||||
pal = (u16*)&GPU.Palette[0];
|
||||
}
|
||||
|
||||
// adjust Y position in tilemap
|
||||
|
@ -1177,14 +1117,14 @@ void SoftRenderer::DrawBG_Affine(u32 line, u32 bgnum)
|
|||
tilesetaddr = ((bgcnt & 0x003C) << 12);
|
||||
tilemapaddr = ((bgcnt & 0x1F00) << 3);
|
||||
|
||||
pal = (u16*)&GPU::Palette[0x400];
|
||||
pal = (u16*)&GPU.Palette[0x400];
|
||||
}
|
||||
else
|
||||
{
|
||||
tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12);
|
||||
tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3);
|
||||
|
||||
pal = (u16*)&GPU::Palette[0];
|
||||
pal = (u16*)&GPU.Palette[0];
|
||||
}
|
||||
|
||||
u16 curtile;
|
||||
|
@ -1331,8 +1271,8 @@ void SoftRenderer::DrawBG_Extended(u32 line, u32 bgnum)
|
|||
{
|
||||
// 256-color bitmap
|
||||
|
||||
if (CurUnit->Num) pal = (u16*)&GPU::Palette[0x400];
|
||||
else pal = (u16*)&GPU::Palette[0];
|
||||
if (CurUnit->Num) pal = (u16*)&GPU.Palette[0x400];
|
||||
else pal = (u16*)&GPU.Palette[0];
|
||||
|
||||
u8 color;
|
||||
|
||||
|
@ -1390,14 +1330,14 @@ void SoftRenderer::DrawBG_Extended(u32 line, u32 bgnum)
|
|||
tilesetaddr = ((bgcnt & 0x003C) << 12);
|
||||
tilemapaddr = ((bgcnt & 0x1F00) << 3);
|
||||
|
||||
pal = (u16*)&GPU::Palette[0x400];
|
||||
pal = (u16*)&GPU.Palette[0x400];
|
||||
}
|
||||
else
|
||||
{
|
||||
tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12);
|
||||
tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3);
|
||||
|
||||
pal = (u16*)&GPU::Palette[0];
|
||||
pal = (u16*)&GPU.Palette[0];
|
||||
}
|
||||
|
||||
u16 curtile;
|
||||
|
@ -1508,8 +1448,8 @@ void SoftRenderer::DrawBG_Large(u32 line) // BG is always BG2
|
|||
|
||||
// 256-color bitmap
|
||||
|
||||
if (CurUnit->Num) pal = (u16*)&GPU::Palette[0x400];
|
||||
else pal = (u16*)&GPU::Palette[0];
|
||||
if (CurUnit->Num) pal = (u16*)&GPU.Palette[0x400];
|
||||
else pal = (u16*)&GPU.Palette[0];
|
||||
|
||||
u8 color;
|
||||
|
||||
|
@ -1563,7 +1503,7 @@ void SoftRenderer::ApplySpriteMosaicX()
|
|||
|
||||
u32* objLine = OBJLine[CurUnit->Num];
|
||||
|
||||
u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]];
|
||||
u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]].data();
|
||||
|
||||
u32 lastcolor = objLine[0];
|
||||
|
||||
|
@ -1582,7 +1522,7 @@ template <SoftRenderer::DrawPixel drawPixel>
|
|||
void SoftRenderer::InterleaveSprites(u32 prio)
|
||||
{
|
||||
u32* objLine = OBJLine[CurUnit->Num];
|
||||
u16* pal = (u16*)&GPU::Palette[CurUnit->Num ? 0x600 : 0x200];
|
||||
u16* pal = (u16*)&GPU.Palette[CurUnit->Num ? 0x600 : 0x200];
|
||||
|
||||
if (CurUnit->DispCnt & 0x80000000)
|
||||
{
|
||||
|
@ -1656,13 +1596,13 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit)
|
|||
|
||||
if (CurUnit->Num == 0)
|
||||
{
|
||||
auto objDirty = GPU::VRAMDirty_AOBJ.DeriveState(GPU::VRAMMap_AOBJ);
|
||||
GPU::MakeVRAMFlat_AOBJCoherent(objDirty);
|
||||
auto objDirty = GPU.VRAMDirty_AOBJ.DeriveState(GPU.VRAMMap_AOBJ, GPU);
|
||||
GPU.MakeVRAMFlat_AOBJCoherent(objDirty);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto objDirty = GPU::VRAMDirty_BOBJ.DeriveState(GPU::VRAMMap_BOBJ);
|
||||
GPU::MakeVRAMFlat_BOBJCoherent(objDirty);
|
||||
auto objDirty = GPU.VRAMDirty_BOBJ.DeriveState(GPU.VRAMMap_BOBJ, GPU);
|
||||
GPU.MakeVRAMFlat_BOBJCoherent(objDirty);
|
||||
}
|
||||
|
||||
NumSprites[CurUnit->Num] = 0;
|
||||
|
@ -1670,7 +1610,7 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit)
|
|||
memset(OBJWindow[CurUnit->Num], 0, 256);
|
||||
if (!(CurUnit->DispCnt & 0x1000)) return;
|
||||
|
||||
u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0];
|
||||
u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0];
|
||||
|
||||
const s32 spritewidth[16] =
|
||||
{
|
||||
|
@ -1765,7 +1705,7 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit)
|
|||
template<bool window>
|
||||
void SoftRenderer::DrawSprite_Rotscale(u32 num, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos)
|
||||
{
|
||||
u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0];
|
||||
u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0];
|
||||
u16* attrib = &oam[num * 4];
|
||||
u16* rotparams = &oam[(((attrib[1] >> 9) & 0x1F) * 16) + 3];
|
||||
|
||||
|
@ -1977,7 +1917,7 @@ void SoftRenderer::DrawSprite_Rotscale(u32 num, u32 boundwidth, u32 boundheight,
|
|||
template<bool window>
|
||||
void SoftRenderer::DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s32 ypos)
|
||||
{
|
||||
u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0];
|
||||
u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0];
|
||||
u16* attrib = &oam[num * 4];
|
||||
|
||||
u32 pixelattr = ((attrib[2] & 0x0C00) << 6) | 0xC0000;
|
||||
|
@ -2226,3 +2166,4 @@ void SoftRenderer::DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue