mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-08 17:12:25 -10:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
187edde155 | ||
|
|
8dc35a3916 | ||
|
|
8766195f75 | ||
|
|
e2c590c8a3 | ||
|
|
06f61ef9a0 | ||
|
|
87c1840010 | ||
|
|
8de28dbe74 |
No files matched your search
@@ -1,2 +0,0 @@
|
||||
buy_me_a_coffee: hhwtpernwq
|
||||
custom: ['https://www.paypal.com/paypalme/FlycastEmu']
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Please Note: This form is the minimum required information for submitting bugs. -->
|
||||
<!-- Removing this form may lead to your issue being closed until it is completed. -->
|
||||
|
||||
Platform / OS / Hardware:
|
||||
|
||||
Flycast version:
|
||||
|
||||
Hardware:
|
||||
|
||||
**_Description of the Issue_**
|
||||
|
||||
<!-- Add a short, concise description of the issue here -->
|
||||
|
||||
**_Debugging Steps Tested_**
|
||||
|
||||
<!-- Fill in any steps already tried here -->
|
||||
<!-- Begin each new line with an asterisk -->
|
||||
<!-- If no steps are required, please skip -->
|
||||
|
||||
**_Logs Gathered_**
|
||||
|
||||
```
|
||||
Please paste the contents of the log / logs here
|
||||
Delete this field if you have none.
|
||||
|
||||
```
|
||||
|
||||
|
||||
**_Screenshots_**
|
||||
|
||||
<!-- Replace this line with any issue screenshots -->
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
@@ -1,11 +0,0 @@
|
||||
## Description
|
||||
<!-- Summarize the change and its motivation. -->
|
||||
<!-- Use "Fixes #123" for an issue this PR closes, or "Related to #123" otherwise. -->
|
||||
|
||||
## Checklist
|
||||
- [ ] This PR targets `dev`, unless it is a critical, low-risk bug fix.
|
||||
- [ ] I linked the relevant issue or discussion, if one exists.
|
||||
- [ ] I added or updated tests where appropriate.
|
||||
- [ ] I tested the change on the relevant platform(s) and noted any limitations.
|
||||
- [ ] For UI changes, I included screenshots or a recording where helpful.
|
||||
- [ ] I kept the change focused, followed existing project patterns and avoided code duplication.
|
||||
@@ -1,101 +0,0 @@
|
||||
name: Android
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Android
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
|
||||
steps:
|
||||
- name: Set up build environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache libcurl4-openssl-dev
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-android-${{ github.sha }}
|
||||
restore-keys: ccache-android-
|
||||
|
||||
- name: Disable release signing
|
||||
run: sed -i 's/signingConfigs.getByName("release")/signingConfigs.getByName("debug")/' shell/android-studio/flycast/build.gradle.kts
|
||||
if: github.repository != 'flyinghead/flycast' || github.event_name != 'push'
|
||||
|
||||
- name: Bump version code
|
||||
run: sed -i 's/versionCode = 8/versionCode = ${{ github.run_number }}/' shell/android-studio/flycast/build.gradle.kts
|
||||
|
||||
- name: Gradle
|
||||
working-directory: shell/android-studio
|
||||
run: ./gradlew assembleRelease bundleRelease --parallel
|
||||
env:
|
||||
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
|
||||
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: flycast-release.apk
|
||||
path: shell/android-studio/flycast/build/outputs/apk/release/flycast-release.apk
|
||||
|
||||
- name: Build dump_syms
|
||||
working-directory: core/deps/breakpad
|
||||
run: |
|
||||
./configure --prefix=$PWD
|
||||
make -j2
|
||||
make install
|
||||
|
||||
- name: Extract symbols
|
||||
run: |
|
||||
DUMP_SYMS=./core/deps/breakpad/bin/dump_syms
|
||||
SO_DIR=shell/android-studio/flycast/build/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib
|
||||
for arch in arm64-v8a armeabi-v7a x86 x86_64 ; do
|
||||
$DUMP_SYMS $SO_DIR/$arch/libflycast.so > libflycast.so.sym
|
||||
BUILD_ID=`head -1 libflycast.so.sym | awk '{ print $4 }'`
|
||||
mkdir -p symbols/libflycast.so/$BUILD_ID
|
||||
mv libflycast.so.sym symbols/libflycast.so/$BUILD_ID
|
||||
done
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to S3
|
||||
run: |
|
||||
cp shell/android-studio/flycast/build/outputs/bundle/release/*.aab shell/android-studio/flycast/build/outputs/apk/release/
|
||||
rclone copy shell/android-studio/flycast/build/outputs/apk/release scaleway:flycast-builds/android/${GITHUB_REF#refs/}-$GITHUB_SHA --exclude '*.json'
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
|
||||
|
||||
- uses: getsentry/action-setup-cli@v1
|
||||
|
||||
- name: Upload symbols to Sentry
|
||||
run: |
|
||||
VERSION=$(git describe --tags --always)
|
||||
sentry-cli releases new "$VERSION"
|
||||
sentry-cli releases set-commits "$VERSION" --auto
|
||||
sentry-cli debug-files upload symbols
|
||||
shell: bash
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
SENTRY_ORG: flycast
|
||||
SENTRY_PROJECT: minidump
|
||||
if: ${{ env.SENTRY_AUTH_TOKEN != '' && github.event_name == 'push' }}
|
||||
@@ -1,60 +0,0 @@
|
||||
name: BSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
operating_system: [ freebsd, netbsd, openbsd ]
|
||||
architecture: [ arm64, x86-64 ]
|
||||
libretro: [ OFF, ON ]
|
||||
include:
|
||||
- operating_system: freebsd
|
||||
version: '15.1'
|
||||
pkginstall: sudo pkg upgrade -y && sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libcdio libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2
|
||||
- operating_system: netbsd
|
||||
version: '11.0'
|
||||
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache clang cmake git libao libcdio libusb1 libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2
|
||||
- operating_system: openbsd
|
||||
version: '7.9'
|
||||
pkginstall: sudo pkg_add -u && sudo pkg_add ccache cmake git libao libcdio libusb1-1.0.29 libzip lua-5.4.8p0 miniupnpc ninja pulseaudio sdl2
|
||||
exclude:
|
||||
- architecture: arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ matrix.libretro }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ matrix.libretro }}-
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@v1
|
||||
with:
|
||||
operating_system: ${{ matrix.operating_system }}
|
||||
architecture: ${{ matrix.architecture }}
|
||||
version: ${{ matrix.version }}
|
||||
environment_variables: CCACHE_DIR
|
||||
|
||||
- name: Set up build environment
|
||||
run: ${{ matrix.pkginstall }}
|
||||
shell: cpa.sh {0}
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja -DLIBRETRO=${{ matrix.libretro }}
|
||||
cmake --build build --config Release
|
||||
shell: cpa.sh {0}
|
||||
@@ -1,212 +0,0 @@
|
||||
name: Linux, macOS, Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.config.shell }}
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { name: i686-pc-windows-msvc, os: windows-2022, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release }
|
||||
- { name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON -DMOLTENVK_DYLIB=$MOLTENVK_DYLIB, destDir: osx, buildType: RelWithDebInfo, packagePath: artifact/flycast-apple-darwin.zip }
|
||||
- { name: x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: linux, buildType: RelWithDebInfo }
|
||||
- { name: x86_64-pc-windows-msvc, os: windows-2022, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release }
|
||||
- { name: x86_64-w64-mingw32, os: windows-2022, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: win, buildType: RelWithDebInfo, packagePath: artifact/flycast-x86_64-w64-mingw32.zip }
|
||||
- { name: libretro-x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release }
|
||||
- { name: libretro-x86_64-w64-mingw32, os: windows-2022, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -G Ninja, buildType: Release }
|
||||
|
||||
steps:
|
||||
- name: Set up build environment (macOS)
|
||||
run: |
|
||||
# Unlink and re-link to prevent errors when github mac runner images install python outside of brew. See https://github.com/actions/setup-python/issues/577
|
||||
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
|
||||
brew update || :
|
||||
brew install libao ldid pulseaudio
|
||||
|
||||
moltenvk_tag="v1.4.2"
|
||||
moltenvk_root="$RUNNER_TEMP/MoltenVK-$moltenvk_tag"
|
||||
rm -rf "$moltenvk_root"
|
||||
mkdir -p "$moltenvk_root"
|
||||
aria2c "https://github.com/KhronosGroup/MoltenVK/releases/download/$moltenvk_tag/MoltenVK-macos.tar" -d "$RUNNER_TEMP" -o "MoltenVK-macos-$moltenvk_tag.tar"
|
||||
tar -xf "$RUNNER_TEMP/MoltenVK-macos-$moltenvk_tag.tar" -C "$moltenvk_root"
|
||||
moltenvk_dylib="$(find "$moltenvk_root" -type f -path '*/macOS/*' -name libMoltenVK.dylib | head -n 1)"
|
||||
if [ -z "$moltenvk_dylib" ]; then
|
||||
echo "MoltenVK $moltenvk_tag was downloaded, but macOS libMoltenVK.dylib was not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "MOLTENVK_DYLIB=$moltenvk_dylib" >> $GITHUB_ENV
|
||||
if: runner.os == 'macOS'
|
||||
|
||||
- name: Set up build environment (Linux)
|
||||
run: |
|
||||
sudo add-apt-repository ppa:christianrauch/libdecoration
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev libcurl4-openssl-dev libcdio-dev libfuse2 locales
|
||||
sudo apt-get -y install libwayland-dev libdecor-0-dev libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
|
||||
sudo sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/g' /etc/locale.gen
|
||||
sudo locale-gen
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
- name: Set up build environment (Windows, MinGW)
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
install: git make
|
||||
pacboy: ccache:p cmake:p libcdio:p lua:p ninja:p SDL2:p toolchain:p
|
||||
if: matrix.config.shell == 'msys2 {0}'
|
||||
|
||||
- name: Set up build environment (Windows, Visual Studio)
|
||||
run: |
|
||||
choco install ccache directx-sdk
|
||||
echo DXSDK_DIR=C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)>> %GITHUB_ENV%
|
||||
if: matrix.config.shell == 'cmd'
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.config.arch }}
|
||||
if: matrix.config.shell == 'cmd'
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Compile universal dependencies (macOS)
|
||||
run: |
|
||||
for formula in ./shell/apple/libomp.rb ./shell/apple/libpng-flycast.rb; do
|
||||
libname=$(basename "$formula" .rb)
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew install --build-from-source --formula "$formula" || \
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew reinstall --build-from-source --formula "$formula"
|
||||
|
||||
prefix=$(brew --prefix $libname)
|
||||
# Find and verify the static library
|
||||
libfile=$(find "$prefix/lib" -name "*.a" | head -n1)
|
||||
lipo -info "$libfile"
|
||||
|
||||
# Export prefix for subsequent CMake steps
|
||||
if [ -z "$CMAKE_PREFIX_PATH" ]; then
|
||||
CMAKE_PREFIX_PATH="$prefix"
|
||||
else
|
||||
CMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH:$prefix"
|
||||
fi
|
||||
done
|
||||
echo "CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH" >> $GITHUB_ENV
|
||||
# After the last brew download: brew's own curl links brew's zstd
|
||||
brew uninstall --ignore-dependencies zstd
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.config.name }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.config.name }}-
|
||||
if: runner.os != 'macOS'
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact ${{ matrix.config.cmakeArgs }} -DSENTRY_UPLOAD_URL=${{ env.SENTRY_UPLOAD_URL }}
|
||||
cmake --build build --config ${{ matrix.config.buildType }} --target install
|
||||
env:
|
||||
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact -DENABLE_CTEST=ON ${{ matrix.config.cmakeArgs }}
|
||||
cmake --build build --config ${{ matrix.config.buildType }}
|
||||
./build/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols
|
||||
run: |
|
||||
mkdir -p symbols
|
||||
core/deps/breakpad/bin/dump_syms artifact/bin/flycast.exe > symbols/flycast.sym 2>/dev/null
|
||||
strip artifact/bin/flycast.exe
|
||||
if: matrix.config.name == 'x86_64-w64-mingw32'
|
||||
|
||||
- name: Build breakpad (linux)
|
||||
working-directory: core/deps/breakpad
|
||||
run: |
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols and make AppImage (linux)
|
||||
run: |
|
||||
mkdir -p symbols
|
||||
dump_syms artifact/bin/flycast > symbols/flycast.sym 2>/dev/null
|
||||
strip artifact/bin/flycast
|
||||
shell/linux/make-appimage.sh . artifact/bin appimage
|
||||
mv flycast-x86_64.AppImage artifact/bin
|
||||
rm artifact/bin/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- name: Package app (macos)
|
||||
run: |
|
||||
cd artifact/bin
|
||||
codesign --verify --deep --strict --all-architectures --verbose=4 Flycast.app
|
||||
zip -rm flycast.app.zip Flycast.app
|
||||
cp flycast.app.zip "../../${{ matrix.config.packagePath }}"
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- name: Package app (windows)
|
||||
run: |
|
||||
powershell Compress-Archive artifact/bin/flycast.exe artifact/bin/flycast.zip
|
||||
rm artifact/bin/flycast.exe
|
||||
cp artifact/bin/flycast.zip "${{ matrix.config.packagePath }}"
|
||||
if: matrix.config.name == 'x86_64-w64-mingw32'
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: flycast-${{ matrix.config.name }}
|
||||
archive: ${{ !matrix.config.packagePath }}
|
||||
path: |
|
||||
${{ matrix.config.packagePath || 'artifact/bin' }}
|
||||
${{ !matrix.config.packagePath && 'artifact/lib/libretro' || '' }}
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to S3
|
||||
run: rclone copy artifact/bin scaleway:flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA
|
||||
shell: bash
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
|
||||
|
||||
- name: Setup Sentry CLI
|
||||
uses: mathieu-bour/setup-sentry-cli@v2
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
with:
|
||||
url: https://sentry.io
|
||||
token: ${{ env.SENTRY_TOKEN }}
|
||||
organization: flycast
|
||||
project: minidump
|
||||
version: 2.21.2
|
||||
if: ${{ env.SENTRY_TOKEN != '' }}
|
||||
|
||||
- name: Upload symbols to Sentry (Windows, macOS, Linux)
|
||||
run: |
|
||||
VERSION=$(git describe --tags --always)
|
||||
sentry-cli releases new "$VERSION"
|
||||
sentry-cli releases set-commits "$VERSION" --auto
|
||||
sentry-cli debug-files upload symbols
|
||||
shell: bash
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') && github.event_name == 'push' }}
|
||||
@@ -1,36 +0,0 @@
|
||||
# Prepare source texts & upload them to Crowdin
|
||||
|
||||
name: Crowdin Source Texts Upload
|
||||
|
||||
# on change to the English texts
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'shell/libretro/libretro_core_options.h'
|
||||
|
||||
jobs:
|
||||
upload_source_file:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Upload Source
|
||||
shell: bash
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: |
|
||||
python3 intl/upload_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options.h"
|
||||
@@ -1,49 +0,0 @@
|
||||
# Download translations form Crowdin & Recreate libretro_core_options_intl.h
|
||||
|
||||
name: Crowdin Translation Integration
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# please choose a random time & weekday to avoid all repos synching at the same time
|
||||
- cron: '15 22 * * 5' # Fridays at 10:15 PM, UTC
|
||||
|
||||
jobs:
|
||||
create_intl_file:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
|
||||
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
|
||||
|
||||
- name: Create intl file
|
||||
shell: bash
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: |
|
||||
python3 intl/download_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options_intl.h"
|
||||
|
||||
- name: Commit files
|
||||
run: |
|
||||
git config --local user.email "[email protected]"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add intl/*_workflow.py "shell/libretro/libretro_core_options_intl.h"
|
||||
git commit -m "Fetch translations & Recreate libretro_core_options_intl.h"
|
||||
|
||||
- name: GitHub Push
|
||||
uses: ad-m/github-push-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ github.ref }}
|
||||
@@ -1,67 +0,0 @@
|
||||
name: Nintendo Switch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Switch ${{ matrix.config.name }}
|
||||
runs-on: ubuntu-latest
|
||||
container: devkitpro/devkita64:latest
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- { name: standalone, destDir: switch }
|
||||
- { name: libretro, cmakeArgs: -DLIBRETRO=ON }
|
||||
|
||||
steps:
|
||||
- name: Set up build environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache gh
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Mark git repository as safe
|
||||
run: git config --global --add safe.directory $PWD
|
||||
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-switch-${{ matrix.config.name }}-${{ github.sha }}
|
||||
restore-keys: ccache-switch-${{ matrix.config.name }}-
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
$DEVKITPRO/portlibs/switch/bin/aarch64-none-elf-cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=artifact -G Ninja ${{ matrix.config.cmakeArgs }}
|
||||
cmake --build build --config Release --target install
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: flycast-switch-${{ matrix.config.name }}
|
||||
path: |
|
||||
artifact/bin
|
||||
artifact/lib/libretro
|
||||
|
||||
- name: Package app
|
||||
run: zip -m artifact/bin/flycast.nro.zip artifact/bin/flycast.nro
|
||||
if: hashFiles('artifact/bin/flycast.nro') != ''
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to S3
|
||||
run: rclone copy artifact/bin scaleway:flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
|
||||
@@ -1,59 +0,0 @@
|
||||
name: Xbox, UWP
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: UWP
|
||||
runs-on: windows-2022
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [ Win32, x64, ARM64 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" -A ${{ matrix.arch }} -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.22000.0
|
||||
cmake --build build --config Release -- /m
|
||||
shell: cmd
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Package app
|
||||
run: |
|
||||
mkdir build\artifact
|
||||
cd build\AppPackages\flycast\flycast_*_Test
|
||||
mkdir tmp
|
||||
ren *.msix flycast.msix
|
||||
makeappx unpack /p .\flycast.msix /d tmp
|
||||
copy ..\..\..\Release\*.dll tmp
|
||||
makeappx pack /d tmp /p ..\..\..\artifact\flycast.appx
|
||||
|
||||
- name: Sign app
|
||||
run: signtool sign /f shell\uwp\sign_cert.pfx /p '${{ secrets.SIGN_CERT_PWD }}' /v /fd SHA256 build\artifact\flycast.appx
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: flycast-uwp-${{ matrix.arch }}
|
||||
path: build/artifact
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to S3
|
||||
run: rclone copy build/artifact scaleway:flycast-builds/xbox/${GITHUB_REF#refs/}-$GITHUB_SHA
|
||||
shell: bash
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.arch == 'x64'
|
||||
+69
-15
@@ -1,22 +1,76 @@
|
||||
build/
|
||||
artifact/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
shell/apple/emulator-ios/OpenMP/OpenMP.xcframework
|
||||
*.class
|
||||
*.orig
|
||||
*build_obj
|
||||
Workdir/data
|
||||
Workdir/reicast*
|
||||
Workdir/vmu*
|
||||
Workdir/webui*
|
||||
Workdir/emu.cfg
|
||||
Workdir/lib*ant.properties
|
||||
|
||||
# iOS General
|
||||
.DS_Store
|
||||
*/build/*
|
||||
**/build/*
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
profile
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
emulator.xccheckout
|
||||
reicast.xccheckout
|
||||
build/*
|
||||
*.so
|
||||
*Karen_angelXwind*
|
||||
local.properties
|
||||
ant.properties
|
||||
*build_obj
|
||||
*.d
|
||||
Workdir/data
|
||||
Workdir/reicast*
|
||||
Workdir/vmu*
|
||||
Workdir/webui*
|
||||
Workdir/emu.cfg
|
||||
Workdir/lib*ant.properties
|
||||
reicast-ios.xccheckout
|
||||
|
||||
# Linux General
|
||||
shell/linux/.map
|
||||
shell/linux/nosym-reicast.elf
|
||||
shell/linux/reicast.elf
|
||||
shell/linux/reicast_naomi.elf
|
||||
shell/linux/reicast_awave.elf
|
||||
shell/linux/dispframe.elf
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
.vscode/
|
||||
out/
|
||||
generated
|
||||
.vs
|
||||
|
||||
# Android General
|
||||
bin/
|
||||
gen/
|
||||
obj/
|
||||
build/*
|
||||
*.so
|
||||
local.properties
|
||||
ant.properties
|
||||
|
||||
# Android Studio
|
||||
.gradle
|
||||
*.iml
|
||||
.externalNativeBuild
|
||||
gradle.properties
|
||||
shell/android-studio/reicast/src/main/assets/build
|
||||
|
||||
# JetBrains IDES
|
||||
.idea/
|
||||
cmake-build-*/
|
||||
|
||||
# Dependencies from .gitmodules
|
||||
core/deps/breakpad/
|
||||
core/deps/glslang/
|
||||
.cache/
|
||||
compile_commands.json
|
||||
CMakeUserPresets.json
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
|
||||
|
||||
##############################################################################
|
||||
################################# BOILERPLATE ################################
|
||||
##############################################################################
|
||||
|
||||
# Core definitions
|
||||
.core-defs:
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CORENAME: flycast
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON
|
||||
|
||||
.core-defs-linux:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE
|
||||
CC: /usr/bin/gcc-12
|
||||
CXX: /usr/bin/g++-12
|
||||
|
||||
.core-defs-osx-x64:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64
|
||||
EXTRA_PATH: Release
|
||||
|
||||
.core-defs-osx-arm64:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -G Xcode
|
||||
EXTRA_PATH: Release
|
||||
|
||||
.core-defs-ios-arm64:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_OPENMP=OFF -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0
|
||||
IOS_MINVER: 13.0
|
||||
MINVER: 13.0
|
||||
|
||||
.core-defs-libnx:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_OPENMP=OFF
|
||||
|
||||
.core-defs-android:
|
||||
extends: .core-defs
|
||||
script:
|
||||
- echo y | /android-sdk-linux/cmdline-tools/latest/bin/sdkmanager "ndk;${ANDROID_NDK_VERSION}"
|
||||
- cmake -DLIBRETRO=ON -DUSE_OPENMP=OFF -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm -DANDROID_WEAK_API_DEFS=ON "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
|
||||
- cmake --build $BUILD_DIR --target ${CORENAME}_libretro --config Release -- -j $NUMPROC
|
||||
- mv $BUILD_DIR/${CORENAME}_libretro.so $LIBNAME
|
||||
- if [ $STRIP_CORE_LIB -eq 1 ]; then $NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip $LIBNAME; fi
|
||||
variables:
|
||||
ANDROID_NDK_VERSION: 29.0.14206865
|
||||
API_LEVEL: 21
|
||||
NDK_ROOT: /android-sdk-linux/ndk/$ANDROID_NDK_VERSION
|
||||
|
||||
# Inclusion templates, required for the build to work
|
||||
include:
|
||||
################################## DESKTOPS ################################
|
||||
# Windows
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/windows-cmake-mingw.yml'
|
||||
|
||||
# MacOS
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: 'osx-cmake-x86.yml'
|
||||
|
||||
# MacOS arm64
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: 'osx-cmake-arm64.yml'
|
||||
|
||||
# Linux
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/linux-cmake.yml'
|
||||
|
||||
################################## CELLULAR ################################
|
||||
# Android
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/android-cmake.yml'
|
||||
|
||||
# iOS arm64
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/ios-cmake.yml'
|
||||
|
||||
################################## CONSOLES ################################
|
||||
# Nintendo Switch
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/libnx-static-cmake.yml'
|
||||
|
||||
# tvOS arm64
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/tvos-cmake.yml'
|
||||
|
||||
#################################### MISC ##################################
|
||||
# webOS 32-bit (LGTV)
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/webos-cmake.yml'
|
||||
|
||||
# Stages for building
|
||||
stages:
|
||||
- build-prepare
|
||||
- build-shared
|
||||
- build-static
|
||||
|
||||
##############################################################################
|
||||
#################################### STAGES ##################################
|
||||
##############################################################################
|
||||
#
|
||||
################################### DESKTOPS #################################
|
||||
# Windows 64-bit
|
||||
libretro-build-windows-x64:
|
||||
extends:
|
||||
- .libretro-windows-cmake-x86_64
|
||||
- .core-defs
|
||||
|
||||
# Windows 32-bit
|
||||
libretro-build-windows-i686:
|
||||
extends:
|
||||
- .libretro-windows-cmake-x86
|
||||
- .core-defs
|
||||
|
||||
# Linux 64-bit
|
||||
libretro-build-linux-x64:
|
||||
extends:
|
||||
- .libretro-linux-cmake-x86_64
|
||||
- .core-defs-linux
|
||||
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:backports
|
||||
|
||||
# Linux 32-bit
|
||||
libretro-build-linux-i686:
|
||||
extends:
|
||||
- .libretro-linux-cmake-x86
|
||||
- .core-defs-linux
|
||||
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-i386-ubuntu:backports
|
||||
|
||||
# Linux 64-bit (ARM)
|
||||
libretro-build-linux-aarch64:
|
||||
extends:
|
||||
- .libretro-linux-cmake-aarch64
|
||||
- .core-defs-linux
|
||||
|
||||
|
||||
# MacOS 64-bit
|
||||
libretro-build-osx-x64:
|
||||
extends:
|
||||
- .libretro-osx-cmake-x86
|
||||
- .core-defs-osx-x64
|
||||
tags:
|
||||
- mac-apple-silicon
|
||||
|
||||
# MacOS arm 64-bit
|
||||
libretro-build-osx-arm64:
|
||||
extends:
|
||||
- .libretro-osx-cmake-arm64
|
||||
- .core-defs-osx-arm64
|
||||
|
||||
################################### CELLULAR #################################
|
||||
# Android ARMv7a
|
||||
android-armeabi-v7a:
|
||||
extends:
|
||||
- .libretro-android-cmake-armeabi-v7a
|
||||
- .core-defs-android
|
||||
|
||||
# Android ARMv8a
|
||||
android-arm64-v8a:
|
||||
extends:
|
||||
- .libretro-android-cmake-arm64-v8a
|
||||
- .core-defs-android
|
||||
|
||||
# Android 64-bit x86
|
||||
android-x86_64:
|
||||
extends:
|
||||
- .libretro-android-cmake-x86_64
|
||||
- .core-defs-android
|
||||
|
||||
# Android 32-bit x86
|
||||
android-x86:
|
||||
extends:
|
||||
- .libretro-android-cmake-x86
|
||||
- .core-defs-android
|
||||
|
||||
libretro-build-ios-arm64:
|
||||
extends:
|
||||
- .libretro-ios-cmake-arm64
|
||||
- .core-defs-ios-arm64
|
||||
|
||||
################################### CONSOLES #################################
|
||||
# Nintendo Switch
|
||||
libretro-build-libnx-aarch64:
|
||||
extends:
|
||||
- .libretro-libnx-static-cmake-retroarch-master
|
||||
- .core-defs-libnx
|
||||
# The libnx image ships CMake 3.18.4 and this tree asks for 3.22.1, so the
|
||||
# job stops at configure. Put a newer one in front of it - in /tmp, since
|
||||
# the job does not run as a user who can write /opt.
|
||||
before_script:
|
||||
- !reference [.libretro-libnx-static-cmake, before_script]
|
||||
- |
|
||||
CMAKE_VER=3.31.6
|
||||
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-linux-x86_64.tar.gz" | tar -xz -C /tmp
|
||||
export PATH="/tmp/cmake-${CMAKE_VER}-linux-x86_64/bin:$PATH"
|
||||
# The template's script, with one word changed. It builds --target
|
||||
# ${CORENAME}_libretro, and on Switch that target is flycast's own objects
|
||||
# and nothing else. `combined` is the target CMakeLists.txt defines for
|
||||
# exactly this case: it folds in xxhash, chdr, zip, elf and the resources,
|
||||
# which is what RetroArch links against afterwards.
|
||||
script:
|
||||
- cmake $CORE_ARGS "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR -DCMAKE_BUILD_TYPE=Release -DLIBRETRO_STATIC=ON -DLIBRETRO_SUFFIX=_libnx -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake -DNSWITCH=ON -DCFLAGS=-DHAVE_LIBNX=1 -DCXXFLAGS=-DHAVE_LIBNX=1
|
||||
- cmake --build $BUILD_DIR --target combined --config Release -- -j $NUMPROC
|
||||
- mv $BUILD_DIR/$EXTRA_PATH/${CORENAME}_libretro_libnx.a retroarch-precompiled/libretro_libnx.a
|
||||
- cd retroarch-precompiled/
|
||||
- make -f Makefile.libnx -j10
|
||||
- mv retroarch_switch.nro ../${CORENAME}_libretro_libnx.nro
|
||||
- mv retroarch_switch.elf ../${CORENAME}_libretro_libnx.elf
|
||||
- cd ../
|
||||
|
||||
# tvOS arm64
|
||||
libretro-build-tvos-arm64:
|
||||
extends:
|
||||
- .libretro-tvos-cmake-arm64
|
||||
- .core-defs-ios-arm64
|
||||
|
||||
#################################### MISC ##################################
|
||||
# webOS 32-bit
|
||||
libretro-build-webos-armv7a:
|
||||
extends:
|
||||
- .libretro-webos-armv7a-cmake
|
||||
- .core-defs
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
[submodule "core/deps/SDL"]
|
||||
path = core/deps/SDL
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
[submodule "core/deps/libchdr"]
|
||||
path = core/deps/libchdr
|
||||
url = https://github.com/flyinghead/libchdr.git
|
||||
ignore = dirty
|
||||
[submodule "core/deps/luabridge"]
|
||||
path = core/deps/luabridge
|
||||
url = https://github.com/vinniefalco/LuaBridge.git
|
||||
[submodule "core/deps/breakpad"]
|
||||
path = core/deps/breakpad
|
||||
url = https://github.com/flyinghead/mingw-breakpad
|
||||
[submodule "core/deps/Vulkan-Headers"]
|
||||
path = core/deps/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||
[submodule "core/deps/VulkanMemoryAllocator"]
|
||||
path = core/deps/VulkanMemoryAllocator
|
||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||
[submodule "core/deps/glslang"]
|
||||
path = core/deps/glslang
|
||||
url = https://github.com/KhronosGroup/glslang.git
|
||||
[submodule "core/deps/oboe"]
|
||||
path = core/deps/oboe
|
||||
url = https://github.com/google/oboe.git
|
||||
[submodule "core/deps/Syphon"]
|
||||
path = core/deps/Syphon
|
||||
url = https://github.com/vkedwardli/Syphon-Framework.git
|
||||
[submodule "core/deps/Spout"]
|
||||
path = core/deps/Spout
|
||||
url = https://github.com/vkedwardli/Spout2.git
|
||||
[submodule "core/deps/discord-rpc"]
|
||||
path = core/deps/discord-rpc
|
||||
url = https://github.com/harmonytf/discord-rpc
|
||||
[submodule "core/deps/libadrenotools"]
|
||||
path = core/deps/libadrenotools
|
||||
url = https://github.com/bylaws/libadrenotools
|
||||
[submodule "core/deps/rcheevos"]
|
||||
path = core/deps/rcheevos
|
||||
url = https://github.com/RetroAchievements/rcheevos.git
|
||||
[submodule "core/deps/googletest"]
|
||||
path = core/deps/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "core/deps/asio"]
|
||||
path = core/deps/asio
|
||||
url = https://github.com/flyinghead/asio.git
|
||||
[submodule "core/deps/xbyak"]
|
||||
path = core/deps/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "core/deps/libjuice"]
|
||||
path = core/deps/libjuice
|
||||
url = https://github.com/paullouisageneau/libjuice.git
|
||||
[submodule "core/deps/websocketpp"]
|
||||
path = core/deps/websocketpp
|
||||
url = https://github.com/zaphoyd/websocketpp.git
|
||||
[submodule "core/deps/DreamPicoPort-API"]
|
||||
path = core/deps/DreamPicoPort-API
|
||||
url = https://github.com/OrangeFox86/DreamPicoPort-API.git
|
||||
[submodule "core/deps/tinygettext"]
|
||||
path = core/deps/tinygettext
|
||||
url = https://github.com/flyinghead/tinygettext
|
||||
[submodule "core/deps/freetype"]
|
||||
path = core/deps/freetype
|
||||
url = https://gitlab.freedesktop.org/freetype/freetype.git
|
||||
[submodule "core/deps/gamesdk"]
|
||||
path = core/deps/gamesdk
|
||||
url = https://github.com/flyinghead/gamesdk.git
|
||||
@@ -0,0 +1,4 @@
|
||||
extraction:
|
||||
cpp:
|
||||
index:
|
||||
build_command: cd shell/linux && make
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
language: android
|
||||
dist: trusty
|
||||
android:
|
||||
components:
|
||||
- platform-tools
|
||||
- tools
|
||||
- build-tools-27.0.3
|
||||
- android-25
|
||||
- extra-google-m2repository
|
||||
- extra-android-m2repository
|
||||
licenses:
|
||||
- android-sdk-preview-license-.+
|
||||
- android-sdk-license-.+
|
||||
sudo: false
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
cache:
|
||||
directories:
|
||||
- "$HOME/.gradle/caches/"
|
||||
- "$HOME/.gradle/wrapper/"
|
||||
- "$HOME/.android/build-cache"
|
||||
before_install:
|
||||
- GIT_HASH=`git log --pretty=format:'%h' -n 1`
|
||||
- GIT_BUILD=`git describe --all --always`-$GIT_HASH
|
||||
- mkdir "$ANDROID_HOME/licenses" || true
|
||||
- echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license"
|
||||
- echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license"
|
||||
- chmod +x shell/android-studio/gradlew
|
||||
install:
|
||||
- sudo apt-get -qq update
|
||||
- touch ~/.android/repositories.cfg
|
||||
- echo y | sdkmanager 'ndk-bundle'
|
||||
- export NDK=ANDROID_NDK=ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle
|
||||
- export PATH=${PATH}:$ANDROID_HOME/tools
|
||||
- export PATH=${PATH}:$ANDROID_HOME/platform-tools
|
||||
- export PATH=${PATH}:$NDK
|
||||
- mkdir -p ~/.android
|
||||
before_script:
|
||||
script:
|
||||
- git fetch --depth 250
|
||||
- cd shell/android-studio
|
||||
- export NUMBER_OF_PROCESSORS=2
|
||||
- sudo chmod 755 travis-build.sh
|
||||
- "./travis-build.sh"
|
||||
before_deploy:
|
||||
- cd ../../
|
||||
- mkdir -p artifacts/$GIT_BUILD/
|
||||
- cp shell/android-studio/reicast/build/outputs/apk/dreamcast/debug/reicast-dreamcast-debug.apk artifacts/$GIT_BUILD/flycast-debug-$GIT_HASH.apk
|
||||
deploy:
|
||||
provider: s3
|
||||
access_key_id: AKIAJOZQS4H2PHQWYFCA
|
||||
secret_access_key:
|
||||
secure: $S3_SECRET_KEY
|
||||
bucket: flycast-builds
|
||||
local-dir: artifacts
|
||||
upload-dir: android
|
||||
acl: public_read
|
||||
skip_cleanup: true
|
||||
on:
|
||||
all_branches: true
|
||||
repo: flyinghead/flycast
|
||||
|
||||
+284
-1545
File diff suppressed because it is too large.
Load diff
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "clang",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "build-clang",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "build-gcc",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"environments": [
|
||||
{
|
||||
"environment": "toolchain.generic",
|
||||
"TOOLCHAIN_FILE": "ps4sdk.cmake"
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"name": "win-x86-Debug",
|
||||
"generator": "Ninja",
|
||||
"description": "TemplateDescription_Localize_x86Debug",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x86"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "win-x86-Release",
|
||||
"generator": "Ninja",
|
||||
"description": "TemplateDescription_Localize_x86Release",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x86"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "win-x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"description": "TemplateDescription_Localize_x64Debug",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x64_x64"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "-DNINJA=1",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "win-x64-Release",
|
||||
"generator": "Ninja",
|
||||
"description": "TemplateDescription_Localize_x64Release",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x64_x64"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "-DNINJA=1",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "win-x64-Clang-RelWithDebInfo",
|
||||
"generator": "Ninja",
|
||||
"description": "TemplateDescription_Localize_x64Release",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x64_x64"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": "",
|
||||
"variables": [
|
||||
{
|
||||
"name": "CMAKE_C_COMPILER",
|
||||
"value": "clang-cl.exe"
|
||||
},
|
||||
{
|
||||
"name": "CMAKE_CXX_COMPILER",
|
||||
"value": "clang-cl.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Console SDK's
|
||||
{
|
||||
"name": "PS4 SDK",
|
||||
"generator": "Ninja",
|
||||
"description": "TemplateDescription_Localize_PS4SDK",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"toolchain.generic"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=${projectDir}\\cmake\\ps4sdk.cmake",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "NSW SDK",
|
||||
"generator": "Ninja",
|
||||
"description": "TemplateDescription_Localize_PS4SDK",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"toolchain.generic"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=${projectDir}\\cmake\\devkitA64.cmake",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
|
||||
// UWP and VS Gen (temp?)
|
||||
{
|
||||
"name": "uwp-x64-Release",
|
||||
"generator": "Visual Studio 15 2017 Win64",
|
||||
"description": "TemplateDescription_Localize_x64Release",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x64_x64"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "-DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "win-x64-MSBuild-Release",
|
||||
"generator": "Visual Studio 15 2017 Win64",
|
||||
"description": "TemplateDescription_Localize_x64Release",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x64_x64"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"environments": [
|
||||
{
|
||||
//"MINGW64_ROOT": "C:\\msys64\\mingw64",
|
||||
"BIN_ROOT": "${env.MINGW64_ROOT}\\bin",
|
||||
"FLAVOR": "x86_64-w64-mingw32",
|
||||
"TOOLSET_VERSION": "7.3.0",
|
||||
"PATH": "${env.MINGW64_ROOT}\\bin;${env.MINGW64_ROOT}\\..\\usr\\local\\bin;${env.MINGW64_ROOT}\\..\\usr\\bin;${env.MINGW64_ROOT}\\..\\bin;${env.PATH}",
|
||||
"INCLUDE": "${env.INCLUDE};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\tr1;${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\${env.FLAVOR}",
|
||||
"environment": "mingw_64"
|
||||
}
|
||||
],
|
||||
"name": "Mingw64-Release",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"mingw_64"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": "",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"variables": [
|
||||
{
|
||||
"name": "CMAKE_C_COMPILER",
|
||||
"value": "${env.BIN_ROOT}\\gcc.exe"
|
||||
},
|
||||
{
|
||||
"name": "CMAKE_CXX_COMPILER",
|
||||
"value": "${env.BIN_ROOT}\\g++.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"environments": [
|
||||
{
|
||||
//"MINGW64_ROOT": "C:\\msys64\\mingw64",
|
||||
"BIN_ROOT": "${env.MINGW64_ROOT}\\bin",
|
||||
"FLAVOR": "x86_64-w64-mingw32",
|
||||
"TOOLSET_VERSION": "7.3.0",
|
||||
"PATH": "${env.MINGW64_ROOT}\\bin;${env.MINGW64_ROOT}\\..\\usr\\local\\bin;${env.MINGW64_ROOT}\\..\\usr\\bin;${env.MINGW64_ROOT}\\..\\bin;${env.PATH}",
|
||||
"INCLUDE": "${env.INCLUDE};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\tr1;${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\${env.FLAVOR}",
|
||||
"environment": "mingw_64"
|
||||
}
|
||||
],
|
||||
"name": "Mingw64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [
|
||||
"mingw_64"
|
||||
],
|
||||
"buildRoot": "${projectDir}\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\build\\${name}\\install",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": "",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"variables": [
|
||||
{
|
||||
"name": "CMAKE_C_COMPILER",
|
||||
"value": "${env.BIN_ROOT}\\gcc.exe"
|
||||
},
|
||||
{
|
||||
"name": "CMAKE_CXX_COMPILER",
|
||||
"value": "${env.BIN_ROOT}\\g++.exe"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,107 +1,15 @@
|
||||
# Flycast
|
||||
flycast
|
||||
===========
|
||||
**flycast** is a multi-platform Sega Dreamcast, Naomi and Atomiswave emulator derived from [**reicast**](https://reicast.com/)
|
||||
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/android.yml)
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/c-cpp.yml)
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/switch.yml)
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml)
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/bsd.yml)
|
||||
Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
|
||||
|
||||
<img src="shell/linux/flycast.png" alt="flycast logo" width="150"/>
|
||||
Binaries
|
||||
========
|
||||
Get fresh builds for your system [on the builds page](https://flyinghead.github.io/flycast-builds/).
|
||||
|
||||
**Flycast** is a multi-platform Sega Dreamcast, Naomi, Naomi 2, and Atomiswave emulator derived from [**reicast**](https://github.com/skmp/reicast-emulator).
|
||||
**New:** Now automated test results are available as well.
|
||||
|
||||
Information about configuration and supported features can be found on [**TheArcadeStriker's flycast wiki**](https://github.com/TheArcadeStriker/flycast-wiki/wiki).
|
||||
|
||||
Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
|
||||
|
||||
## Downloads      
|
||||
|
||||
Get builds for your system from the [**builds page**](https://flyinghead.github.io/flycast-builds/) or [**GitHub Releases**](https://github.com/flyinghead/flycast/releases).
|
||||
|
||||
- **Latest master builds:** regular builds from the `master` branch with recent fixes and updates.
|
||||
- **Nightly dev builds:** experimental builds with the latest features and changes.
|
||||
- **Stable tagged releases:** versioned release builds published on GitHub Releases.
|
||||
|
||||
Automated test results are available from the builds page as well.
|
||||
|
||||
## Install
|
||||
|
||||
### Android 
|
||||
|
||||
 Install Flycast from [**Google Play**](https://play.google.com/store/apps/details?id=com.flycast.emulator).
|
||||
|
||||
### Flatpak (Linux )
|
||||
|
||||
1. [Set up Flatpak](https://www.flatpak.org/setup/).
|
||||
|
||||
2. Install Flycast from [Flathub](https://flathub.org/apps/details/org.flycast.Flycast):
|
||||
|
||||
 `flatpak install -y org.flycast.Flycast`
|
||||
|
||||
3. Run Flycast:
|
||||
|
||||
 `flatpak run org.flycast.Flycast`
|
||||
|
||||
### Homebrew (macOS )
|
||||
|
||||
1. [Set up Homebrew](https://brew.sh) or run `brew update` if already installed.
|
||||
|
||||
2. Choose one channel:
|
||||
|
||||
| Channel | Install command |
|
||||
| -------------------- | ------------------------------------------------------- |
|
||||
| Master (recommended) | `brew install --cask flyinghead/flycast/flycast@master` |
|
||||
| Stable | `brew install --cask flyinghead/flycast/flycast` |
|
||||
| Nightly dev | `brew install --cask flyinghead/flycast/flycast@dev` |
|
||||
|
||||
3. Run Flycast from your Application folder
|
||||
|
||||
 See the <a href="https://github.com/flyinghead/homebrew-flycast#readme">Flycast tap</a> for updating, uninstalling, and switching channels.
|
||||
|
||||
### iOS
|
||||
|
||||
 Due to persistent harassment from an iOS user, support for this platform has been dropped.
|
||||
|
||||
### Xbox One/Series 
|
||||
|
||||
 Grab the latest build from [**the builds page**](https://flyinghead.github.io/flycast-builds/), or the [**GitHub Actions**](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml). Then install it using the **Xbox Device Portal**.
|
||||
|
||||
## Build from source
|
||||
|
||||
### macOS
|
||||
|
||||
 Right-click the bootstrap script and choose **Open**:
|
||||
|
||||
 `shell/apple/generate_xcode_project.command`
|
||||
|
||||
### Windows
|
||||
|
||||
 Double-click the bootstrap script:
|
||||
|
||||
 `shell\windows\generate_vs_project.bat`
|
||||
|
||||
### Linux
|
||||
|
||||
#### Dependencies
|
||||
|
||||
- **C/C++ compiler toolchain** (e.g. `gcc`/`g++`)
|
||||
- **CMake**
|
||||
- **make**
|
||||
- **libcurl** (development headers)
|
||||
- **libudev** (development headers)
|
||||
- **SDL2** (development headers)
|
||||
- **Graphics API**: Vulkan, OpenGL
|
||||
|
||||
#### Build
|
||||
|
||||
```
|
||||
$ git clone --recursive https://github.com/flyinghead/flycast.git
|
||||
$ cd flycast
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
```
|
||||
|
||||
## Packaging status
|
||||
|
||||
[](https://repology.org/project/flycast/versions)
|
||||
Disclaimer
|
||||
==========
|
||||
All code contritbuted to this fork is *not* bound by the Individual Contributor License Agreement of the upstream repository (https://github.com/reicast/reicast-emulator) and shall *not* be considered as a contribution to the upstream repository.
|
||||
@@ -0,0 +1,46 @@
|
||||
version: git-{branch}-{build}
|
||||
image: Visual Studio 2017
|
||||
|
||||
configuration:
|
||||
- fast
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
before_build:
|
||||
- cmd: >-
|
||||
set BUILD_PATH=shell\linux
|
||||
|
||||
set EXTRA_PATH=C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin\
|
||||
|
||||
set PATH=%EXTRA_PATH%;%PATH%
|
||||
|
||||
if not exist %BUILD_PATH% (mkdir %BUILD_PATH%)
|
||||
|
||||
cd %BUILD_PATH%
|
||||
|
||||
build_script:
|
||||
- cmd: >-
|
||||
if "%CONFIGURATION%"=="fast" (mingw32-make platform=win32)
|
||||
|
||||
after_build:
|
||||
- cmd: >-
|
||||
if "%CONFIGURATION%"=="fast" (cd ..\.. && set EXE_PATH=shell\linux\nosym-reicast.exe)
|
||||
|
||||
mkdir artifacts
|
||||
|
||||
move %EXE_PATH% artifacts\flycast-win_%PLATFORM%-%CONFIGURATION%-%APPVEYOR_REPO_COMMIT%.exe
|
||||
|
||||
artifacts:
|
||||
- path: artifacts
|
||||
name: flycast-win_$(PLATFORM)-$(CONFIGURATION)-$(APPVEYOR_REPO_COMMIT)
|
||||
deploy:
|
||||
- provider: S3
|
||||
access_key_id: AKIAJOZQS4H2PHQWYFCA
|
||||
secret_access_key:
|
||||
secure: Y/QobuGrBsa137sbi9a+UnvSLqpWITTCp5yFTgDJ4i1Gduyi9V7i/g3zLZ4s2J7d
|
||||
bucket: flycast-builds
|
||||
folder: 'win/heads/$(APPVEYOR_REPO_BRANCH)-$(APPVEYOR_REPO_COMMIT)'
|
||||
artifact: flycast-win_$(PLATFORM)-$(CONFIGURATION)-$(APPVEYOR_REPO_COMMIT)
|
||||
region: us-east-2
|
||||
set_public: true
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
---
|
||||
format_version: '7'
|
||||
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
|
||||
project_type: macos
|
||||
trigger_map:
|
||||
- push_branch: "*"
|
||||
workflow: deploy
|
||||
- pull_request_source_branch: "*"
|
||||
workflow: deploy
|
||||
workflows:
|
||||
deploy:
|
||||
envs:
|
||||
- opts:
|
||||
is_expand: false
|
||||
BITRISE_SCHEME: reicast-osx
|
||||
steps:
|
||||
- [email protected]:
|
||||
run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
|
||||
- brew-install:
|
||||
title: Install SDL2
|
||||
inputs:
|
||||
- packages: "sdl2"
|
||||
- [email protected]: {}
|
||||
- [email protected]: {}
|
||||
- [email protected]:
|
||||
inputs:
|
||||
- content: |
|
||||
#!/bin/bash
|
||||
export GIT_HASH=`git log --pretty=format:'%h' -n 1`
|
||||
envman add --key GIT_HASH --value $GIT_HASH
|
||||
envman add --key GIT_BUILD --value `git describe --all --always | sed 's/remotes\/origin/heads/'`-$GIT_HASH
|
||||
- [email protected]: {}
|
||||
- [email protected]:
|
||||
inputs:
|
||||
- project_path: "$BITRISE_PROJECT_PATH"
|
||||
- [email protected]:
|
||||
inputs:
|
||||
- project_path: "$BITRISE_PROJECT_PATH"
|
||||
- scheme: "$BITRISE_SCHEME"
|
||||
- export_method: "$BITRISE_EXPORT_METHOD"
|
||||
- [email protected]:
|
||||
inputs:
|
||||
- aws_access_key: AKIAJOZQS4H2PHQWYFCA
|
||||
- aws_secret_key: "$S3_SECRET_KEY"
|
||||
- bucket_name: flycast-builds
|
||||
- path_in_bucket: osx/$GIT_BUILD
|
||||
- file_path: "$BITRISE_EXPORTED_FILE_PATH"
|
||||
- [email protected]: {}
|
||||
app:
|
||||
envs:
|
||||
- opts:
|
||||
is_expand: false
|
||||
BITRISE_PROJECT_PATH: shell/apple/reicast.xcworkspace
|
||||
- opts:
|
||||
is_expand: false
|
||||
BITRISE_EXPORT_METHOD: none
|
||||
+7
-8
@@ -1,4 +1,4 @@
|
||||
# core
|
||||
# core/libdreamcast
|
||||
===========
|
||||
|
||||
Here lies the core of our codebase. Everything that's OS inspecific rests here.
|
||||
@@ -6,11 +6,10 @@ Here lies the core of our codebase. Everything that's OS inspecific rests here.
|
||||
|
||||
### Some rudimentary categories are:
|
||||
- hw -- DC Hardware Components Implementation
|
||||
- nullDC.cpp -- Main entry points
|
||||
- oslib -- Audio drivers
|
||||
- nullDC.cpp -- NullDC, thy mighty child (also referenced as "debugger")
|
||||
- emitter -- Cookie machine
|
||||
- khronos -- Vulkan stuff
|
||||
- oslib -- Codebase abstraction effort
|
||||
- cfg -- Configuration backend structure
|
||||
- reios -- (Our) Implementation of the DreamCast BIOS
|
||||
- deps -- External dependencies
|
||||
- imgread -- Content drivers
|
||||
- rec-* -- Recompilers
|
||||
- rend -- Graphics renderers and UI
|
||||
- reios -- (Our)Implementation of the DreamCast BIOS (Not functional)
|
||||
- deps -- External C libraries (hackish, hand-written versions)
|
||||
@@ -1,3 +0,0 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
achievements.cpp
|
||||
achievements.h)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
namespace achievements
|
||||
{
|
||||
#ifdef USE_RACHIEVEMENTS
|
||||
|
||||
struct Game
|
||||
{
|
||||
std::string image;
|
||||
std::string title;
|
||||
u32 unlockedAchievements;
|
||||
u32 totalAchievements;
|
||||
u32 points;
|
||||
u32 totalPoints;
|
||||
};
|
||||
|
||||
struct Achievement
|
||||
{
|
||||
Achievement() = default;
|
||||
Achievement(const std::string& image, const std::string& title, const std::string& description, const std::string& category, const std::string& status)
|
||||
: image(image), title(title), description(description), category(category), status(status) {}
|
||||
std::string image;
|
||||
std::string title;
|
||||
std::string description;
|
||||
std::string category;
|
||||
std::string status;
|
||||
};
|
||||
|
||||
bool init();
|
||||
void term();
|
||||
std::future<void> login(const char *username, const char *password);
|
||||
void logout();
|
||||
bool isLoggedOn();
|
||||
bool isActive();
|
||||
Game getCurrentGame();
|
||||
std::vector<Achievement> getAchievementList();
|
||||
bool canPause();
|
||||
void setHostOverride(const std::string& host);
|
||||
|
||||
#else
|
||||
|
||||
static inline bool isActive() {
|
||||
return false;
|
||||
}
|
||||
static inline bool canPause() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
void deserialize(Deserializer& deser);
|
||||
|
||||
}
|
||||
+12
-63
@@ -19,52 +19,25 @@
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "7zArchive.h"
|
||||
#include "lzma/7z.h"
|
||||
#include "lzma/7zCrc.h"
|
||||
#include "lzma/Alloc.h"
|
||||
|
||||
#include <cstring>
|
||||
#include "deps/lzma/7z.h"
|
||||
#include "deps/lzma/7zCrc.h"
|
||||
#include "deps/lzma/Alloc.h"
|
||||
|
||||
#define kInputBufSize ((size_t)1 << 18)
|
||||
|
||||
static bool crc_tables_generated;
|
||||
|
||||
SRes SzArchive::ArchiveStream::Read(const ISeekInStream *p, void *buf, size_t *size)
|
||||
{
|
||||
auto file_archive = CONTAINER_FROM_VTBL(p, ArchiveStream, vt);
|
||||
*size = file_archive->file->read(buf, 1, *size);
|
||||
return file_archive->file->error() ? SZ_ERROR_READ : SZ_OK;
|
||||
}
|
||||
|
||||
SRes SzArchive::ArchiveStream::Seek(const ISeekInStream *p, Int64 *pos, ESzSeek origin)
|
||||
{
|
||||
auto file_archive = CONTAINER_FROM_VTBL(p, ArchiveStream, vt);
|
||||
if (file_archive->file->seek(*pos, origin) == 0) {
|
||||
*pos = file_archive->file->tell();
|
||||
return SZ_OK;
|
||||
}
|
||||
else {
|
||||
return SZ_ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
bool SzArchive::Open(hostfs::File *file)
|
||||
bool SzArchive::Open(const char* path)
|
||||
{
|
||||
SzArEx_Init(&szarchive);
|
||||
|
||||
if (archiveStream.file != nullptr)
|
||||
delete archiveStream.file;
|
||||
archiveStream.vt.Read = ArchiveStream::Read;
|
||||
archiveStream.vt.Seek = ArchiveStream::Seek;
|
||||
archiveStream.file = file;
|
||||
|
||||
LookToRead2_CreateVTable(&lookStream, 0);
|
||||
if (InFile_Open(&archiveStream.file, path))
|
||||
return false;
|
||||
FileInStream_CreateVTable(&archiveStream);
|
||||
LookToRead2_CreateVTable(&lookStream, false);
|
||||
lookStream.buf = (Byte *)ISzAlloc_Alloc(&g_Alloc, kInputBufSize);
|
||||
if (lookStream.buf == NULL)
|
||||
{
|
||||
delete archiveStream.file;
|
||||
return false;
|
||||
}
|
||||
lookStream.bufSize = kInputBufSize;
|
||||
lookStream.realStream = &archiveStream.vt;
|
||||
LookToRead2_Init(&lookStream);
|
||||
@@ -82,14 +55,14 @@ bool SzArchive::Open(hostfs::File *file)
|
||||
ArchiveFile* SzArchive::OpenFile(const char* name)
|
||||
{
|
||||
u16 fname[512];
|
||||
for (UInt32 i = 0; i < szarchive.NumFiles; i++)
|
||||
for (int i = 0; i < szarchive.NumFiles; i++)
|
||||
{
|
||||
if (SzArEx_IsDir(&szarchive, i))
|
||||
continue;
|
||||
|
||||
size_t len = SzArEx_GetFileNameUtf16(&szarchive, i, fname);
|
||||
int len = SzArEx_GetFileNameUtf16(&szarchive, i, fname);
|
||||
char szname[512];
|
||||
size_t j = 0;
|
||||
int j = 0;
|
||||
for (; j < len && j < sizeof(szname) - 1; j++)
|
||||
szname[j] = fname[j];
|
||||
szname[j] = 0;
|
||||
@@ -107,35 +80,11 @@ ArchiveFile* SzArchive::OpenFile(const char* name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ArchiveFile* SzArchive::OpenFileByCrc(u32 crc)
|
||||
{
|
||||
if (crc == 0)
|
||||
return NULL;
|
||||
for (UInt32 i = 0; i < szarchive.NumFiles; i++)
|
||||
{
|
||||
unsigned isDir = SzArEx_IsDir(&szarchive, i);
|
||||
if (isDir)
|
||||
continue;
|
||||
|
||||
if (crc != szarchive.CRCs.Vals[i])
|
||||
continue;
|
||||
|
||||
size_t offset = 0;
|
||||
size_t out_size_processed = 0;
|
||||
SRes res = SzArEx_Extract(&szarchive, &lookStream.vt, i, &block_idx, &out_buffer, &out_buffer_size, &offset, &out_size_processed, &g_Alloc, &g_Alloc);
|
||||
if (res != SZ_OK)
|
||||
return NULL;
|
||||
|
||||
return new SzArchiveFile(out_buffer, offset, (u32)out_size_processed);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SzArchive::~SzArchive()
|
||||
{
|
||||
if (lookStream.buf != NULL)
|
||||
{
|
||||
delete archiveStream.file;
|
||||
File_Close(&archiveStream.file);
|
||||
ISzAlloc_Free(&g_Alloc, lookStream.buf);
|
||||
if (out_buffer != NULL)
|
||||
ISzAlloc_Free(&g_Alloc, out_buffer);
|
||||
|
||||
+15
-29
@@ -18,13 +18,13 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef CORE_ARCHIVE_7ZARCHIVE_H_
|
||||
#define CORE_ARCHIVE_7ZARCHIVE_H_
|
||||
|
||||
#include "archive.h"
|
||||
#include "lzma/7z.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "deps/lzma/7z.h"
|
||||
#include "deps/lzma/7zFile.h"
|
||||
|
||||
class SzArchive : public Archive
|
||||
{
|
||||
@@ -33,29 +33,18 @@ public:
|
||||
memset(&archiveStream, 0, sizeof(archiveStream));
|
||||
memset(&lookStream, 0, sizeof(lookStream));
|
||||
}
|
||||
~SzArchive() override;
|
||||
virtual ~SzArchive();
|
||||
|
||||
ArchiveFile* OpenFile(const char* name) override;
|
||||
ArchiveFile *OpenFileByCrc(u32 crc) override;
|
||||
|
||||
protected:
|
||||
bool Open(hostfs::File *file) override;
|
||||
virtual ArchiveFile* OpenFile(const char* name) override;
|
||||
|
||||
private:
|
||||
struct ArchiveStream
|
||||
{
|
||||
static SRes Read(const ISeekInStream *p, void *buf, size_t *size);
|
||||
static SRes Seek(const ISeekInStream *p, Int64 *pos, ESzSeek origin);
|
||||
|
||||
ISeekInStream vt;
|
||||
hostfs::File *file;
|
||||
};
|
||||
virtual bool Open(const char* path) override;
|
||||
|
||||
CSzArEx szarchive;
|
||||
UInt32 block_idx; /* it can have any value before first call (if outBuffer = 0) */
|
||||
Byte *out_buffer; /* it must be 0 before first call for each new archive. */
|
||||
size_t out_buffer_size; /* it can have any value before first call (if outBuffer = 0) */
|
||||
ArchiveStream archiveStream;
|
||||
CFileInStream archiveStream;
|
||||
CLookToRead2 lookStream;
|
||||
|
||||
};
|
||||
@@ -63,21 +52,18 @@ private:
|
||||
class SzArchiveFile : public ArchiveFile
|
||||
{
|
||||
public:
|
||||
SzArchiveFile(u8 *data, u32 offset, u32 length)
|
||||
: data(data), offset(offset), _length(length) {}
|
||||
u32 Read(void *buffer, u32 length) override
|
||||
SzArchiveFile(u8 *data, u32 offset, u32 length) : data(data), offset(offset), length(length) {}
|
||||
virtual u32 Read(void *buffer, u32 length) override
|
||||
{
|
||||
length = std::min(length, this->_length);
|
||||
length = min(length, this->length);
|
||||
memcpy(buffer, data + offset, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t length() override {
|
||||
return _length;
|
||||
}
|
||||
|
||||
private:
|
||||
u8 *data;
|
||||
u32 offset;
|
||||
u32 _length;
|
||||
u32 length;
|
||||
};
|
||||
|
||||
#endif /* CORE_ARCHIVE_7ZARCHIVE_H_ */
|
||||
@@ -1,9 +0,0 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
7zArchive.cpp
|
||||
7zArchive.h
|
||||
archive.cpp
|
||||
archive.h
|
||||
rzip.cpp
|
||||
rzip.h
|
||||
ZipArchive.cpp
|
||||
ZipArchive.h)
|
||||
+7
-194
@@ -20,214 +20,27 @@
|
||||
*/
|
||||
#include "ZipArchive.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
ZipArchive::~ZipArchive()
|
||||
{
|
||||
zip_close(zip);
|
||||
}
|
||||
|
||||
static zip_int64_t ZipSourceCallback(void *userdata, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
|
||||
bool ZipArchive::Open(const char* path)
|
||||
{
|
||||
using Error = std::array<int, 2>;
|
||||
static Error error = {ZIP_ER_OK, 0};
|
||||
auto file = static_cast<hostfs::File*>(userdata);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case ZIP_SOURCE_OPEN:
|
||||
return 0;
|
||||
|
||||
case ZIP_SOURCE_READ:
|
||||
{
|
||||
const auto bytes_read = file->read(data, 1, len);
|
||||
|
||||
if (file->error())
|
||||
{
|
||||
error = {ZIP_ER_INTERNAL, 0};
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
case ZIP_SOURCE_CLOSE:
|
||||
return 0;
|
||||
|
||||
case ZIP_SOURCE_STAT:
|
||||
{
|
||||
auto size = file->size();
|
||||
|
||||
if (size == -1)
|
||||
{
|
||||
error = {ZIP_ER_INTERNAL, 0};
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto stat = static_cast<zip_stat_t*>(data);
|
||||
zip_stat_init(stat);
|
||||
|
||||
stat->valid |= ZIP_STAT_SIZE;
|
||||
stat->size = size;
|
||||
|
||||
return sizeof(*stat);
|
||||
}
|
||||
|
||||
case ZIP_SOURCE_ERROR:
|
||||
{
|
||||
auto error_out = static_cast<std::array<int, 2>*>(data);
|
||||
*error_out = error;
|
||||
return sizeof(*error_out);
|
||||
}
|
||||
|
||||
case ZIP_SOURCE_FREE:
|
||||
delete file;
|
||||
return 0;
|
||||
|
||||
case ZIP_SOURCE_SEEK:
|
||||
{
|
||||
zip_error_t error;
|
||||
auto seek_info = ZIP_SOURCE_GET_ARGS(zip_source_args_seek, data, len, &error);
|
||||
|
||||
if (seek_info == nullptr)
|
||||
{
|
||||
error = {ZIP_ER_INTERNAL, 0};
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file->seek(seek_info->offset, seek_info->whence) != 0)
|
||||
{
|
||||
error = {ZIP_ER_INTERNAL, 0};
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case ZIP_SOURCE_TELL:
|
||||
{
|
||||
const auto position = file->tell();
|
||||
|
||||
if (position == -1)
|
||||
{
|
||||
error = {ZIP_ER_INTERNAL, 0};
|
||||
return -1;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
case ZIP_SOURCE_SUPPORTS:
|
||||
return zip_source_make_command_bitmap(
|
||||
ZIP_SOURCE_OPEN,
|
||||
ZIP_SOURCE_READ,
|
||||
ZIP_SOURCE_CLOSE,
|
||||
ZIP_SOURCE_STAT,
|
||||
ZIP_SOURCE_ERROR,
|
||||
ZIP_SOURCE_FREE,
|
||||
ZIP_SOURCE_SEEK,
|
||||
ZIP_SOURCE_TELL,
|
||||
ZIP_SOURCE_SUPPORTS,
|
||||
-1
|
||||
);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Unsupported command.
|
||||
error = {ZIP_ER_OPNOTSUPP, 0};
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ZipArchive::Open(hostfs::File *file)
|
||||
{
|
||||
zip_error_t error;
|
||||
zip_source_t *source = zip_source_function_create(ZipSourceCallback, file, &error);
|
||||
if (source == nullptr)
|
||||
{
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
zip = zip_open_from_source(source, 0, nullptr);
|
||||
if (zip == nullptr)
|
||||
zip_source_free(source);
|
||||
return zip != nullptr;
|
||||
zip = zip_open(path, 0, NULL);
|
||||
return (zip != NULL);
|
||||
}
|
||||
|
||||
ArchiveFile* ZipArchive::OpenFile(const char* name)
|
||||
{
|
||||
zip_file_t *zip_file = zip_fopen(zip, name, 0);
|
||||
if (zip_file == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
zip_stat(zip, name, 0, &stat);
|
||||
return new ZipArchiveFile(zip_file, stat.size, stat.name);
|
||||
}
|
||||
zip_file *zip_file = zip_fopen(zip, name, 0);
|
||||
if (zip_file == NULL)
|
||||
return NULL;
|
||||
|
||||
static zip_file *zip_fopen_by_crc(zip_t *za, u32 crc, int flags, zip_uint64_t& index)
|
||||
{
|
||||
if (crc == 0)
|
||||
return nullptr;
|
||||
|
||||
zip_int64_t n = zip_get_num_entries(za, 0);
|
||||
for (index = 0; index < (zip_uint64_t)n; index++)
|
||||
{
|
||||
zip_stat_t stat;
|
||||
if (zip_stat_index(za, index, flags, &stat) < -1)
|
||||
return nullptr;
|
||||
if (stat.crc == crc)
|
||||
return zip_fopen_index(za, index, flags);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ArchiveFile* ZipArchive::OpenFileByCrc(u32 crc)
|
||||
{
|
||||
zip_uint64_t index;
|
||||
zip_file_t *zip_file = zip_fopen_by_crc(zip, crc, 0, index);
|
||||
if (zip_file == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
zip_stat_index(zip, index, 0, &stat);
|
||||
|
||||
return new ZipArchiveFile(zip_file, stat.size, stat.name);
|
||||
return new ZipArchiveFile(zip_file);
|
||||
}
|
||||
|
||||
u32 ZipArchiveFile::Read(void* buffer, u32 length)
|
||||
{
|
||||
return zip_fread(zip_file, buffer, length);
|
||||
}
|
||||
|
||||
bool ZipArchive::Open(const void *data, size_t size)
|
||||
{
|
||||
zip_error_t error;
|
||||
zip_source_t *source = zip_source_buffer_create(data, size, 0, &error);
|
||||
if (source == nullptr)
|
||||
return false;
|
||||
zip = zip_open_from_source(source, 0, nullptr);
|
||||
if (zip == nullptr)
|
||||
zip_source_free(source);
|
||||
return zip != nullptr;
|
||||
}
|
||||
|
||||
ArchiveFile *ZipArchive::OpenFirstFile()
|
||||
{
|
||||
zip_file_t *zipFile = zip_fopen_index(zip, 0, 0);
|
||||
if (zipFile == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
zip_stat_index(zip, 0, 0, &stat);
|
||||
return new ZipArchiveFile(zipFile, stat.size, stat.name);
|
||||
}
|
||||
|
||||
ArchiveFile *ZipArchive::OpenFileByIndex(size_t index)
|
||||
{
|
||||
zip_file_t *zipFile = zip_fopen_index(zip, index, 0);
|
||||
if (zipFile == nullptr)
|
||||
return nullptr;
|
||||
zip_stat_t stat;
|
||||
zip_stat_index(zip, index, 0, &stat);
|
||||
return new ZipArchiveFile(zipFile, stat.size, stat.name);
|
||||
}
|
||||
+15
-27
@@ -18,47 +18,35 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef CORE_ARCHIVE_ZIPARCHIVE_H_
|
||||
#define CORE_ARCHIVE_ZIPARCHIVE_H_
|
||||
#include "archive.h"
|
||||
#include <zip.h>
|
||||
#include "deps/libzip/zip.h"
|
||||
|
||||
class ZipArchive : public Archive
|
||||
{
|
||||
public:
|
||||
~ZipArchive() override;
|
||||
ZipArchive() : zip(NULL) {}
|
||||
virtual ~ZipArchive();
|
||||
|
||||
ArchiveFile* OpenFile(const char* name) override;
|
||||
ArchiveFile* OpenFileByCrc(u32 crc) override;
|
||||
|
||||
bool Open(hostfs::File *file) override;
|
||||
bool Open(const void *data, size_t size);
|
||||
|
||||
ArchiveFile *OpenFirstFile();
|
||||
ArchiveFile *OpenFileByIndex(size_t index);
|
||||
virtual ArchiveFile* OpenFile(const char* name) override;
|
||||
|
||||
private:
|
||||
zip_t *zip = nullptr;
|
||||
virtual bool Open(const char* path) override;
|
||||
|
||||
struct zip *zip;
|
||||
};
|
||||
|
||||
class ZipArchiveFile : public ArchiveFile
|
||||
{
|
||||
public:
|
||||
ZipArchiveFile(zip_file_t *zip_file, size_t length, const char *name)
|
||||
: zip_file(zip_file), _length(length), name(name) {}
|
||||
~ZipArchiveFile() override {
|
||||
zip_fclose(zip_file);
|
||||
}
|
||||
u32 Read(void* buffer, u32 length) override;
|
||||
size_t length() override {
|
||||
return _length;
|
||||
}
|
||||
const char *getName() override {
|
||||
return name;
|
||||
}
|
||||
ZipArchiveFile(struct zip_file *zip_file) : zip_file(zip_file) {}
|
||||
virtual ~ZipArchiveFile() { zip_fclose(zip_file); }
|
||||
virtual u32 Read(void* buffer, u32 length) override;
|
||||
|
||||
private:
|
||||
zip_file_t *zip_file;
|
||||
size_t _length;
|
||||
const char *name;
|
||||
struct zip_file *zip_file;
|
||||
};
|
||||
|
||||
#endif /* CORE_ARCHIVE_ZIPARCHIVE_H_ */
|
||||
+16
-49
@@ -20,63 +20,30 @@
|
||||
*/
|
||||
|
||||
#include "archive.h"
|
||||
#ifndef _MSC_VER
|
||||
#include "7zArchive.h"
|
||||
#include "ZipArchive.h"
|
||||
#include "oslib/storage.h"
|
||||
#endif
|
||||
|
||||
Archive *OpenArchive(const std::string& path)
|
||||
Archive *OpenArchive(const char *path)
|
||||
{
|
||||
hostfs::File *file = nullptr;
|
||||
hostfs::FileInfo fileInfo;
|
||||
try {
|
||||
fileInfo = hostfs::storage().getFileInfo(path);
|
||||
if (!fileInfo.isDirectory)
|
||||
file = hostfs::storage().openFile(path, "rb");
|
||||
} catch (const hostfs::StorageException& e) {
|
||||
}
|
||||
if (file == nullptr)
|
||||
{
|
||||
file = hostfs::storage().openFile(path + ".7z", "rb");
|
||||
if (file == nullptr)
|
||||
file = hostfs::storage().openFile(path + ".7Z", "rb");
|
||||
}
|
||||
if (file != nullptr)
|
||||
{
|
||||
Archive *sz_archive = new SzArchive();
|
||||
if (sz_archive->Open(file))
|
||||
return sz_archive;
|
||||
delete sz_archive;
|
||||
file = nullptr;
|
||||
}
|
||||
// Retry as a zip file
|
||||
try {
|
||||
if (!fileInfo.isDirectory)
|
||||
file = hostfs::storage().openFile(path, "rb");
|
||||
} catch (const hostfs::StorageException& e) {
|
||||
}
|
||||
if (file == nullptr)
|
||||
{
|
||||
file = hostfs::storage().openFile(path + ".zip", "rb");
|
||||
if (file == nullptr)
|
||||
{
|
||||
file = hostfs::storage().openFile(path + ".ZIP", "rb");
|
||||
if (file == nullptr)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
std::string base_path(path);
|
||||
|
||||
#ifndef _MSC_VER
|
||||
Archive *sz_archive = new SzArchive();
|
||||
if (sz_archive->Open(base_path.c_str()) || sz_archive->Open((base_path + ".7z").c_str()) || sz_archive->Open((base_path + ".7Z").c_str()))
|
||||
return sz_archive;
|
||||
delete sz_archive;
|
||||
|
||||
Archive *zip_archive = new ZipArchive();
|
||||
if (zip_archive->Open(file))
|
||||
if (zip_archive->Open(base_path.c_str()) || zip_archive->Open((base_path + ".zip").c_str()) || zip_archive->Open((base_path + ".ZIP").c_str()))
|
||||
return zip_archive;
|
||||
delete zip_archive;
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Archive::Open(const char* path)
|
||||
{
|
||||
hostfs::File *file = hostfs::storage().openFile(path, "rb");
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
return Open(file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+9
-13
@@ -18,34 +18,30 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef CORE_ARCHIVE_ARCHIVE_H_
|
||||
#define CORE_ARCHIVE_ARCHIVE_H_
|
||||
|
||||
#include "types.h"
|
||||
#include "oslib/storage.h"
|
||||
|
||||
class ArchiveFile
|
||||
{
|
||||
public:
|
||||
virtual ~ArchiveFile() = default;
|
||||
virtual ~ArchiveFile() {}
|
||||
virtual u32 Read(void *buffer, u32 length) = 0;
|
||||
virtual size_t length() = 0;
|
||||
virtual const char *getName() { return nullptr; }
|
||||
};
|
||||
|
||||
class Archive
|
||||
{
|
||||
public:
|
||||
virtual ~Archive() = default;
|
||||
virtual ~Archive() {}
|
||||
virtual ArchiveFile *OpenFile(const char *name) = 0;
|
||||
virtual ArchiveFile *OpenFileByCrc(u32 crc) = 0;
|
||||
|
||||
protected:
|
||||
virtual bool Open(hostfs::File *file) = 0;
|
||||
friend Archive *OpenArchive(const char *path);
|
||||
|
||||
private:
|
||||
bool Open(const char *name);
|
||||
|
||||
friend Archive *OpenArchive(const std::string& path);
|
||||
virtual bool Open(const char *name) = 0;
|
||||
};
|
||||
|
||||
Archive *OpenArchive(const std::string& path);
|
||||
Archive *OpenArchive(const char *path);
|
||||
|
||||
#endif
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "rzip.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
const u8 RZipHeader[8] = { '#', 'R', 'Z', 'I', 'P', 'v', 1, '#' };
|
||||
|
||||
bool RZipFile::Open(hostfs::File *file, bool write)
|
||||
{
|
||||
verify(this->file == nullptr);
|
||||
verify(file != nullptr);
|
||||
startOffset = file->tell();
|
||||
if (!write)
|
||||
{
|
||||
u8 header[sizeof(RZipHeader)];
|
||||
if (file->read(header, sizeof(header), 1) != 1
|
||||
|| memcmp(header, RZipHeader, sizeof(header))
|
||||
|| file->read(&maxChunkSize, sizeof(maxChunkSize), 1) != 1
|
||||
|| file->read(&size, sizeof(size), 1) != 1)
|
||||
{
|
||||
file->seek(startOffset, SEEK_SET);
|
||||
return false;
|
||||
}
|
||||
// savestates created on 32-bit platforms used to have a 32-bit size
|
||||
if (size >> 32 != 0)
|
||||
{
|
||||
size &= 0xffffffff;
|
||||
file->seek(-4, SEEK_CUR);
|
||||
}
|
||||
chunk = new u8[maxChunkSize];
|
||||
chunkIndex = 0;
|
||||
chunkSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxChunkSize = 1_MB;
|
||||
if (file->write(RZipHeader, sizeof(RZipHeader), 1) != 1
|
||||
|| file->write(&maxChunkSize, sizeof(maxChunkSize), 1) != 1
|
||||
|| file->write(&size, sizeof(size), 1) != 1)
|
||||
{
|
||||
file->seek(startOffset, SEEK_SET);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->write = write;
|
||||
this->file = file;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RZipFile::Open(const std::string& path, bool write)
|
||||
{
|
||||
hostfs::File *f = hostfs::storage().openFile(path.c_str(), write ? "wb" : "rb");
|
||||
if (f == nullptr)
|
||||
return false;
|
||||
if (!Open(f, write)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RZipFile::Close()
|
||||
{
|
||||
if (file != nullptr)
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
file->seek(startOffset + sizeof(RZipHeader) + sizeof(maxChunkSize), SEEK_SET);
|
||||
file->write(&size, sizeof(size), 1);
|
||||
}
|
||||
delete file;
|
||||
file = nullptr;
|
||||
if (chunk != nullptr)
|
||||
{
|
||||
delete [] chunk;
|
||||
chunk = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t RZipFile::Read(void *data, size_t length)
|
||||
{
|
||||
verify(file != nullptr);
|
||||
verify(!write);
|
||||
|
||||
u8 *p = (u8 *)data;
|
||||
size_t rv = 0;
|
||||
while (rv < length)
|
||||
{
|
||||
if (chunkIndex == chunkSize)
|
||||
{
|
||||
chunkSize = 0;
|
||||
chunkIndex = 0;
|
||||
u32 zippedSize;
|
||||
if (file->read(&zippedSize, sizeof(zippedSize), 1) != 1)
|
||||
break;
|
||||
if (zippedSize == 0)
|
||||
continue;
|
||||
u8 *zipped = new u8[zippedSize];
|
||||
if (file->read(zipped, zippedSize, 1) != 1)
|
||||
{
|
||||
delete [] zipped;
|
||||
break;
|
||||
}
|
||||
uLongf tl = maxChunkSize;
|
||||
if (uncompress(chunk, &tl, zipped, zippedSize) != Z_OK)
|
||||
{
|
||||
delete [] zipped;
|
||||
break;
|
||||
}
|
||||
delete [] zipped;
|
||||
chunkSize = (u32)tl;
|
||||
}
|
||||
u32 l = std::min(chunkSize - chunkIndex, (u32)(length - rv));
|
||||
memcpy(p, chunk + chunkIndex, l);
|
||||
p += l;
|
||||
chunkIndex += l;
|
||||
rv += l;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
size_t RZipFile::Write(const void *data, size_t length)
|
||||
{
|
||||
verify(file != nullptr);
|
||||
verify(write);
|
||||
|
||||
size += length;
|
||||
const u8 *p = (const u8 *)data;
|
||||
// compression output buffer must be 0.1% larger + 12 bytes
|
||||
uLongf maxZippedSize = maxChunkSize + maxChunkSize / 1000 + 12;
|
||||
u8 *zipped = new u8[maxZippedSize];
|
||||
size_t rv = 0;
|
||||
while (rv < length)
|
||||
{
|
||||
uLongf zippedSize = maxZippedSize;
|
||||
uLongf uncompressedSize = std::min(maxChunkSize, (u32)(length - rv));
|
||||
u32 rc = compress(zipped, &zippedSize, p, uncompressedSize);
|
||||
if (rc != Z_OK)
|
||||
{
|
||||
WARN_LOG(SAVESTATE, "Compression error: %d", rc);
|
||||
break;
|
||||
}
|
||||
u32 sz = (u32)zippedSize;
|
||||
if (file->write(&sz, sizeof(sz), 1) != 1
|
||||
|| file->write(zipped, zippedSize, 1) != 1)
|
||||
{
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
p += uncompressedSize;
|
||||
rv += uncompressedSize;
|
||||
}
|
||||
delete [] zipped;
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Implementation of the RZIP stream format as defined by libretro
|
||||
// https://github.com/libretro/libretro-common/blob/master/include/streams/rzip_stream.h
|
||||
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "oslib/storage.h"
|
||||
|
||||
class RZipFile
|
||||
{
|
||||
public:
|
||||
~RZipFile() { Close(); }
|
||||
|
||||
bool Open(const std::string& path, bool write);
|
||||
bool Open(hostfs::File *file, bool write);
|
||||
void Close();
|
||||
size_t Size() const { return size; }
|
||||
size_t Read(void *data, size_t length);
|
||||
size_t Write(const void *data, size_t length);
|
||||
hostfs::File *rawFile() const { return file; }
|
||||
|
||||
private:
|
||||
hostfs::File *file = nullptr;
|
||||
u64 size = 0;
|
||||
u32 maxChunkSize = 0;
|
||||
u8 *chunk = nullptr;
|
||||
u32 chunkSize = 0;
|
||||
u32 chunkIndex = 0;
|
||||
bool write = false;
|
||||
long startOffset = 0;
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* E_Branches.h
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
EAPI B(u32 sImm24, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0A000000);
|
||||
|
||||
SET_CC;
|
||||
I |= ((sImm24>>2)&0xFFFFFF);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI BL(u32 sImm24, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0B000000);
|
||||
|
||||
SET_CC;
|
||||
I |= ((sImm24>>2)&0xFFFFFF);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
// Note: Either X variant will switch to THUMB* if bit0 of addr is 1
|
||||
//
|
||||
|
||||
EAPI BX(eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x012FFF10);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI BLX(eReg Rm, ConditionCode CC=AL) // Form II
|
||||
{
|
||||
DECL_Id(0x012FFF30);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI BXJ(eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x012FFF20);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// This encoding looks correct, but segfaults, the pc val is align(pc,4) but this should be right in ARM
|
||||
//
|
||||
#if defined(_DEVEL)
|
||||
EAPI BLX(u32 sImm24, bool toThumb) // Form I * H is derived so not needed, fixup sImm24 so one can just pass a real addr
|
||||
{
|
||||
DECL_Id(0xFA000000);
|
||||
|
||||
if(toThumb)
|
||||
I |= 1<<24; // SET_H
|
||||
|
||||
I |= ((sImm24>>2)&0xFFFFFF);
|
||||
EMIT_I;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
Executable
+860
@@ -0,0 +1,860 @@
|
||||
/*
|
||||
* <E_DataOp.h Title="ARMv7 ISA Emitter Middle-ware: Data-Processing Instructions" />
|
||||
*
|
||||
* <?CTYPE
|
||||
*
|
||||
* <opcode1>{<cond>}{S} <Rd>, <shifter_operand>
|
||||
* <opcode1> := MOV | MVN
|
||||
*
|
||||
* <opcode2>{<cond>} <Rn>, <shifter_operand>
|
||||
* <opcode2> := CMP | CMN | TST | TEQ
|
||||
*
|
||||
* <opcode3>{<cond>}{S} <Rd>, <Rn>, <shifter_operand>
|
||||
* <opcode3> := ADD | SUB | RSB | ADC | SBC | RSC | AND | BIC | EOR | ORR
|
||||
*
|
||||
* ?/>
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* imm Rd,[Rn,] imm
|
||||
* reg Rd,Rn,Rm shift
|
||||
* rsr Rd,Rn,Rm type Rs
|
||||
*
|
||||
* sp.imm Rd {SP} imm
|
||||
* sp.reg Rd {SP} Rm shift
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#if 0
|
||||
#define dpInstr(iName, iId) \
|
||||
EAPI iName (eReg Rd, eReg Rn, u32 Imm) ; \
|
||||
EAPI iName (eReg Rd, eReg Rn, eReg Rm, eShiftOp type=S_LSL, u32 Imm=0) ; \
|
||||
EAPI iName (eReg Rd, eReg Rn, eReg Rm, eShiftOp type, eReg Rs) ;
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
ADC IMM 0x02A00000
|
||||
ADC REG 0x00A00000
|
||||
ADC RSR 0x00A00010
|
||||
|
||||
ADD IMM 0x02800000
|
||||
ADD REG 0x00800000
|
||||
ADD RSR 0x00800010
|
||||
ADD.SP.IMM 0x028D0000
|
||||
ADD.SP.REG 0x008D0000
|
||||
|
||||
AND IMM 0x02000000
|
||||
AND REG 0x00000000
|
||||
AND RSR 0x00000010
|
||||
|
||||
|
||||
// ASR's do not fit this pattern moved elsewhere //
|
||||
|
||||
|
||||
|
||||
BIC IMM 0x03C00000
|
||||
BIC REG 0x01C00000
|
||||
BIC RSR 0x01C00010
|
||||
|
||||
CMN IMM 0x03700000 // N imm
|
||||
CMN REG 0x01700000 // NMshift
|
||||
CMN RSR 0x01700010 // NMtypeS
|
||||
|
||||
CMP IMM 0x03500000 // N imm
|
||||
CMP REG 0x01500000
|
||||
CMP RSR 0x01500010
|
||||
|
||||
|
||||
EOR IMM 0x02200000
|
||||
EOR REG 0x00200000
|
||||
EOR REG 0x00200010
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#define DP_PARAMS (eReg Rd, eReg Rn, ShiftOp Shift, ConditionCode CC=AL)
|
||||
#define DP_RPARAMS (eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
|
||||
#define DP_COMMON \
|
||||
DECL_I; \
|
||||
\
|
||||
SET_CC; \
|
||||
I |= (Rn&15)<<16; \
|
||||
I |= (Rd&15)<<12; \
|
||||
I |= (Shift&0xFFF)
|
||||
|
||||
#define DP_RCOMMON \
|
||||
DECL_I; \
|
||||
\
|
||||
SET_CC; \
|
||||
I |= (Rn&15)<<16; \
|
||||
I |= (Rd&15)<<12; \
|
||||
I |= (Rm&15)
|
||||
|
||||
#define DP_OPCODE(opcode) \
|
||||
I |= (opcode)<<21
|
||||
|
||||
|
||||
|
||||
EAPI AND DP_PARAMS { DP_COMMON; DP_OPCODE(DP_AND); EMIT_I; }
|
||||
EAPI EOR DP_PARAMS { DP_COMMON; DP_OPCODE(DP_EOR); EMIT_I; }
|
||||
EAPI SUB DP_PARAMS { DP_COMMON; DP_OPCODE(DP_SUB); EMIT_I; }
|
||||
EAPI RSB DP_PARAMS { DP_COMMON; DP_OPCODE(DP_RSB); EMIT_I; }
|
||||
EAPI ADD DP_PARAMS { DP_COMMON; DP_OPCODE(DP_ADD); EMIT_I; }
|
||||
EAPI ADC DP_PARAMS { DP_COMMON; DP_OPCODE(DP_ADC); EMIT_I; }
|
||||
EAPI SBC DP_PARAMS { DP_COMMON; DP_OPCODE(DP_SBC); EMIT_I; }
|
||||
EAPI RSC DP_PARAMS { DP_COMMON; DP_OPCODE(DP_RSC); EMIT_I; }
|
||||
EAPI TST DP_PARAMS { DP_COMMON; DP_OPCODE(DP_TST); EMIT_I; }
|
||||
EAPI TEQ DP_PARAMS { DP_COMMON; DP_OPCODE(DP_TEQ); EMIT_I; }
|
||||
// EAPI CMP DP_PARAMS { DP_COMMON; DP_OPCODE(DP_CMP); EMIT_I; }
|
||||
EAPI CMN DP_PARAMS { DP_COMMON; DP_OPCODE(DP_CMN); EMIT_I; }
|
||||
EAPI ORR DP_PARAMS { DP_COMMON; DP_OPCODE(DP_ORR); EMIT_I; }
|
||||
EAPI MOV DP_PARAMS { DP_COMMON; DP_OPCODE(DP_MOV); EMIT_I; }
|
||||
EAPI BIC DP_PARAMS { DP_COMMON; DP_OPCODE(DP_BIC); EMIT_I; }
|
||||
EAPI MVN DP_PARAMS { DP_COMMON; DP_OPCODE(DP_MVN); EMIT_I; }
|
||||
|
||||
#if defined(_DEVEL) && defined(_NODEF_) // These require testing -> CMP/MOV Shifter(Reg)? fmt broken? // Simple third reg type w/ no shifter
|
||||
EAPI AND DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_AND); EMIT_I; }
|
||||
EAPI EOR DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_EOR); EMIT_I; }
|
||||
EAPI SUB DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_SUB); EMIT_I; }
|
||||
EAPI RSB DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_RSB); EMIT_I; }
|
||||
EAPI ADD DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_ADD); EMIT_I; }
|
||||
EAPI ADC DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_ADC); EMIT_I; }
|
||||
EAPI SBC DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_SBC); EMIT_I; }
|
||||
EAPI RSC DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_RSC); EMIT_I; }
|
||||
EAPI TST DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_TST); EMIT_I; }
|
||||
EAPI TEQ DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_TEQ); EMIT_I; }
|
||||
EAPI CMP DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_CMP); EMIT_I; }
|
||||
EAPI CMN DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_CMN); EMIT_I; }
|
||||
EAPI ORR DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_ORR); EMIT_I; }
|
||||
EAPI MOV DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_MOV); EMIT_I; }
|
||||
EAPI BIC DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_BIC); EMIT_I; }
|
||||
EAPI MVN DP_PARAMS { DP_RCOMMON; DP_OPCODE(DP_MVN); EMIT_I; }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static u32 ARMImmid8r4_enc(u32 imm32)
|
||||
{
|
||||
for (int i=0;i<=30;i+=2)
|
||||
{
|
||||
u32 immv=(imm32<<i) | (imm32>>(32-i));
|
||||
if (i == 0)
|
||||
immv = imm32;
|
||||
if (immv<256)
|
||||
{
|
||||
return ((i/2)<<8) | immv;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static u32 ARMImmid8r4(u32 imm8r4)
|
||||
{
|
||||
u32 rv = ARMImmid8r4_enc(imm8r4);
|
||||
|
||||
verify(rv!=-1);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool is_i8r4(u32 i32) { return ARMImmid8r4_enc(i32) != -1; }
|
||||
|
||||
|
||||
|
||||
EAPI ADD(eReg Rd, eReg Rn, eReg Rm, u32 RmLSL, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00800000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
I |= (RmLSL&31)<<7;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ADD(eReg Rd, eReg Rn, eReg Rm, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
ADD(Rd,Rn,Rm,0,S,CC);
|
||||
}
|
||||
|
||||
EAPI ADD(eReg Rd, eReg Rn, eReg Rm, ShiftOp Shift, u32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00800000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Imm8&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ADD(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
ADD(Rd,Rn,Rm,false,CC);
|
||||
}
|
||||
|
||||
|
||||
EAPI ADD(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02800000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ADC(eReg Rd, eReg Rn, eReg Rm, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00A00000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ADC(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
ADC(Rd,Rn,Rm,false,CC);
|
||||
}
|
||||
|
||||
EAPI ADC(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02A00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ADC(eReg Rd, eReg Rn, eReg Rm, bool S, ShiftOp Shift, u32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00A00000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Imm8&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EAPI ADR(eReg Rd, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x028F0000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ADR_Zero(eReg Rd, s32 Imm8, ConditionCode CC=AL) // Special case for subtraction of 0
|
||||
{
|
||||
DECL_Id(0x024F0000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI ORR(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01800000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ORR(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03800000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ORR(eReg Rd, eReg Rn, eReg Rm, ShiftOp Shift, eReg Rs, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01800000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= 1<<4;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ORR(eReg Rd, eReg Rn, eReg Rm, bool S, ShiftOp Shift, u32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01800000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Imm8&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI AND(eReg Rd, eReg Rn, eReg Rm, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00000000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI AND(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00000000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI AND(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02000000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI AND(eReg Rd, eReg Rn, s32 Imm8, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02000000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI EOR(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00200000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI EOR(eReg Rd, eReg Rn, eReg Rm, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00200000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI EOR(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02200000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EAPI SUB(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00400000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SUB(eReg Rd, eReg Rn, s32 Imm8, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02400000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
EMIT_I;
|
||||
}
|
||||
EAPI SUB(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL) { SUB(Rd,Rn,Imm8,false,CC); }
|
||||
|
||||
EAPI SBC(eReg Rd, eReg Rn, eReg Rm, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00C00000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SBC(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
SBC(Rd,Rn,Rm,false,CC);
|
||||
}
|
||||
|
||||
EAPI RSB(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x00600000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI RSB(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02600000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI RSB(eReg Rd, eReg Rn, s32 Imm8, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x02600000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI MVN(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01E00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI MVN(eReg Rd, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03E00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI TST(eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01100000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI TST(eReg Rn, u32 Imm12, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03100000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= ARMImmid8r4(Imm12);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI BIC(eReg Rd, eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03C00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
EAPI UBFX(eReg Rd, eReg Rm, u8 lsb, u8 width, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x07E00050);
|
||||
verify(lsb+width<=32);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= (lsb&31)<<7;
|
||||
I |= ((width-1)&31)<<16;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SBFX(eReg Rd, eReg Rm, u8 lsb, u8 width, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x07A00050);
|
||||
verify(lsb+width<=32);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= (lsb&31)<<7;
|
||||
I |= ((width-1)&31)<<16;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MOV(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MOV(eReg Rd, eReg Rm, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00000);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MOV(eReg Rd, eReg Rm, ShiftOp Shift, u32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Imm8&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MOV(eReg Rd, eReg Rm, ShiftOp Shift, eReg Rs, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= 1<<4;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MOVW(eReg Rd, u32 Imm16, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03000000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Imm16&0xF000)<<4;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Imm16&0x0FFF);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MOVT(eReg Rd, u32 Imm16, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03400000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Imm16&0xF000)<<4;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Imm16&0x0FFF);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MOV(eReg Rd, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03A00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= ARMImmid8r4(Imm8); // * 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EAPI CMP(eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01500000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI CMP(eReg Rn, s32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03500000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= ARMImmid8r4(Imm8); // *FIXME* 12b imm is 8b imm 4b rot. spec, add rot support!
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI CMP(eReg Rd, eReg Rm, ShiftOp Shift, eReg Rs, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01500000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= 1<<4;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI CMP(eReg Rd, eReg Rm, ShiftOp Shift, u32 Imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01500000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
I |= Shift<<5;
|
||||
I |= (Imm8&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI LSL(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00010);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<8;
|
||||
I |= (Rn&15)<<0;
|
||||
EMIT_I;
|
||||
}
|
||||
EAPI LSL(eReg Rd, eReg Rm, s32 imm5, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00000);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<0;
|
||||
I |= (imm5&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI LSR(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00030);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<8;
|
||||
I |= (Rn&15)<<0;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI RRX(eReg Rd, eReg Rm,bool S=false, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00060);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<0;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI LSR(eReg Rd, eReg Rm, s32 imm5, bool S, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00020);
|
||||
|
||||
if (S)
|
||||
I |= 1<<20;
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<0;
|
||||
I |= (imm5&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI LSR(eReg Rd, eReg Rm, s32 imm5, ConditionCode CC=AL)
|
||||
{
|
||||
LSR(Rd,Rm,imm5,false,CC);
|
||||
}
|
||||
|
||||
|
||||
EAPI ASR(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00050);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<8;
|
||||
I |= (Rn&15)<<0;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ASR(eReg Rd, eReg Rm, s32 imm5, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00040);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<0;
|
||||
I |= (imm5&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ROR(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<8;
|
||||
I |= (Rn&15)<<0;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ROR(eReg Rd, eReg Rm, s32 imm5, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01A00060);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15)<<0;
|
||||
I |= (imm5&31)<<7;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#undef DP_PARAMS
|
||||
#undef DP_RPARAMS
|
||||
|
||||
#undef DP_COMMON
|
||||
#undef DP_RCOMMON
|
||||
|
||||
#undef DP_OPCODE
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* E_Extend.h
|
||||
*
|
||||
|
||||
There are six basic instructions:
|
||||
XTAB16 Extend bits[23:16] and bits[7:0] of one register to 16 bits, and add corresponding halfwordsto the values in another register.
|
||||
XTAB Extend bits[ 7: 0] of one register to 32 bits, and add to the value in another register.
|
||||
XTAH Extend bits[15: 0] of one register to 32 bits, and add to the value in another register.
|
||||
XTB16 Extend bits[23:16] and bits[7:0] to 16 bits each.
|
||||
XTB Extend bits[ 7: 0] to 32 bits.
|
||||
XTH Extend bits[15: 0] to 32 bits.
|
||||
|
||||
Each of the six instructions is available in the following variations, indicated by the prefixes shown:
|
||||
S Sign extension, with or without addition modulo 216 or 232.
|
||||
U Zero (unsigned) extension, with or without addition modulo 216 or 232.
|
||||
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
EAPI SXTAB16(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06800070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SXTAB(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06A00070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SXTAH(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06B00070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SXTB16(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x068F0070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SXTB(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06AF0070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SXTH(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06BF0070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI UXTAB16(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06C00070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI UXTAB(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06E00070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI UXTAH(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06F00070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI UXTB16(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06CF0070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI UXTB(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06EF0070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI UXTH(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06FF0070);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* E_LoadStore.h
|
||||
*
|
||||
* LDR|STR{<cond>}{B}{T} Rd, <addressing_mode>
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
enum AddrMode {
|
||||
|
||||
Offset, // [Rn, offset] - Base:Offset used for access, no writeback
|
||||
PostIndexed, // [Rn, offset] ! - Base:Offset used for access, written back to Base reg.
|
||||
PreIndexed // [Rn],offset - Base used then Base:Offset written back to Base reg.
|
||||
|
||||
// offset: imm{8,12}, Rm, Rm <Shifter> #Shift
|
||||
};
|
||||
|
||||
|
||||
#define SET_Rn I |= (Rn&15)<<16
|
||||
#define SET_Rt I |= (Rt&15)<<12
|
||||
#define SET_Rm I |= (Rm&15)
|
||||
|
||||
|
||||
#define SET_Rtn SET_Rt; SET_Rn
|
||||
#define SET_Rtnm SET_Rt; SET_Rn; SET_Rm
|
||||
|
||||
|
||||
|
||||
#define SET_I I |= (1<<25) // Sets Register (Rather than Immediate) Addressing
|
||||
#define SET_P I |= (1<<24) // If NOT Set: post-indexed, else offset||pre-indexed (W. determines which)
|
||||
#define SET_U I |= (1<<23) // If SET: Offset is added to base, else its subtracted (Sign of sImm12)
|
||||
#define SET_B I |= (1<<22) // If SET: Access is a byte access, else its a word access.
|
||||
#define SET_W I |= (1<<21) // (P==0) [ W==0: reg. post indexed, W==1: Unprivileged T variant ] (P==1) [ W==0: Offset , W==1: Pre-indexed ]
|
||||
#define SET_L I |= (1<<20) // (L==1) Load / Store
|
||||
|
||||
|
||||
#define SET_AddrMode \
|
||||
if(mode==Offset) { SET_P; } \
|
||||
else if(mode==PreIndexed) { SET_P; SET_W; }
|
||||
|
||||
/*
|
||||
#define SET_AddrMode \
|
||||
\
|
||||
switch(mode) { \
|
||||
case PostIndexed: break; \
|
||||
case Offset: SET_P; break; \
|
||||
case PreIndexed: SET_P; SET_W; break; \
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#define SET_sImm12 \
|
||||
\
|
||||
if (0 != sImm12) { \
|
||||
if (sImm12 > 0) { SET_U; } \
|
||||
I |= (abs(sImm12) &0xFFF); \
|
||||
}
|
||||
|
||||
#define SET_sImm8 \
|
||||
\
|
||||
if(0 != sImm8) { \
|
||||
verify(is_s8(sImm8)); \
|
||||
if(sImm8 > 0) { SET_U; } \
|
||||
sImm8 = abs(sImm8); \
|
||||
I |= ((u32)sImm8 &0x0F); \
|
||||
I |= ((u32)sImm8 &0xF0)<<4; \
|
||||
}
|
||||
|
||||
|
||||
#define SET_Imm5 I |= ((Imm5&0x1F)<<7)
|
||||
#define SET_type I |= ((type&3)<<5)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* - Load/Store Encoding
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
cond 01-IPUBWL Rn Rt addr_mode -- Load/Store Word || Unsigned Byte
|
||||
|
||||
I,P,U,W - Specify type of addressing mode
|
||||
L - Specify Load (L==1), else Store
|
||||
B - Specify Byte (B==1), else Word
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
cond 000-PUIWL Rn Rt addr_mode 1SH1 addr_mode -- Load/Store Halfword || Signed byte
|
||||
|
||||
I,P,U,W - Specify type of addressing mode
|
||||
L - Specify Load (L==1), else Store
|
||||
S - Specify signed (S==1), else unsigned access
|
||||
H - Specify Halfword access, else signed byte
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
cond 010-PUBWL Rn Rt sImm12-iiii-iiii Imm Offset
|
||||
cond 011-PUBWL Rn Rt #sft sft_op 0 Rm Reg Offset
|
||||
|
||||
cond 000 PU1WL Rn Rt imHI 1011 imLO Imm Offset - halfword
|
||||
cond 000 PU0WL Rn Rt SBZ 1011 Rm Reg Offset - halfword
|
||||
cond 000 PU1W1 Rn Rt imHI 11H1 imLO Imm Offset - signed halfword/byte
|
||||
cond 000 PU0W1 Rn Rt SBZ 11H1 Rm Reg Offset - signed halfword/byte
|
||||
cond 000 PU1W0 Rn Rt imHI 11S1 imLO Imm Offset - two words
|
||||
cond 000 PU0W0 Rn Rt SBZ 11S1 Rm Reg Offset - two words
|
||||
|
||||
cond 100-PUSWL Rn Register-list Multiple
|
||||
cond 110-PUNWL Rn CRd cp_num offs8 COP & double reg xfer
|
||||
|
||||
cond 0001 0B00 Rn Rt SBZ 1001 Rm Swap [byte]
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
LDR L 010 1U001 11 Rt imm12 - *no literal stores*
|
||||
LDR I 010 PU0W1 Rn Rt imm12 - STR I 010 PU0W0 Rn Rt imm12
|
||||
LDR R 011 PU0W1 Rn Rt imm5 type 0 Rm - STR R 011 PU0W0 Rn Rt imm5 type 0 Rm
|
||||
|
||||
LDRT I 010 0U011 Rn Rt imm12 - STRT I 010 0U010 Rn Rt imm12
|
||||
LDRT R 011 0U011 Rn Rt imm5 type 0 Rm - STRT R 011 0U010 Rn Rt imm5 type 0 Rm
|
||||
|
||||
LDRB L 010 1U101 11 Rt imm12 - *^^
|
||||
LDRB I 010 PU1W1 Rn Rt imm12 - STRB I 010 PU1W0 Rn Rt imm12
|
||||
LDRB R 011 PU1W1 Rn Rt imm5 type 0 Rm - STRB R 011 PU1W0 Rn Rt imm5 type 0 Rm
|
||||
|
||||
LDRBT I
|
||||
LDRBT R
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
LDRH L 000 1U101 11 Rt imm4H 1011 imm4L - *^^
|
||||
LDRH I 000 PU1W1 Rn Rt imm4H 1011 imm4L - STRH I 000 PU1W0 Rn Rt imm4H 1011 imm4L
|
||||
LDRH R 000 PU0W1 Rn Rt 0000 1011 Rm - STRH R 000 PU0W0 Rn Rt 0000 1011 Rm
|
||||
|
||||
LDRD L 000 1U100 11 Rt imm4H 1101 imm4L - *^^
|
||||
LDRD I 000 PU1W0 Rn Rt imm4H 1101 imm4L - STRD I 000 PU1W0 Rn Rt imm4H 1111 imm4L
|
||||
LDRD R 000 PU0W0 Rn Rt 0000 1101 Rm - STRD R 000 PU0W0 Rn Rt 0000 1111 Rm
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
LDRHT/STRHT
|
||||
|
||||
LDREX/STREX
|
||||
LDREXB/STREXB
|
||||
LDREXH/STREXH
|
||||
LDREXD/STREXD
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
EAPI LDR 0x04100000 // These were all the A1 Versions for Imm IIRC
|
||||
EAPI LDRB 0x04500000
|
||||
EAPI LDRBT 0x06700000
|
||||
EAPI LDRD 0x00000000
|
||||
EAPI LDREX 0x01900090
|
||||
EAPI LDRH 0x00100090
|
||||
EAPI LDRSB 0x00100000
|
||||
EAPI LDRSH 0x00100000
|
||||
EAPI LDRT 0x04300000
|
||||
|
||||
EAPI STR 0x04000000
|
||||
EAPI STRB 0x04400000
|
||||
EAPI STRBT 0x06600000
|
||||
EAPI STRD 0x00000000
|
||||
EAPI STREX 0x01800090
|
||||
EAPI STRH 0x00000090
|
||||
EAPI STRT 0x04200000
|
||||
|
||||
*/
|
||||
|
||||
|
||||
extern u8* emit_opt;
|
||||
extern eReg reg_addr;
|
||||
extern eReg reg_dst;
|
||||
extern s32 imma;
|
||||
|
||||
EAPI LDR (eReg Rt, eReg Rn, s32 sImm12=0, AddrMode mode=Offset, ConditionCode CC=AL)
|
||||
{
|
||||
if (emit_opt+4==(u8*)EMIT_GET_PTR() && reg_addr==Rn && imma==sImm12)
|
||||
{
|
||||
if (reg_dst!=Rt)
|
||||
MOV(Rt,reg_dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
DECL_Id(0x04100000); SET_CC; SET_Rtn; SET_AddrMode; SET_sImm12; EMIT_I;
|
||||
}
|
||||
}
|
||||
EAPI STR (eReg Rt, eReg Rn, s32 sImm12=0, AddrMode mode=Offset, ConditionCode CC=AL)
|
||||
{
|
||||
emit_opt=0;//(u8*)EMIT_GET_PTR();
|
||||
reg_addr=Rn;
|
||||
reg_dst=Rt;
|
||||
imma=sImm12;
|
||||
DECL_Id(0x04000000); SET_CC; SET_Rtn; SET_AddrMode; SET_sImm12; EMIT_I;
|
||||
}
|
||||
EAPI LDRB(eReg Rt, eReg Rn, s32 sImm12=0, AddrMode mode=Offset, ConditionCode CC=AL) { DECL_Id(0x04500000); SET_CC; SET_Rtn; SET_AddrMode; SET_sImm12; SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
EAPI STRB(eReg Rt, eReg Rn, s32 sImm12=0, AddrMode mode=Offset, ConditionCode CC=AL) { DECL_Id(0x04400000); SET_CC; SET_Rtn; SET_AddrMode; SET_sImm12; SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
|
||||
EAPI LDRT (eReg Rt, eReg Rn, s32 sImm12=0, ConditionCode CC=AL) { DECL_Id(0x04300000); SET_CC; SET_Rtn; SET_sImm12; EMIT_I; }
|
||||
EAPI STRT (eReg Rt, eReg Rn, s32 sImm12=0, ConditionCode CC=AL) { DECL_Id(0x04200000); SET_CC; SET_Rtn; SET_sImm12; EMIT_I; }
|
||||
EAPI LDRBT(eReg Rt, eReg Rn, s32 sImm12=0, ConditionCode CC=AL) { DECL_Id(0x04700000); SET_CC; SET_Rtn; SET_sImm12; SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
EAPI STRBT(eReg Rt, eReg Rn, s32 sImm12=0, ConditionCode CC=AL) { DECL_Id(0x04600000); SET_CC; SET_Rtn; SET_sImm12; SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
|
||||
// LDR(r1,r2,r3, Offset, true, L_LSL, 5, EQ); ... LDR r1, r2, +r3 LSL #5
|
||||
EAPI LDR (eReg Rt, eReg Rn, eReg Rm, AddrMode mode=Offset, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06100000); SET_CC; SET_Rtnm; SET_AddrMode; SET_Imm5; SET_type; if(Add){ SET_U; } EMIT_I; }
|
||||
EAPI STR (eReg Rt, eReg Rn, eReg Rm, AddrMode mode=Offset, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06000000); SET_CC; SET_Rtnm; SET_AddrMode; SET_Imm5; SET_type; if(Add){ SET_U; } EMIT_I; }
|
||||
EAPI LDRB(eReg Rt, eReg Rn, eReg Rm, AddrMode mode=Offset, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06500000); SET_CC; SET_Rtnm; SET_AddrMode; SET_Imm5; SET_type; if(Add){ SET_U; } SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
EAPI STRB(eReg Rt, eReg Rn, eReg Rm, AddrMode mode=Offset, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06400000); SET_CC; SET_Rtnm; SET_AddrMode; SET_Imm5; SET_type; if(Add){ SET_U; } SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
|
||||
EAPI LDRT (eReg Rt, eReg Rn, eReg Rm, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06300000); SET_CC; SET_Rtnm; SET_Imm5; SET_type; if(Add){ SET_U; } EMIT_I; }
|
||||
EAPI STRT (eReg Rt, eReg Rn, eReg Rm, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06200000); SET_CC; SET_Rtnm; SET_Imm5; SET_type; if(Add){ SET_U; } EMIT_I; }
|
||||
EAPI LDRBT(eReg Rt, eReg Rn, eReg Rm, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06700000); SET_CC; SET_Rtnm; SET_Imm5; SET_type; if(Add){ SET_U; } SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
EAPI STRBT(eReg Rt, eReg Rn, eReg Rm, bool Add=false, ShiftOp type=S_LSL, u32 Imm5=0, ConditionCode CC=AL) { DECL_Id(0x06600000); SET_CC; SET_Rtnm; SET_Imm5; SET_type; if(Add){ SET_U; } SET_B; EMIT_I; } // Prob don't need SET_B, in iID
|
||||
|
||||
|
||||
|
||||
// LDR Rt,[PC, #(s:+/-)Imm12] *Special Case - Literal / PC Relative
|
||||
EAPI LDR(eReg Rt, s32 sImm12, ConditionCode CC=AL) {
|
||||
DECL_Id(0x051F0000); SET_CC; SET_Rt; SET_sImm12; EMIT_I;
|
||||
}
|
||||
|
||||
// LDRB Rt,[PC, #(s:+/-)Imm12] *Special Case - Literal / PC Relative
|
||||
EAPI LDRB(eReg Rt, s32 sImm12, ConditionCode CC=AL) {
|
||||
DECL_Id(0x055F0000); SET_CC; SET_Rt; SET_sImm12; EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Note: Following support Post-Indexed addressing only //
|
||||
|
||||
|
||||
EAPI LDRH(eReg Rt, eReg Rn, s32 sImm8, ConditionCode CC=AL) { DECL_Id(0x005000B0); SET_CC; SET_Rtn; SET_P; SET_sImm8; EMIT_I; }
|
||||
EAPI STRH(eReg Rt, eReg Rn, s32 sImm8, ConditionCode CC=AL) { DECL_Id(0x004000B0); SET_CC; SET_Rtn; SET_P; SET_sImm8; EMIT_I; }
|
||||
EAPI LDRD(eReg Rt, eReg Rn, s32 sImm8, ConditionCode CC=AL) { DECL_Id(0x004000D0); SET_CC; SET_Rtn; SET_P; SET_sImm8; EMIT_I; }
|
||||
EAPI STRD(eReg Rt, eReg Rn, s32 sImm8, ConditionCode CC=AL) { DECL_Id(0x004000F0); SET_CC; SET_Rtn; SET_P; SET_sImm8; EMIT_I; }
|
||||
EAPI LDRSB(eReg Rt, eReg Rn, s32 sImm8 = 0, ConditionCode CC = AL) { DECL_Id(0x005000D0); SET_CC; SET_Rtn; SET_P; SET_sImm8; EMIT_I; }
|
||||
EAPI LDRSH(eReg Rt, eReg Rn, s32 sImm8 = 0, ConditionCode CC = AL) { DECL_Id(0x005000F0); SET_CC; SET_Rtn; SET_P; SET_sImm8; EMIT_I; }
|
||||
|
||||
EAPI LDRH(eReg Rt, eReg Rn, eReg Rm, bool Add=true,ConditionCode CC=AL) { DECL_Id(0x001000B0); SET_CC; SET_Rtnm; SET_P; if (Add) {SET_U;} EMIT_I; }
|
||||
EAPI STRH(eReg Rt, eReg Rn, eReg Rm, bool Add=true,ConditionCode CC=AL) { DECL_Id(0x000000B0); SET_CC; SET_Rtnm; SET_P; if (Add) {SET_U;} EMIT_I; }
|
||||
EAPI LDRD(eReg Rt, eReg Rn, eReg Rm, bool Add=true, ConditionCode CC=AL) { DECL_Id(0x000000D0); SET_CC; SET_Rtnm; SET_P; if (Add) {SET_U;} EMIT_I; }
|
||||
EAPI STRD(eReg Rt, eReg Rn, eReg Rm, bool Add=true, ConditionCode CC=AL) { DECL_Id(0x000000F0); SET_CC; SET_Rtnm; SET_P; if (Add) {SET_U;} EMIT_I; }
|
||||
|
||||
EAPI LDRSB(eReg Rt, eReg Rn, eReg Rm, bool Add=true,ConditionCode CC=AL) { DECL_Id(0x001000D0); SET_CC; SET_Rtnm; SET_P; if (Add) {SET_U;} EMIT_I; }
|
||||
EAPI LDRSH(eReg Rt, eReg Rn, eReg Rm, bool Add=true,ConditionCode CC=AL) { DECL_Id(0x001000F0); SET_CC; SET_Rtnm; SET_P; if (Add) {SET_U;} EMIT_I; }
|
||||
|
||||
|
||||
EAPI LDRH(eReg Rt, s32 sImm8, ConditionCode CC=AL) { DECL_Id(0x015F00B0); SET_CC; SET_Rt; SET_sImm8; EMIT_I; } // *Special Case - Literal / PC Relative
|
||||
EAPI STRH(eReg Rt, s32 sImm8, ConditionCode CC=AL) { DECL_Id(0x014F00D0); SET_CC; SET_Rt; SET_sImm8; EMIT_I; } // *Special Case - Literal / PC Relative
|
||||
|
||||
|
||||
|
||||
// TODO: {LD,ST}R{SB,EX*} && friends (If required).
|
||||
|
||||
|
||||
// Must use _Reg format
|
||||
EAPI PUSH(u32 RegList, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x092D0000);
|
||||
|
||||
SET_CC;
|
||||
I |= (RegList&0xFFFF);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI POP(u32 RegList, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x08BD0000);
|
||||
|
||||
SET_CC;
|
||||
I |= (RegList&0xFFFF);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
#undef SET_Rtn
|
||||
#undef SET_Rtnm
|
||||
|
||||
#undef SET_Rn
|
||||
#undef SET_Rt
|
||||
#undef SET_Rm
|
||||
|
||||
#undef SET_I
|
||||
#undef SET_P
|
||||
#undef SET_U
|
||||
#undef SET_B
|
||||
#undef SET_W
|
||||
#undef SET_L
|
||||
|
||||
#undef SET_AddrMode
|
||||
|
||||
#undef SET_sImm12
|
||||
#undef SET_sImm8
|
||||
#undef SET_Imm5
|
||||
#undef SET_type
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* E_Misc.h
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
/*
|
||||
* Misc. Arithmetic Instructions
|
||||
*/
|
||||
|
||||
// Count Leading Zero's
|
||||
//
|
||||
EAPI CLZ(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x016F0F10);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
// Unsigned sum of absolute differences
|
||||
//
|
||||
EAPI USAD8(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0780F010);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<16;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI USADA8(eReg Rd, eReg Rm, eReg Rs, eReg Rn, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x07800010);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<16;
|
||||
I |= (Rn&15)<<12;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Packing Instructions
|
||||
*/
|
||||
|
||||
|
||||
EAPI PKHBT(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL) // * shift_imm
|
||||
{
|
||||
DECL_Id(0x06800010);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI PKHTB(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL) // * shift_imm
|
||||
{
|
||||
DECL_Id(0x06800050);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Swapping Instructions
|
||||
*/
|
||||
|
||||
EAPI REV(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06BF0F30);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI REV16(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06BF0FB0);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI REVSH(eReg Rd, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06FF0F30);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI SEL(eReg Rd, eReg Rn, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06800FB0);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rn&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Saturate Instructions
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// SSAT{<cond>} <Rd>, #<immed>, <Rm>{, <shift>}
|
||||
//
|
||||
EAPI SSAT(eReg Rd, u32 sat_imm, eReg Rm, u32 sft_imm, ConditionCode CC=AL) // sh&1 << 6
|
||||
{
|
||||
DECL_Id(0x06A00010);
|
||||
|
||||
SET_CC;
|
||||
I |= (sat_imm&31)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (sft_imm&31)<<7;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
// SSAT16{<cond>} <Rd>, #<immed>, <Rm>
|
||||
//
|
||||
EAPI SSAT16(eReg Rd, u32 sat_imm, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06A00030);
|
||||
|
||||
SET_CC;
|
||||
I |= (sat_imm&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= 15<<8; // * SBO
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
// USAT{<cond>} <Rd>, #<immed>, <Rm>{, <shift>}
|
||||
//
|
||||
EAPI USAT(eReg Rd, u32 sat_imm, eReg Rm, u32 sft_imm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06E00010);
|
||||
|
||||
SET_CC;
|
||||
I |= (sat_imm&31)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= (sft_imm&31)<<7;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
// USAT16{<cond>} <Rd>, #<immed>, <Rm>
|
||||
//
|
||||
EAPI USAT16(eReg Rd, u32 sat_imm, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x06E00030);
|
||||
|
||||
SET_CC;
|
||||
I |= (sat_imm&15)<<16;
|
||||
I |= (Rd&15)<<12;
|
||||
I |= 15<<8; // * SBO
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* E_Multiply.h
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
EAPI MLA(eReg Rd, eReg Rn, eReg Rs, eReg Rm, ConditionCode CC=AL) // *FIXME* S
|
||||
{
|
||||
DECL_Id(0x00200090);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<16;
|
||||
I |= (Rn&15)<<12;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI MUL(eReg Rd, eReg Rs, eReg Rm, ConditionCode CC=AL) // *FIXME* S
|
||||
{
|
||||
DECL_Id(0x00000090);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rd&15)<<16;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI UMULL(eReg Rdhi, eReg Rdlo, eReg Rs, eReg Rm, ConditionCode CC=AL) // *FIXME* S
|
||||
{
|
||||
DECL_Id(0x00800090);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rdhi&15)<<16;
|
||||
I |= (Rdlo&15)<<12;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SMULL(eReg Rdhi, eReg Rdlo, eReg Rs, eReg Rm, ConditionCode CC=AL) // *FIXME* S
|
||||
{
|
||||
DECL_Id(0x00C00090);
|
||||
|
||||
SET_CC;
|
||||
I |= (Rdhi&15)<<16;
|
||||
I |= (Rdlo&15)<<12;
|
||||
I |= (Rs&15)<<8;
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
SMLA<x><y> Signed halfword Multiply Accumulate.
|
||||
SMLAD Signed halfword Multiply Accumulate, Dual.
|
||||
SMLAL Signed Multiply Accumulate Long.
|
||||
SMLAL<x><y> Signed halfword Multiply Accumulate Long.
|
||||
SMLALD Signed halfword Multiply Accumulate Long, Dual.
|
||||
SMLAW<y> Signed halfword by word Multiply Accumulate.
|
||||
SMLSD Signed halfword Multiply Subtract, Dual.
|
||||
SMLSLD Signed halfword Multiply Subtract Long Dual.
|
||||
SMMLA Signed Most significant word Multiply Accumulate.
|
||||
SMMLS Signed Most significant word Multiply Subtract.
|
||||
SMMUL Signed Most significant word Multiply.
|
||||
SMUAD Signed halfword Multiply, Add, Dual.
|
||||
SMUL<x><y> Signed halfword Multiply.
|
||||
SMULL Signed Multiply Long.
|
||||
SMULW<y> Signed halfword by word Multiply.
|
||||
SMUSD Signed halfword Multiply, Subtract, Dual.
|
||||
UMAAL Unsigned Multiply Accumulate significant Long.
|
||||
UMLAL Unsigned Multiply Accumulate Long.
|
||||
UMULL Unsigned Multiply Long.
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* E_Parallel.h
|
||||
*
|
||||
|
||||
ADD8 Adds each byte of the second operand register to the corresponding byte of the first operand
|
||||
register to form the corresponding byte of the result.
|
||||
|
||||
ADD16 Adds the top halfwords of two registers to form the top halfword of the result.
|
||||
Adds the bottom halfwords of the same two registers to form the bottom halfword of the result.
|
||||
|
||||
SUB8 Subtracts each byte of the second operand register from the corresponding byte of the first
|
||||
operand register to form the corresponding byte of the result.
|
||||
|
||||
SUB16 Subtracts the top halfword of the first operand register from the top halfword of the second operand register to form the top halfword of the result.
|
||||
Subtracts the bottom halfword of the second operand registers from the bottom halfword of
|
||||
the first operand register to form the bottom halfword of the result.
|
||||
|
||||
ADDSUBX Does the following:
|
||||
1. Exchanges halfwords of the second operand register.
|
||||
2. Adds top halfwords and subtracts bottom halfwords.
|
||||
|
||||
SUBADDX Does the following:
|
||||
1. Exchanges halfwords of the second operand register.
|
||||
2. Subtracts top halfwords and adds bottom halfwords.
|
||||
|
||||
|
||||
Each of the six instructions is available in the following variations, indicated by the prefixes shown:
|
||||
|
||||
S Signed arithmetic modulo 28 or 216. Sets the CPSR GE bits (see The GE[3:0] bits on page A2-13).
|
||||
Q Signed saturating arithmetic.
|
||||
SH Signed arithmetic, halving the results to avoid overflow.
|
||||
U Unsigned arithmetic modulo 28 or 216. Sets the CPSR GE bits (see The GE[3:0] bits on page A2-13).
|
||||
UQ Unsigned saturating arithmetic.
|
||||
UH Unsigned arithmetic, halving the results to avoid overflow.
|
||||
|
||||
|
||||
Status:
|
||||
These routines require implementation if needed.
|
||||
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
#if defined(_DEVEL) && 0
|
||||
|
||||
// S
|
||||
//
|
||||
EAPI SADD8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SADD16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SSUB8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SSUB16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SADDSUBX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SSUBADDX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
|
||||
|
||||
// Q
|
||||
//
|
||||
EAPI QADD8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI QADD16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI QSUB8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI QSUB16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI QADDSUBX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI QSUBADDX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
|
||||
|
||||
// SH
|
||||
//
|
||||
EAPI SHADD8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SHADD16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SHSUB8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SHSUB16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SHADDSUBX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI SHSUBADDX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
|
||||
|
||||
// U
|
||||
//
|
||||
EAPI UADD8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UADD16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI USUB8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI USUB16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UADDSUBX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI USUBADDX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
|
||||
|
||||
// UQ
|
||||
//
|
||||
EAPI UQADD8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UQADD16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UQSUB8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UQSUB16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UQADDSUBX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UQSUBADDX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
|
||||
|
||||
// UH
|
||||
//
|
||||
EAPI UHADD8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UHADD16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UHSUB8 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UHSUB16 (eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UHADDSUBX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
EAPI UHSUBADDX(eReg Rd, eReg Rm, eReg Rs, ConditionCode CC=AL) ;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* E_Special.h
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EAPI ARMERROR()
|
||||
{
|
||||
DECL_Id(0xFFFFFFFF);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI NOP()
|
||||
{
|
||||
DECL_Id(0xE320F000);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI SVC(u32 code)
|
||||
{
|
||||
DECL_Id(0x0F000000);
|
||||
I |= code&0xFFFFFF;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI BKPT()
|
||||
{
|
||||
DECL_Id(0x01200070);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
#define SWI SVC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Synchronization & Barrier Instructions.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
EAPI DSB()
|
||||
{
|
||||
DECL_Id(0xF57FF04F);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI DMB()
|
||||
{
|
||||
DECL_Id(0xF57FF05F);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI ISB()
|
||||
{
|
||||
DECL_Id(0xF57FF06F);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* E_Status.h
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
#if defined(_DEVEL)
|
||||
|
||||
// MRS Move PSR to General-purpose Register.
|
||||
//
|
||||
EAPI MRS(eReg Rd, u32 R, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01000000);
|
||||
|
||||
SET_CC;
|
||||
I |= (R&1) << 22;
|
||||
I |= 15<<16; // * SBO
|
||||
I |= (Rd &15)<<12;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
/* MSR Move General-purpose Register to PSR.
|
||||
|
||||
MSR{<cond>} CPSR_<fields>, #<immediate>
|
||||
MSR{<cond>} CPSR_<fields>, <Rm>
|
||||
MSR{<cond>} SPSR_<fields>, #<immediate>
|
||||
MSR{<cond>} SPSR_<fields>, <Rm>
|
||||
*/
|
||||
|
||||
|
||||
// MSR: Immediate operand
|
||||
//
|
||||
EAPI MSR(u32 R, u32 fmask, u32 rot_imm, u32 imm8, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x03200000);
|
||||
|
||||
SET_CC;
|
||||
I |= (R&1) << 22;
|
||||
I |= (fmask &15)<<16;
|
||||
I |= 15<<12; // * SBO
|
||||
I |= (rot_imm &15)<<8;
|
||||
I |= (imm8 &255);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
// MSR: Register operand
|
||||
//
|
||||
EAPI MSR(u32 R, u32 fmask, eReg Rm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x01200000);
|
||||
|
||||
SET_CC;
|
||||
I |= (R&1) << 22;
|
||||
I |= (fmask &15)<<16;
|
||||
I |= 15<<12; // * SBO
|
||||
I |= (Rm&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// CPS Change Processor State.
|
||||
//
|
||||
EAPI CPS(u32 imod, u32 mmod, u32 mode) // ** [A|I|F]
|
||||
{
|
||||
DECL_Id(0xF1000000);
|
||||
|
||||
// Note: UNconditional instruction!
|
||||
I |= (imod&3)<<18;
|
||||
I |= (mmod&1)<<17;
|
||||
I |= (mode&15);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// SETEND Modifies the CPSR endianness, E, bit, without changing any other bits in the CPSR.
|
||||
//
|
||||
EAPI SETEND(u32 E)
|
||||
{
|
||||
DECL_Id(0xF1010000);
|
||||
|
||||
// Note: UNconditional instruction!
|
||||
I |= (E &1) << 9;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
Executable
+667
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
* E_VDataOp.h * VFP/A.SIMD Data Processing Instruction Set Encoding
|
||||
*
|
||||
* V{<modifier>}<operation>{<shape>}<c><q>{.<dt>} {<dest>,} <src1>, <src2>
|
||||
*
|
||||
* <modifier> Meaning
|
||||
* Q The operation uses saturating arithmetic.
|
||||
* R The operation performs rounding.
|
||||
* D The operation doubles the result (before accumulation, if any).
|
||||
* H The operation halves the result.
|
||||
*
|
||||
* <shape> Meaning Typical register shape
|
||||
* (none) The operands and result are all the same width. Dd, Dn, Dm - Qd, Qn, Qm
|
||||
* L Long operation - result is 2 x width of both operands Qd, Dn, Dm
|
||||
* N Narrow operation - result is width/2 both operands Dd, Qn, Qm
|
||||
* W Wide operation - result and oper[1] are 2x width oper[2] Qd, Qn, Dm
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
#define SET_Qd \
|
||||
I |= (((Qd<<1)&0x0E)<<12); \
|
||||
I |= (((Qd<<1)&0x10)<<18)
|
||||
|
||||
#define SET_Qn \
|
||||
I |= (((Qn<<1)&0x0E)<<16); \
|
||||
I |= (((Qn<<1)&0x10)<<3)
|
||||
|
||||
#define SET_Qm \
|
||||
I |= (((Qm<<1)&0x0E)); \
|
||||
I |= (((Qm<<1)&0x10)<<1)
|
||||
|
||||
|
||||
|
||||
#define SET_Dd \
|
||||
I |= ((Dd&0x0F)<<12); \
|
||||
I |= ((Dd&0x10)<<18)
|
||||
|
||||
#define SET_Dn \
|
||||
I |= ((Dn&0x0F)<<16); \
|
||||
I |= ((Dn&0x10)<<3)
|
||||
|
||||
#define SET_Dm \
|
||||
I |= ((Dm&0x0F)); \
|
||||
I |= ((Dm&0x10)<<1)
|
||||
|
||||
|
||||
|
||||
|
||||
#define SET_Q I |= 0x40
|
||||
#define SET_U I |= ((U&1)<<24);
|
||||
#define SET_Size I |= (((Size>>4)&3)<<20) // 8,16,32 -> 0,1,2
|
||||
|
||||
|
||||
#define SET_Qdnm \
|
||||
SET_Qd; SET_Qn; SET_Qm; SET_Q
|
||||
|
||||
#define SET_Ddnm \
|
||||
SET_Dd; SET_Dn; SET_Dm
|
||||
|
||||
#define DECL_Qdnm
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define VdpInstrF(viName, viId) \
|
||||
EAPI V##viName##_F32 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { DECL_Id(viId); SET_Qdnm; EMIT_I; } \
|
||||
EAPI V##viName##_F32 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { DECL_Id(viId); SET_Ddnm; EMIT_I; }
|
||||
|
||||
|
||||
#define VdpInstrI_EOR(viName, viId) \
|
||||
EAPI V##viName (eFQReg Qd, eFQReg Qn, eFQReg Qm) { DECL_Id(viId); SET_Qdnm; EMIT_I; } \
|
||||
EAPI V##viName (eFDReg Dd, eFDReg Dn, eFDReg Dm) { DECL_Id(viId); SET_Ddnm; EMIT_I; } \
|
||||
|
||||
#define VdpInstrI(viName, viId) \
|
||||
EAPI V##viName (eFQReg Qd, eFQReg Qn, eFQReg Qm, u32 Size=32) { DECL_Id(viId); SET_Qdnm; SET_Size; EMIT_I; } \
|
||||
EAPI V##viName (eFDReg Dd, eFDReg Dn, eFDReg Dm, u32 Size=32) { DECL_Id(viId); SET_Ddnm; SET_Size; EMIT_I; } \
|
||||
\
|
||||
EAPI V##viName##_I8 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 8); } \
|
||||
EAPI V##viName##_I8 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 8); } \
|
||||
EAPI V##viName##_I16 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 16); } \
|
||||
EAPI V##viName##_I16 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 16); } \
|
||||
EAPI V##viName##_I32 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 32); } \
|
||||
EAPI V##viName##_I32 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 32); }
|
||||
|
||||
#define VdpInstrU(viName, viId) \
|
||||
EAPI V##viName (eFQReg Qd, eFQReg Qn, eFQReg Qm, u32 Size=32, u32 U=0) { DECL_Id(viId); SET_Qdnm; SET_U; SET_Size; EMIT_I; } \
|
||||
EAPI V##viName (eFDReg Dd, eFDReg Dn, eFDReg Dm, u32 Size=32, u32 U=0) { DECL_Id(viId); SET_Ddnm; SET_U; SET_Size; EMIT_I; } \
|
||||
\
|
||||
EAPI V##viName##_U8 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 8, 1); } \
|
||||
EAPI V##viName##_U8 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 8, 1); } \
|
||||
EAPI V##viName##_S8 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 8, 0); } \
|
||||
EAPI V##viName##_S8 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 8, 0); } \
|
||||
EAPI V##viName##_U16 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 16, 1); } \
|
||||
EAPI V##viName##_U16 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 16, 1); } \
|
||||
EAPI V##viName##_S16 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 16, 0); } \
|
||||
EAPI V##viName##_S16 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 16, 0); } \
|
||||
EAPI V##viName##_U32 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 32, 1); } \
|
||||
EAPI V##viName##_U32 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 32, 1); } \
|
||||
EAPI V##viName##_S32 (eFQReg Qd, eFQReg Qn, eFQReg Qm) { V##viName (Qd, Qn, Qm, 32, 0); } \
|
||||
EAPI V##viName##_S32 (eFDReg Dd, eFDReg Dn, eFDReg Dm) { V##viName (Dd, Dn, Dm, 32, 0); }
|
||||
|
||||
//# define VdpInstrImm (viName, viId)
|
||||
|
||||
|
||||
// Another three or four like the above .. immN, above w/ size, F32 w/ sz
|
||||
|
||||
|
||||
/*
|
||||
* A.SIMD Parallel Add/Sub
|
||||
*
|
||||
Vector Add VADD (integer), VADD (floating-point)
|
||||
Vector Add and Narrow, returning High Half VADDHN
|
||||
Vector Add Long, Vector Add Wide VADDL, VADDW
|
||||
Vector Halving Add, Vector Halving Subtract VHADD, VHSUB
|
||||
Vector Pairwise Add and Accumulate Long VPADAL
|
||||
Vector Pairwise Add VPADD (integer) , VPADD (floating-point)
|
||||
Vector Pairwise Add Long VPADDL
|
||||
Vector Rounding Add & Narrow, returning High Half VRADDHN
|
||||
Vector Rounding Halving Add VRHADD
|
||||
Vector Rounding Subtract & Narrow, ret. High Half VRSUBHN
|
||||
Vector Saturating Add VQADD
|
||||
Vector Saturating Subtract VQSUB
|
||||
Vector Subtract VSUB (integer), VSUB (floating-point)
|
||||
Vector Subtract and Narrow, returning High Half VSUBHN
|
||||
Vector Subtract Long, Vector Subtract Wide VSUBL, VSUBW
|
||||
*/
|
||||
|
||||
VdpInstrI(ADD, 0xF2000800)
|
||||
VdpInstrF(ADD, 0xF2000D00)
|
||||
|
||||
|
||||
VdpInstrI(HADD, 0xF2000800)
|
||||
VdpInstrI(HSUB, 0xF2000A00)
|
||||
|
||||
VdpInstrI(PADD, 0xF2000B10)
|
||||
VdpInstrF(PADD, 0xF3000D00)
|
||||
|
||||
|
||||
VdpInstrU(RHADD, 0xF3000100)
|
||||
VdpInstrU(QADD, 0xF2000010)
|
||||
VdpInstrU(QSUB, 0xF3000210)
|
||||
|
||||
VdpInstrI(SUB, 0xF3000800)
|
||||
VdpInstrF(SUB, 0xF2200D00)
|
||||
|
||||
|
||||
// VADD I { DECL_Id(0xF2000800); SET_Qdnm; EMIT_I; }
|
||||
// VADD F { DECL_Id(0xF2000D00); SET_Qdnm; EMIT_I; }
|
||||
// VADDHN { DECL_Id(0xF2800400); SET_Qdnm; EMIT_I; } // DQQ
|
||||
// VADD{L,W} { DECL_Id(0xF2800000); SET_Qdnm; EMIT_I; } // QDD || QQD
|
||||
// VH{ADD,SUB} { DECL_Id(0xF2000000); SET_Qdnm; EMIT_I; }
|
||||
// VPADAL { DECL_Id(0xF3B00600); SET_Qdnm; EMIT_I; } // QdQm || DdDm
|
||||
// VPADD I { DECL_Id(0xF2000B10); SET_Qdnm; EMIT_I; } // DDD only
|
||||
// VPADD F { DECL_Id(0xF3000D00); SET_Qdnm; EMIT_I; } // DDD only
|
||||
// VPADDL { DECL_Id(0xF3B00200); SET_Qdnm; EMIT_I; } // QdQm || DdDm
|
||||
// VRADDHN { DECL_Id(0xF3800400); SET_Qdnm; EMIT_I; } // DQQ
|
||||
// VRHADD { DECL_Id(0xF3000100); SET_Qdnm; EMIT_I; }
|
||||
// VRSUBHN { DECL_Id(0xF3800600); SET_Qdnm; EMIT_I; } // DQQ
|
||||
// VQADD { DECL_Id(0xF2000010); SET_Qdnm; EMIT_I; }
|
||||
// VQSUB { DECL_Id(0xF3000210); SET_Qdnm; EMIT_I; }
|
||||
// VSUB I { DECL_Id(0xF3000800); SET_Qdnm; EMIT_I; }
|
||||
// VSUB F { DECL_Id(0xF2200D00); SET_Qdnm; EMIT_I; }
|
||||
// VSUBHN { DECL_Id(0xF2800600); SET_Qdnm; EMIT_I; } // DQQ
|
||||
// VSUB{L,W} { DECL_Id(0xF2800200); SET_Qdnm; EMIT_I; } // QDD || QQD
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A.SIMD Bitwise
|
||||
*
|
||||
Vector Bitwise AND VAND (register)
|
||||
Vector Bitwise Bit Clear (AND complement) VBIC (immediate), VBIC (register)
|
||||
Vector Bitwise Exclusive OR VEOR
|
||||
Vector Bitwise Move VMOV (immediate), VMOV (register)
|
||||
Vector Bitwise NOT VMVN (immediate), VMVN (register)
|
||||
Vector Bitwise OR VORR (immediate), VORR (register)
|
||||
Vector Bitwise OR NOT VORN (register)
|
||||
Vector Bitwise Insert if False VBIF
|
||||
Vector Bitwise Insert if True VBIT
|
||||
Vector Bitwise Select VBSL
|
||||
*/
|
||||
|
||||
VdpInstrI(AND, 0xF2000110)
|
||||
VdpInstrI(BIC, 0xF2100110)
|
||||
VdpInstrI_EOR(EOR, 0xF3000110)
|
||||
VdpInstrI_EOR(BSL, 0xF3100110)
|
||||
VdpInstrI_EOR(BIT, 0xF3200110)
|
||||
VdpInstrI_EOR(BIF, 0xF3300110)
|
||||
VdpInstrI(ORN, 0xF2300110)
|
||||
// VAND R { DECL_Id(0xF2000110); SET_Qdnm; EMIT_I; }
|
||||
// VBIC R { DECL_Id(0xF2100110); SET_Qdnm; EMIT_I; }
|
||||
// VEOR { DECL_Id(0xF3000110); SET_Qdnm; EMIT_I; }
|
||||
// VBSL { DECL_Id(0xF3100110); SET_Qdnm; EMIT_I; }
|
||||
// VBIT { DECL_Id(0xF3200110); SET_Qdnm; EMIT_I; }
|
||||
// VBIF { DECL_Id(0xF3300110); SET_Qdnm; EMIT_I; }
|
||||
// VORN R { DECL_Id(0xF2300110); SET_Qdnm; EMIT_I; }
|
||||
|
||||
// VBIC I { DECL_Id(0xF2800030); SET_Qd; SET_IMM??; EMIT_I; }
|
||||
// VMOV I { DECL_Id(0xF2800010); SET_Qd; SET_IMM??; EMIT_I; }
|
||||
// VMVN I { DECL_Id(0xF2800030); SET_Qd; SET_IMM??; EMIT_I; }
|
||||
// VORR I { DECL_Id(0xF2800010); SET_Qd; SET_IMM??; EMIT_I; }
|
||||
// VAND I VBIC I
|
||||
// VORN I VORR I
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A.SIMD comparison
|
||||
*
|
||||
Vector Absolute Compare VACGE, VACGT, VACLE,VACLT
|
||||
Vector Compare Equal VCEQ (register)
|
||||
Vector Compare Equal to Zer VCEQ (immediate #0)
|
||||
Vector Compare Greater Than or Equal VCGE (register)
|
||||
Vector Compare Greater Than or Equal to Zero VCGE (immediate #0)
|
||||
Vector Compare Greater Than VCGT (register)
|
||||
Vector Compare Greater Than Zero VCGT (immediate #0)
|
||||
Vector Compare Less Than or Equal to Zero VCLE (immediate #0)
|
||||
Vector Compare Less Than Zero VCLT (immediate #0)
|
||||
Vector Test Bits VTST
|
||||
*/
|
||||
// VAC{COND} { DECL_Id(0xF3000E10); SET_Vdnm; EMIT_I; }
|
||||
|
||||
// VCEQ { DECL_Id(0xF3000810); SET_Vdnm; EMIT_I; } .F32 { DECL_Id(0xF2000E00); SET_Vdnm; EMIT_I; }
|
||||
VdpInstrI(CEQ, 0xF3200810)
|
||||
// VdpInstrF(CEQ, 0xF2000e00)
|
||||
// VCGE { DECL_Id(0xF2000310); SET_Vdnm; EMIT_I; } .F32 { DECL_Id(0xF3000E00); SET_Vdnm; EMIT_I; }
|
||||
VdpInstrI(CGE, 0xF2200310)
|
||||
// VdpInstrF(CGE, 0xF3000e00)
|
||||
// VCGT { DECL_Id(0xF2000300); SET_Vdnm; EMIT_I; } .F32 { DECL_Id(0xF3200E00); SET_Vdnm; EMIT_I; }
|
||||
//*SEB* 0xF220030 for S32, 0xF3200300 for U32, 0xF2000300 is for S8.
|
||||
VdpInstrI(CGT, 0xF2200300)
|
||||
//VdpInstrF(CGT, 0xF3200e00)
|
||||
// VCLE { DECL_Id(0xF3B10180); SET_Vdnm; EMIT_I; } // R is VCGE w/ operands reversed
|
||||
// VCLT { DECL_Id(0xF3B10200); SET_Vdnm; EMIT_I; } // R is VCGT w/ operands reversed
|
||||
// VTST { DECL_Id(0xF2000810); SET_Vdnm; EMIT_I; }
|
||||
|
||||
// VCEQZ { DECL_Id(0xF3B10100); SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VCGEZ { DECL_Id(0xF3B10080); SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VCGTZ { DECL_Id(0xF3B10000); SET_Vd; SET_Vm; EMIT_I; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A.SIMD shift ** SET_imm6; needed for non Vdnm
|
||||
*
|
||||
Vector Saturating Rounding Shift Left VQRSHL
|
||||
Vector Saturating Rounding Shift Right and Narrow VQRSHRN, VQRSHRUN
|
||||
Vector Saturating Shift Left VQSHL (register), VQSHL, VQSHLU (immediate)
|
||||
Vector Saturating Shift Right and Narrow VQSHRN, VQSHRUN
|
||||
Vector Rounding Shift Left VRSHL
|
||||
Vector Rounding Shift Right VRSHR
|
||||
Vector Rounding Shift Right and Accumulate VRSRA
|
||||
Vector Rounding Shift Right and Narrow VRSHRN
|
||||
Vector Shift Left VSHL (immediate) on page A8-750 VSHL (register)
|
||||
Vector Shift Left Long VSHLL
|
||||
Vector Shift Right VSHR
|
||||
Vector Shift Right and Narrow VSHRN
|
||||
Vector Shift Left and Insert VSLI
|
||||
Vector Shift Right and Accumulate VSRA
|
||||
Vector Shift Right and Insert VSRI
|
||||
*/
|
||||
// VQRSHL { DECL_Id(0xF SET_Vdnm; EMIT_I; } // * TODO
|
||||
// VQRSHRN { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VQRSHRUN { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VQSHL R { DECL_Id(0xF SET_Vdnm; EMIT_I; }
|
||||
// VQSHL I { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VQSHLU { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VQSHRN { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VQSHRUN { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VRSHL { DECL_Id(0xF SET_Vdnm; EMIT_I; }
|
||||
// VRSHR { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VRSRA { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VRSHRN { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VSHL I { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VSHL R { DECL_Id(0xF SET_Vdnm; EMIT_I; }
|
||||
// VSHLL { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VSHR { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VSHRN { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VSLI { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VSRA { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VSRI { DECL_Id(0xF SET_Vd; SET_Vm; EMIT_I; }
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A.SIMD multiply
|
||||
*
|
||||
Vector Multiply Accumulate VMLA, VMLAL, VMLS, VMLSL (integer) , VMLA, VMLS (floating-point), VMLA, VMLAL, VMLS, VMLSL (by scalar)
|
||||
Vector Multiply Accumulate Long
|
||||
Vector Multiply Subtract
|
||||
Vector Multiply Subtract Long
|
||||
Vector Multiply VMUL, VMULL (integer and polynomial)
|
||||
Vector Multiply Long VMUL (floating-point) on page A8-664 VMUL, VMULL (by scalar)
|
||||
Vector Saturating Doubling Multiply Accumulate Long VQDMLAL, VQDMLSL
|
||||
Vector Saturating Doubling Multiply Subtract Long
|
||||
Vector Saturating Doubling Multiply Returning High Half VQDMULH
|
||||
Vector Saturating Rounding Doubling Multiply Ret. High Half VQRDMULH
|
||||
Vector Saturating Doubling Multiply Long VQDMULL
|
||||
*/
|
||||
|
||||
VdpInstrI(MLA, 0xF2000900)
|
||||
VdpInstrI(MLS, 0xF3000900)
|
||||
|
||||
VdpInstrF(MLA, 0xF2000D10)
|
||||
VdpInstrF(MLS, 0xF2200D10)
|
||||
|
||||
//by scalar
|
||||
//this should be really qd,dn,sm not dm
|
||||
EAPI VMUL_F32(eFQReg Qd,eFQReg Qn, eFDReg Dm, int idx)
|
||||
{
|
||||
DECL_Id(0xF2800840);
|
||||
|
||||
SET_Qd;
|
||||
SET_Qn;
|
||||
I |= 1<<8; //SET_F
|
||||
|
||||
SET_Dm;
|
||||
I |= 0x1<<24; //SET_Q not compatible
|
||||
|
||||
I |= 2<<20; //size to 32
|
||||
|
||||
I |= Dm&15; //only lower 15 regs are avail
|
||||
|
||||
|
||||
//set register sub index
|
||||
if (idx)
|
||||
I |= 1<<5;
|
||||
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VMLA_F32(eFQReg Qd,eFQReg Qn, eFDReg Dm, int idx)
|
||||
{
|
||||
DECL_Id(0xF2800040);
|
||||
|
||||
SET_Qd;
|
||||
SET_Qn;
|
||||
I |= 1<<8; //SET_F
|
||||
|
||||
SET_Dm;
|
||||
I |= 0x1<<24; //SET_Q not compatible
|
||||
|
||||
I |= 2<<20; //size to 32
|
||||
|
||||
I |= Dm&15; //only lower 15 regs are avail
|
||||
|
||||
|
||||
//set register sub index
|
||||
if (idx)
|
||||
I |= 1<<5;
|
||||
|
||||
EMIT_I;
|
||||
}
|
||||
// VdpInstrU(MLAL, 0xF2000D10) *QDD
|
||||
// VdpInstrU(MLSL, 0xF2200D10) *QDD
|
||||
|
||||
|
||||
VdpInstrI(MUL, 0xF2000910)
|
||||
|
||||
VdpInstrF(MUL, 0xF3000D10)
|
||||
|
||||
// VdpInstrU(MULL, 0xF3000D10) *QDD
|
||||
|
||||
// VMLA I { DECL_Id(0xF2000900); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VMLS I { DECL_Id(0xF3000900); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VMLAL I { DECL_Id(0xF2800800); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20) * I |= ((U&1)<<24)
|
||||
// VMLSL I { DECL_Id(0xF2800A00); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20) * I |= ((U&1)<<24)
|
||||
|
||||
// VMLA F { DECL_Id(0xF2000D10); SET_Vdnm; EMIT_I; } // * I |= ((sz&1)<<20)
|
||||
// VMLS F { DECL_Id(0xF2200D10); SET_Vdnm; EMIT_I; } // * I |= ((sz&1)<<20)
|
||||
|
||||
// VMLA S { DECL_Id(0xF2800040); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VMLS S { DECL_Id(0xF2800440); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// CMLAL S { DECL_Id(0xF2800240); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20) * I |= ((U&1)<<24)
|
||||
// VMLSL S { DECL_Id(0xF2800640); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20) * I |= ((U&1)<<24)
|
||||
|
||||
// VMUL IP { DECL_Id(0xF2000910); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20) * I |= 1<<24 for polynomial
|
||||
// VMULL IP { DECL_Id(0xF2800C00); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20) * I |= 1<<9 for polynomial * I |= ((U&1)<<24)
|
||||
|
||||
// VMUL F { DECL_Id(0xF3000D10); SET_Vdnm; EMIT_I; } // * I |= ((sz&1)<<20)
|
||||
|
||||
// VMUL S { DECL_Id(0xF2800840); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VMULL S { DECL_Id(0xF2800A40); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20) * I |= ((U&1)<<24)
|
||||
// VQDMLAL { DECL_Id(0xF2800900); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VQDMLSL { DECL_Id(0xF2800B00); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VQDMULH { DECL_Id(0xF2000B00); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VQRDMULH { DECL_Id(0xF3000B00); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
// VQDMULL { DECL_Id(0xF2800D00); SET_Vdnm; EMIT_I; } // * I |= ((size&3)<<20)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A.SIMD misc
|
||||
*
|
||||
Vector Absolute Difference and Accumulate VABA, VABAL
|
||||
Vector Absolute Difference VABD, VABDL (integer) , VABD (floating-point)
|
||||
Vector Absolute VABS
|
||||
Vector Convert between floating-point and fixed point VCVT (between floating-point and fixed-point, Advanced SIMD)
|
||||
Vector Convert between floating-point and integer VCVT (between floating-point and integer, Advanced SIMD)
|
||||
Vector Convert between half-precision and single-precision VCVT (between half-precision and single-precision, Advanced SIMD)
|
||||
Vector Count Leading Sign Bits VCLS
|
||||
Vector Count Leading Zeros VCLZ
|
||||
Vector Count Set Bits VCNT
|
||||
Vector Duplicate scalar VDUP (scalar)
|
||||
Vector Extract VEXT
|
||||
Vector Move and Narrow VMOVN
|
||||
Vector Move Long VMOVL
|
||||
Vector Maximum, Minimum VMAX, VMIN (integer) , VMAX, VMIN (floating-point)
|
||||
Vector Negate VNEG
|
||||
Vector Pairwise Maximum, Minimum VPMAX, VPMIN (integer) , VPMAX, VPMIN (floating-point)
|
||||
Vector Reciprocal Estimate VRECPE
|
||||
Vector Reciprocal Step VRECPS
|
||||
Vector Reciprocal Square Root Estimate VRSQRTE
|
||||
Vector Reciprocal Square Root Step VRSQRTS
|
||||
Vector Reverse VREV16, VREV32, VREV64
|
||||
Vector Saturating Absolute VQABS
|
||||
Vector Saturating Move and Narrow VQMOVN, VQMOVUN
|
||||
Vector Saturating Negate VQNEG
|
||||
Vector Swap VSWP
|
||||
Vector Table Lookup VTBL, VTBX
|
||||
Vector Transpose VTRN
|
||||
Vector Unzip VUZP
|
||||
Vector Zip VZIP
|
||||
*/
|
||||
// VABA { DECL_Id(0xF2000710); // WIP
|
||||
// VABAL { DECL_Id(0xF2800500);
|
||||
|
||||
// VABD I { DECL_Id(0xF);
|
||||
// VABDL I { DECL_Id(0xF);
|
||||
|
||||
// VABD F { DECL_Id(0xF);
|
||||
|
||||
// VABS { DECL_Id(0xF); SET_Vd; SET_Vm; EMIT_I; }
|
||||
|
||||
EAPI VSQRT_F32(eFSReg Sd, eFSReg Sm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EB10AC0); SET_CC;
|
||||
|
||||
I |= ((Sd&0x1E)<<11) | ((Sd&1)<<22);
|
||||
I |= ((Sm&0x1E)>>1) | ((Sm&1)<<5);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VABS_F32(eFSReg Sd, eFSReg Sm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EB00AC0); SET_CC;
|
||||
|
||||
I |= ((Sd&0x1E)<<11) | ((Sd&1)<<22);
|
||||
I |= ((Sm&0x1E)>>1) | ((Sm&1)<<5);
|
||||
EMIT_I;
|
||||
}
|
||||
EAPI VNEG_F32(eFSReg Sd, eFSReg Sm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EB10A40); SET_CC;
|
||||
|
||||
I |= ((Sd&0x1E)<<11) | ((Sd&1)<<22);
|
||||
I |= ((Sm&0x1E)>>1) | ((Sm&1)<<5);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
//imm move, fpu
|
||||
EAPI VMOV(eFSReg Sd, u32 imm8_fpu, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EB10A00); SET_CC;
|
||||
|
||||
I |= (imm8_fpu&0x0F); //bits 3:0
|
||||
I |= (imm8_fpu&0xF0)<<12; //bits 19:16
|
||||
|
||||
I |= ((Sd&0x1E)<<11) | ((Sd&1)<<22);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
const u32 fpu_imm_1=0x70;//01110000
|
||||
|
||||
EAPI VCMP_F32(eFSReg Sd, eFSReg Sm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EB40A40); SET_CC;
|
||||
|
||||
I |= ((Sd&0x1E)<<11) | ((Sd&1)<<22);
|
||||
I |= ((Sm&0x1E)>>1) | ((Sm&1)<<5);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
VdpInstrF(CEQ, 0xF2000E00)
|
||||
VdpInstrF(CGE, 0xF3000E00)
|
||||
VdpInstrF(CGT, 0xF3200E00)
|
||||
|
||||
// VCVT FFP { DECL_Id(0xF); SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VCVT FI { DECL_Id(0xF); SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VCVT HS { DECL_Id(0xF); SET_Vd; SET_Vm; EMIT_I; }
|
||||
|
||||
// VCLS { DECL_Id(0xF); SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VCLZ { DECL_Id(0xF); SET_Vd; SET_Vm; EMIT_I; }
|
||||
// VCNT { DECL_Id(0xF); SET_Vd; SET_Vm; EMIT_I; }
|
||||
|
||||
|
||||
// VEXT { DECL_Id(0xF); SET_Vdnm; EMIT_I; }
|
||||
|
||||
// VMAX I { DECL_Id(0xF);
|
||||
// VMIN I { DECL_Id(0xF);
|
||||
// VMAX F { DECL_Id(0xF);
|
||||
// VMIN F { DECL_Id(0xF);
|
||||
// VNEG { DECL_Id(0xF);
|
||||
|
||||
// VPMAX I { DECL_Id(0xF);
|
||||
// VPMIN I { DECL_Id(0xF);
|
||||
// VPMAX F { DECL_Id(0xF);
|
||||
// VPMIN F { DECL_Id(0xF);
|
||||
|
||||
// VRECPE { DECL_Id(0xF3B30400); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
// VRECPS { DECL_Id(0xF2000F10); SET_Vdnm; EMIT_I; } // sz&1<<20 (.F32)
|
||||
// VRSQRTE { DECL_Id(0xF3B30480); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18 F&1<<8 ***
|
||||
// VRSQRTS { DECL_Id(0xF2200F10); SET_Vdnm; EMIT_I; } // sz&1<<20 (.F32)
|
||||
// VREVsz { DECL_Id(0xF3B00000); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18 op&3<<7 ***
|
||||
|
||||
// VQABS { DECL_Id(0xF3B00700); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
// VQMOVN { DECL_Id(0xF3B20200); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18 op&3<<6 op:00=MOVN op11=srcUnsigned op:x1=dstUnsigned
|
||||
// VQMOVUN { DECL_Id(0xF3B20200); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
// VQNEG { DECL_Id(0xF3B00780); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
|
||||
// VSWP { DECL_Id(0xF3B20000); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
// VTBL { DECL_Id(0xF3B00800); SET_Vdnm; EMIT_I; } // len&3<<8
|
||||
// VTBX { DECL_Id(0xF3B00840); SET_Vdnm; EMIT_I; } // len&3<<8
|
||||
// VTRN { DECL_Id(0xF3B20080); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
// VUZP { DECL_Id(0xF3B20100); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
// VZIP { DECL_Id(0xF3B20180); SET_Vd; SET_Vm; EMIT_I; } // size&3<<18
|
||||
//
|
||||
|
||||
|
||||
// VDUP & VMOVN & VMOVL are implemented in VRegXfer.h ...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VFPv3 Instructions
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#define SET_Sd \
|
||||
I |= ((Sd&0x1E)<<11); \
|
||||
I |= ((Sd&0x01)<<22)
|
||||
|
||||
#define SET_Sn \
|
||||
I |= ((Sn&0x1E)<<15); \
|
||||
I |= ((Sn&0x01)<<7)
|
||||
|
||||
#define SET_Sm \
|
||||
I |= ((Sm&0x1E)>>1); \
|
||||
I |= ((Sm&0x01)<<5)
|
||||
|
||||
|
||||
|
||||
#define SET_Sdnm SET_Sd; SET_Sn; SET_Sm
|
||||
|
||||
|
||||
#define VfpInstrS(viName, viId) EAPI V##viName##_VFP (eFSReg Sd, eFSReg Sn, eFSReg Sm, ConditionCode CC=CC_AL) { DECL_Id(viId); SET_CC; SET_Sdnm; EMIT_I; }
|
||||
|
||||
VfpInstrS(MLA, 0x0E000A00)
|
||||
VfpInstrS(MLS, 0x0E000A40)
|
||||
|
||||
VfpInstrS(NMLA, 0x0E100A40)
|
||||
VfpInstrS(NMLS, 0x0E100A00)
|
||||
VfpInstrS(NMUL, 0x0E200A40)
|
||||
|
||||
|
||||
VfpInstrS(MUL, 0x0E200A00)
|
||||
|
||||
VfpInstrS(ADD, 0x0E300A00)
|
||||
VfpInstrS(SUB, 0x0E300A40)
|
||||
|
||||
VfpInstrS(DIV, 0x0E800A00)
|
||||
|
||||
|
||||
EAPI VCVT_to_S32_VFP (eFSReg Sd, eFSReg Sm, ConditionCode CC=CC_AL) { DECL_Id(0x0EBD0AC0); SET_CC; SET_Sd; SET_Sm; EMIT_I; } // VfpInstrS(ABS, 0x0EB00AC0) ** {D,S}dm
|
||||
// 0x0EB80A40 is to_U32. to_S32 is 0x0EB80AC0
|
||||
EAPI VCVT_from_S32_VFP (eFSReg Sd, eFSReg Sm, ConditionCode CC=CC_AL) { DECL_Id(0x0EB80AC0); SET_CC; SET_Sd; SET_Sm; EMIT_I; } // VfpInstrS(ABS, 0x0EB00AC0) ** {D,S}dm
|
||||
|
||||
EAPI VABS_VFP (eFSReg Sd, eFSReg Sm, ConditionCode CC=CC_AL) { DECL_Id(0x0EB00AC0); SET_CC; SET_Sd; SET_Sm; EMIT_I; } // VfpInstrS(ABS, 0x0EB00AC0) ** {D,S}dm
|
||||
EAPI VNEG_VFP (eFSReg Sd, eFSReg Sm, ConditionCode CC=CC_AL) { DECL_Id(0x0EB10A40); SET_CC; SET_Sd; SET_Sm; EMIT_I; } // VfpInstrS(NEG, 0x0EB10A40) ** {D,S}dm
|
||||
EAPI VSQRT_VFP(eFSReg Sd, eFSReg Sm, ConditionCode CC=CC_AL) { DECL_Id(0x0EB10AC0); SET_CC; SET_Sd; SET_Sm; EMIT_I; } // VfpInstrS(SQRT, 0x0EB10AC0) ** {D,S}dm
|
||||
|
||||
// - x0 Vector Move VMOV (immediate) on page A8-640
|
||||
// 0000 01 Vector Move VMOV (register) on page A8-642
|
||||
|
||||
// 001x x1 Vector Convert VCVTB, VCVTT (between half-precision and single-precision, VFP) on page A8-588
|
||||
// 010x x1 Vector Compare VCMP, VCMPE on page A8-572
|
||||
// 0111 11 Vector Convert VCVT (between double-precision and single-precision) on page A8-584
|
||||
// 1000 x1 Vector Convert VCVT, VCVTR (between floating-point and integer, VFP) on page A8-578
|
||||
// 101x x1 Vector Convert VCVT (between floating-point and fixed-point, VFP) on page A8-582
|
||||
// 110x x1 Vector Convert VCVT, VCVTR (between floating-point and integer, VFP) on page A8-578
|
||||
// 111x x1 Vector Convert VCVT (between floating-point and fixed-point, VFP) on page A8-582
|
||||
|
||||
////// hack
|
||||
|
||||
EAPI VDIV_HACKF32(eFDReg Sd, eFDReg Sn, eFDReg Sm)
|
||||
{
|
||||
ConditionCode CC=AL;
|
||||
|
||||
eFSReg SdS=(eFSReg)(Sd*2);
|
||||
eFSReg SnS=(eFSReg)(Sn*2);
|
||||
eFSReg SmS=(eFSReg)(Sm*2);
|
||||
|
||||
verify((int)Sd < 32 && (int)Sn < 32 && (int)Sm < 32);
|
||||
|
||||
VDIV_VFP(SdS,SnS,SmS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#undef SET_Qd
|
||||
#undef SET_Dd
|
||||
|
||||
#undef SET_Qn
|
||||
#undef SET_Dn
|
||||
|
||||
#undef SET_Qm
|
||||
#undef SET_Dm
|
||||
|
||||
#undef SET_Q
|
||||
|
||||
#undef SET_Qdnm
|
||||
#undef SET_Ddnm
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* E_VLoadStore.h * VFP/A.SIMD Load/Store Instruction Set Encoding
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
// [cond][110][Opcode][-Rn-][----][101][---------]
|
||||
//
|
||||
// cond != 0b1111 | LDC && STC consume these
|
||||
|
||||
|
||||
|
||||
#define SET_Dd \
|
||||
I |= ((Dd&0x0F)<<12); \
|
||||
I |= ((Dd&0x10)<<18)
|
||||
|
||||
#define SET_Sd \
|
||||
I |= ((Sd&0x1E)<<11); \
|
||||
I |= ((Sd&0x01)<<22)
|
||||
|
||||
|
||||
#define SET_Rn \
|
||||
I |= ((Rn&15)<<16)
|
||||
|
||||
#define SET_uImm8 \
|
||||
I |= (uImm8 & 255)
|
||||
|
||||
#define SET_sImm8 \
|
||||
if (sImm8 > 0) { \
|
||||
I |= (1<<23); \
|
||||
} \
|
||||
I |= (abs(sImm8) & 255)
|
||||
|
||||
|
||||
#define SET_PUW(_P,_U,_W) \
|
||||
I |= ( ((_P&1)<<24) | ((_U&1)<<23) | ((_W&1)<<21) )
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* V{LD,ST}R: Vector Load/Store Register
|
||||
*
|
||||
* V{LD,ST}R.64 // VFP && A.SIMD
|
||||
* V{LD,ST}R.32 // VFP
|
||||
*/
|
||||
|
||||
EAPI VLDR(eFDReg Dd, eReg Rn, s32 sImm8, ConditionCode CC=AL) // VLDR.64
|
||||
{
|
||||
DECL_Id(0x0D100B00); SET_CC;
|
||||
SET_Dd; SET_Rn; SET_sImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VLDR(eFSReg Sd, eReg Rn, s32 sImm8, ConditionCode CC=AL) // VLDR.32
|
||||
{
|
||||
DECL_Id(0x0D100A00); SET_CC;
|
||||
SET_Sd; SET_Rn; SET_sImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI VSTR(eFDReg Dd, eReg Rn, s32 sImm8, ConditionCode CC=AL) // VSTR.64
|
||||
{
|
||||
DECL_Id(0x0D000B00); SET_CC;
|
||||
SET_Dd; SET_Rn; SET_sImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VSTR(eFSReg Sd, eReg Rn, s32 sImm8, ConditionCode CC=AL) // VSTR.32
|
||||
{
|
||||
DECL_Id(0x0D000A00); SET_CC;
|
||||
SET_Sd; SET_Rn; SET_sImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* V{LD,ST}M: Vector Load/Store Multiple
|
||||
*
|
||||
* V{LD,ST}R.64 // VFP && A.SIMD
|
||||
* V{LD,ST}R.32 // VFP
|
||||
*
|
||||
* uImm8: directional count, abs(sImm8) is the reg. count
|
||||
* Dd: Register to start sequential operation from..
|
||||
*
|
||||
* suffix DB: P=1 U=0 W=1, Decrement Before, Addresses end just before the address in Rn.
|
||||
* suffix IA: P=0 U=1 W=?, Increment After, Addresses start at address in Rn
|
||||
*
|
||||
* ** These are very complicated encoding and require a lot of error checking and even more thought when used. **
|
||||
* ** Simply using, MOV32(R4, &double_array[0]); VLDM(D0, R4, 4); should however work to load an array of 4 doubles **
|
||||
*/
|
||||
|
||||
EAPI VLDM(eFDReg Dd, eReg Rn, u32 uImm8, u32 WB=0, ConditionCode CC=AL) // VLDM.64
|
||||
{
|
||||
// ASSERT( (uImm8>0) && (uImm8<=16) && ((Dd+uImm8) <= 32) )
|
||||
uImm8<<=1;
|
||||
DECL_Id(0x0C100B00); SET_CC;
|
||||
SET_Dd; SET_Rn;
|
||||
SET_uImm8; SET_PUW(0,1,WB); // Defaulting to IA w/o ! (Write-back)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VLDM(eFSReg Sd, eReg Rn, u32 uImm8, ConditionCode CC=AL) // VLDM.32
|
||||
{
|
||||
// ASSERT( (uImm8>0) && ((Dd+uImm8) <= 32) )
|
||||
DECL_Id(0x0C100A00); SET_CC;
|
||||
SET_Sd; SET_Rn;
|
||||
SET_uImm8; SET_PUW(0,1,0); // Defaulting to IA w/o ! (Write-back)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI VSTM(eFDReg Dd, eReg Rn, u32 uImm8, ConditionCode CC=AL) // VSTM.64
|
||||
{
|
||||
// ASSERT( (uImm8>0) && (uImm8<=16) && ((Dd+uImm8) <= 32) )
|
||||
uImm8<<=1;
|
||||
DECL_Id(0x0C000B00); SET_CC;
|
||||
SET_Dd; SET_Rn;
|
||||
SET_uImm8; SET_PUW(0,1,0); // Defaulting to IA w/o ! (Write-back)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VSTM(eFSReg Sd, eReg Rn, u32 uImm8, ConditionCode CC=AL) // VSTM.32
|
||||
{
|
||||
// ASSERT( (uImm8>0) && ((Dd+uImm8) <= 32) )
|
||||
DECL_Id(0x0C000A00); SET_CC;
|
||||
SET_Sd; SET_Rn;
|
||||
SET_uImm8; SET_PUW(0,1,0); // Defaulting to IA w/o ! (Write-back)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* V{LD,ST}n: Various extra load/store multiple.
|
||||
*
|
||||
* Not Implemented.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VPUSH/VPOP: Vector Load/Store multiple consecutive vector registers to the stack.
|
||||
*
|
||||
* V{PUSH,POP} .64: A.SIMD .32 VFP
|
||||
*/
|
||||
|
||||
EAPI VPUSH(eFDReg Dd, eReg Rn, u32 uImm8, ConditionCode CC=AL) // VPUSH.64
|
||||
{
|
||||
uImm8<<=1;
|
||||
DECL_Id(0x0D2D0B00); SET_CC;
|
||||
SET_Dd; SET_uImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VPUSH(eFSReg Sd, eReg Rn, u32 uImm8, ConditionCode CC=AL) // VPUSH.32
|
||||
{
|
||||
DECL_Id(0x0D2D0A00); SET_CC;
|
||||
SET_Sd; SET_uImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
EAPI VPOP(eFDReg Dd, eReg Rn, u32 uImm8, ConditionCode CC=AL) // VPOP.64
|
||||
{
|
||||
uImm8<<=1;
|
||||
DECL_Id(0x0CBD0B00); SET_CC;
|
||||
SET_Dd; SET_uImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VPOP(eFSReg Sd, eReg Rn, u32 uImm8, ConditionCode CC=AL) // VPOP.32
|
||||
{
|
||||
DECL_Id(0x0CBD0A00); SET_CC;
|
||||
SET_Sd; SET_uImm8;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Best practice is to remove macro definitions,
|
||||
* this way they can't affect subsequent headers.
|
||||
*/
|
||||
|
||||
#undef SET_Dd
|
||||
#undef SET_Sd
|
||||
|
||||
#undef SET_Rn
|
||||
|
||||
#undef SET_uImm8
|
||||
#undef SET_sImm8
|
||||
|
||||
#undef SET_PUW
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* E_VRegXfer.h * VFP/A.SIMD Register Transfer Instruction Set Encoding
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
|
||||
/////// REPLACE THIS MESS W. SET_Rto16 / SET_Rto16_H22 , SET_Rto12 , SET_Rto0_H5 OR just leave it all in the fn's //////
|
||||
|
||||
|
||||
#define SET_Qd \
|
||||
I |= (((Qd<<1)&0x0E)<<16); \
|
||||
I |= (((Qd<<1)&0x10)<<3)
|
||||
|
||||
#define SET_Qm \
|
||||
I |= (((Qm<<1)&0x0E)); \
|
||||
I |= (((Qm<<1)&0x10)<<1)
|
||||
|
||||
|
||||
#define SET_Dd \
|
||||
I |= ((Dd&0x0F)<<16); \
|
||||
I |= ((Dd&0x10)<<3)
|
||||
|
||||
#define SET_Dm \
|
||||
I |= ((Dm&0x0F)); \
|
||||
I |= ((Dm&0x10)<<1)
|
||||
|
||||
|
||||
#define SET_Sn \
|
||||
I |= ((Sn&0x1E)<<15); \
|
||||
I |= ((Sn&0x01)<<7)
|
||||
|
||||
#define SET_Sm \
|
||||
I |= ((Sm&0x1E)>>1); \
|
||||
I |= ((Sm&0x01)<<5)
|
||||
|
||||
|
||||
#define SET_Rn \
|
||||
I |= ((Rn&15)<<16)
|
||||
|
||||
#define SET_Rt \
|
||||
I |= ((Rt&15)<<12)
|
||||
|
||||
#define SET_Rtx2 \
|
||||
I |= ((Rt&15)<<12) | ((Rt2&15)<<16)
|
||||
|
||||
// VDUP VMOV VMRS VMSR
|
||||
|
||||
/*
|
||||
* VDUP.SZ: Duplicates an element from ARM reg Rt into every element of {Q,D}d A.SIMD
|
||||
*
|
||||
*/
|
||||
EAPI VDUP(eFQReg Qd, eReg Rt, u32 Size=32, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0E800B10); SET_CC;
|
||||
SET_Qd; SET_Rt; I |= (1<<21); // Q
|
||||
if (Size==16) { I |= (1<<5); } // e
|
||||
if (Size==8) { I |= (1<<22); } // b
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VDUP(eFDReg Dd, eReg Rt, u32 Size=32, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0E800B10); SET_CC;
|
||||
SET_Dd; SET_Rt; // No Q
|
||||
if (Size==16) { I |= (1<<5); } // e
|
||||
if (Size==8) { I |= (1<<22); } // b
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VDUP8 (eFQReg Qd, eReg Rt, ConditionCode=AL) { VDUP(Qd,Rt,8, CC); }
|
||||
EAPI VDUP8 (eFDReg Dd, eReg Rt, ConditionCode=AL) { VDUP(Dd,Rt,8, CC); }
|
||||
EAPI VDUP16(eFQReg Qd, eReg Rt, ConditionCode=AL) { VDUP(Qd,Rt,16,CC); }
|
||||
EAPI VDUP16(eFDReg Dd, eReg Rt, ConditionCode=AL) { VDUP(Dd,Rt,16,CC); }
|
||||
EAPI VDUP32(eFQReg Qd, eReg Rt, ConditionCode=AL) { VDUP(Qd,Rt,32,CC); }
|
||||
EAPI VDUP32(eFDReg Dd, eReg Rt, ConditionCode=AL) { VDUP(Dd,Rt,32,CC); }
|
||||
|
||||
EAPI VDUP32(eFQReg Qd, eFDReg Dm, int idx)
|
||||
{
|
||||
DECL_Id(0xF3B00C00);
|
||||
|
||||
//Set_Qd seems to be incompitable here ?
|
||||
I |= (((Qd<<1)&0x0E)<<12); \
|
||||
I |= (((Qd<<1)&0x10)<<18);
|
||||
SET_Dm;
|
||||
I |= 0x40; //SET_Q
|
||||
I |= 0x4 << 16; // 32 bits 4=0100
|
||||
I |= (idx&1) << 19; // set idx
|
||||
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOV: (register)
|
||||
*
|
||||
* VMOV { <Qd, Qm> , <Dd, Dm> } A.SIMD
|
||||
* VMOV { <Dd, Dm> , <Sd, Sm> } VFP sz1 UNDEFINED in single only VFP
|
||||
*/
|
||||
|
||||
EAPI VMOV(eFQReg Qd, eFQReg Qm) // UNCONDITIONAL
|
||||
{
|
||||
DECL_Id(0xF2200110);
|
||||
|
||||
I |= ((Qd&0x0F)<<12) | ((Qd&0x10)<<18);
|
||||
I |= ((Qm&0x0F)<<16) | ((Qm&0x10)<<1); // If !Consistent(M:Qm) then its VORR
|
||||
I |= ((Qm&0x0F)) | ((Qm&0x10)<<3); // If !Consistent(M:Qm) then its VORR
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VMOV(eFDReg Dd, eFDReg Dm) // UNCONDITIONAL
|
||||
{
|
||||
DECL_Id(0xF2200110);
|
||||
|
||||
I |= ((Dd&0x0F)<<12) | ((Dd&0x10)<<18);
|
||||
I |= ((Dm&0x0F)<<16) | ((Dm&0x10)<<3); // If !Consistent(M:Dm) then its VORR
|
||||
I |= ((Dm&0x0F)) | ((Dm&0x10)<<1); // If !Consistent(M:Dm) then its VORR
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
// EAPI VMOV(eFDReg Dd, eFDReg Dm, ConditionCode CC=AL) {} VFP Double Version Not Implemented here for obvious reasons : same as below except would set SZ @bit8 : 0x0EB00B40
|
||||
|
||||
EAPI VMOV(eFSReg Sd, eFSReg Sm, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EB00A40); SET_CC;
|
||||
|
||||
I |= ((Sd&0x1E)<<11) | ((Sd&1)<<22);
|
||||
I |= ((Sm&0x1E)>>1) | ((Sm&1)<<5);
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOV: (Immediate) A.SIMD / VFP
|
||||
*
|
||||
*/
|
||||
|
||||
//// TO BE IMPLEMENTED ////
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOV: (ARM to scalar) A.SIMD / VFP IF Size=32
|
||||
*
|
||||
*/
|
||||
EAPI VMOV(eFDReg Dd, u32 Index, eReg Rt, u32 Size=32, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0E000B10); SET_CC;
|
||||
SET_Dd; SET_Rt;
|
||||
// Dd[x] where x==Index Dd is 64b, 2x32[0,1](1bit) 4x16[0-3](2bits) 8x8[0-7](3bits)
|
||||
if (Size== 8) { I |= (1<<22) | ((Index&4)<<18) | ((Index&3)<<5) ; } // x -> opc1:0, opc2 (3bits) | opc1:1 SET
|
||||
if (Size==16) { I |= (1<<5) | ((Index&2)<<20) | ((Index&1<<6)) ; } // x -> opc1:0, opc2:1 (2bits) | opc2:0 SET
|
||||
if (Size==32) { I |= ((Index&1)<<21) ; } // x -> opc1:0 (1bit)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOV: (scalar to ARM) A.SIMD / VFP IF Size=32
|
||||
*
|
||||
* Note: U (bit32) is unsigned bit, invalid for 32b and we do not handle it at all.. might want to set it for byte,short
|
||||
*/
|
||||
EAPI VMOV(eReg Rt, eFDReg Dd, u32 Index, u32 Size=32, ConditionCode CC=AL) // This is really Vn, but we'll use the same macros..
|
||||
{
|
||||
DECL_Id(0x0E100B10); SET_CC;
|
||||
SET_Dd; SET_Rt;
|
||||
// Dd[x] where x==Index Dd is 64b, 2x32[0,1](1bit) 4x16[0-3](2bits) 8x8[0-7](3bits)
|
||||
if (Size== 8) { I |= (1<<22) | ((Index&4)<<18) | ((Index&3)<<5) ; } // x -> opc1:0, opc2 (3bits) | opc1:1 SET
|
||||
if (Size==16) { I |= (1<<5) | ((Index&2)<<20) | ((Index&1<<6)) ; } // x -> opc1:0, opc2:1 (2bits) | opc2:0 SET
|
||||
if (Size==32) { I |= ((Index&1)<<21) ; } // x -> opc1:0 (1bit)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOV: (between ARM and single either direction) VFP
|
||||
*
|
||||
*/
|
||||
EAPI VMOV(eReg Rt, eFSReg Sn, ConditionCode CC=AL) // Sn !d
|
||||
{
|
||||
DECL_Id(0x0E000A10); SET_CC;
|
||||
SET_Sn; SET_Rt; I |= (1<<20); // op set = TO ARM reg
|
||||
EMIT_I;
|
||||
}
|
||||
EAPI VMOV(eFSReg Sn, eReg Rt, ConditionCode CC=AL) // Sn !d
|
||||
{
|
||||
DECL_Id(0x0E000A10); SET_CC;
|
||||
SET_Sn; SET_Rt; // op NOT set = TO FP Single reg
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOV: (between two ARM regs and two contiguous singles either direction) VFP
|
||||
*
|
||||
*/
|
||||
EAPI VMOV(eReg Rt, eReg Rt2, eFSReg Sm, ConditionCode CC=AL) // Sn !d
|
||||
{
|
||||
DECL_Id(0x0E000A10); SET_CC;
|
||||
SET_Sm; SET_Rtx2; I |= (1<<20); // op set = TO ARM regs
|
||||
EMIT_I;
|
||||
}
|
||||
EAPI VMOV(eFSReg Sm, eReg Rt, eReg Rt2, ConditionCode CC=AL) // Sn !d
|
||||
{
|
||||
DECL_Id(0x0E000A10); SET_CC;
|
||||
SET_Sm; SET_Rtx2; // op NOT set = TO FP Single(s)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOV: (between two ARM regs and a Double) Dm <-> Rt2:Rt A.SIMD/VFP
|
||||
*
|
||||
*/
|
||||
EAPI VMOV(eReg Rt, eReg Rt2, eFDReg Dm, ConditionCode CC=AL) // Sn !d
|
||||
{
|
||||
DECL_Id(0x0C400B10); SET_CC;
|
||||
SET_Dm; SET_Rtx2; I |= (1<<20); // op set = TO ARM regs
|
||||
EMIT_I;
|
||||
}
|
||||
EAPI VMOV(eFDReg Dm, eReg Rt, eReg Rt2, ConditionCode CC=AL) // Sn !d
|
||||
{
|
||||
DECL_Id(0x0C400B10); SET_CC;
|
||||
SET_Dm; SET_Rtx2; // op NOT set = TO FP Single(s)
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOVL: Takes each element in a VDouble && Sign or Zero extends into a VQuad A.SIMD
|
||||
*
|
||||
*/
|
||||
EAPI VMOVL(eFQReg Qd, eFDReg Dm, u32 Size=32, u32 Sign=0) // UNCONDITIONAL Q & ~1
|
||||
{
|
||||
Size >>= 3; // Sz/8 = 1,2 or 4 else if >0 its VSHLL
|
||||
Sign = (Sign>0)?0:1; // Invert to Unsigned
|
||||
DECL_Id(0xF2800A10);
|
||||
|
||||
SET_Dm;
|
||||
I |= ((Qd&0x0F)<<12);
|
||||
I |= ((Qd&0x10)<<18);
|
||||
I |= ((Size &7)<<19); // imm3
|
||||
I |= ((Sign &1)<<24); // U
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VMOVL_S8 (eFQReg Qd, eFDReg Dm) { VMOVL(Qd,Dm,8,1); }
|
||||
EAPI VMOVL_U8 (eFQReg Qd, eFDReg Dm) { VMOVL(Qd,Dm,8,0); }
|
||||
EAPI VMOVL_S16(eFQReg Qd, eFDReg Dm) { VMOVL(Qd,Dm,16,1); }
|
||||
EAPI VMOVL_U16(eFQReg Qd, eFDReg Dm) { VMOVL(Qd,Dm,16,0); }
|
||||
EAPI VMOVL_S32(eFQReg Qd, eFDReg Dm) { VMOVL(Qd,Dm,32,1); }
|
||||
EAPI VMOVL_U32(eFQReg Qd, eFDReg Dm) { VMOVL(Qd,Dm,32,0); }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VMOVN: Copies least significant half of each element of a VQuad into the elements of a VDouble A.SIMD
|
||||
*
|
||||
*/
|
||||
EAPI VMOVN(eFDReg Dd, eFQReg Qm, u32 Size=32) // UNCONDITIONAL Q & ~1
|
||||
{
|
||||
Size >>= 4; // Sz/32 = 0,1 or 2
|
||||
DECL_Id(0xF3B20200);
|
||||
|
||||
SET_Qm;
|
||||
I |= ((Dd&0x0F)<<12);
|
||||
I |= ((Dd&0x10)<<18);
|
||||
I |= ((Size &3)<<18); // size
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
EAPI VMOVN16(eFDReg Dd, eFQReg Qm) { VMOVN(Dd,Qm,16); }
|
||||
EAPI VMOVN32(eFDReg Dd, eFQReg Qm) { VMOVN(Dd,Qm,32); }
|
||||
EAPI VMOVN64(eFDReg Dd, eFQReg Qm) { VMOVN(Dd,Qm,64); }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* VM{RS,SR} Move ARM reg To/From FPSCR A.SIMD/VFP
|
||||
*/
|
||||
EAPI VMRS(eReg Rt, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EF10A10);
|
||||
SET_CC; SET_Rt;
|
||||
EMIT_I;
|
||||
}
|
||||
EAPI VMSR(eReg Rt, ConditionCode CC=AL)
|
||||
{
|
||||
DECL_Id(0x0EE10A10);
|
||||
SET_CC; SET_Rt;
|
||||
EMIT_I;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#undef SET_Qd
|
||||
#undef SET_Qm
|
||||
|
||||
#undef SET_Dd
|
||||
#undef SET_Dm
|
||||
|
||||
#undef SET_Sn
|
||||
#undef SET_Sm
|
||||
|
||||
#undef SET_Rn
|
||||
#undef SET_Rt
|
||||
#undef SET_Rtx2
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* H_Branches.h
|
||||
*
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
|
||||
inline static snat Literal(unat FnAddr)
|
||||
{
|
||||
u8* pc_addr = (u8*)EMIT_GET_PTR();
|
||||
return (snat)((snat)FnAddr - ((snat)pc_addr+8));
|
||||
//return -(snat)((pc_addr+8)-(snat)FnAddr);
|
||||
}
|
||||
|
||||
EAPI CALL(unat FnAddr, ConditionCode CC=AL)
|
||||
{
|
||||
bool isThumb = FnAddr & 1;
|
||||
FnAddr &= ~1;
|
||||
snat lit = Literal(FnAddr);
|
||||
|
||||
if(0==lit) {
|
||||
printf("Error, Compiler caught NULL literal, CALL(%08zX)\n", FnAddr);
|
||||
verify(false);
|
||||
return;
|
||||
}
|
||||
if( (lit<-33554432) || (lit>33554428) ) // ..28 for BL ..30 for BLX
|
||||
{
|
||||
printf("Warning, CALL(%08zX) is out of range for literal(%08zX)\n", FnAddr, lit);
|
||||
// verify(false);
|
||||
|
||||
MOV32(IP, FnAddr, CC);
|
||||
BLX(IP, CC);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isThumb) {
|
||||
verify (CC==CC_EQ);
|
||||
BLX(lit, isThumb);
|
||||
} else {
|
||||
BL(lit,CC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
EAPI JUMP(unat FnAddr, ConditionCode CC=AL)
|
||||
{
|
||||
bool isThumb = FnAddr & 1;
|
||||
FnAddr &= ~1;
|
||||
|
||||
verify(!isThumb);
|
||||
snat lit = Literal(FnAddr);
|
||||
|
||||
/*if(0==lit) {
|
||||
printf("Error, Compiler caught NULL literal, JUMP(%08X)\n", FnAddr);
|
||||
verify(false);
|
||||
return;
|
||||
}*/
|
||||
if( (lit<-33554432) || (lit>33554428) ) // ..28 for BL ..30 for BLX
|
||||
{
|
||||
printf("Warning, %zX is out of range for imm jump! \n", FnAddr);
|
||||
//verify(false);
|
||||
|
||||
MOV32(IP, FnAddr, CC);
|
||||
BX(IP, CC);
|
||||
return;
|
||||
}
|
||||
B(lit,CC); // Note, wont work for THUMB*, have to use bx which is reg only !
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* H_LoadStore.h
|
||||
*
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
/*
|
||||
* Load Helpers
|
||||
*/
|
||||
|
||||
EAPI LoadImmBase(eReg Rt, u32 Base, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rt, Base, CC);
|
||||
|
||||
#if defined(_DEVEL)
|
||||
LDR(Rt,Rt,0, Offset, CC);
|
||||
#else
|
||||
LDR(Rt,Rt,0, CC);
|
||||
#endif
|
||||
}
|
||||
|
||||
EAPI LoadImmBase(eReg Rt, eReg Rn, u32 Base, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rn, Base, CC);
|
||||
|
||||
#if defined(_DEVEL)
|
||||
LDR(Rt,Rn,0, Offset, CC);
|
||||
#else
|
||||
LDR(Rt,Rn,0, CC);
|
||||
#endif
|
||||
}
|
||||
|
||||
EAPI LoadImmBase16(eReg Rt, u32 Base, bool Extend=false, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rt, Base, CC);
|
||||
LDRH(Rt,Rt,0, CC);
|
||||
|
||||
if(Extend)
|
||||
SXTH(Rt,Rt);
|
||||
}
|
||||
|
||||
EAPI LoadImmBase16(eReg Rt, eReg Rn, u32 Base, bool Extend=false, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rn, Base, CC);
|
||||
LDRH(Rt,Rn,0, CC);
|
||||
|
||||
if(Extend)
|
||||
SXTH(Rt,Rt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Store Helpers
|
||||
*/
|
||||
|
||||
// you pick regs, loads Base with reg addr, you supply data in Rt
|
||||
EAPI StoreImmBase(eReg Rt, eReg Rn, u32 Base, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rn, Base, CC);
|
||||
|
||||
#if defined(_DEVEL)
|
||||
STR(Rt,Rn,0, Offset, CC);
|
||||
#else
|
||||
STR(Rt,Rn,0, CC);
|
||||
#endif
|
||||
}
|
||||
|
||||
// you pick regs, loads Rt with const val, you supply base for Rn
|
||||
EAPI StoreImmVal(eReg Rt, eReg Rn, u32 Val, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rt, Val, CC);
|
||||
|
||||
#if defined(_DEVEL)
|
||||
STR(Rt,Rn,0, Offset, CC);
|
||||
#else
|
||||
STR(Rt,Rn,0, CC);
|
||||
#endif
|
||||
}
|
||||
|
||||
// you pick regs, loads Base with reg addr, loads Rt with const val
|
||||
EAPI StoreImms(eReg Rt, eReg Rn, u32 Base, u32 Val, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rn, Base, CC);
|
||||
MOV32(Rt, Val, CC);
|
||||
|
||||
#if defined(_DEVEL)
|
||||
STR(Rt,Rn,0, Offset, CC);
|
||||
#else
|
||||
STR(Rt,Rn,0, CC);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(_DEVEL) && 0 // These require testing //
|
||||
|
||||
EAPI LoadImmBase8(eReg Rt, u32 Base, bool Extend=false, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rt, Base, CC);
|
||||
LDRB(Rt,Rt,0, CC);
|
||||
|
||||
if(Extend)
|
||||
SXTB(Rt,Rt);
|
||||
}
|
||||
|
||||
EAPI LoadImmBase8(eReg Rt, eReg Rn, u32 Base, bool Extend=false, ConditionCode CC=AL)
|
||||
{
|
||||
MOV32(Rn, Base, CC);
|
||||
LDRB(Rt,Rn,0, CC);
|
||||
|
||||
if(Extend)
|
||||
SXTB(Rt,Rt);
|
||||
}
|
||||
|
||||
#endif // defined(_DEVEL)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* H_fp.h
|
||||
*
|
||||
* ARMv7 floating point help routines.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
/// WIP ///
|
||||
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
#if defined(_DEVEL)
|
||||
|
||||
|
||||
/*
|
||||
* vfpVersion(): Returns VFP Arch. Version, or -1 if not supported
|
||||
*/
|
||||
int vfpVersion()
|
||||
{
|
||||
// FPSID bits [22:16] contain version
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int neonVersion()
|
||||
{
|
||||
// ??
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define VFP_SINGLE (1<<0)
|
||||
#define VFP_DOUBLE (1<<1)
|
||||
#define NEON_INTEGER (1<<2)
|
||||
#define NEON_SINGLE (1<<3)
|
||||
#define VFP_TRAPS (1<<8) // VFPv3U
|
||||
#define FPEXT_HALF (1<<16) // Half precision extension
|
||||
|
||||
u32 fpFeatures()
|
||||
{
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif // _DEVEL
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
Version 1 of the Common VFP subarchitecture has special behavior when the FPSCR.IXE bit is set to 1.
|
||||
The Common VFP subarchitecture version can be identified by checking FPSID bits [22:16]. This field is
|
||||
0b0000001 for version 1. In version 1 of the Common VFP subarchitecture the FPEXC.DEX bit is RAZ/WI.
|
||||
|
||||
|
||||
Detecting which VFP Common subarchitecture registers are implemented
|
||||
An implementation can choose not to implement FPINST and FPINST2, if these registers are not required.
|
||||
|
||||
Set FPEXC.EX=1 and FPEXC.FP2V=1
|
||||
Read back the FPEXC register
|
||||
if FPEXC.EX == 0 then
|
||||
Neither FPINST nor FPINST2 are implemented
|
||||
else
|
||||
if FPEXC.FP2V == 0 then
|
||||
FPINST is implemented, FPINST2 is not implemented.
|
||||
else
|
||||
Both FPINST and FPINST2 are implemented.
|
||||
Clean up
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* H_psuedo.h
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
EAPI MOV32(eReg Rd, u32 Imm32, ConditionCode CC=AL)
|
||||
{
|
||||
MOVW(Rd,((Imm32)&0xFFFF),CC);
|
||||
if (Imm32>>16)
|
||||
MOVT(Rd,((Imm32>>16)&0xFFFF),CC);
|
||||
}
|
||||
#if 0
|
||||
EAPI NEG(eReg Rd,eReg Rs)
|
||||
{
|
||||
RSB(Rd,Rs,0);
|
||||
}
|
||||
#endif
|
||||
EAPI NOT(eReg Rd,eReg Rs)
|
||||
{
|
||||
MVN(Rd,Rs);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* H_state.h
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
enum InstructionSet {
|
||||
IS_ARM,
|
||||
IS_Thumb,
|
||||
IS_Jazelle,
|
||||
IS_ThumbEE
|
||||
};
|
||||
|
||||
enum Endian {
|
||||
E_Little,
|
||||
E_Big
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* coding.h, arm binary instruction coding
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
/*
|
||||
* Encoding Hi-Order 4bits, ConditionCode
|
||||
*
|
||||
*/
|
||||
|
||||
enum ConditionCode
|
||||
{
|
||||
EQ = 0x00, Equal = EQ, // 0000 Equal Z set
|
||||
NE = 0x01, NotEqual = NE, // 0001 Not equal Z clear
|
||||
CS = 0x02, CarrySet = CS, // 0010 Carry set/unsigned higher or same C set
|
||||
CC = 0x03, CarryClr = CC, // 0011 Carry clear/unsigned lowe C clear
|
||||
MI = 0x04, Minus = MI, // 0100 Minus/negative N set
|
||||
PL = 0x05, Plus = PL, // 0101 Plus/positive or zero N clear
|
||||
VS = 0x06, Overflow = VS, // 0110 Overflow V set
|
||||
VC = 0x07, NoOverflow = VC, // 0111 No overflow V clear
|
||||
|
||||
HI = 0x08, UnHigher = HI, // 1000 Unsigned higher C set and Z clear
|
||||
LS = 0x09, UnLower = LS, // 1001 Unsigned lower or same C clear or Z set
|
||||
|
||||
GE = 0x0A, GrOrEqual = GE, // 1010 Signed greater than or equal N set and V set, or N clear and V clear (N == V)
|
||||
LT = 0x0B, Less = LT, // 1011 Signed less than N set and V clear, or N clear and V set (N != V)
|
||||
GT = 0x0C, Greater = GT, // 1100 Signed greater than Z clear, and either N set and V set, or N clear and V clear (Z == 0,N == V)
|
||||
LE = 0x0D, LessOrEqual = LE, // 1101 Signed less than or equal Z set, or N set and V clear, or N clear and V set (Z == 1 or N != V)
|
||||
|
||||
AL = 0x0E, Always = AL, // 1110 Always (unconditional) -
|
||||
|
||||
|
||||
UC = 0x0F, Unconditional= UC, // 1111 Unconditional Special Instruction for ARMv5? and above
|
||||
|
||||
#define _ARM_COMPAT
|
||||
#if defined(_ARM_COMPAT)
|
||||
CC_EQ=EQ, CC_NE=NE, CC_CS=CS, CC_CC=CC, CC_MI=MI, CC_PL=PL, CC_VS=VS, CC_VC=VC,
|
||||
CC_HI=HI, CC_LS=LS, CC_GE=GE, CC_LT=LT, CC_GT=GT, CC_LE=LE, CC_AL=AL, CC_UC=UC,
|
||||
|
||||
CC_HS=CS, CC_LO=CC,
|
||||
#endif
|
||||
|
||||
ConditionCode_Size
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Data-processing OPCODE 4bits, DPOP
|
||||
*
|
||||
*/
|
||||
|
||||
enum DPOP
|
||||
{
|
||||
DP_AND, // 0000 Logical AND Rd := Rn AND shifter_operand
|
||||
DP_EOR, // 0001 Logical Exclusive OR Rd := Rn EOR shifter_operand
|
||||
DP_SUB, // 0010 Subtract Rd := Rn - shifter_operand
|
||||
DP_RSB, // 0011 Reverse Subtract Rd := shifter_operand - Rn
|
||||
DP_ADD, // 0100 Add Rd := Rn + shifter_operand
|
||||
DP_ADC, // 0101 Add with Carry Rd := Rn + shifter_operand + Carry Flag
|
||||
DP_SBC, // 0110 Subtract with Carry Rd := Rn - shifter_operand - NOT(Carry Flag)
|
||||
DP_RSC, // 0111 Reverse Subtract with Carry Rd := shifter_operand - Rn - NOT(Carry Flag)
|
||||
DP_TST, // 1000 Test Update flags after Rn AND shifter_operand
|
||||
DP_TEQ, // 1001 Test Equivalence Update flags after Rn EOR shifter_operand
|
||||
DP_CMP, // 1010 Compare Update flags after Rn - shifter_operand
|
||||
DP_CMN, // 1011 Compare Negated Update flags after Rn + shifter_operand
|
||||
DP_ORR, // 1100 Logical (inclusive) OR Rd := Rn OR shifter_operand
|
||||
DP_MOV, // 1101 Move Rd := shifter_operand (no first operand)
|
||||
DP_BIC, // 1110 Bit Clear Rd := Rn AND NOT(shifter_operand)
|
||||
DP_MVN // 1111 Move Not Rd := NOT shifter_operand (no first operand)
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum ShiftOp {
|
||||
S_LSL,
|
||||
S_LSR,
|
||||
S_ASR,
|
||||
S_ROR,
|
||||
S_RRX=S_ROR
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* eReg: ARM Register ID
|
||||
*
|
||||
*/
|
||||
enum eReg
|
||||
{
|
||||
r0=0,r1, r2, r3,
|
||||
r4, r5, r6, r7,
|
||||
r8, r9, r10, r11,
|
||||
r12, r13, r14, r15,
|
||||
|
||||
R0=0,R1, R2, R3,
|
||||
R4, R5, R6, R7,
|
||||
R8, R9, R10, R11,
|
||||
R12, R13, R14, R15,
|
||||
|
||||
// Aliases
|
||||
|
||||
a1 = r0, a2 = r1, a3 = r2, a4 = r3,
|
||||
A1 = R0, A2 = R1, A3 = R2, A4 = R3,
|
||||
|
||||
v1 = r4, v2 = r5, v3 = r6, v4 = r7, v5 = r8, v6 = r9,
|
||||
V1 = R4, V2 = R5, V3 = R6, V4 = R7, V5 = R8, V6 = R9,
|
||||
|
||||
rfp = r9, sl = r10, fp = r11, ip = r12, sp = r13, lr = r14, pc = r15,
|
||||
RFP = R9, SL = R10, FP = R11, IP = R12, SP = R13, LR = R14, PC = R15,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* eFQReg: Float [Quad] Register ID (A.SIMD)
|
||||
*
|
||||
*/
|
||||
enum eFQReg
|
||||
{
|
||||
q0=0,q1, q2, q3,
|
||||
q4, q5, q6, q7,
|
||||
q8, q9, q10, q11,
|
||||
q12, q13, q14, q15,
|
||||
|
||||
Q0=0,Q1, Q2, Q3,
|
||||
Q4, Q5, Q6, Q7,
|
||||
Q8, Q9, Q10, Q11,
|
||||
Q12, Q13, Q14, Q15
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* eFDReg: Float [Double] Register ID (VFP / A.SIMD)
|
||||
*
|
||||
*/
|
||||
enum eFDReg
|
||||
{
|
||||
d0=0,d1, d2, d3,
|
||||
d4, d5, d6, d7,
|
||||
d8, d9, d10, d11,
|
||||
d12, d13, d14, d15,
|
||||
d16, d17, d18, d19,
|
||||
d20, d21, d22, d23,
|
||||
d24, d25, d26, d27,
|
||||
d28, d29, d30, d31,
|
||||
|
||||
D0=0,D1, D2, D3,
|
||||
D4, D5, D6, D7,
|
||||
D8, D9, D10, D11,
|
||||
D12, D13, D14, D15,
|
||||
D16, D17, D18, D19,
|
||||
D20, D21, D22, D23,
|
||||
D24, D25, D26, D27,
|
||||
D28, D29, D30, D31
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* eFSReg: Float [Single] Register ID (VFP)
|
||||
*
|
||||
* Note: Using [f,F]regN syntax to avoid clash with s{8,16,32} types.
|
||||
*/
|
||||
enum eFSReg
|
||||
{
|
||||
f0=0,f1, f2, f3,
|
||||
f4, f5, f6, f7,
|
||||
f8, f9, f10, f11,
|
||||
f12, f13, f14, f15,
|
||||
f16, f17, f18, f19,
|
||||
f20, f21, f22, f23,
|
||||
f24, f25, f26, f27,
|
||||
f28, f29, f30, f31,
|
||||
|
||||
F0=0,F1, F2, F3,
|
||||
F4, F5, F6, F7,
|
||||
F8, F9, F10, F11,
|
||||
F12, F13, F14, F15,
|
||||
F16, F17, F18, F19,
|
||||
F20, F21, F22, F23,
|
||||
F24, F25, F26, F27,
|
||||
F28, F29, F30, F31
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum eFSpecialReg
|
||||
{
|
||||
|
||||
// VM**
|
||||
|
||||
FPINST = 9,
|
||||
FPINST2=10,
|
||||
|
||||
FP_R_ERROR=0xFF
|
||||
};
|
||||
|
||||
|
||||
enum ePushPopReg
|
||||
{
|
||||
_r0 =0x0001, _r1 =0x0002, _r2 =0x0004, _r3 =0x0008,
|
||||
_r4 =0x0010, _r5 =0x0020, _r6 =0x0040, _r7 =0x0080,
|
||||
_r8 =0x0100, _r9 =0x0200, _r10=0x0400, _r11=0x0800,
|
||||
_r12=0x1000, _r13=0x2000, _r14=0x4000, _r15=0x8000,
|
||||
|
||||
_a1 = _r0, _a2 = _r1, _a3 = _r2, _a4 = _r3,
|
||||
_v1 = _r4, _v2 = _r5, _v3 = _r6, _v4 = _r7,
|
||||
_v5 = _r8, _v6 = _r9, _rfp = _r9, _sl = _r10,
|
||||
_fp = _r11, _ip = _r12, _sp = _r13, _lr = _r14,
|
||||
_pc = _r15,
|
||||
|
||||
_push_all = 0xFFFF, // Save All 15
|
||||
_push_call = _lr|_rfp, // Save lr && _rfb(cycle count)
|
||||
|
||||
_push_eabi = _lr|_v1|_v2|_v3|_v4|_v5|_v6|_sl|_fp|_ip, // this is guesswork ..
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// WIP ///
|
||||
|
||||
struct FPReg
|
||||
{
|
||||
union
|
||||
{
|
||||
u8 vQ8[16];
|
||||
u8 vD8[8];
|
||||
u8 vS8[4];
|
||||
|
||||
u16 vQ16[8]; // *VFP: If half-word extensions are enabled
|
||||
u16 vD16[4]; // *VFP: If half-word extensions are enabled
|
||||
u16 vS16[2]; // *VFP: If half-word extensions are enabled
|
||||
|
||||
u32 vQ32[4];
|
||||
u32 vD32[2];
|
||||
u32 S;
|
||||
|
||||
// u64 vQ64[2];
|
||||
// u64 D;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* disasm.h, a horribly static yet (hopefully) correct disassembler
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
|
||||
|
||||
inline static void armdis_cc(u32 cond, char *ccbuff) // Length is always 8 for our static'{n,m}ess
|
||||
{
|
||||
switch(cond)
|
||||
{
|
||||
case EQ: sprintf(ccbuff, "EQ"); return;
|
||||
case NE: sprintf(ccbuff, "NE"); return;
|
||||
case CS: sprintf(ccbuff, "CS"); return;
|
||||
case CC: sprintf(ccbuff, "CC"); return;
|
||||
case MI: sprintf(ccbuff, "MI"); return;
|
||||
case PL: sprintf(ccbuff, "PL"); return;
|
||||
case VS: sprintf(ccbuff, "VS"); return;
|
||||
case VC: sprintf(ccbuff, "VC"); return;
|
||||
|
||||
case HI: sprintf(ccbuff, "HI"); return;
|
||||
case LS: sprintf(ccbuff, "LS"); return;
|
||||
|
||||
case GE: sprintf(ccbuff, "GE"); return;
|
||||
case LT: sprintf(ccbuff, "LT"); return;
|
||||
case GT: sprintf(ccbuff, "GT"); return;
|
||||
case LE: sprintf(ccbuff, "LE"); return;
|
||||
|
||||
case AL: return; // sprintf(ccbuff, "AL"); -- ALways doesn't need to be specified
|
||||
|
||||
case UC: //
|
||||
default: return; // DIE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline static void armdis_dp(u32 dpop, char *dpbuff) // Length is always 8 ...
|
||||
{
|
||||
switch(dpop)
|
||||
{
|
||||
case DP_AND: sprintf(dpbuff, "AND"); return;
|
||||
case DP_EOR: sprintf(dpbuff, "EOR"); return;
|
||||
case DP_SUB: sprintf(dpbuff, "SUB"); return;
|
||||
case DP_RSB: sprintf(dpbuff, "RSB"); return;
|
||||
case DP_ADD: sprintf(dpbuff, "ADD"); return;
|
||||
case DP_ADC: sprintf(dpbuff, "ADC"); return;
|
||||
case DP_SBC: sprintf(dpbuff, "SBC"); return;
|
||||
case DP_RSC: sprintf(dpbuff, "RSC"); return;
|
||||
case DP_TST: sprintf(dpbuff, "TST"); return;
|
||||
case DP_TEQ: sprintf(dpbuff, "TEQ"); return;
|
||||
case DP_CMP: sprintf(dpbuff, "CMP"); return;
|
||||
case DP_CMN: sprintf(dpbuff, "CMN"); return;
|
||||
case DP_ORR: sprintf(dpbuff, "ORR"); return;
|
||||
case DP_MOV: sprintf(dpbuff, "MOV"); return;
|
||||
case DP_BIC: sprintf(dpbuff, "BIC"); return;
|
||||
case DP_MVN: sprintf(dpbuff, "MVN"); return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline static void armdis(u32 op, char *disbuf, u32 len=512)
|
||||
{
|
||||
char ipref[8]={0}, isuff[8]={0}, icond[8]={0} ;
|
||||
|
||||
|
||||
// u32 uOP = ((op>>12)&0xFF00) | ((op>>4)&255) ;
|
||||
|
||||
u32 uCC = ((op>>28) & 0x0F) ; //
|
||||
|
||||
u32 uO1 = ((op>>25) & 0x07) ; //
|
||||
u32 uO2 = ((op>> 4) & 0x01) ; //
|
||||
u32 uC1 = ((op>>21) & 0x0F) ; //
|
||||
u32 uC2 = ((op>> 5) & 0x07) ; //
|
||||
u32 uSB = ((op>>20) & 0x01) ; // Sign Change Bit
|
||||
|
||||
|
||||
/*
|
||||
if (uCC == UC) {
|
||||
|
||||
printf ("DBG armdis has UC instruction %X\n", op);
|
||||
sprintf (disbuf, "UNCONDITIONAL / UNHANDLED INSTRUCTION");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (uCC != AL) {
|
||||
armdis_cc(uCC,isuff);
|
||||
}
|
||||
|
||||
|
||||
if (uO1 == 0)
|
||||
{
|
||||
|
||||
if (uO2 == 0) {
|
||||
|
||||
if ((uC1 & 0xC) == 8) {
|
||||
printf ("DBG armdis 0:0 10xx misc instruction \n", uCC);
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 0:");
|
||||
return;
|
||||
}
|
||||
|
||||
// DP imm.shift
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
else if (uO2 == 1) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 0:");
|
||||
}
|
||||
|
||||
}
|
||||
else if (uO1 == 1) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 1:");
|
||||
}
|
||||
else if (uO1 == 2) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 2:");
|
||||
}
|
||||
else if (uO1 == 3) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 3:");
|
||||
}
|
||||
else if (uO1 == 4) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 4:");
|
||||
}
|
||||
else if (uO1 == 5) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 5:");
|
||||
}
|
||||
else if (uO1 == 6) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 6:");
|
||||
}
|
||||
else if (uO1 == 7) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 7:");
|
||||
}
|
||||
else if (uO1 == 8) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 8:");
|
||||
}
|
||||
else if (uO1 == 9) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 9:");
|
||||
}
|
||||
else if (uO1 == 10) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 10:");
|
||||
}
|
||||
else if (uO1 == 11) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 11:");
|
||||
}
|
||||
else if (uO1 == 12) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 12:");
|
||||
}
|
||||
else if (uO1 == 13) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 13:");
|
||||
}
|
||||
else if (uO1 == 14) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 14:");
|
||||
}
|
||||
else if (uO1 == 15) {
|
||||
|
||||
sprintf (disbuf, "UNHANDLED INSTRUCTION 15:");
|
||||
}
|
||||
else {
|
||||
|
||||
sprintf (disbuf, "INVALID INSTRUCTION");
|
||||
}
|
||||
*/
|
||||
if (!uC1 && uO1==5) {
|
||||
//B
|
||||
char tmp[20];
|
||||
tmp[0]='\0';
|
||||
armdis_cc(uCC, tmp);
|
||||
sprintf(disbuf, "B%s %08X", tmp, (op&0xffffff)<<2);
|
||||
} else {
|
||||
armdis_dp(uC1, disbuf);
|
||||
char tmp[20];
|
||||
tmp[0]='\0';
|
||||
armdis_cc(uCC, tmp);
|
||||
if (tmp[0]) {
|
||||
strcat(disbuf, ".\0");
|
||||
strcat(disbuf, tmp);
|
||||
}
|
||||
if (uSB) strcat(disbuf, ".S\0");
|
||||
bool shifter=false;
|
||||
switch (uO1) {
|
||||
case 0:
|
||||
// reg_reg
|
||||
sprintf(tmp,"\tr%d, r%d", (op>>12)&0x0f, (op)&0x0f);
|
||||
shifter=true;
|
||||
break;
|
||||
case 1:
|
||||
// reg_imm
|
||||
sprintf(tmp,"\tr%d, %04X", (op>>16)&0x0f, (op)&0xffff);
|
||||
break;
|
||||
default:
|
||||
shifter=true;
|
||||
sprintf(tmp, " 0x%0X", uO1);
|
||||
}
|
||||
strcat(disbuf, tmp);
|
||||
char* ShiftOpStr[]={"LSL","LSR","ASR","ROR"};
|
||||
u32 shiftop=(op>>5)&0x3;
|
||||
u32 shiftoptype=(op>>4)&0x1;
|
||||
u32 shiftopreg=(op>>8)&0xf;
|
||||
u32 shiftopimm=(op>>7)&0x1f;
|
||||
if (shifter) {
|
||||
if (!shiftop && !shiftoptype && !shiftopimm)
|
||||
{
|
||||
//nothing
|
||||
} else {
|
||||
if ((shiftop==1) || (shiftop==2)) if (!shiftoptype) if (!shiftopimm) shiftopimm=32;
|
||||
sprintf(tmp, " ,%s %s%d", ShiftOpStr[shiftop], (shiftoptype)?" r":" #", (shiftoptype)?shiftopreg:shiftopimm);
|
||||
strcat(disbuf, tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Emitter.h
|
||||
*
|
||||
* ARMv7 ISA Emitter for code generation.
|
||||
*
|
||||
* David Miller, 2011.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "arm_coding.h"
|
||||
#include "arm_registers.h"
|
||||
//#include "arm_disasm.h"
|
||||
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
/*
|
||||
* Emitter - Reserved for use w/ static members ..
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
class Emitter
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
|
||||
#define EAPI static void
|
||||
|
||||
#define DECL_I \
|
||||
u32 Instruction=0
|
||||
|
||||
#define DECL_Id(d) \
|
||||
u32 Instruction=(d)
|
||||
|
||||
#else
|
||||
|
||||
// #define _inlineExSVoidA __extension__ static __inline void __attribute__ ((__always_inline__))
|
||||
|
||||
#define EAPI \
|
||||
inline static void
|
||||
|
||||
#define DECL_I \
|
||||
static u32 Instruction; \
|
||||
Instruction=0
|
||||
|
||||
#define DECL_Id(d) \
|
||||
static u32 Instruction; \
|
||||
Instruction=(d)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* TEMP
|
||||
*/
|
||||
|
||||
|
||||
#define I (Instruction)
|
||||
|
||||
#define SET_CC I |= (((u32)CC&15)<<28)
|
||||
|
||||
#ifndef EMIT_I
|
||||
#define EMIT_I emit_Write32((I));
|
||||
#endif
|
||||
|
||||
#ifndef EMIT_GET_PTR
|
||||
#define EMIT_GET_PTR() emit_GetCCPtr()
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ARM Core Instructions
|
||||
*/
|
||||
|
||||
#include "E_Branches.h"
|
||||
#include "E_DataOp.h"
|
||||
#include "E_Multiply.h"
|
||||
#include "E_Parallel.h"
|
||||
#include "E_Extend.h"
|
||||
#include "E_Misc.h"
|
||||
#include "E_Status.h"
|
||||
#include "E_LoadStore.h"
|
||||
#include "E_Special.h"
|
||||
|
||||
|
||||
/*
|
||||
* ARM VFP/A.SIMD Extension Instructions
|
||||
*/
|
||||
|
||||
#include "E_VLoadStore.h"
|
||||
#include "E_VRegXfer.h"
|
||||
#include "E_VDataOp.h"
|
||||
|
||||
|
||||
/*
|
||||
* Helper Routines & Psuedo-Instructions
|
||||
*/
|
||||
|
||||
#include "H_psuedo.h"
|
||||
|
||||
#include "H_Branches.h"
|
||||
#include "H_LoadStore.h"
|
||||
|
||||
//#include "H_state.h"
|
||||
//#include "H_fp.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* registers.h
|
||||
*
|
||||
* ARMv7-A system register(s).
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace ARM
|
||||
{
|
||||
|
||||
/*************************************************************************************************
|
||||
* CP15 Registers for VMSA Implementation. [ref.DDI0406B B3.12]
|
||||
*************************************************************************************************/
|
||||
/*
|
||||
<CRn> <opc1> <CRm> <opc2> [NAME,] Description [Flags]
|
||||
c0 {
|
||||
0 c0 {
|
||||
0 MIDR, Main ID RO
|
||||
1 CTR, Cache Type RO
|
||||
2 TCMTR, TCM Type RO, IMPL.DEFINED
|
||||
3 TLBTR, TLB Type RO, IMPL.DEFINED
|
||||
5 MPIDR, Multiprocessor Affinity RO
|
||||
{4,6,7} MIDR$, Main ID Aliases RO
|
||||
}
|
||||
|
||||
0 c[1-7] [0-7] CPUID ID_{PFRn,DFRn,AFR0,MMFRn,ISARn} RO
|
||||
|
||||
1 c0 0 CCSIDR, Cache Size ID RO
|
||||
1 c0 1 CLIDR, Cache Level ID RO
|
||||
1 c0 7 AIDR, Aux ID RO, IMPL.DEFINED
|
||||
|
||||
2 c0 0 CSSELR, Cache Size Selection RW
|
||||
}
|
||||
|
||||
c1 0 c0 [0-2] System Control RW
|
||||
c1 0 c1 [0-2] Security Extension RW, IMPL.OPTIONAL
|
||||
|
||||
c2 0 c0 [0-2] Translation Table Base RW
|
||||
|
||||
c3 0 c0 0 DACR, Domain Access Control RW
|
||||
|
||||
c5 0 c{0,1} {0,1} Fault Status RW
|
||||
|
||||
c6 0 c0 {0,2] Fault Address RW
|
||||
|
||||
c7 0 {
|
||||
c0 4 NOP WO
|
||||
c1 {0,6} Cache Maintenance operations, Multiprocessing Extensions WO
|
||||
c4 0 PAR, Physical Address RW
|
||||
c5 {0,1,6,7} Cache and branch predictor maintenance operations WO
|
||||
c5 4 CP15ISB, Instruction barrier operation WO, USER
|
||||
c6 {1,2} Cache Maintenance operations WO
|
||||
c8 [0-7] VA to PA translation ops. WO
|
||||
c10 {1,2} Cache management ops. WO
|
||||
c10 {4,5} Data barrier ops. WO, USER
|
||||
c11 1 DCCMVAU, Cache barrier ops. WO
|
||||
c13 1 NOP WO
|
||||
c14 {1,2} Cache management ops. WO
|
||||
}
|
||||
|
||||
c8 0 c{3,5,6,7} [0-3] TLB maintenance ops. * WO
|
||||
|
||||
c9 [0-7] c{0,2,5,8} [0-7] Reserved for Branch Predictor, Cache and TCM ops. RSVD, OP.ACCESS
|
||||
c9 [0-7] c[12-15] [0-7] Reserved for Performance monitors. RSVD, OP.ACCESS
|
||||
|
||||
c10 0 c{0,1,4,8} [0-7] Reserved for TLB lockdown ops. RSVD, OP.ACCESS
|
||||
c10 0 c2 {0,1} PRRR, NMRRR, TEX Remap RW
|
||||
|
||||
c11 [0-7] c{0,8,15} [0-7] Reserved for DMA ops. TCM access. RSVD, OP.ACCESS
|
||||
|
||||
c12 0 c0 {0,1} Security Extensions RW, IMPL.OPTIONAL
|
||||
c12 0 c1 0 ISR, Security Extensions RO, IMPL.OPTIONAL
|
||||
|
||||
c13 0 c0 0 FCSEIDR, FCSE PID RO-if-FCSE-!IMPL / RW?
|
||||
c13 0 c0 [1-4] Software Thread and Context ID RW
|
||||
|
||||
c15 * * * IMPLEMENTATION DEFINED IMPL.DEFINED
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************
|
||||
* CP15 c0: ID codes registers
|
||||
*************************************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* MIDR: Main ID Register
|
||||
*/
|
||||
struct MIDR
|
||||
{
|
||||
u32 Revision : 4;
|
||||
u32 PriPartNum : 12; // IF Impl:ARM && PriPartNo top 4bits are 0 || 7: arch&variant encoding differs
|
||||
u32 Architecture : 4;
|
||||
u32 Variant : 4;
|
||||
u32 Implementer : 8;
|
||||
};
|
||||
|
||||
enum MIDR_Implementer
|
||||
{
|
||||
ARM_Ltd = 0x41, // 'A'
|
||||
DigitalEquipment_Corp = 0x44, // 'D'
|
||||
Motorola_FreescaleSemi_Inc = 0x4D, // 'M'
|
||||
QUALCOMM_Inc = 0x51, // 'Q'
|
||||
MarvellSemi_Inc = 0x56, // 'V'
|
||||
Intel_Corp = 0x69, // 'i'
|
||||
|
||||
TexasInstruments_Inc = 0xFF // 'T' ???
|
||||
};
|
||||
|
||||
enum MIDR_Arch
|
||||
{
|
||||
ARMv4 = 1,
|
||||
ARMv4T = 2,
|
||||
ARMv5 = 3, // obselete
|
||||
ARMv5T = 4,
|
||||
ARMv5TE = 5,
|
||||
ARMv5TEJ = 6,
|
||||
ARMv6 = 7,
|
||||
CPUID_Defined = 15
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CTR, Cache Type Register
|
||||
*/
|
||||
struct CTR
|
||||
{
|
||||
u32 IminLine : 4;
|
||||
u32 SBZ : 10;
|
||||
u32 L1Ip : 2;
|
||||
u32 DminLine : 4; //
|
||||
u32 ERG : 4; // Exclusives Reservation Granule.
|
||||
u32 CWG : 4; // Cache Writeback Granule.
|
||||
u32 RAZ : 1;
|
||||
u32 REGFMT : 3; // Set to 0b100 for ARMv7 register format, or 0b000 for <=ARMv6 format
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* TCMTR, TCM Type Register
|
||||
*/
|
||||
// High 3 bits is 0b100, the rest is IMPL.DEFINED
|
||||
typedef u32 TCMTR;
|
||||
|
||||
|
||||
/*
|
||||
* TLBTR, TLB Type Register
|
||||
*/
|
||||
// Low bit is nU : SET:1: Not unified ( separate instruction and data TLBs )
|
||||
typedef u32 TLBTR;
|
||||
|
||||
|
||||
/*
|
||||
* MPIDR, Multiprocessor Affinity Register
|
||||
*/
|
||||
|
||||
struct MPIDR
|
||||
{
|
||||
u32 AffinityLevel0 : 8;
|
||||
u32 AffinityLevel1 : 8;
|
||||
u32 AffinityLevel2 : 8;
|
||||
u32 MT : 1;
|
||||
u32 RAZ : 5; // Reserved As Zero
|
||||
u32 U : 1; // Set: Processor is part of a Uniprocessor system.
|
||||
u32 MP_Impl : 1; // RAO if MP Extensions are implemented.
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* CCSIDR, Cache Size ID Registers
|
||||
*/
|
||||
struct CCSIDR
|
||||
{
|
||||
u32 LineSize : 3;
|
||||
u32 Associativity : 10;
|
||||
u32 NumSets : 15;
|
||||
u32 WA : 1;
|
||||
u32 RA : 1;
|
||||
u32 WB : 1;
|
||||
u32 WT : 1;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CLIDR, Cache Level ID Register
|
||||
*/
|
||||
struct CLIDR
|
||||
{
|
||||
u32 Ctype1 : 3;
|
||||
u32 Ctype2 : 3;
|
||||
u32 Ctype3 : 3;
|
||||
u32 Ctype4 : 3;
|
||||
u32 Ctype5 : 3;
|
||||
u32 Ctype6 : 3;
|
||||
u32 Ctype7 : 3;
|
||||
u32 LoUIS : 3;
|
||||
u32 LoC : 3;
|
||||
u32 LoUU : 3;
|
||||
u32 RAZ : 2; // RAZ
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* AIDR, Auxiliary ID Register.
|
||||
*/
|
||||
typedef u32 AIDR; // IMPLEMENTATION DEFINED
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CSSELR, Cache Size Selection Register
|
||||
*/
|
||||
struct CSSELR
|
||||
{
|
||||
u32 InD : 1;
|
||||
u32 Level : 3;
|
||||
u32 SBZP : 28;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************
|
||||
* CP15 c1: System control registers
|
||||
*************************************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
// SCTRL, ACTLR ////////////////////// TODO ///////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CPACR: Coprocessor Access Control Register.
|
||||
*
|
||||
* Controls access to all coprocessors other than CP14 & CP15.
|
||||
* It may be used to check for their presence by testing modification to cpN bits.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* D32DIS:1 && ASEDIS:0 is INVALID
|
||||
* ASEDIS on hw { w. VFP & w.o A.SIMD } is RAO/WI, if bit is not supported it is RAZ/WI.
|
||||
*
|
||||
* When Security Extensions are enabled, NSACR controls CP access from non-secure state.
|
||||
*
|
||||
* VFP uses CP10 && CP11, the values of .cp10 && .cp11 should be the same.
|
||||
*/
|
||||
union CPACR
|
||||
{
|
||||
struct {
|
||||
u32 cp0 : 2; // cpN [0-13]:
|
||||
u32 cp1 : 2; // Defines access rights for individual coprocessors.
|
||||
u32 cp2 : 2; // See CP_Access enum below for possible values;
|
||||
u32 cp3 : 2; //
|
||||
u32 cp4 : 2; // To test
|
||||
u32 cp5 : 2;
|
||||
u32 cp6 : 2;
|
||||
u32 cp7 : 2;
|
||||
u32 cp8 : 2;
|
||||
u32 cp9 : 2;
|
||||
u32 cp10 : 2;
|
||||
u32 cp11 : 2;
|
||||
u32 cp12 : 2;
|
||||
u32 cp13 : 2;
|
||||
u32 rsvcd: 2; // SBZP
|
||||
u32 D32DIS:1; // SET: Disables use of D16-D32 of the VFP register file.
|
||||
u32 ASEDIS:1; // SET: Disables all A.SIMD Instructions, VFPv3 shall remain valid.
|
||||
};
|
||||
|
||||
u32 R;
|
||||
};
|
||||
|
||||
/*
|
||||
* CP_Access: Enumerates access rights for CPACR.cpN
|
||||
*
|
||||
*/
|
||||
enum CP_Access
|
||||
{
|
||||
A_Deny, // Deny Access, Attempts to access cause Exception::Undefined_Instruction
|
||||
A_Privileged, // Privileged Access, Attempts to access cause Exception::Undefined_Instruction in User mode.
|
||||
A_Reserved, // Reserved Value, Use of this value is UNPREDICTABLE.
|
||||
A_Full // Full Access, Access is defined by coprocessor.
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* SCR: Secure Configuration Register.
|
||||
*
|
||||
* Requires: Security Extension.
|
||||
*/
|
||||
union SCR
|
||||
{
|
||||
struct {
|
||||
u32 NS : 1; //
|
||||
u32 IRQ : 1; //
|
||||
u32 FIQ : 1; //
|
||||
u32 EA : 1; //
|
||||
u32 FW : 1; //
|
||||
u32 AW : 1; //
|
||||
u32 nET : 1; //
|
||||
u32 SBZP:25;
|
||||
};
|
||||
|
||||
u32 R;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// SDER, Secure Debug Enable Register
|
||||
|
||||
// NSACR, Non-Secure Access Control Register
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************
|
||||
* CP15 c{2,3}: Memory protection and control registers
|
||||
*************************************************************************************************/
|
||||
|
||||
|
||||
// TTBR0 TTVR1 TTVCR
|
||||
// DACR, Domain Access Control Register
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************
|
||||
* CP15 c4: Not used
|
||||
*************************************************************************************************/
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************
|
||||
* CP15 c{5,6}: Memory system fault registers
|
||||
*************************************************************************************************/
|
||||
|
||||
// DFSR, Data Fault Status Register
|
||||
// IFSR, Instruction Fault Status Register
|
||||
// ADFSR, Aux. DFSR
|
||||
// AIFSR, Aux. IFSR
|
||||
// DFAR, Data Fault Address Register
|
||||
// IFAR, Instruction Fault Address Register
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************
|
||||
* CP15 c7: Cache maintenance / misc
|
||||
*************************************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************
|
||||
*************************************************************************************************
|
||||
* A.SIMD and VFP extension system registers
|
||||
*************************************************************************************************
|
||||
*************************************************************************************************/
|
||||
|
||||
enum FP_SysRegs
|
||||
{
|
||||
R_FPSID = 0, // 0b0000
|
||||
R_MVFR1 = 6, // 0b0110
|
||||
R_MVFR0 = 7, // 0b0111
|
||||
};
|
||||
|
||||
|
||||
// FPSID Floating Point System ID Register
|
||||
// MVFR1 Media and VFP Feature Register 1
|
||||
// MVFR0 Media and VFP Feature Register 0
|
||||
|
||||
struct FPSID
|
||||
{
|
||||
u32 Revision : 4; // IMPL.DEFINED
|
||||
u32 Variant : 4; // IMPL.DEFINED
|
||||
u32 PartNumber : 8; // IMPL.DEFINED
|
||||
u32 SubArch : 7; // MSB:1 when designed by ARM
|
||||
u32 SW : 1; // Is a software impl. if set
|
||||
u32 Implementer : 8; // Same as MIDR.Implementer
|
||||
};
|
||||
|
||||
enum FP_SubArch
|
||||
{
|
||||
VFPv1 = 0, // Not Permitted in ARMv7
|
||||
VFPv2_Cv1, // Not Permitted in ARMv7
|
||||
VFPv3_Cv2, //
|
||||
VFPv3_Null, // Full hardware, no trap
|
||||
VFPv3_Cv3, //
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Floating-point status and control register
|
||||
//
|
||||
struct FPSCR
|
||||
{
|
||||
u32 IOC : 1; // * All * bits are cumulative exception bits
|
||||
u32 DZC : 1; // *
|
||||
u32 OFC : 1; // *
|
||||
u32 UFC : 1; // *
|
||||
u32 IXC : 1; // *
|
||||
u32 SBZP1 : 2; //
|
||||
u32 IDC : 1; // *
|
||||
u32 IOE : 1; // ** All ** bits are FP trap enable bits
|
||||
u32 DZE : 1; // ** only supported in VFPv2 && VFPv3U
|
||||
u32 OFE : 1; // ** - RAZ elsewhere -
|
||||
u32 UFE : 1; // **
|
||||
u32 IXE : 1; // **
|
||||
u32 SBZP2 : 2; //
|
||||
u32 IDE : 1; // **
|
||||
u32 Len : 3; // SBZ for ARMv7 VFP, ignored for A.SIMD
|
||||
u32 SBZP3 : 1; //
|
||||
u32 Stride : 2; // SBZ for ARMv7 VFP, ignored for A.SIMD
|
||||
u32 RMode : 2; // Rounding Mode
|
||||
u32 FZ : 1; // Flush-to-Zero
|
||||
u32 DN : 1; // Default NaN mode control
|
||||
u32 AHP : 1; // Alt. Half-precision
|
||||
u32 QC : 1; // Cumulative saturation, A.SIMD
|
||||
u32 V : 1; // CC Overflow
|
||||
u32 C : 1; // CC Carry
|
||||
u32 Z : 1; // CC Zero
|
||||
u32 N : 1; // CC Negative
|
||||
};
|
||||
|
||||
enum FP_RoundingMode // A.SIMD Always uses RN !
|
||||
{
|
||||
RN, // Round to Nearest
|
||||
RP, // Round towards Plus Infinity
|
||||
RM, // Round towards Minus Infinity
|
||||
RZ // Round towards Zero
|
||||
};
|
||||
|
||||
|
||||
struct MVFR0
|
||||
{
|
||||
u32 A_SIMD : 4;
|
||||
u32 Single : 4;
|
||||
u32 Double : 4;
|
||||
u32 Trap : 4;
|
||||
u32 Divide : 4;
|
||||
u32 Sqrt : 4;
|
||||
u32 ShortVec: 4;
|
||||
u32 Rounding: 4;
|
||||
};
|
||||
|
||||
struct MVFR1
|
||||
{
|
||||
u32 FtZ_mode : 4;
|
||||
u32 D_NaN_mode : 4;
|
||||
u32 NFP_LdStr : 4;
|
||||
u32 NFP_int : 4;
|
||||
u32 NFP_SPFP : 4;
|
||||
u32 NFP_HPFP : 4;
|
||||
u32 VFP_HPFP : 4;
|
||||
u32 RAZ : 4;
|
||||
|
||||
};
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
audiostream.h)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
audiobackend_alsa.cpp
|
||||
audiobackend_coreaudio.cpp
|
||||
audiobackend_directsound.cpp
|
||||
audiobackend_libao.cpp
|
||||
audiobackend_null.cpp
|
||||
audiobackend_oboe.cpp
|
||||
audiobackend_omx.cpp
|
||||
audiobackend_oss.cpp
|
||||
audiobackend_pulseaudio.cpp
|
||||
audiobackend_sdl2.cpp
|
||||
audiostream.cpp)
|
||||
endif()
|
||||
@@ -1,358 +0,0 @@
|
||||
#if USE_ALSA
|
||||
#include "audiostream.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "cfg/cfg.h"
|
||||
#include "cfg/option.h"
|
||||
|
||||
class AlsaAudioBackend : public AudioBackend
|
||||
{
|
||||
snd_pcm_t *handle = nullptr;
|
||||
bool pcm_blocking = true;
|
||||
snd_pcm_uframes_t buffer_size = 0;
|
||||
snd_pcm_uframes_t period_size = 0;
|
||||
snd_pcm_t *handle_record = nullptr;
|
||||
|
||||
public:
|
||||
AlsaAudioBackend()
|
||||
: AudioBackend("alsa", "Advanced Linux Sound Architecture") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
snd_pcm_hw_params_t *params;
|
||||
|
||||
std::string device = config::loadStr("alsa", "device");
|
||||
|
||||
int rc = -1;
|
||||
if (!device.empty() && device != "auto") {
|
||||
rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (rc < 0)
|
||||
WARN_LOG(AUDIO, "ALSA: Cannot open device %s. Trying auto", device.c_str());
|
||||
}
|
||||
if (rc < 0)
|
||||
{
|
||||
INFO_LOG(AUDIO, "ALSA: trying to determine audio device");
|
||||
|
||||
// trying default device
|
||||
device = "default";
|
||||
rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
|
||||
// "default" didn't work, try first device
|
||||
if (rc < 0)
|
||||
{
|
||||
device = "plughw:0,0,0";
|
||||
rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
device = "plughw:0,0";
|
||||
rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// first didn't work, try second
|
||||
if (rc < 0)
|
||||
{
|
||||
device = "plughw:1,0";
|
||||
rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
}
|
||||
|
||||
// try pulse audio backend
|
||||
if (rc < 0)
|
||||
{
|
||||
device = "pulse";
|
||||
rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
INFO_LOG(AUDIO, "ALSA: unable to automatically determine audio device.");
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: unable to open PCM device %s: %s", device.c_str(), snd_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO_LOG(AUDIO, "ALSA: Successfully initialized \"%s\"", device.c_str());
|
||||
|
||||
/* Allocate a hardware parameters object. */
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
|
||||
/* Fill it in with default values. */
|
||||
rc=snd_pcm_hw_params_any(handle, params);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_any %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the desired hardware parameters. */
|
||||
|
||||
/* Interleaved mode */
|
||||
rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_access %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Signed 16-bit little-endian format */
|
||||
rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_format %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Two channels (stereo) */
|
||||
rc=snd_pcm_hw_params_set_channels(handle, params, 2);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_channels %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 44100 samples/second
|
||||
rc = snd_pcm_hw_params_set_rate(handle, params, 44100, 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_rate %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Period size (512)
|
||||
period_size = std::min(SAMPLE_COUNT, (u32)config::AudioBufferSize / 4);
|
||||
rc = snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, nullptr);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_periods_near %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(AUDIO, "ALSA: period size set to %zd", (size_t)period_size);
|
||||
|
||||
// Sample buffer size
|
||||
buffer_size = config::AudioBufferSize;
|
||||
rc = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Error:snd_pcm_hw_params_set_buffer_size_near %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(AUDIO, "ALSA: buffer size set to %ld", buffer_size);
|
||||
|
||||
/* Write the parameters to the driver */
|
||||
rc = snd_pcm_hw_params(handle, params);
|
||||
if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Unable to set hw parameters: %s", snd_strerror(rc));
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
int err;
|
||||
if ((err = snd_pcm_open(&handle_record, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot open default audio capture device: %s", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot allocate hardware parameter structure: %s", snd_strerror(err));
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_any(handle_record, hw_params)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot initialize hardware parameter structure: %s", snd_strerror(err));
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_access(handle_record, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot set access type: %s\n", snd_strerror(err));
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_format(handle_record, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot set sample format: %s", snd_strerror(err));
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_rate(handle_record, hw_params, sampling_freq, 0)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot set sample rate to %d Hz: %s", sampling_freq, snd_strerror(err));
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_channels(handle_record, hw_params, 1)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot set channel count: %s", snd_strerror(err));
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params(handle_record, hw_params)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot set parameters: %s", snd_strerror(err));
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_nonblock(handle_record, 1);
|
||||
if ((err = snd_pcm_prepare(handle_record)) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "ALSA: Cannot prepare device: %s", snd_strerror(err));
|
||||
snd_pcm_close(handle_record);
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(AUDIO, "ALSA: Successfully initialized capture device");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void termRecord() override
|
||||
{
|
||||
snd_pcm_close(handle_record);
|
||||
}
|
||||
|
||||
u32 record(void* frame, u32 samples) override
|
||||
{
|
||||
int err = snd_pcm_readi(handle_record, frame, samples);
|
||||
if (err < (int)samples)
|
||||
{
|
||||
if (err < 0)
|
||||
{
|
||||
DEBUG_LOG(AUDIO, "ALSA: Recording error: %s", snd_strerror(err));
|
||||
err = 0;
|
||||
err = snd_pcm_prepare(handle_record);
|
||||
}
|
||||
u8 *buffer = (u8 *)frame + err;
|
||||
memset(buffer, 0, (samples - err) * 2);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
if (wait != pcm_blocking) {
|
||||
snd_pcm_nonblock(handle, wait ? 0 : 1);
|
||||
pcm_blocking = wait;
|
||||
}
|
||||
|
||||
int rc = snd_pcm_writei(handle, frame, samples);
|
||||
if (rc < 0)
|
||||
{
|
||||
snd_pcm_recover(handle, rc, 1);
|
||||
if (rc == -EPIPE)
|
||||
{
|
||||
// EPIPE means underrun
|
||||
// Write some silence then our samples
|
||||
const size_t silence_size = buffer_size - samples;
|
||||
void *silence = alloca(silence_size * 4);
|
||||
memset(silence, 0, silence_size * 4);
|
||||
snd_pcm_writei(handle, silence, silence_size);
|
||||
snd_pcm_writei(handle, frame, samples);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
snd_pcm_drop(handle);
|
||||
snd_pcm_close(handle);
|
||||
}
|
||||
|
||||
std::vector<std::string> getDeviceList()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
char **hints;
|
||||
int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
|
||||
|
||||
// Error initializing ALSA
|
||||
if (err != 0)
|
||||
return result;
|
||||
|
||||
// special value to automatically detect on initialization
|
||||
result.emplace_back("auto");
|
||||
|
||||
char** n = hints;
|
||||
while (*n != NULL)
|
||||
{
|
||||
// Get the type (NULL/Input/Output)
|
||||
char *type = snd_device_name_get_hint(*n, "IOID");
|
||||
char *name = snd_device_name_get_hint(*n, "NAME");
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
// We only want output or special devices (like "default" or "pulse")
|
||||
// TODO Only those with type == NULL?
|
||||
if (type == NULL || strcmp(type, "Output") == 0)
|
||||
{
|
||||
// TODO Check if device works (however we need to hash the resulting list then)
|
||||
/*snd_pcm_t *handle;
|
||||
int rc = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
result.push_back(name);
|
||||
snd_pcm_close(handle);
|
||||
}
|
||||
*/
|
||||
|
||||
result.emplace_back(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (type != NULL)
|
||||
free(type);
|
||||
|
||||
if (name != NULL)
|
||||
free(name);
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
snd_device_name_free_hint((void**)hints);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Option* getOptions(int *count) override
|
||||
{
|
||||
*count = 1;
|
||||
static Option result;
|
||||
|
||||
result.name = "device";
|
||||
result.caption = "Device";
|
||||
result.type = Option::list;
|
||||
result.values = getDeviceList();
|
||||
|
||||
return &result;
|
||||
}
|
||||
};
|
||||
static AlsaAudioBackend alsaBackend;
|
||||
|
||||
#endif
|
||||
@@ -1,308 +0,0 @@
|
||||
/*
|
||||
Simple Core Audio backend for osx (and maybe ios?)
|
||||
Based off various audio core samples and dolphin's code
|
||||
|
||||
This is part of the Reicast project, please consult the
|
||||
LICENSE file for licensing & related information
|
||||
|
||||
This could do with some locking logic to avoid
|
||||
race conditions, and some variable length buffer
|
||||
logic to support chunk sizes other than 512 bytes
|
||||
|
||||
It does work on my macmini though
|
||||
*/
|
||||
#if defined(__APPLE__)
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioQueue.h>
|
||||
|
||||
class CoreAudioBackend : public AudioBackend
|
||||
{
|
||||
AudioUnit audioUnit;
|
||||
|
||||
u32 BUFSIZE = 0;
|
||||
u8 *samples_temp = nullptr;
|
||||
|
||||
std::atomic<int> samples_wptr;
|
||||
std::atomic<int> samples_rptr;
|
||||
cResetEvent bufferEmpty;
|
||||
|
||||
// input buffer and indexes
|
||||
u8 samples_input[2400];
|
||||
static constexpr size_t InputBufSize = sizeof(samples_input);
|
||||
std::atomic<int> input_wptr;
|
||||
std::atomic<int> input_rptr;
|
||||
AudioQueueRef recordQueue;
|
||||
|
||||
static OSStatus renderCallback(void* ctx, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* ts,
|
||||
UInt32 bus, UInt32 frames, AudioBufferList* abl)
|
||||
{
|
||||
CoreAudioBackend *backend = (CoreAudioBackend *)ctx;
|
||||
for (int i = 0; i < abl->mNumberBuffers; i++)
|
||||
{
|
||||
int size = abl->mBuffers[i].mDataByteSize;
|
||||
u8 *outBuffer = (u8 *)abl->mBuffers[i].mData;
|
||||
while (size != 0)
|
||||
{
|
||||
int avail = (backend->samples_wptr - backend->samples_rptr + backend->BUFSIZE) % backend->BUFSIZE;
|
||||
if (avail == 0)
|
||||
{
|
||||
//printf("Core Audio: buffer underrun %d bytes (%d)", size, abl->mBuffers[i].mDataByteSize);
|
||||
memset(outBuffer, '\0', size);
|
||||
return noErr;
|
||||
}
|
||||
avail = std::min(avail, size);
|
||||
avail = std::min(avail, (int)backend->BUFSIZE - backend->samples_rptr);
|
||||
memcpy(outBuffer, backend->samples_temp + backend->samples_rptr, avail);
|
||||
backend->samples_rptr = (backend->samples_rptr + avail) % backend->BUFSIZE;
|
||||
size -= avail;
|
||||
outBuffer += avail;
|
||||
// Set the mutex to allow writing
|
||||
backend->bufferEmpty.Set();
|
||||
}
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
public:
|
||||
CoreAudioBackend()
|
||||
: AudioBackend("coreaudio", "Core Audio") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
OSStatus err;
|
||||
AURenderCallbackStruct callback_struct;
|
||||
AudioStreamBasicDescription format;
|
||||
AudioComponentDescription desc;
|
||||
AudioComponent component;
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if !defined(TARGET_IPHONE)
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#endif
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
component = AudioComponentFindNext(nullptr, &desc);
|
||||
|
||||
if (component == nullptr) {
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioComponentFindNext failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = AudioComponentInstanceNew(component, &audioUnit);
|
||||
if (err != noErr) {
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioComponentInstanceNew failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
FillOutASBDForLPCM(format, 44100,
|
||||
2, 16, 16, false, false, false);
|
||||
err = AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &format,
|
||||
sizeof(AudioStreamBasicDescription));
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed");
|
||||
AudioComponentInstanceDispose(audioUnit);
|
||||
return false;
|
||||
}
|
||||
|
||||
callback_struct.inputProc = renderCallback;
|
||||
callback_struct.inputProcRefCon = this;
|
||||
err = AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &callback_struct,
|
||||
sizeof callback_struct);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed");
|
||||
AudioComponentInstanceDispose(audioUnit);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
err = AudioUnitSetParameter(audioUnit,
|
||||
kHALOutputParam_Volume,
|
||||
kAudioUnitParameterFlag_Output, 0,
|
||||
1, 0);
|
||||
verify(err == noErr);
|
||||
*/
|
||||
|
||||
err = AudioUnitInitialize(audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioUnitInitialize failed");
|
||||
AudioComponentInstanceDispose(audioUnit);
|
||||
return false;
|
||||
}
|
||||
|
||||
BUFSIZE = config::AudioBufferSize * 4;
|
||||
samples_temp = new u8[BUFSIZE]();
|
||||
samples_rptr = 0;
|
||||
samples_wptr = 0;
|
||||
|
||||
err = AudioOutputUnitStart(audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "coreaudio: AudioOutputUnitStart failed");
|
||||
AudioUnitUninitialize(audioUnit);
|
||||
AudioComponentInstanceDispose(audioUnit);
|
||||
return false;
|
||||
}
|
||||
|
||||
bufferEmpty.Set();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
int size = samples * 4;
|
||||
while (size != 0)
|
||||
{
|
||||
int avail = (samples_rptr - samples_wptr - 4 + BUFSIZE) % BUFSIZE;
|
||||
if (avail == 0)
|
||||
{
|
||||
if (!wait)
|
||||
break;
|
||||
bufferEmpty.Wait();
|
||||
continue;
|
||||
}
|
||||
avail = std::min(avail, size);
|
||||
avail = std::min(avail, (int)BUFSIZE - samples_wptr);
|
||||
memcpy(&samples_temp[samples_wptr], frame, avail);
|
||||
samples_wptr = (samples_wptr + avail) % BUFSIZE;
|
||||
frame = (u8 *)frame + avail;
|
||||
size -= avail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
AudioOutputUnitStop(audioUnit);
|
||||
AudioUnitUninitialize(audioUnit);
|
||||
AudioComponentInstanceDispose(audioUnit);
|
||||
bufferEmpty.Set();
|
||||
delete [] samples_temp;
|
||||
samples_temp = nullptr;
|
||||
}
|
||||
|
||||
static void recordCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp *inStartTime, UInt32 frameSize, const AudioStreamPacketDescription *dataFormat)
|
||||
{
|
||||
//DEBUG_LOG(AUDIO, "AudioQueue callback: wptr %d rptr %d bytes %d", (int)input_wptr, (int)input_rptr, inBuffer->mAudioDataByteSize);
|
||||
CoreAudioBackend *backend = (CoreAudioBackend *)inUserData;
|
||||
UInt32 size = inBuffer->mAudioDataByteSize;
|
||||
UInt32 freeSpace = (backend->input_rptr - backend->input_wptr - 2 + InputBufSize) % InputBufSize;
|
||||
if (size > freeSpace)
|
||||
{
|
||||
DEBUG_LOG(AUDIO, "coreaudio: record overrun %d bytes", size - freeSpace);
|
||||
size = freeSpace;
|
||||
}
|
||||
while (size != 0)
|
||||
{
|
||||
UInt32 chunk = std::min(size, (UInt32)(InputBufSize - backend->input_wptr));
|
||||
memcpy(backend->samples_input + backend->input_wptr, inBuffer->mAudioData, chunk);
|
||||
backend->input_wptr = (backend->input_wptr + chunk) % InputBufSize;
|
||||
size -= chunk;
|
||||
}
|
||||
AudioQueueEnqueueBuffer(backend->recordQueue, inBuffer, 0, nullptr);
|
||||
}
|
||||
|
||||
void termRecord() override
|
||||
{
|
||||
if (recordQueue != nullptr)
|
||||
{
|
||||
AudioQueueStop(recordQueue, true);
|
||||
AudioQueueDispose(recordQueue, true);
|
||||
recordQueue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
AudioStreamBasicDescription desc{};
|
||||
desc.mFormatID = kAudioFormatLinearPCM;
|
||||
desc.mSampleRate = (double)sampling_freq;
|
||||
desc.mChannelsPerFrame = 1;
|
||||
desc.mBitsPerChannel = 16;
|
||||
desc.mBytesPerPacket = desc.mBytesPerFrame = 2;
|
||||
desc.mFramesPerPacket = 1;
|
||||
desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||
desc.mReserved = 0;
|
||||
|
||||
OSStatus err = AudioQueueNewInput(&desc,
|
||||
recordCallback,
|
||||
this,
|
||||
nullptr,
|
||||
kCFRunLoopCommonModes,
|
||||
0,
|
||||
&recordQueue);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "AudioQueueNewInput failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioQueueBufferRef buffers[2];
|
||||
for (UInt32 i = 0; i < std::size(buffers) && err == noErr; i++)
|
||||
{
|
||||
err = AudioQueueAllocateBuffer(recordQueue, 480, &buffers[i]);
|
||||
if (err == noErr)
|
||||
err = AudioQueueEnqueueBuffer(recordQueue, buffers[i], 0, nullptr);
|
||||
}
|
||||
input_wptr = 0;
|
||||
input_rptr = 0;
|
||||
if (err == noErr)
|
||||
err = AudioQueueStart(recordQueue, nullptr);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "AudioQueue init failed: %d", err);
|
||||
termRecord();
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(AUDIO, "AudioQueue initialized - sample rate %f", desc.mSampleRate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 record(void* frame, u32 samples) override
|
||||
{
|
||||
// DEBUG_LOG(AUDIO, "coreaudio_record: wptr %d rptr %d", (int)input_wptr, (int)input_rptr);
|
||||
u32 size = samples * 2;
|
||||
while (size != 0)
|
||||
{
|
||||
u32 avail = (input_wptr - input_rptr + InputBufSize) % InputBufSize;
|
||||
if (avail == 0)
|
||||
{
|
||||
DEBUG_LOG(AUDIO, "coreaudio: record underrun %d bytes", size);
|
||||
break;
|
||||
}
|
||||
avail = std::min(avail, size);
|
||||
avail = std::min(avail, (u32)(InputBufSize - input_rptr));
|
||||
|
||||
memcpy(frame, &samples_input[input_rptr], avail);
|
||||
frame = (u8 *)frame + avail;
|
||||
input_rptr = (input_rptr + avail) % InputBufSize;
|
||||
size -= avail;
|
||||
}
|
||||
|
||||
return samples - size / 2;
|
||||
}
|
||||
};
|
||||
static CoreAudioBackend coreAudioBackend;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,242 +0,0 @@
|
||||
#include "build.h"
|
||||
#if defined(_WIN32) && !defined(TARGET_UWP)
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include <initguid.h>
|
||||
#include <dsound.h>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include "stdclass.h"
|
||||
#include "windows/comptr.h"
|
||||
#include "oslib/oslib.h"
|
||||
|
||||
HWND getNativeHwnd();
|
||||
#define verifyc(x) verify(!FAILED(x))
|
||||
|
||||
#ifdef __CRT_UUID_DECL
|
||||
__CRT_UUID_DECL(IDirectSoundBuffer8, 0x6825A449,0x7524,0x4D82,0x92,0x0F,0x50,0xE3,0x6A,0xB3,0xAB,0x1E);
|
||||
__CRT_UUID_DECL(IDirectSoundNotify, 0xB0210783,0x89cd,0x11d0,0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16);
|
||||
__CRT_UUID_DECL(IDirectSoundCaptureBuffer8, 0x00990DF4,0x0DBB,0x4872,0x83,0x3E,0x6D,0x30,0x3E,0x80,0xAE,0xB6);
|
||||
#else
|
||||
struct __declspec(uuid("{6825A449-7524-4D82-920F-50E36AB3AB1E}")) IDirectSoundBuffer8;
|
||||
struct __declspec(uuid("{B0210783-89cd-11d0-AF08-00A0C925CD16}")) IDirectSoundNotify;
|
||||
struct __declspec(uuid("{00990DF4-0DBB-4872-833E-6D303E80AEB6}")) IDirectSoundCaptureBuffer8;
|
||||
#endif
|
||||
|
||||
class DirectSoundBackend : public AudioBackend
|
||||
{
|
||||
ComPtr<IDirectSound8> dsound;
|
||||
ComPtr<IDirectSoundBuffer8> buffer;
|
||||
std::vector<HANDLE> notificationEvents;
|
||||
|
||||
ComPtr<IDirectSoundCapture8> dcapture;
|
||||
ComPtr<IDirectSoundCaptureBuffer8> capture_buffer;
|
||||
|
||||
std::atomic_bool audioThreadRunning;
|
||||
std::thread audioThread;
|
||||
cResetEvent pushWait;
|
||||
|
||||
static constexpr u32 SAMPLE_BYTES = SAMPLE_COUNT * 4;
|
||||
|
||||
RingBuffer ringBuffer;
|
||||
|
||||
u32 notificationOffset(int index) {
|
||||
return index * SAMPLE_BYTES;
|
||||
}
|
||||
|
||||
void audioThreadMain()
|
||||
{
|
||||
ThreadName _("FlyDirectSound");
|
||||
audioThreadRunning = true;
|
||||
while (true)
|
||||
{
|
||||
u32 rv = WaitForMultipleObjects(notificationEvents.size(), ¬ificationEvents[0], false, 100);
|
||||
|
||||
if (!audioThreadRunning)
|
||||
break;
|
||||
if (rv == WAIT_TIMEOUT || rv == WAIT_FAILED)
|
||||
continue;
|
||||
rv -= WAIT_OBJECT_0;
|
||||
|
||||
void *p1, *p2;
|
||||
DWORD sz1, sz2;
|
||||
|
||||
if (SUCCEEDED(buffer->Lock(notificationOffset(rv), SAMPLE_BYTES, &p1, &sz1, &p2, &sz2, 0)))
|
||||
{
|
||||
if (!ringBuffer.read((u8*)p1, sz1))
|
||||
memset(p1, 0, sz1);
|
||||
if (sz2 != 0)
|
||||
{
|
||||
if (!ringBuffer.read((u8*)p2, sz2))
|
||||
memset(p2, 0, sz2);
|
||||
}
|
||||
buffer->Unlock(p1, sz1, p2, sz2);
|
||||
pushWait.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
DirectSoundBackend()
|
||||
: AudioBackend("directsound", "Microsoft DirectSound") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
if (FAILED(DirectSoundCreate8(NULL, &dsound.get(), NULL))) {
|
||||
ERROR_LOG(AUDIO, "DirectSound8 initialization failed");
|
||||
return false;
|
||||
}
|
||||
if (FAILED(dsound->SetCooperativeLevel(getNativeHwnd(), DSSCL_PRIORITY))) {
|
||||
ERROR_LOG(AUDIO, "DirectSound8 SetCooperativeLevel failed");
|
||||
dsound.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up WAV format structure.
|
||||
WAVEFORMATEX wfx;
|
||||
memset(&wfx, 0, sizeof(WAVEFORMATEX));
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = 2;
|
||||
wfx.nSamplesPerSec = 44100;
|
||||
wfx.nBlockAlign = 4;
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.wBitsPerSample = 16;
|
||||
|
||||
// Set up DSBUFFERDESC structure.
|
||||
DSBUFFERDESC desc;
|
||||
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;
|
||||
desc.dwBufferBytes = SAMPLE_BYTES * 2;
|
||||
desc.lpwfxFormat = &wfx;
|
||||
|
||||
// Create the buffer
|
||||
ComPtr<IDirectSoundBuffer> buffer_;
|
||||
if (FAILED(dsound->CreateSoundBuffer(&desc, &buffer_.get(), 0))
|
||||
|| FAILED(buffer_.as(buffer)))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "DirectSound8 CreateSoundBuffer failed");
|
||||
dsound.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up notifications
|
||||
ComPtr<IDirectSoundNotify> bufferNotify;
|
||||
verifyc(buffer.as(bufferNotify));
|
||||
notificationEvents.clear();
|
||||
std::vector<DSBPOSITIONNOTIFY> posNotify;
|
||||
for (int i = 0; notificationOffset(i) < desc.dwBufferBytes; i++)
|
||||
{
|
||||
notificationEvents.push_back(CreateEvent(nullptr, false, false, nullptr));
|
||||
posNotify.push_back({ notificationOffset(i), notificationEvents.back() });
|
||||
}
|
||||
bufferNotify->SetNotificationPositions(posNotify.size(), &posNotify[0]);
|
||||
|
||||
// Clear the buffers
|
||||
void *p1, *p2;
|
||||
DWORD sz1, sz2;
|
||||
verifyc(buffer->Lock(0, desc.dwBufferBytes, &p1, &sz1, &p2, &sz2, 0));
|
||||
verify(p2 == nullptr);
|
||||
memset(p1, 0, sz1);
|
||||
verifyc(buffer->Unlock(p1, sz1, p2, sz2));
|
||||
ringBuffer.setCapacity(config::AudioBufferSize * 4);
|
||||
|
||||
// Start the thread
|
||||
audioThread = std::thread(&DirectSoundBackend::audioThreadMain, this);
|
||||
|
||||
// Play the buffer !
|
||||
if (FAILED(buffer->Play(0, 0, DSBPLAY_LOOPING)))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "DirectSound8 Play failed");
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(AUDIO, "DirectSound playback started");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
while (!ringBuffer.write((const u8 *)frame, samples * 4) && wait)
|
||||
pushWait.Wait();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
audioThreadRunning = false;
|
||||
audioThread.join();
|
||||
buffer->Stop();
|
||||
|
||||
for (HANDLE event : notificationEvents)
|
||||
CloseHandle(event);
|
||||
buffer.reset();
|
||||
dsound.reset();
|
||||
INFO_LOG(AUDIO, "DirectSound playback stopped");
|
||||
}
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
if (FAILED(DirectSoundCaptureCreate8(&DSDEVID_DefaultVoiceCapture, &dcapture.get(), NULL)))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "DirectSound capture device creation failed");
|
||||
return false;
|
||||
}
|
||||
HRESULT hr;
|
||||
WAVEFORMATEX wfx =
|
||||
{ WAVE_FORMAT_PCM, 1, sampling_freq, sampling_freq * 2, 2, 16, 0 };
|
||||
// wFormatTag, nChannels, nSamplesPerSec, nAvgBytesPerSec,
|
||||
// nBlockAlign, wBitsPerSample, cbSize
|
||||
|
||||
DSCBUFFERDESC dscbd;
|
||||
dscbd.dwSize = sizeof(DSCBUFFERDESC);
|
||||
dscbd.dwFlags = 0;
|
||||
dscbd.dwBufferBytes = 480 * 2;
|
||||
dscbd.dwReserved = 0;
|
||||
dscbd.lpwfxFormat = &wfx;
|
||||
dscbd.dwFXCount = 0;
|
||||
dscbd.lpDSCFXDesc = NULL;
|
||||
|
||||
ComPtr<IDirectSoundCaptureBuffer> pDSCB;
|
||||
if (FAILED(hr = dcapture->CreateCaptureBuffer(&dscbd, &pDSCB.get(), NULL)))
|
||||
{
|
||||
ERROR_LOG(AUDIO, "DirectSound capture buffer creation failed");
|
||||
dcapture.reset();
|
||||
return false;
|
||||
}
|
||||
pDSCB.as(capture_buffer);
|
||||
capture_buffer->Start(DSCBSTART_LOOPING);
|
||||
INFO_LOG(AUDIO, "DirectSound capture device and buffer created");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
{
|
||||
DWORD readPos;
|
||||
capture_buffer->GetCurrentPosition(NULL, &readPos);
|
||||
void *p1, *p2;
|
||||
DWORD p1bytes, p2bytes;
|
||||
capture_buffer->Lock(readPos, samples * 2, &p1, &p1bytes, &p2, &p2bytes, 0);
|
||||
memcpy(buffer, p1, p1bytes);
|
||||
if (p2bytes > 0)
|
||||
memcpy((u8 *)buffer + p1bytes, p2, p2bytes);
|
||||
capture_buffer->Unlock(p1, p1bytes, p2, p2bytes);
|
||||
return (p1bytes + p2bytes) / 2;
|
||||
}
|
||||
|
||||
void termRecord() override
|
||||
{
|
||||
if (!dcapture)
|
||||
return;
|
||||
capture_buffer->Stop();
|
||||
capture_buffer.reset();
|
||||
dcapture.reset();
|
||||
}
|
||||
};
|
||||
static DirectSoundBackend directSoundBackend;
|
||||
|
||||
#endif
|
||||
@@ -1,54 +0,0 @@
|
||||
#ifdef USE_LIBAO
|
||||
#include "audiostream.h"
|
||||
#include <ao/ao.h>
|
||||
|
||||
class LibAOBackend : public AudioBackend
|
||||
{
|
||||
ao_device *aodevice = nullptr;
|
||||
|
||||
public:
|
||||
LibAOBackend()
|
||||
: AudioBackend("libao", "libao") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
ao_initialize();
|
||||
|
||||
ao_sample_format aoformat {};
|
||||
aoformat.bits = 16;
|
||||
aoformat.channels = 2;
|
||||
aoformat.rate = 44100;
|
||||
aoformat.byte_format = AO_FMT_LITTLE;
|
||||
|
||||
aodevice = ao_open_live(ao_default_driver_id(), &aoformat, NULL); // Live output
|
||||
if (aodevice == nullptr)
|
||||
aodevice = ao_open_live(ao_driver_id("null"), &aoformat, NULL);
|
||||
if (aodevice == nullptr) {
|
||||
ERROR_LOG(AUDIO, "Cannot open libao driver");
|
||||
ao_shutdown();
|
||||
}
|
||||
|
||||
return aodevice != nullptr;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
if (aodevice != nullptr)
|
||||
ao_play(aodevice, (char*)frame, samples * 4);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
if (aodevice != nullptr)
|
||||
{
|
||||
ao_close(aodevice);
|
||||
aodevice = nullptr;
|
||||
ao_shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
static LibAOBackend libAOBackend;
|
||||
|
||||
#endif
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "audiostream.h"
|
||||
#include "oslib/i18n.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
class NullAudioBackend : public AudioBackend
|
||||
{
|
||||
using the_clock = std::chrono::high_resolution_clock;
|
||||
|
||||
public:
|
||||
NullAudioBackend()
|
||||
: AudioBackend("null", Tnop("No Audio")) {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
last_time = the_clock::time_point();
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
if (wait && last_time.time_since_epoch() != the_clock::duration::zero())
|
||||
{
|
||||
auto fduration = std::chrono::nanoseconds(1'000'000'000LL * samples / 44100);
|
||||
auto duration = fduration - (the_clock::now() - last_time);
|
||||
if (duration > std::chrono::nanoseconds::zero())
|
||||
std::this_thread::sleep_for(duration);
|
||||
if (duration < -std::chrono::milliseconds(67))
|
||||
// if ~4 frames ahead, reset time (fast forward detection)
|
||||
last_time = the_clock::now();
|
||||
else
|
||||
last_time += fduration;
|
||||
}
|
||||
else {
|
||||
last_time = the_clock::now();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
{
|
||||
memset(buffer, 0, samples * 2);
|
||||
return samples;
|
||||
}
|
||||
|
||||
std::string getName() const override {
|
||||
return i18n::Ts(name);
|
||||
}
|
||||
|
||||
private:
|
||||
the_clock::time_point last_time;
|
||||
};
|
||||
static NullAudioBackend nullBackend;
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef USE_OBOE
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include "stdclass.h"
|
||||
#include "oslib/i18n.h"
|
||||
#include <oboe/Oboe.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
class OboeBackend : AudioBackend
|
||||
{
|
||||
RingBuffer ringBuffer;
|
||||
cResetEvent pushWait;
|
||||
|
||||
std::shared_ptr<oboe::AudioStream> stream;
|
||||
std::shared_ptr<oboe::AudioStream> recordStream;
|
||||
|
||||
class AudioCallback : public oboe::AudioStreamDataCallback
|
||||
{
|
||||
public:
|
||||
AudioCallback(OboeBackend *backend) : backend(backend) {}
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) override
|
||||
{
|
||||
if (!backend->ringBuffer.read((u8 *)audioData, numFrames * 4))
|
||||
// underrun
|
||||
memset(audioData, 0, numFrames * 4);
|
||||
backend->pushWait.Set();
|
||||
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
OboeBackend *backend;
|
||||
};
|
||||
AudioCallback audioCallback;
|
||||
|
||||
class AudioErrorCallback : public oboe::AudioStreamErrorCallback
|
||||
{
|
||||
public:
|
||||
AudioErrorCallback(OboeBackend *backend) : backend(backend) {}
|
||||
|
||||
void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) override
|
||||
{
|
||||
// Only attempt to recover if init was successful
|
||||
if (backend->stream != nullptr)
|
||||
{
|
||||
WARN_LOG(AUDIO, "Audio device lost. Attempting to reopen the audio stream");
|
||||
// the oboe stream is already closed so make sure we don't close it twice
|
||||
backend->stream.reset();
|
||||
backend->term();
|
||||
backend->init();
|
||||
}
|
||||
}
|
||||
|
||||
OboeBackend *backend;
|
||||
};
|
||||
AudioErrorCallback errorCallback;
|
||||
|
||||
public:
|
||||
OboeBackend()
|
||||
: AudioBackend("Oboe", Tnop("Automatic AAudio / OpenSL selection")), audioCallback(this), errorCallback(this) {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
// Actual capacity is size-1 to avoid overrun so add one buffer
|
||||
ringBuffer.setCapacity((config::AudioBufferSize + SAMPLE_COUNT) * 4);
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
oboe::Result result = builder.setDirection(oboe::Direction::Output)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setSharingMode(oboe::SharingMode::Exclusive)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setChannelCount(oboe::ChannelCount::Stereo)
|
||||
->setSampleRate(44100)
|
||||
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
||||
->setFramesPerCallback(SAMPLE_COUNT)
|
||||
->setDataCallback(&audioCallback)
|
||||
->setErrorCallback(&errorCallback)
|
||||
->setUsage(oboe::Usage::Game)
|
||||
->openStream(stream);
|
||||
if (result != oboe::Result::OK)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Oboe open stream failed: %s", oboe::convertToText(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stream->getAudioApi() == oboe::AudioApi::AAudio && config::AudioBufferSize < 1764)
|
||||
{
|
||||
// Reduce internal buffer for low latency (< 40 ms)
|
||||
int bufSize = stream->getBufferSizeInFrames();
|
||||
int burst = stream->getFramesPerBurst();
|
||||
if (bufSize - burst > SAMPLE_COUNT)
|
||||
{
|
||||
while (bufSize - burst > SAMPLE_COUNT)
|
||||
bufSize -= burst;
|
||||
stream->setBufferSizeInFrames(bufSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream->requestStart();
|
||||
NOTICE_LOG(AUDIO, "Oboe driver started. stream capacity: %d frames, size: %d frames, frames/callback: %d, frames/burst: %d",
|
||||
stream->getBufferCapacityInFrames(), stream->getBufferSizeInFrames(),
|
||||
stream->getFramesPerCallback(), stream->getFramesPerBurst());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
NOTICE_LOG(AUDIO, "Oboe driver stopping");
|
||||
if (stream != nullptr)
|
||||
{
|
||||
// Don't let the AudioErrorCallback term/reinit while we are stopping
|
||||
// This won't prevent shit to hit the fan if it's already in the process
|
||||
// of doing so but this is a pretty rare event and happens on devices
|
||||
// that have audio issues already.
|
||||
auto localStream = stream;
|
||||
stream.reset();
|
||||
localStream->stop();
|
||||
localStream->close();
|
||||
}
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
while (!ringBuffer.write((const u8 *)frame, samples * 4) && wait)
|
||||
pushWait.Wait();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void termRecord() override
|
||||
{
|
||||
if (recordStream != nullptr)
|
||||
{
|
||||
recordStream->stop();
|
||||
recordStream->close();
|
||||
recordStream.reset();
|
||||
}
|
||||
NOTICE_LOG(AUDIO, "Oboe recorder stopped");
|
||||
}
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
oboe::AudioStreamBuilder builder;
|
||||
oboe::Result result = builder.setDirection(oboe::Direction::Input)
|
||||
->setPerformanceMode(oboe::PerformanceMode::None)
|
||||
->setSharingMode(oboe::SharingMode::Exclusive)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setChannelCount(oboe::ChannelCount::Mono)
|
||||
->setSampleRate(sampling_freq)
|
||||
->openStream(recordStream);
|
||||
if (result != oboe::Result::OK)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Oboe open record stream failed: %s", oboe::convertToText(result));
|
||||
return false;
|
||||
}
|
||||
recordStream->requestStart();
|
||||
NOTICE_LOG(AUDIO, "Oboe recorder started. stream capacity: %d frames",
|
||||
recordStream->getBufferCapacityInFrames());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 record(void *data, u32 samples) override
|
||||
{
|
||||
if (recordStream == nullptr)
|
||||
return 0;
|
||||
oboe::ResultWithValue<int32_t> result = recordStream->read(data, samples, 0);
|
||||
if (result == oboe::Result::ErrorDisconnected)
|
||||
{
|
||||
WARN_LOG(AUDIO, "Recording device lost. Attempting to reopen the audio stream");
|
||||
u32 sampleRate = recordStream->getSampleRate();
|
||||
termRecord();
|
||||
initRecord(sampleRate);
|
||||
}
|
||||
return std::max(0, result.value());
|
||||
}
|
||||
|
||||
|
||||
std::string getName() const override {
|
||||
return i18n::Ts(name);
|
||||
}
|
||||
};
|
||||
static OboeBackend oboeBackend;
|
||||
|
||||
#endif
|
||||
@@ -1,337 +0,0 @@
|
||||
#if USE_OMX
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include <IL/OMX_Broadcom.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PORT_INDEX 100
|
||||
#define OUTPUT_FREQ 44100
|
||||
|
||||
class OMXAudioBackend : public AudioBackend
|
||||
{
|
||||
OMX_HANDLETYPE omx_handle = nullptr;
|
||||
OMX_STATETYPE omx_state = OMX_StateInvalid;
|
||||
size_t audio_buffer_idx = 0;
|
||||
OMX_BUFFERHEADERTYPE** audio_buffers = nullptr;
|
||||
u32 latency_max = 0;
|
||||
u32 buffer_count = 0;
|
||||
u32 buffer_size = 0;
|
||||
u32 buffer_length = 0;
|
||||
pthread_mutex_t audio_lock;
|
||||
pthread_cond_t omx_state_cond;
|
||||
|
||||
static OMX_ERRORTYPE eventHandler(
|
||||
OMX_IN OMX_HANDLETYPE hComponent,
|
||||
OMX_IN OMX_PTR pAppData,
|
||||
OMX_IN OMX_EVENTTYPE eEvent,
|
||||
OMX_IN OMX_U32 nData1,
|
||||
OMX_IN OMX_U32 nData2,
|
||||
OMX_IN OMX_PTR pEventData)
|
||||
{
|
||||
OMXAudioBackend *backend = (OMXAudioBackend *)pAddData;
|
||||
pthread_mutex_lock(&backend->audio_lock);
|
||||
if(eEvent == OMX_EventCmdComplete && nData1 == OMX_CommandStateSet)
|
||||
{
|
||||
backend->omx_state = (OMX_STATETYPE)nData2;
|
||||
pthread_cond_signal(&backend->omx_state_cond);
|
||||
}
|
||||
pthread_mutex_unlock(&backend->audio_lock);
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
||||
static OMX_ERRORTYPE emptyBufferDone(
|
||||
OMX_IN OMX_HANDLETYPE hComponent,
|
||||
OMX_IN OMX_PTR pAppData,
|
||||
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
|
||||
{
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
||||
static OMX_ERRORTYPE fillBufferDone(
|
||||
OMX_OUT OMX_HANDLETYPE hComponent,
|
||||
OMX_OUT OMX_PTR pAppData,
|
||||
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
|
||||
{
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
||||
void waitForState(OMX_STATETYPE state)
|
||||
{
|
||||
pthread_mutex_lock(&audio_lock);
|
||||
while(omx_state != state)
|
||||
pthread_cond_wait(&omx_state_cond, &audio_lock);
|
||||
pthread_mutex_unlock(&audio_lock);
|
||||
}
|
||||
|
||||
u32 getLatency()
|
||||
{
|
||||
OMX_PARAM_U32TYPE param;
|
||||
memset(¶m, 0, sizeof(OMX_PARAM_U32TYPE));
|
||||
param.nSize = sizeof(OMX_PARAM_U32TYPE);
|
||||
param.nVersion.nVersion = OMX_VERSION;
|
||||
param.nPortIndex = PORT_INDEX;
|
||||
|
||||
OMX_ERRORTYPE error = OMX_GetConfig(omx_handle, OMX_IndexConfigAudioRenderingLatency, ¶m);
|
||||
if(error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to get OMX configuration (OMX_IndexConfigAudioRenderingLatency). Error 0x%X", error);
|
||||
|
||||
return param.nU32 * 1000 / OUTPUT_FREQ;
|
||||
}
|
||||
|
||||
public:
|
||||
OMXAudioBackend()
|
||||
: AudioBackend("omx", "OpenMAX IL") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
OMX_ERRORTYPE error;
|
||||
|
||||
error = OMX_Init();
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: OMX_Init() failed. Error 0x%X", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize settings
|
||||
latency_max = config::OmxAudioLatency;
|
||||
buffer_size = config::AudioBufferSize * 4;
|
||||
buffer_count = 2 + OUTPUT_FREQ * latency_max / (buffer_size * 1000);
|
||||
|
||||
OMX_CALLBACKTYPE callbacks;
|
||||
callbacks.EventHandler = eventHandler;
|
||||
callbacks.EmptyBufferDone = emptyBufferDone;
|
||||
callbacks.FillBufferDone = fillBufferDone;
|
||||
|
||||
error = OMX_GetHandle(&omx_handle, (OMX_STRING)"OMX.broadcom.audio_render", this, &callbacks);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: OMX_GetHandle() failed. Error 0x%X", error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
OMX_PARAM_PORTDEFINITIONTYPE param;
|
||||
memset(¶m, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
|
||||
param.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
|
||||
param.nVersion.nVersion = OMX_VERSION;
|
||||
param.nPortIndex = PORT_INDEX;
|
||||
param.nBufferSize = buffer_size;
|
||||
param.nBufferCountActual = buffer_count;
|
||||
param.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
|
||||
|
||||
error = OMX_SetParameter(omx_handle, OMX_IndexParamPortDefinition, ¶m);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: failed to set OMX_IndexParamPortDefinition. Error 0x%X", error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
OMX_AUDIO_PARAM_PCMMODETYPE pcm;
|
||||
memset(&pcm, 0, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE));
|
||||
pcm.nSize = sizeof(OMX_AUDIO_PARAM_PCMMODETYPE);
|
||||
pcm.nVersion.nVersion = OMX_VERSION;
|
||||
pcm.nPortIndex = PORT_INDEX;
|
||||
pcm.nChannels = 2;
|
||||
pcm.eNumData = OMX_NumericalDataSigned;
|
||||
pcm.eEndian = OMX_EndianLittle;
|
||||
pcm.nSamplingRate = OUTPUT_FREQ;
|
||||
pcm.bInterleaved = OMX_TRUE;
|
||||
pcm.nBitPerSample = 16;
|
||||
pcm.ePCMMode = OMX_AUDIO_PCMModeLinear;
|
||||
pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
|
||||
pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
|
||||
|
||||
error = OMX_SetParameter(omx_handle, OMX_IndexParamAudioPcm, &pcm);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: failed to set OMX_IndexParamAudioPcm. Error 0x%X", error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable all ports
|
||||
error = OMX_SendCommand(omx_handle, OMX_CommandPortDisable, PORT_INDEX, NULL);
|
||||
if (error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to do OMX_CommandPortDisable. Error 0x%X", error);
|
||||
|
||||
OMX_PORT_PARAM_TYPE param2;
|
||||
memset(¶m2, 0, sizeof(OMX_PORT_PARAM_TYPE));
|
||||
param2.nSize = sizeof(OMX_PORT_PARAM_TYPE);
|
||||
param2.nVersion.nVersion = OMX_VERSION;
|
||||
error = OMX_GetParameter(omx_handle, OMX_IndexParamOtherInit, ¶m2);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: failed to get OMX_IndexParamOtherInit. Error 0x%X", error);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(u32 i = 0; i < param2.nPorts; i++)
|
||||
{
|
||||
u32 port = param2.nStartPortNumber + i;
|
||||
error = OMX_SendCommand(omx_handle, OMX_CommandPortDisable, port, NULL);
|
||||
if(error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to do OMX_CommandPortDisable on port %u. Error 0x%X", port, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Go into idle state
|
||||
error = OMX_SendCommand(omx_handle, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: failed to set OMX_CommandStateSet. Error 0x%X", error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
waitForState(OMX_StateIdle);
|
||||
|
||||
// Check if we're in a state able to recieve buffers
|
||||
OMX_STATETYPE state;
|
||||
error = OMX_GetState(omx_handle, &state);
|
||||
if (error != OMX_ErrorNone || !(state == OMX_StateIdle || state == OMX_StateExecuting || state == OMX_StatePause))
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: state is incorrect. State 0x%X; Error 0x%X", state, error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create audio buffers
|
||||
INFO_LOG(AUDIO, "OMX: creating %u buffers", buffer_count);
|
||||
|
||||
// Enable port
|
||||
error = OMX_SendCommand(omx_handle, OMX_CommandPortEnable, PORT_INDEX, NULL);
|
||||
if (error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to do OMX_CommandPortEnable. Error 0x%X", error);
|
||||
|
||||
// Free audio buffers if they're allocated
|
||||
if (audio_buffers != NULL)
|
||||
delete[] audio_buffers;
|
||||
|
||||
// Allocate buffers
|
||||
audio_buffers = new OMX_BUFFERHEADERTYPE*[buffer_count];
|
||||
for (size_t i = 0; i < buffer_count; i++)
|
||||
{
|
||||
error = OMX_AllocateBuffer(omx_handle, &audio_buffers[i], PORT_INDEX, NULL, buffer_size);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: failed to allocate buffer[%u]. Error 0x%X", i, error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set state to executing
|
||||
error = OMX_SendCommand(omx_handle, OMX_CommandStateSet, OMX_StateExecuting, NULL);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: failed to do OMX_CommandStateSet. Error 0x%X", error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
waitForState(OMX_StateExecuting);
|
||||
|
||||
// Empty buffers
|
||||
for (size_t i = 0; i < buffer_count; i++)
|
||||
{
|
||||
memset(audio_buffers[i]->pBuffer, 0, buffer_size);
|
||||
audio_buffers[i]->nOffset = 0;
|
||||
audio_buffers[i]->nFilledLen = buffer_size;
|
||||
|
||||
error = OMX_EmptyThisBuffer(omx_handle, audio_buffers[i]);
|
||||
if(error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: failed to empty buffer[%u]. Error 0x%X", i, error);
|
||||
}
|
||||
|
||||
const char* output_device = "local";
|
||||
if (config::OmxAudioHdmi)
|
||||
output_device = (const char*)"hdmi";
|
||||
|
||||
// Set audio destination
|
||||
OMX_CONFIG_BRCMAUDIODESTINATIONTYPE ar_dest;
|
||||
memset(&ar_dest, 0, sizeof(ar_dest));
|
||||
ar_dest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE);
|
||||
ar_dest.nVersion.nVersion = OMX_VERSION;
|
||||
strcpy((char *)ar_dest.sName, output_device);
|
||||
error = OMX_SetConfig(omx_handle, OMX_IndexConfigBrcmAudioDestination, &ar_dest);
|
||||
if (error != OMX_ErrorNone)
|
||||
{
|
||||
WARN_LOG(AUDIO, "OMX: failed to set OMX configuration (OMX_IndexConfigBrcmAudioDestination). Error 0x%X", error);
|
||||
OMX_Deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
audio_buffer_idx = 0;
|
||||
buffer_length = 0;
|
||||
|
||||
INFO_LOG(AUDIO, "OMX: audio output to '%s'", ar_dest.sName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
if(audio_buffers == NULL)
|
||||
return 1;
|
||||
|
||||
size_t data_size = samples * 4;
|
||||
|
||||
while(data_size > 0)
|
||||
{
|
||||
size_t copy_size = std::min(buffer_size - buffer_length, data_size);
|
||||
|
||||
// Don't have more than maximum audio latency
|
||||
u32 latency = getLatency();
|
||||
if(latency > latency_max)
|
||||
{
|
||||
usleep((latency - latency_max) * 1000);
|
||||
}
|
||||
else if(latency == 0)
|
||||
{
|
||||
INFO_LOG(AUDIO, "OMX: underrun occurred");
|
||||
}
|
||||
|
||||
memcpy(audio_buffers[audio_buffer_idx]->pBuffer + buffer_length, frame, copy_size);
|
||||
buffer_length += copy_size;
|
||||
frame = (char *)frame + copy_size;
|
||||
|
||||
// Flush buffer and swap
|
||||
if(buffer_length >= buffer_size)
|
||||
{
|
||||
audio_buffers[audio_buffer_idx]->nOffset = 0;
|
||||
audio_buffers[audio_buffer_idx]->nFilledLen = buffer_size;
|
||||
|
||||
OMX_ERRORTYPE error = OMX_EmptyThisBuffer(omx_handle, audio_buffers[audio_buffer_idx]);
|
||||
if(error != OMX_ErrorNone)
|
||||
INFO_LOG(AUDIO, "OMX: failed to empty buffer[%u]. Error 0x%X", audio_buffer_idx, error);
|
||||
|
||||
audio_buffer_idx = (audio_buffer_idx + 1) % buffer_count;
|
||||
buffer_length = 0;
|
||||
}
|
||||
|
||||
data_size -= copy_size;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
OMX_ERRORTYPE error;
|
||||
|
||||
// Is there anything else that needs to be done for omx?
|
||||
|
||||
error = OMX_Deinit();
|
||||
if(error != OMX_ErrorNone)
|
||||
WARN_LOG(AUDIO, "OMX: OMX_Deinit() failed. Error 0x%X", error);
|
||||
|
||||
if(audio_buffers != NULL)
|
||||
{
|
||||
delete[] audio_buffers;
|
||||
audio_buffers = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
static OMXAudioBackend omxAudioBackend;
|
||||
|
||||
#endif
|
||||
@@ -1,151 +0,0 @@
|
||||
#ifdef USE_OSS
|
||||
#include "audiostream.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class OSSAudioBackend : public AudioBackend
|
||||
{
|
||||
int audioFD = -1;
|
||||
int recordFD = -1;
|
||||
|
||||
static int openDevice(int flags)
|
||||
{
|
||||
const char* path = "/dev/dsp";
|
||||
int fd = open(path, flags);
|
||||
|
||||
if (fd < 0)
|
||||
ERROR_LOG(AUDIO, "OSS: open(%s) failed: %s", path, strerror(errno));
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool setRate(int fd, int rate)
|
||||
{
|
||||
int tmp = rate;
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_SPEED) failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp != rate)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: sample rate unsupported: %d => %d", rate, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setChannels(int fd, int channels)
|
||||
{
|
||||
int tmp = channels;
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_CHANNELS) failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp != channels)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: channels unsupported: %d => %d", channels, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setFormat(int fd, int format)
|
||||
{
|
||||
int tmp = format;
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_SETFMT) failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp != format)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: sample format unsupported: %#.8x => %#.8x", format, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
OSSAudioBackend()
|
||||
: AudioBackend("oss", "Open Sound System") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
audioFD = openDevice(O_WRONLY);
|
||||
|
||||
if (audioFD < 0 || !setRate(audioFD, 44100) || !setChannels(audioFD, 2) || !setFormat(audioFD, AFMT_S16_LE))
|
||||
{
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
write(audioFD, frame, samples * 4);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
if (audioFD >= 0)
|
||||
close(audioFD);
|
||||
audioFD = -1;
|
||||
}
|
||||
|
||||
// recording untested
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
recordFD = openDevice(O_RDONLY);
|
||||
|
||||
if (recordFD < 0 || !setRate(recordFD, sampling_freq) || !setChannels(recordFD, 1) || !setFormat(recordFD, AFMT_S16_NE /* Native 16 bits */ ))
|
||||
{
|
||||
termRecord();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void termRecord() override
|
||||
{
|
||||
if (recordFD >= 0)
|
||||
close(recordFD);
|
||||
recordFD = -1;
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
{
|
||||
samples *= 2;
|
||||
int l = read(recordFD, buffer, samples);
|
||||
if (l < (int)samples)
|
||||
{
|
||||
if (l < 0)
|
||||
{
|
||||
INFO_LOG(AUDIO, "OSS: Recording error");
|
||||
l = 0;
|
||||
}
|
||||
memset((u8 *)buffer + l, 0, samples - l);
|
||||
}
|
||||
return l / 2;
|
||||
}
|
||||
};
|
||||
static OSSAudioBackend ossAudioBackend;
|
||||
|
||||
#endif
|
||||
@@ -1,252 +0,0 @@
|
||||
#ifdef USE_PULSEAUDIO
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
class PulseAudioBackend : public AudioBackend
|
||||
{
|
||||
pa_threaded_mainloop *mainloop = nullptr;
|
||||
pa_context *context = nullptr;
|
||||
pa_stream *stream = nullptr;
|
||||
pa_stream *record_stream = nullptr;
|
||||
|
||||
static void context_state_cb(pa_context *c, void *userdata)
|
||||
{
|
||||
PulseAudioBackend *backend = (PulseAudioBackend *)userdata;
|
||||
switch (pa_context_get_state(c))
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
case PA_CONTEXT_FAILED:
|
||||
pa_threaded_mainloop_signal(backend->mainloop, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
|
||||
{
|
||||
PulseAudioBackend *backend = (PulseAudioBackend *)userdata;
|
||||
pa_threaded_mainloop_signal(backend->mainloop, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
PulseAudioBackend()
|
||||
: AudioBackend("pulse", "PulseAudio") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
mainloop = pa_threaded_mainloop_new();
|
||||
if (!mainloop)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: pa_threaded_mainloop_new failed");
|
||||
return false;
|
||||
}
|
||||
context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "flycast");
|
||||
if (!context)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: pa_context_new failed");
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
pa_context_set_state_callback(context, context_state_cb, this);
|
||||
|
||||
if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: pa_context_connect failed");
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
if (pa_threaded_mainloop_start(mainloop) < 0)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: pa_threaded_mainloop_start failed");
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
|
||||
if (pa_context_get_state(context) != PA_CONTEXT_READY)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: context isn't ready");
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
pa_sample_spec spec{ PA_SAMPLE_S16LE, 44100, 2 };
|
||||
|
||||
stream = pa_stream_new(context, "audio", &spec, NULL);
|
||||
if (!stream)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: pa_stream_new failed");
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
//pa_stream_set_state_callback(stream, stream_state_cb, this);
|
||||
pa_stream_set_write_callback(stream, stream_request_cb, this);
|
||||
//pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, this);
|
||||
//pa_stream_set_underflow_callback(stream, underrun_update_cb, this);
|
||||
//pa_stream_set_buffer_attr_callback(stream, buffer_attr_cb, this);
|
||||
|
||||
pa_buffer_attr buffer_attr;
|
||||
buffer_attr.maxlength = -1;
|
||||
buffer_attr.tlength = pa_usec_to_bytes(config::AudioBufferSize * PA_USEC_PER_SEC / 44100, &spec);
|
||||
buffer_attr.prebuf = -1;
|
||||
buffer_attr.minreq = -1;
|
||||
buffer_attr.fragsize = -1;
|
||||
|
||||
if (pa_stream_connect_playback(stream, nullptr, &buffer_attr, PA_STREAM_ADJUST_LATENCY, nullptr, nullptr) < 0)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: pa_stream_connect_playback failed");
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
|
||||
if (pa_stream_get_state(stream) != PA_STREAM_READY)
|
||||
{
|
||||
WARN_LOG(AUDIO, "PulseAudio: stream isn't ready");
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
|
||||
const pa_buffer_attr *server_attr = pa_stream_get_buffer_attr(stream);
|
||||
if (server_attr)
|
||||
DEBUG_LOG(AUDIO, "PulseAudio: requested %d samples buffer, got %d", buffer_attr.tlength / 4, server_attr->tlength / 4);
|
||||
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
const u8 *buf = (const u8 *)frame;
|
||||
size_t size = samples * 4;
|
||||
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
while (size)
|
||||
{
|
||||
size_t writable = std::min(size, pa_stream_writable_size(stream));
|
||||
|
||||
if (writable)
|
||||
{
|
||||
pa_stream_write(stream, buf, writable, NULL, 0, PA_SEEK_RELATIVE);
|
||||
buf += writable;
|
||||
size -= writable;
|
||||
}
|
||||
else if (wait)
|
||||
pa_threaded_mainloop_wait(mainloop);
|
||||
else
|
||||
break;
|
||||
}
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
if (mainloop)
|
||||
pa_threaded_mainloop_stop(mainloop);
|
||||
|
||||
if (stream)
|
||||
{
|
||||
pa_stream_disconnect(stream);
|
||||
pa_stream_unref(stream);
|
||||
stream = nullptr;
|
||||
}
|
||||
if (context)
|
||||
{
|
||||
pa_context_disconnect(context);
|
||||
pa_context_unref(context);
|
||||
context = nullptr;
|
||||
}
|
||||
|
||||
if (mainloop)
|
||||
pa_threaded_mainloop_free(mainloop);
|
||||
mainloop = nullptr;
|
||||
}
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
pa_sample_spec spec{ PA_SAMPLE_S16LE, sampling_freq, 1 };
|
||||
|
||||
record_stream = pa_stream_new(context, "record", &spec, NULL);
|
||||
if (!record_stream)
|
||||
{
|
||||
INFO_LOG(AUDIO, "PulseAudio: pa_stream_new failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
|
||||
pa_buffer_attr buffer_attr;
|
||||
buffer_attr.fragsize = 240 * 2;
|
||||
buffer_attr.maxlength = buffer_attr.fragsize * 2;
|
||||
|
||||
if (pa_stream_connect_record(record_stream, nullptr, &buffer_attr, PA_STREAM_NOFLAGS) < 0)
|
||||
{
|
||||
INFO_LOG(AUDIO, "PulseAudio: pa_stream_connect_record failed");
|
||||
pa_stream_unref(record_stream);
|
||||
record_stream = nullptr;
|
||||
return false;
|
||||
}
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
INFO_LOG(AUDIO, "PulseAudio: Successfully initialized capture device");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void termRecord() override
|
||||
{
|
||||
if (record_stream != nullptr)
|
||||
{
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
pa_stream_disconnect(record_stream);
|
||||
pa_stream_unref(record_stream);
|
||||
record_stream = nullptr;
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
u32 record(void *buffer, u32 samples) override
|
||||
{
|
||||
if (record_stream == nullptr)
|
||||
return 0;
|
||||
pa_threaded_mainloop_lock(mainloop);
|
||||
const void *data;
|
||||
size_t size;
|
||||
if (pa_stream_peek(record_stream, &data, &size) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
DEBUG_LOG(AUDIO, "PulseAudio: pa_stream_peek error");
|
||||
return 0;
|
||||
}
|
||||
if (size == 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
return 0;
|
||||
}
|
||||
size = std::min((size_t)samples * 2, size);
|
||||
if (data != nullptr)
|
||||
memcpy(buffer, data, size);
|
||||
else
|
||||
memset(buffer, 0, size);
|
||||
pa_stream_drop(record_stream);
|
||||
pa_threaded_mainloop_unlock(mainloop);
|
||||
|
||||
return size / 2;
|
||||
}
|
||||
};
|
||||
static PulseAudioBackend pulseAudioBackend;
|
||||
|
||||
#endif
|
||||
@@ -1,243 +0,0 @@
|
||||
#if defined(USE_SDL_AUDIO)
|
||||
|
||||
#include <SDL.h>
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
class SDLAudioBackend : AudioBackend
|
||||
{
|
||||
SDL_AudioDeviceID audiodev {};
|
||||
bool needs_resampling = false;
|
||||
cResetEvent read_wait;
|
||||
std::mutex stream_mutex;
|
||||
uint32_t *sample_buffer;
|
||||
unsigned sample_buffer_size = 0;
|
||||
unsigned sample_count = 0;
|
||||
SDL_AudioCVT audioCvt;
|
||||
|
||||
SDL_AudioDeviceID recorddev {};
|
||||
u8 recordbuf[480 * 4];
|
||||
std::atomic<size_t> rec_read;
|
||||
std::atomic<size_t> rec_write;
|
||||
|
||||
static void audioCallback(void* userdata, Uint8* stream, int len)
|
||||
{
|
||||
SDLAudioBackend *backend = (SDLAudioBackend *)userdata;
|
||||
|
||||
backend->stream_mutex.lock();
|
||||
// Wait until there's enough samples to feed the kraken
|
||||
unsigned oslen = len / sizeof(uint32_t);
|
||||
unsigned islen = backend->needs_resampling ? std::ceil(oslen / backend->audioCvt.len_ratio) : oslen;
|
||||
|
||||
if (backend->sample_count < islen)
|
||||
{
|
||||
// No data, just output a bit of silence for the underrun
|
||||
memset(stream, 0, len);
|
||||
backend->stream_mutex.unlock();
|
||||
backend->read_wait.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!backend->needs_resampling) {
|
||||
// Just copy bytes for this case.
|
||||
memcpy(stream, &backend->sample_buffer[0], len);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_AudioCVT& cvt = backend->audioCvt;
|
||||
cvt.len = islen * sizeof(uint32_t);
|
||||
memcpy(cvt.buf, &backend->sample_buffer[0], cvt.len);
|
||||
SDL_ConvertAudio(&cvt);
|
||||
memcpy(stream, cvt.buf, cvt.len_cvt);
|
||||
}
|
||||
|
||||
// Move samples in the buffer and consume them
|
||||
memmove(&backend->sample_buffer[0], &backend->sample_buffer[islen], (backend->sample_count - islen) * sizeof(uint32_t));
|
||||
backend->sample_count -= islen;
|
||||
|
||||
backend->stream_mutex.unlock();
|
||||
backend->read_wait.Set();
|
||||
}
|
||||
|
||||
public:
|
||||
SDLAudioBackend()
|
||||
: AudioBackend("sdl2", "Simple DirectMedia Layer 2") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO))
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
ERROR_LOG(AUDIO, "SDL2 error initializing audio subsystem: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sample_buffer_size = std::max<u32>(SAMPLE_COUNT * 2, config::AudioBufferSize);
|
||||
sample_buffer = new uint32_t[sample_buffer_size]();
|
||||
sample_count = 0;
|
||||
|
||||
// Support 44.1KHz (native) but also upsampling to 48KHz
|
||||
SDL_AudioSpec wav_spec, out_spec;
|
||||
memset(&wav_spec, 0, sizeof(wav_spec));
|
||||
wav_spec.freq = 44100;
|
||||
wav_spec.format = AUDIO_S16;
|
||||
wav_spec.channels = 2;
|
||||
wav_spec.samples = SAMPLE_COUNT * 2; // Must be power of two
|
||||
wav_spec.callback = audioCallback;
|
||||
wav_spec.userdata = this;
|
||||
needs_resampling = false;
|
||||
|
||||
// Try 44.1KHz which should be faster since it's native.
|
||||
audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0);
|
||||
if (audiodev == 0)
|
||||
{
|
||||
INFO_LOG(AUDIO, "SDL2: SDL_OpenAudioDevice failed: %s", SDL_GetError());
|
||||
needs_resampling = true;
|
||||
wav_spec.freq = 48000;
|
||||
audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0);
|
||||
if (audiodev == 0)
|
||||
ERROR_LOG(AUDIO, "SDL2: SDL_OpenAudioDevice failed: %s", SDL_GetError());
|
||||
else
|
||||
{
|
||||
INFO_LOG(AUDIO, "SDL2: Using resampling to 48 KHz");
|
||||
int ret = SDL_BuildAudioCVT(&audioCvt, AUDIO_S16, 2, 44100, AUDIO_S16, 2, 48000);
|
||||
if (ret != 1 || audioCvt.needed == 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "SDL2: can't build audio converter: %s", SDL_GetError());
|
||||
SDL_CloseAudioDevice(audiodev);
|
||||
audiodev = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
audioCvt.buf = new u8[SAMPLE_COUNT * 2 * sizeof(uint32_t) * audioCvt.len_mult];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return audiodev != 0;
|
||||
}
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
// Unpause the device shall it be paused.
|
||||
if (SDL_GetAudioDeviceStatus(audiodev) != SDL_AUDIO_PLAYING)
|
||||
SDL_PauseAudioDevice(audiodev, 0);
|
||||
|
||||
// If wait, then wait for the buffer to be smaller than a certain size.
|
||||
stream_mutex.lock();
|
||||
if (wait) {
|
||||
while (sample_count + samples > sample_buffer_size) {
|
||||
stream_mutex.unlock();
|
||||
read_wait.Wait();
|
||||
stream_mutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy as many samples as possible, drop any remaining (this should not happen usually)
|
||||
unsigned free_samples = sample_buffer_size - sample_count;
|
||||
unsigned tocopy = samples < free_samples ? samples : free_samples;
|
||||
memcpy(&sample_buffer[sample_count], frame, tocopy * sizeof(uint32_t));
|
||||
sample_count += tocopy;
|
||||
stream_mutex.unlock();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void term() override
|
||||
{
|
||||
if (audiodev)
|
||||
{
|
||||
// Stop audio playback.
|
||||
SDL_PauseAudioDevice(audiodev, 1);
|
||||
read_wait.Set();
|
||||
SDL_CloseAudioDevice(audiodev);
|
||||
audiodev = SDL_AudioDeviceID();
|
||||
}
|
||||
delete [] sample_buffer;
|
||||
sample_buffer = nullptr;
|
||||
if (needs_resampling)
|
||||
{
|
||||
delete [] audioCvt.buf;
|
||||
audioCvt.buf = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void recordCallback(void *userdata, u8 *stream, int len)
|
||||
{
|
||||
SDLAudioBackend *backend = (SDLAudioBackend *)userdata;
|
||||
DEBUG_LOG(AUDIO, "SDL2: sdl2_record_cb len %d write %zd read %zd", len, (size_t)backend->rec_write, (size_t)backend->rec_read);
|
||||
while (len > 0)
|
||||
{
|
||||
size_t plen = std::min((size_t)len, sizeof(backend->recordbuf) - backend->rec_write);
|
||||
memcpy(&backend->recordbuf[backend->rec_write], stream, plen);
|
||||
len -= plen;
|
||||
backend->rec_write = (backend->rec_write + plen) % sizeof(backend->recordbuf);
|
||||
stream += plen;
|
||||
}
|
||||
}
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
rec_write = 0;
|
||||
rec_read = 0;
|
||||
|
||||
SDL_AudioSpec wav_spec, out_spec;
|
||||
memset(&wav_spec, 0, sizeof(wav_spec));
|
||||
wav_spec.freq = sampling_freq;
|
||||
wav_spec.format = AUDIO_S16;
|
||||
wav_spec.channels = 1;
|
||||
wav_spec.samples = 256; // Must be power of two
|
||||
wav_spec.callback = recordCallback;
|
||||
wav_spec.userdata = this;
|
||||
recorddev = SDL_OpenAudioDevice(NULL, 1, &wav_spec, &out_spec, 0);
|
||||
if (recorddev == 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "SDL2: Cannot open audio capture device: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
SDL_PauseAudioDevice(recorddev, 0);
|
||||
INFO_LOG(AUDIO, "SDL2: opened audio capture device");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void termRecord() override
|
||||
{
|
||||
if (recorddev != 0)
|
||||
{
|
||||
SDL_PauseAudioDevice(recorddev, 1);
|
||||
SDL_CloseAudioDevice(recorddev);
|
||||
recorddev = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 record(void* frame, u32 samples) override
|
||||
{
|
||||
u32 count = 0;
|
||||
samples *= 2;
|
||||
while (samples > 0)
|
||||
{
|
||||
u32 avail = std::min(rec_write - rec_read, sizeof(recordbuf) - rec_read);
|
||||
if (avail == 0)
|
||||
break;
|
||||
avail = std::min(avail, samples);
|
||||
memcpy((u8 *)frame + count, &recordbuf[rec_read], avail);
|
||||
rec_read = (rec_read + avail) % sizeof(recordbuf);
|
||||
samples -= avail;
|
||||
count += avail;
|
||||
}
|
||||
DEBUG_LOG(AUDIO, "SDL2: sdl2_record len %d ret %d write %zd read %zd", samples * 2, count, (size_t)rec_write, (size_t)rec_read);
|
||||
|
||||
return count / 2;
|
||||
}
|
||||
};
|
||||
static SDLAudioBackend sdlAudioBackend;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include "emulator.h"
|
||||
|
||||
static void registerForEvents();
|
||||
|
||||
struct SoundFrame { s16 l; s16 r; };
|
||||
|
||||
static SoundFrame Buffer[SAMPLE_COUNT];
|
||||
static u32 writePtr; // next sample index
|
||||
|
||||
static AudioBackend *currentBackend;
|
||||
std::vector<AudioBackend *> *AudioBackend::backends;
|
||||
|
||||
static bool audio_recording_started;
|
||||
static bool eight_khz;
|
||||
|
||||
AudioBackend *AudioBackend::getBackend(const std::string& slug)
|
||||
{
|
||||
if (backends == nullptr)
|
||||
return nullptr;
|
||||
if (slug == "auto")
|
||||
{
|
||||
// Prefer sdl2 if available and avoid the null driver
|
||||
AudioBackend *sdlBackend = nullptr;
|
||||
AudioBackend *autoBackend = nullptr;
|
||||
for (auto backend : *backends)
|
||||
{
|
||||
if (backend->slug == "sdl2")
|
||||
sdlBackend = backend;
|
||||
if (backend->slug != "null" && autoBackend == nullptr)
|
||||
autoBackend = backend;
|
||||
}
|
||||
if (sdlBackend != nullptr)
|
||||
autoBackend = sdlBackend;
|
||||
if (autoBackend == nullptr)
|
||||
autoBackend = backends->front();
|
||||
INFO_LOG(AUDIO, "Auto-selected audio backend \"%s\" (%s).", autoBackend->slug.c_str(), autoBackend->getName().c_str());
|
||||
|
||||
return autoBackend;
|
||||
}
|
||||
for (auto backend : *backends)
|
||||
{
|
||||
if (backend->slug == slug)
|
||||
return backend;
|
||||
}
|
||||
WARN_LOG(AUDIO, "WARNING: Audio backend \"%s\" not found!", slug.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WriteSample(s16 r, s16 l)
|
||||
{
|
||||
Buffer[writePtr].r = r * config::AudioVolume.dbPower();
|
||||
Buffer[writePtr].l = l * config::AudioVolume.dbPower();
|
||||
|
||||
if (++writePtr == SAMPLE_COUNT)
|
||||
{
|
||||
if (currentBackend != nullptr)
|
||||
currentBackend->push(Buffer, SAMPLE_COUNT, config::LimitFPS);
|
||||
writePtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void InitAudio()
|
||||
{
|
||||
registerForEvents();
|
||||
TermAudio();
|
||||
|
||||
std::string slug = config::AudioBackend;
|
||||
currentBackend = AudioBackend::getBackend(slug);
|
||||
if (currentBackend == nullptr && slug != "auto")
|
||||
{
|
||||
slug = "auto";
|
||||
currentBackend = AudioBackend::getBackend(slug);
|
||||
}
|
||||
if (currentBackend != nullptr)
|
||||
{
|
||||
INFO_LOG(AUDIO, "Initializing audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->getName().c_str());
|
||||
if (!currentBackend->init())
|
||||
{
|
||||
currentBackend = nullptr;
|
||||
if (slug != "auto")
|
||||
{
|
||||
WARN_LOG(AUDIO, "Audio driver %s failed to initialize. Defaulting to 'auto'", slug.c_str());
|
||||
slug = "auto";
|
||||
currentBackend = AudioBackend::getBackend(slug);
|
||||
if (!currentBackend->init())
|
||||
currentBackend = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentBackend == nullptr)
|
||||
{
|
||||
WARN_LOG(AUDIO, "Running without audio!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (audio_recording_started)
|
||||
{
|
||||
// Restart recording
|
||||
audio_recording_started = false;
|
||||
StartAudioRecording(eight_khz);
|
||||
}
|
||||
}
|
||||
|
||||
void TermAudio()
|
||||
{
|
||||
if (currentBackend == nullptr)
|
||||
return;
|
||||
|
||||
// Save recording state before stopping
|
||||
bool rec_started = audio_recording_started;
|
||||
StopAudioRecording();
|
||||
audio_recording_started = rec_started;
|
||||
currentBackend->term();
|
||||
INFO_LOG(AUDIO, "Terminating audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->getName().c_str());
|
||||
currentBackend = nullptr;
|
||||
}
|
||||
|
||||
void StartAudioRecording(bool eight_khz)
|
||||
{
|
||||
::eight_khz = eight_khz;
|
||||
if (currentBackend != nullptr)
|
||||
audio_recording_started = currentBackend->initRecord(eight_khz ? 8000 : 11025);
|
||||
else
|
||||
// might be called between TermAudio/InitAudio
|
||||
audio_recording_started = true;
|
||||
}
|
||||
|
||||
u32 RecordAudio(void *buffer, u32 samples)
|
||||
{
|
||||
if (!audio_recording_started || currentBackend == nullptr)
|
||||
return 0;
|
||||
return currentBackend->record(buffer, samples);
|
||||
}
|
||||
|
||||
void StopAudioRecording()
|
||||
{
|
||||
// might be called between TermAudio/InitAudio
|
||||
if (audio_recording_started && currentBackend != nullptr)
|
||||
currentBackend->termRecord();
|
||||
audio_recording_started = false;
|
||||
}
|
||||
|
||||
static void registerForEvents()
|
||||
{
|
||||
static bool done;
|
||||
if (done)
|
||||
return;
|
||||
done = true;
|
||||
// Empty the audio buffer when loading a state or terminating the game
|
||||
const auto& callback = [](Event, void *) {
|
||||
writePtr = 0;
|
||||
};
|
||||
EventManager::listen(Event::Terminate, callback);
|
||||
EventManager::listen(Event::LoadState, callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class AudioBackend
|
||||
{
|
||||
public:
|
||||
virtual ~AudioBackend() = default;
|
||||
|
||||
virtual bool init() = 0;
|
||||
virtual u32 push(const void *data, u32 frames, bool wait) = 0;
|
||||
virtual void term() {}
|
||||
|
||||
struct Option {
|
||||
std::string name;
|
||||
std::string caption;
|
||||
enum { integer, checkbox, list } type;
|
||||
|
||||
int minValue;
|
||||
int maxValue;
|
||||
std::vector<std::string> values;
|
||||
};
|
||||
virtual const Option *getOptions(int *count) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool initRecord(u32 sampling_freq) { return false; }
|
||||
virtual u32 record(void *, u32) { return 0; }
|
||||
virtual void termRecord() {}
|
||||
|
||||
std::string slug;
|
||||
virtual std::string getName() const { return name; }
|
||||
|
||||
static size_t getCount() { return backends == nullptr ? 0 : backends->size(); }
|
||||
static AudioBackend *getBackend(size_t index) { return backends == nullptr ? nullptr : (*backends)[index]; }
|
||||
static AudioBackend *getBackend(const std::string& slug);
|
||||
|
||||
protected:
|
||||
AudioBackend(const std::string& slug, const std::string& name)
|
||||
: slug(slug), name(name) {
|
||||
registerAudioBackend(this);
|
||||
}
|
||||
std::string name;
|
||||
|
||||
private:
|
||||
static void registerAudioBackend(AudioBackend *backend)
|
||||
{
|
||||
if (backends == nullptr)
|
||||
backends = new std::vector<AudioBackend *>();
|
||||
backends->push_back(backend);
|
||||
std::sort(backends->begin(), backends->end(), [](AudioBackend *b1, AudioBackend *b2) { return b1->slug < b2->slug; });
|
||||
}
|
||||
|
||||
static std::vector<AudioBackend *> *backends;
|
||||
};
|
||||
|
||||
void InitAudio();
|
||||
void TermAudio();
|
||||
void WriteSample(s16 right, s16 left);
|
||||
|
||||
void StartAudioRecording(bool eight_khz);
|
||||
u32 RecordAudio(void *buffer, u32 samples);
|
||||
void StopAudioRecording();
|
||||
|
||||
constexpr u32 SAMPLE_COUNT = 512; // AudioBackend::push() is always called with that many frames
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
std::vector<u8> buffer;
|
||||
std::atomic_int readCursor { 0 };
|
||||
std::atomic_int writeCursor { 0 };
|
||||
|
||||
u32 readSize() {
|
||||
return (u32)((writeCursor - readCursor + buffer.size()) % buffer.size());
|
||||
}
|
||||
u32 writeSize() {
|
||||
return (u32)((readCursor - writeCursor + buffer.size() - 1) % buffer.size());
|
||||
}
|
||||
|
||||
public:
|
||||
bool write(const u8 *data, u32 size)
|
||||
{
|
||||
if (size > writeSize())
|
||||
return false;
|
||||
u32 wc = writeCursor;
|
||||
u32 chunkSize = std::min<u32>(size, (u32)buffer.size() - wc);
|
||||
memcpy(&buffer[wc], data, chunkSize);
|
||||
wc = (wc + chunkSize) % buffer.size();
|
||||
size -= chunkSize;
|
||||
if (size > 0)
|
||||
{
|
||||
data += chunkSize;
|
||||
memcpy(&buffer[wc], data, size);
|
||||
wc = (wc + size) % buffer.size();
|
||||
}
|
||||
writeCursor = wc;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(u8 *data, u32 size)
|
||||
{
|
||||
if (size > readSize())
|
||||
return false;
|
||||
u32 rc = readCursor;
|
||||
u32 chunkSize = std::min<u32>(size, (u32)buffer.size() - rc);
|
||||
memcpy(data, &buffer[rc], chunkSize);
|
||||
rc = (rc + chunkSize) % buffer.size();
|
||||
size -= chunkSize;
|
||||
if (size > 0)
|
||||
{
|
||||
data += chunkSize;
|
||||
memcpy(data, &buffer[rc], size);
|
||||
rc = (rc + size) % buffer.size();
|
||||
}
|
||||
readCursor = rc;
|
||||
return true;
|
||||
}
|
||||
|
||||
void setCapacity(size_t size)
|
||||
{
|
||||
std::fill(buffer.begin(), buffer.end(), 0);
|
||||
buffer.resize(size);
|
||||
readCursor = 0;
|
||||
writeCursor = 0;
|
||||
}
|
||||
};
|
||||
+285
-61
@@ -1,50 +1,227 @@
|
||||
#pragma once
|
||||
//#define STRICT_MODE
|
||||
#ifndef STRICT_MODE
|
||||
/*
|
||||
reicast build options
|
||||
|
||||
Reicast can support a lot of stuff, and this is an attempt
|
||||
to organize the build time options
|
||||
|
||||
Option categories
|
||||
|
||||
BUILD_* - BUILD_COMPILER, etc...
|
||||
definitions about the build machine
|
||||
|
||||
HOST_*
|
||||
definitions about the host machine
|
||||
|
||||
FEAT_*
|
||||
definitions about the features that this build targets
|
||||
This is higly related to HOST_*, but it's for options that might
|
||||
or might not be avaiable depending on the target host, or that
|
||||
features that are irrelevant of the host
|
||||
|
||||
Eg, Alsa, Pulse Audio and OSS might sense as HOST dedinitions
|
||||
but it usually makes more sense to detect them as runtime. In
|
||||
that context, HOST_ALSA makes no sense because the host might
|
||||
or might not have alsa installed/ running
|
||||
|
||||
MMU makes no sense as a HOST definition at all, so it should
|
||||
be FEAT_HAS_MMU
|
||||
|
||||
TARGET_*
|
||||
A preconfigured default. Eg TARGET_WIN86.
|
||||
|
||||
Naming of options, option values, and how to use them
|
||||
|
||||
for options that makes sense to have a list of values
|
||||
{CATEGORY}_{OPTION}
|
||||
{OPTION}_{VALUE}
|
||||
|
||||
eg.
|
||||
BUILD_COMPILER == COMPILER_GCC, HOST_CPU != CPU_X64, ...
|
||||
|
||||
for options that are boolean
|
||||
{CATEGORY}_IS_{OPTION} or {CATEGORY}_HAS_{OPTION}
|
||||
|
||||
Evaluates to 0 or 1
|
||||
|
||||
If an configuration cannot be neatly split into a set of
|
||||
of orthogonal options, then it makes sense to break things
|
||||
to "sets" or have a hierarchy of options.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
In the beggining it made sense to have an audio backend
|
||||
per operating system. It made sense to have it depend
|
||||
on HOST_OS and seleect DirectSound or alsa.
|
||||
|
||||
// no option needed
|
||||
|
||||
Then, as android was introduced, which also uses OS_LINUX
|
||||
atm, the audio could have been made an option. It could be
|
||||
a HOST_* option, or FEAT_* one. I'd prefer FEAT_*.
|
||||
FEAT_* makes more sense as future wise we might want
|
||||
to support multiple backends.
|
||||
|
||||
FEAT_AUDIO_BACKEND
|
||||
AUDIO_BACKEND_NONE
|
||||
AUDIO_BACKEND_DS
|
||||
AUDIO_BACKEND_ALSA
|
||||
AUDIO_BACKEND_ANDROID
|
||||
|
||||
Used like
|
||||
#if FEAT_AUDIO_BACKEND == AUDIO_BACKEND_DS ....
|
||||
|
||||
At some point, we might have multiple audio backends that
|
||||
can be compiled in and autodetected/selected at runtime.
|
||||
In that case, it might make sense to have the options like
|
||||
|
||||
FEAT_HAS_ALSA
|
||||
FEAT_HAS_DIRECTSOUND
|
||||
FEAT_HAS_ANDROID_AUDIO
|
||||
|
||||
or
|
||||
FEAT_HAS_AUDIO_ALSA
|
||||
FEAT_HAS_AUDIO_DS
|
||||
FEAT_HAS_AUDIO_ANDROID
|
||||
|
||||
The none option might or might not make sense. In this
|
||||
case it can be removed, as it should always be avaiable.
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
General rule of thumb, don't overcomplicate things. Start
|
||||
with a simple option, and then make it more complicated
|
||||
as new uses apply (see the example above)
|
||||
|
||||
Don't use too long names, don't use too cryptic names.
|
||||
Most team developers should be able to understand or
|
||||
figure out most of the acronyms used.
|
||||
|
||||
Try to be consistent on the acronyms across all definitions
|
||||
|
||||
Code shouldn't depend on build level options whenever possible
|
||||
|
||||
Generally, the file should compile even if the option/module is
|
||||
disabled. This makes makefiles etc much easier to write
|
||||
|
||||
TARGET_* options should generally only be used in this file
|
||||
|
||||
The current source is *not* good example of these guidelines
|
||||
|
||||
We'll try to be smart and figure out some options/defaults on this file
|
||||
but this shouldn't get too complicated
|
||||
|
||||
|
||||
*/
|
||||
|
||||
//#define NO_MMU
|
||||
#define FAST_MMU
|
||||
#define USE_WINCE_HACK
|
||||
#endif
|
||||
|
||||
#define DC_PLATFORM_DREAMCAST 0
|
||||
#define DC_PLATFORM_DEV_UNIT 1
|
||||
#define DC_PLATFORM_NAOMI 2
|
||||
#define DC_PLATFORM_NAOMI2 3
|
||||
#define DC_PLATFORM_ATOMISWAVE 4
|
||||
#define DC_PLATFORM_SYSTEMSP 5
|
||||
#define DC_PLATFORM_MASK 7
|
||||
#define DC_PLATFORM_DREAMCAST 0 /* Works, for the most part */
|
||||
#define DC_PLATFORM_DEV_UNIT 1 /* This is missing hardware */
|
||||
#define DC_PLATFORM_NAOMI 2 /* Works, for the most part */
|
||||
#define DC_PLATFORM_NAOMI2 3 /* Needs to be done, 2xsh4 + 2xpvr + custom TNL */
|
||||
#define DC_PLATFORM_ATOMISWAVE 4 /* Works, for the most part */
|
||||
#define DC_PLATFORM_HIKARU 5 /* Needs to be done, 2xsh4, 2x aica , custom vpu */
|
||||
#define DC_PLATFORM_AURORA 6 /* Needs to be done, Uses newer 300 mhz sh4 + 150 mhz pvr mbx SoC */
|
||||
|
||||
|
||||
|
||||
//HOST_OS
|
||||
#define OS_WINDOWS 0x10000001
|
||||
#define OS_LINUX 0x10000002
|
||||
#define OS_DARWIN 0x10000003
|
||||
#define OS_IOS 0x10000004
|
||||
#define OS_ANDROID 0x10000005
|
||||
|
||||
#define OS_UWP 0x10000011
|
||||
#define OS_NSW_HOS 0x80000001
|
||||
#define OS_PS4_BSD 0x80000002
|
||||
|
||||
|
||||
//HOST_CPU
|
||||
#define CPU_X86 0x20000001
|
||||
#define CPU_ARM 0x20000002
|
||||
#define CPU_ARM64 0x20000003
|
||||
#define CPU_MIPS 0x20000003
|
||||
#define CPU_X64 0x20000004
|
||||
#define CPU_GENERIC 0x20000005 //used for pnacl, emscripten, etc
|
||||
#define CPU_PPC 0x20000006
|
||||
#define CPU_PPC64 0x20000007
|
||||
#define CPU_ARM64 0x20000008
|
||||
#define CPU_MIPS64 0x20000009
|
||||
|
||||
//BUILD_COMPILER
|
||||
#define COMPILER_VC 0x30000001
|
||||
#define COMPILER_GCC 0x30000002
|
||||
#define COMPILER_CLANG 0x30000003
|
||||
#define COMPILER_INTEL 0x30000004
|
||||
|
||||
//FEAT_SHREC, FEAT_AREC, FEAT_DSPREC
|
||||
#define DYNAREC_NONE 0x40000001
|
||||
#define DYNAREC_JIT 0x40000002
|
||||
#define DYNAREC_CPP 0x40000003
|
||||
|
||||
|
||||
//automatic
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define HOST_CPU CPU_X64
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
#define HOST_CPU CPU_X86
|
||||
#elif defined(__arm__) || defined (_M_ARM)
|
||||
#define HOST_CPU CPU_ARM
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define HOST_CPU CPU_ARM64
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#ifndef CMAKE_BUILD
|
||||
|
||||
#if defined(_WIN32) && !defined(TARGET_WIN86) && !defined(TARGET_WIN64)
|
||||
#if !defined(_M_AMD64) && !defined(__x86_64__)
|
||||
#define TARGET_WIN86
|
||||
#else
|
||||
#define TARGET_WIN64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_OS_SIMULATOR
|
||||
// iOS simulator
|
||||
#define TARGET_NO_REC
|
||||
#endif
|
||||
#if defined(TARGET_MAC) && HOST_CPU == CPU_ARM64
|
||||
#define TARGET_ARM_MAC
|
||||
#ifdef __GNUC__
|
||||
#define BUILD_COMPILER COMPILER_GCC
|
||||
#else
|
||||
#define BUILD_COMPILER COMPILER_VC
|
||||
#endif
|
||||
|
||||
//Targets
|
||||
#if defined(TARGET_WIN86)
|
||||
#define HOST_OS OS_WINDOWS
|
||||
#define HOST_CPU CPU_X86
|
||||
#elif defined(TARGET_WIN64)
|
||||
#define HOST_OS OS_WINDOWS
|
||||
#define HOST_CPU CPU_X64
|
||||
#elif defined(TARGET_PANDORA)
|
||||
#define HOST_OS OS_LINUX
|
||||
#define HOST_CPU CPU_ARM
|
||||
#elif defined(TARGET_LINUX_ARMELv7)
|
||||
#define HOST_OS OS_LINUX
|
||||
#define HOST_CPU CPU_ARM
|
||||
#elif defined(TARGET_LINUX_ARMv8)
|
||||
#define HOST_OS OS_LINUX
|
||||
#define HOST_CPU CPU_ARM64
|
||||
#elif defined(TARGET_LINUX_x86)
|
||||
#define HOST_OS OS_LINUX
|
||||
#define HOST_CPU CPU_X86
|
||||
#elif defined(TARGET_LINUX_x64)
|
||||
#define HOST_OS OS_LINUX
|
||||
#define HOST_CPU CPU_X64
|
||||
#elif defined(TARGET_LINUX_MIPS)
|
||||
#define HOST_OS OS_LINUX
|
||||
#define HOST_CPU CPU_MIPS
|
||||
#elif defined(TARGET_GCW0)
|
||||
#define HOST_OS OS_LINUX
|
||||
#define HOST_CPU CPU_MIPS
|
||||
#elif defined(TARGET_IPHONE)
|
||||
#define HOST_OS OS_DARWIN
|
||||
#define HOST_CPU CPU_ARM
|
||||
#elif defined(TARGET_OSX)
|
||||
#define HOST_OS OS_DARWIN
|
||||
#define HOST_CPU CPU_X86
|
||||
#elif defined(TARGET_OSX_X64)
|
||||
#define HOST_OS OS_DARWIN
|
||||
#define HOST_CPU CPU_X64
|
||||
#else
|
||||
#error Invalid Target: TARGET_* not defined
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_NO_REC)
|
||||
@@ -59,22 +236,35 @@
|
||||
#define FEAT_DSPREC DYNAREC_NONE
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#define FEAT_NO_RWX_PAGES
|
||||
#if defined(TARGET_NO_JIT)
|
||||
#define FEAT_SHREC DYNAREC_CPP
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
#define FEAT_DSPREC DYNAREC_NONE
|
||||
#endif
|
||||
|
||||
#endif // !CMAKE_BUILD
|
||||
|
||||
|
||||
#if defined(TARGET_NO_NIXPROF)
|
||||
#define FEAT_HAS_NIXPROF 0
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_NO_COREIO_HTTP)
|
||||
#define FEAT_HAS_COREIO_HTTP 0
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_SOFTREND)
|
||||
#define FEAT_HAS_SOFTREND 1
|
||||
#endif
|
||||
|
||||
//defaults
|
||||
|
||||
#ifndef FEAT_SHREC
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X86 || HOST_CPU == CPU_X64
|
||||
#define FEAT_SHREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_SHREC DYNAREC_NONE
|
||||
#endif
|
||||
#define FEAT_SHREC DYNAREC_JIT
|
||||
#endif
|
||||
|
||||
#ifndef FEAT_AREC
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X64
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64
|
||||
#define FEAT_AREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_AREC DYNAREC_NONE
|
||||
@@ -82,13 +272,54 @@
|
||||
#endif
|
||||
|
||||
#ifndef FEAT_DSPREC
|
||||
#if HOST_CPU == CPU_ARM || HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X86 || HOST_CPU == CPU_X64
|
||||
#if HOST_CPU == CPU_X86 || HOST_CPU == CPU_ARM64
|
||||
#define FEAT_DSPREC DYNAREC_JIT
|
||||
#else
|
||||
#define FEAT_DSPREC DYNAREC_NONE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FEAT_HAS_NIXPROF
|
||||
#ifndef _WIN32
|
||||
#define FEAT_HAS_NIXPROF 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FEAT_HAS_COREIO_HTTP
|
||||
#define FEAT_HAS_COREIO_HTTP 1
|
||||
#endif
|
||||
|
||||
#ifndef FEAT_HAS_SOFTREND
|
||||
#define FEAT_HAS_SOFTREND BUILD_COMPILER == COMPILER_VC //GCC wants us to enable sse4 globaly to enable intrins
|
||||
#endif
|
||||
|
||||
#if HOST_CPU == CPU_X64 || HOST_CPU == CPU_ARM64
|
||||
#define HOST_64BIT_CPU
|
||||
#endif
|
||||
|
||||
//Depricated build configs
|
||||
#ifdef HOST_NO_REC
|
||||
#error Dont use HOST_NO_REC
|
||||
#endif
|
||||
|
||||
#ifdef HOST_NO_AREC
|
||||
#error Dont use HOST_NO_AREC
|
||||
#endif
|
||||
|
||||
|
||||
// Compiler Related
|
||||
|
||||
#define COMPILER_VC_OR_CLANG_WIN32 ((BUILD_COMPILER == COMPILER_VC) || (BUILD_COMPILER == COMPILER_CLANG) && HOST_OS == OS_WINDOWS)
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define ATTR_USED __attribute__((used))
|
||||
#define ATTR_UNUSED __attribute__((used))
|
||||
#else
|
||||
#define ATTR_USED
|
||||
#define ATTR_UNUSED
|
||||
#endif
|
||||
|
||||
|
||||
// Some restrictions on FEAT_NO_RWX_PAGES
|
||||
#if defined(FEAT_NO_RWX_PAGES) && FEAT_SHREC == DYNAREC_JIT
|
||||
#if HOST_CPU != CPU_X64 && HOST_CPU != CPU_ARM64
|
||||
@@ -96,32 +327,25 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
|
||||
#define TARGET_UWP 1
|
||||
#endif
|
||||
#ifdef HAVE_D3D11
|
||||
#define USE_DX11
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(LIBRETRO)
|
||||
#define USE_GGPO
|
||||
#endif
|
||||
|
||||
#if !defined(__ANDROID__) && !defined(TARGET_IPHONE) && !defined(TARGET_UWP) \
|
||||
&& !defined(__SWITCH__) && !defined(LIBRETRO) && !defined(__NetBSD__) && !defined(__OpenBSD__)
|
||||
#define NAOMI_MULTIBOARD
|
||||
#endif
|
||||
|
||||
// TARGET PLATFORM
|
||||
#define GD_CLOCK 33868800 //GDROM XTAL -- 768fs
|
||||
#define AICA_CORE_CLOCK (GD_CLOCK * 4 / 3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs
|
||||
#define AICA_ARM_CLOCK (AICA_CORE_CLOCK / 2) //[22579200] AICA CORE -> PLL 2:1 -> ARM
|
||||
#define SH4_MAIN_CLOCK (200 * 1000 * 1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200)
|
||||
#define G2_BUS_CLOCK (25 * 1000 * 1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls
|
||||
|
||||
#if defined(GLES) && !defined(GLES3) && !defined(GLES2)
|
||||
#define RAM_SIZE_MAX (32*1024*1024)
|
||||
#define VRAM_SIZE_MAX (16*1024*1024)
|
||||
#define ARAM_SIZE_MAX (8*1024*1024)
|
||||
#define BUILD_DREAMCAST 1
|
||||
|
||||
#define GD_CLOCK 33868800 //GDROM XTAL -- 768fs
|
||||
|
||||
#define AICA_CORE_CLOCK (GD_CLOCK*4/3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs
|
||||
#define ADAC_CLOCK (AICA_CORE_CLOCK/2) //[11289600] 44100*256, AICA CORE -> PLL 4:1 -> ADAC -- 256fs
|
||||
#define AICA_ARM_CLOCK (AICA_CORE_CLOCK/2) //[22579200] AICA CORE -> PLL 2:1 -> ARM
|
||||
#define AICA_SDRAM_CLOCK (GD_CLOCK*2) //[67737600] GD-> PLL 2 -> SDRAM
|
||||
#define SH4_MAIN_CLOCK (200*1000*1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200)
|
||||
#define SH4_RAM_CLOCK (100*1000*1000) //[100000000] XTal(13.5) -> PLL (33.3) -> PLL 1:3 (100) , also suplied to HOLLY chip
|
||||
#define G2_BUS_CLOCK (25*1000*1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls
|
||||
|
||||
#if defined(GLES) && !defined(GLES3)
|
||||
// Only use GL ES 2.0 API functions
|
||||
#define GLES2
|
||||
#endif
|
||||
@@ -1,12 +0,0 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
option.h)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
cfg.cpp
|
||||
cfg.h
|
||||
cl.cpp
|
||||
ini.cpp
|
||||
ini.h
|
||||
option.cpp)
|
||||
endif()
|
||||
+166
-94
@@ -1,46 +1,93 @@
|
||||
/*
|
||||
Config file crap
|
||||
Supports various things, as virtual config entries and such crap
|
||||
Works surprisingly well considering how old it is ...
|
||||
*/
|
||||
|
||||
#define _CRT_SECURE_NO_DEPRECATE (1)
|
||||
#include <cerrno>
|
||||
#include "cfg.h"
|
||||
#include "ini.h"
|
||||
#include "stdclass.h"
|
||||
#include <cerrno>
|
||||
|
||||
namespace config
|
||||
static string cfgPath;
|
||||
static bool save_config = true;
|
||||
|
||||
static emucfg::ConfigFile cfgdb;
|
||||
static string game_id;
|
||||
static bool has_game_specific_config = false;
|
||||
|
||||
void savecfgf()
|
||||
{
|
||||
|
||||
static std::string cfgPath;
|
||||
static bool autoSaving = true;
|
||||
static IniFile cfgdb;
|
||||
|
||||
static void saveFile()
|
||||
{
|
||||
hostfs::File* cfgfile = hostfs::storage().openFile(cfgPath.c_str(), "wt");
|
||||
if (!cfgfile) {
|
||||
FILE* cfgfile = fopen(cfgPath.c_str(),"wt");
|
||||
if (!cfgfile)
|
||||
{
|
||||
WARN_LOG(COMMON, "Error: Unable to open file '%s' for saving", cfgPath.c_str());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
cfgdb.save(cfgfile);
|
||||
delete cfgfile;
|
||||
fclose(cfgfile);
|
||||
}
|
||||
}
|
||||
|
||||
static void autoSave() {
|
||||
if (autoSaving)
|
||||
saveFile();
|
||||
void cfgSaveStr(const wchar * Section, const wchar * Key, const wchar * String)
|
||||
{
|
||||
const std::string section(Section);
|
||||
const std::string key(Key);
|
||||
const std::string value(String);
|
||||
if (cfgHasGameSpecificConfig())
|
||||
{
|
||||
if (cfgdb.get(section, key, "") == value)
|
||||
// Same value as main config: delete entry
|
||||
cfgdb.delete_entry(game_id, key);
|
||||
else
|
||||
cfgdb.set(game_id, key, value);
|
||||
}
|
||||
else
|
||||
cfgdb.set(section, key, value);
|
||||
if(save_config)
|
||||
{
|
||||
savecfgf();
|
||||
}
|
||||
}
|
||||
//New config code
|
||||
|
||||
bool open()
|
||||
/*
|
||||
I want config to be really flexible .. so , here is the new implementation :
|
||||
|
||||
Functions :
|
||||
cfgLoadInt : Load an int , if it does not exist save the default value to it and return it
|
||||
cfgSaveInt : Save an int
|
||||
cfgLoadStr : Load a str , if it does not exist save the default value to it and return it
|
||||
cfgSaveStr : Save a str
|
||||
cfgExists : Returns true if the Section:Key exists. If Key is null , it retuns true if Section exists
|
||||
|
||||
Config parameters can be read from the config file , and can be given at the command line
|
||||
-cfg section:key=value -> defines a value at command line
|
||||
If a cfgSave* is made on a value defined by command line , then the command line value is replaced by it
|
||||
|
||||
cfg values set by command line are not written to the cfg file , unless a cfgSave* is used
|
||||
*/
|
||||
|
||||
///////////////////////////////
|
||||
/*
|
||||
** This will verify there is a working file @ ./szIniFn
|
||||
** - if not present, it will write defaults
|
||||
*/
|
||||
|
||||
bool cfgOpen()
|
||||
{
|
||||
if (get_writable_config_path("").empty())
|
||||
// Config dir not set (android onboarding)
|
||||
return false;
|
||||
|
||||
const char* filename = "emu.cfg";
|
||||
std::string config_path_read = get_readonly_config_path(filename);
|
||||
const char* filename = "/emu.cfg";
|
||||
string config_path_read = get_readonly_config_path(filename);
|
||||
cfgPath = get_writable_config_path(filename);
|
||||
|
||||
hostfs::File* cfgfile = hostfs::storage().openFile(config_path_read.c_str(), "rt");
|
||||
if (cfgfile != nullptr) {
|
||||
cfgdb.load(cfgfile);
|
||||
delete cfgfile;
|
||||
FILE* cfgfile = fopen(config_path_read.c_str(),"r");
|
||||
if(cfgfile != NULL) {
|
||||
cfgdb.parse(cfgfile);
|
||||
fclose(cfgfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -51,85 +98,110 @@ bool open()
|
||||
{
|
||||
// Config file didn't exist
|
||||
INFO_LOG(COMMON, "Creating new empty config file at '%s'", cfgPath.c_str());
|
||||
saveFile();
|
||||
savecfgf();
|
||||
}
|
||||
else
|
||||
{
|
||||
// There was some other error (may be a permissions problem or something like that)
|
||||
save_config = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void saveStr(const std::string& section, const std::string& key, const std::string& value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
|
||||
std::string loadStr(const std::string& section, const std::string& key, const std::string& def) {
|
||||
return cfgdb.getString(section, key, def);
|
||||
}
|
||||
|
||||
void saveInt(const std::string& section, const std::string& key, int value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
|
||||
int loadInt(const std::string& section, const std::string& key, int def) {
|
||||
return cfgdb.getInt(section, key, def);
|
||||
}
|
||||
|
||||
int64_t loadInt64(const std::string& section, const std::string& key, int64_t def) {
|
||||
return cfgdb.getInt64(section, key, def);
|
||||
}
|
||||
void saveInt64(const std::string& section, const std::string& key, int64_t value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
|
||||
void saveBool(const std::string& section, const std::string& key, bool value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
|
||||
bool loadBool(const std::string& section, const std::string& key, bool def) {
|
||||
return cfgdb.getBool(section, key, def);
|
||||
}
|
||||
|
||||
void setTransient(const std::string& section, const std::string& key, const std::string& value) {
|
||||
cfgdb.set(section, key, value, true);
|
||||
}
|
||||
|
||||
bool isTransient(const std::string& section, const std::string& key) {
|
||||
return cfgdb.isTransient(section, key);
|
||||
}
|
||||
|
||||
bool hasSection(const std::string& section) {
|
||||
return cfgdb.hasSection(section);
|
||||
}
|
||||
|
||||
void deleteSection(const std::string& section) {
|
||||
cfgdb.deleteSection(section);
|
||||
}
|
||||
|
||||
void deleteEntry(const std::string& section, const std::string& key) {
|
||||
cfgdb.deleteEntry(section, key);
|
||||
}
|
||||
|
||||
void setAutoSave(bool autoSaving)
|
||||
//Implementations of the interface :)
|
||||
//Section must be set
|
||||
//If key is 0 , it looks for the section
|
||||
//0 : not found
|
||||
//1 : found section , key was 0
|
||||
//2 : found section & key
|
||||
s32 cfgExists(const wchar * Section, const wchar * Key)
|
||||
{
|
||||
config::autoSaving = autoSaving;
|
||||
if (autoSaving)
|
||||
saveFile();
|
||||
if(cfgdb.has_entry(string(Section), string(Key)))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (cfgdb.has_section(string(Section)) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void saveFloat(const std::string& section, const std::string& key, float value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
float loadFloat(const std::string& section, const std::string& key, float def) {
|
||||
return cfgdb.getFloat(section, key, def);
|
||||
void cfgLoadStr(const wchar * Section, const wchar * Key, wchar * Return,const wchar* Default)
|
||||
{
|
||||
string value = cfgdb.get(Section, Key, Default);
|
||||
// FIXME: Buffer overflow possible
|
||||
strcpy(Return, value.c_str());
|
||||
}
|
||||
|
||||
std::vector<std::string> getEntries(const std::string& section) {
|
||||
return cfgdb.getEntryNames(section);
|
||||
string cfgLoadStr(const wchar * Section, const wchar * Key, const wchar* Default)
|
||||
{
|
||||
std::string v = cfgdb.get(string(Section), string(Key), string(Default));
|
||||
if (cfgHasGameSpecificConfig())
|
||||
v = cfgdb.get(game_id, string(Key), v);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
//These are helpers , mainly :)
|
||||
void cfgSaveInt(const wchar * Section, const wchar * Key, s32 Int)
|
||||
{
|
||||
char str[32];
|
||||
sprintf(str, "%d", Int);
|
||||
cfgSaveStr(Section, Key, str);
|
||||
}
|
||||
|
||||
s32 cfgLoadInt(const wchar * Section, const wchar * Key,s32 Default)
|
||||
{
|
||||
s32 v = cfgdb.get_int(string(Section), string(Key), Default);
|
||||
if (cfgHasGameSpecificConfig())
|
||||
v = cfgdb.get_int(game_id, string(Key), v);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void cfgSaveBool(const wchar * Section, const wchar * Key, bool BoolValue)
|
||||
{
|
||||
cfgSaveStr(Section, Key, BoolValue ? "yes" : "no");
|
||||
}
|
||||
|
||||
bool cfgLoadBool(const wchar * Section, const wchar * Key,bool Default)
|
||||
{
|
||||
bool v = cfgdb.get_bool(string(Section), string(Key), Default);
|
||||
if (cfgHasGameSpecificConfig())
|
||||
v = cfgdb.get_bool(game_id, string(Key), v);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void cfgSetVirtual(const wchar * Section, const wchar * Key, const wchar * String)
|
||||
{
|
||||
cfgdb.set(string(Section), string(Key), string(String), true);
|
||||
}
|
||||
|
||||
void cfgSetGameId(const char *id)
|
||||
{
|
||||
game_id = id;
|
||||
}
|
||||
|
||||
const char *cfgGetGameId()
|
||||
{
|
||||
return game_id.c_str();
|
||||
}
|
||||
|
||||
bool cfgHasGameSpecificConfig()
|
||||
{
|
||||
return has_game_specific_config || cfgdb.has_section(game_id);
|
||||
}
|
||||
|
||||
void cfgMakeGameSpecificConfig()
|
||||
{
|
||||
has_game_specific_config = true;
|
||||
}
|
||||
|
||||
void cfgDeleteGameSpecificConfig()
|
||||
{
|
||||
has_game_specific_config = false;
|
||||
cfgdb.delete_section(game_id);
|
||||
}
|
||||
+23
-26
@@ -1,31 +1,28 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace config
|
||||
{
|
||||
#include "types.h"
|
||||
/*
|
||||
** cfg* prototypes, if you pass NULL to a cfgSave* it will wipe out the section
|
||||
** } if you pass it to lpKey it will wipe out that particular entry
|
||||
** } if you add write to something it will create it if its not present
|
||||
** } ** Strings passed to LoadStr should be MAX_PATH in size ! **
|
||||
*/
|
||||
|
||||
bool open();
|
||||
int loadInt(const std::string& section, const std::string& key, int def = 0);
|
||||
void saveInt(const std::string& section, const std::string& key, int value);
|
||||
int64_t loadInt64(const std::string& section, const std::string& key, int64_t def = 0);
|
||||
void saveInt64(const std::string& section, const std::string& key, int64_t value);
|
||||
std::string loadStr(const std::string& section, const std::string& key, const std::string& def = {});
|
||||
void saveStr(const std::string& section, const std::string& key, const std::string& value);
|
||||
bool loadBool(const std::string& section, const std::string& key, bool def = false);
|
||||
void saveBool(const std::string& section, const std::string& key, bool value);
|
||||
float loadFloat(const std::string& section, const std::string& key, float def = 0.f);
|
||||
void saveFloat(const std::string& section, const std::string& key, float value);
|
||||
void setTransient(const std::string& section, const std::string& key, const std::string& value);
|
||||
bool isTransient(const std::string& section, const std::string& key);
|
||||
bool cfgOpen();
|
||||
s32 cfgLoadInt(const wchar * lpSection, const wchar * lpKey,s32 Default);
|
||||
void cfgSaveInt(const wchar * lpSection, const wchar * lpKey, s32 Int);
|
||||
void cfgLoadStr(const wchar * lpSection, const wchar * lpKey, wchar * lpReturn,const wchar* lpDefault);
|
||||
string cfgLoadStr(const wchar * Section, const wchar * Key, const wchar* Default);
|
||||
void cfgSaveStr(const wchar * lpSection, const wchar * lpKey, const wchar * lpString);
|
||||
void cfgSaveBool(const wchar * Section, const wchar * Key, bool BoolValue);
|
||||
bool cfgLoadBool(const wchar * Section, const wchar * Key,bool Default);
|
||||
s32 cfgExists(const wchar * Section, const wchar * Key);
|
||||
void cfgSetVirtual(const wchar * lpSection, const wchar * lpKey, const wchar * lpString);
|
||||
|
||||
void parseCommandLine(int argc, const char * const argv[]);
|
||||
bool ParseCommandLine(int argc,wchar* argv[]);
|
||||
|
||||
void setAutoSave(bool autoSave);
|
||||
bool hasSection(const std::string& section);
|
||||
void deleteSection(const std::string& section);
|
||||
void deleteEntry(const std::string& section, const std::string& key);
|
||||
|
||||
std::vector<std::string> getEntries(const std::string& section);
|
||||
}
|
||||
void cfgSetGameId(const char *id);
|
||||
const char *cfgGetGameId();
|
||||
bool cfgHasGameSpecificConfig();
|
||||
void cfgMakeGameSpecificConfig();
|
||||
void cfgDeleteGameSpecificConfig();
|
||||
+140
-209
@@ -1,226 +1,157 @@
|
||||
/*
|
||||
Copyright 2025 flyinghead
|
||||
Command line parsing
|
||||
~yay~
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
Nothing too interesting here, really
|
||||
*/
|
||||
#include "cfg/cfg.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
namespace config
|
||||
{
|
||||
#include "cfg/cfg.h"
|
||||
|
||||
static void usage(const char *exe)
|
||||
wchar* trim_ws(wchar* str)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [option]... [<rom path>]\n", exe);
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr, "-config section:key=value,... set a transient config value.\n");
|
||||
fprintf(stderr, " Transient config values won't be saved to emu.cfg.\n");
|
||||
fprintf(stderr, "-help display this help\n");
|
||||
}
|
||||
if (str==0 || strlen(str)==0)
|
||||
return 0;
|
||||
|
||||
static void parseConfigOption(const std::string& str)
|
||||
{
|
||||
char inQuote = '\0';
|
||||
std::string section, key, value;
|
||||
int step = 0; // section, key, value
|
||||
for (char c : str)
|
||||
while(*str)
|
||||
{
|
||||
if (inQuote != '\0' && c == inQuote)
|
||||
{
|
||||
inQuote = false;
|
||||
step = 0;
|
||||
setTransient(section, key, value);
|
||||
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
|
||||
section.clear();
|
||||
key.clear();
|
||||
value.clear();
|
||||
continue;
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case ':':
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
section = trim_trailing_ws(section);
|
||||
if (section.empty()) {
|
||||
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
|
||||
return;
|
||||
}
|
||||
step = 1;
|
||||
break;
|
||||
case 1:
|
||||
key += c;
|
||||
break;
|
||||
case 2:
|
||||
value += c;
|
||||
break;
|
||||
}
|
||||
if (!isspace(*str))
|
||||
break;
|
||||
case '=':
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
|
||||
return;
|
||||
case 1:
|
||||
key = trim_trailing_ws(key);
|
||||
if (key.empty()) {
|
||||
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
|
||||
return;
|
||||
}
|
||||
step = 2;
|
||||
break;
|
||||
case 2:
|
||||
value += c;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
case '"':
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
section += c;
|
||||
break;
|
||||
case 1:
|
||||
key += c;
|
||||
break;
|
||||
case 2:
|
||||
if (inQuote == '\0') {
|
||||
inQuote = c;
|
||||
value.clear();
|
||||
}
|
||||
else
|
||||
value += c;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
// ignore consecutive commas
|
||||
break;
|
||||
case 1:
|
||||
key += c;
|
||||
break;
|
||||
case 2:
|
||||
if (inQuote != '\0') {
|
||||
value += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
step = 0;
|
||||
setTransient(section, key, value);
|
||||
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
|
||||
section.clear();
|
||||
key.clear();
|
||||
value.clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
// Ignore leading spaces. Inner spaces are kept (game ids such as "INITIAL D Ver.2")
|
||||
// and trailing ones are trimmed at the ':' and '=' separators
|
||||
if (!section.empty())
|
||||
section += c;
|
||||
break;
|
||||
case 1:
|
||||
if (!key.empty())
|
||||
key += c;
|
||||
break;
|
||||
case 2:
|
||||
value += c;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch (step)
|
||||
{
|
||||
case 0:
|
||||
section += c;
|
||||
break;
|
||||
case 1:
|
||||
key += c;
|
||||
break;
|
||||
case 2:
|
||||
value += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
str++;
|
||||
}
|
||||
if (step == 2) {
|
||||
setTransient(section, key, value);
|
||||
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void parseCommandLine(int argc, const char * const argv[])
|
||||
{
|
||||
settings.content.path.clear();
|
||||
const char *exe = argv[0];
|
||||
for (int i = 1; i < argc; i++)
|
||||
size_t l=strlen(str);
|
||||
|
||||
if (l==0)
|
||||
return 0;
|
||||
|
||||
while(l>0)
|
||||
{
|
||||
if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
|
||||
usage(exe);
|
||||
exit(0);
|
||||
}
|
||||
if (!strcmp(argv[i], "-config") || !strcmp(argv[i], "--config"))
|
||||
{
|
||||
if (i < argc - 1)
|
||||
parseConfigOption(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
// macOS
|
||||
if (!strncmp(argv[i], "-NSDocumentRevisions", 20)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (argv[i][0] == '-') {
|
||||
WARN_LOG(COMMON, "Ignoring unknown command line option '%s'", argv[i]);
|
||||
continue;
|
||||
}
|
||||
std::string extension = get_file_extension(argv[i]);
|
||||
if (extension == "cdi" || extension == "chd"
|
||||
|| extension == "gdi"|| extension == "cue")
|
||||
{
|
||||
INFO_LOG(COMMON, "Using '%s' as CD image", argv[i]);
|
||||
settings.content.path = argv[i];
|
||||
}
|
||||
else if (extension == "elf")
|
||||
{
|
||||
INFO_LOG(COMMON, "Using '%s' as reios elf file", argv[i]);
|
||||
setTransient("config", "bios.UseReios", "yes");
|
||||
settings.content.path = argv[i];
|
||||
}
|
||||
else {
|
||||
INFO_LOG(COMMON, "Using '%s' as rom", argv[i]);
|
||||
settings.content.path = argv[i];
|
||||
}
|
||||
if (i < argc - 1)
|
||||
WARN_LOG(COMMON, "Rest of command line ignored: '%s'...", argv[i + 1]);
|
||||
break;
|
||||
if (!isspace(str[l-1]))
|
||||
break;
|
||||
str[l-1]=0;
|
||||
l--;
|
||||
}
|
||||
|
||||
if (l==0)
|
||||
return 0;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
int setconfig(wchar** arg,int cl)
|
||||
{
|
||||
int rv=0;
|
||||
for(;;)
|
||||
{
|
||||
if (cl<1)
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid number of parameters, format is section:key=value");
|
||||
return rv;
|
||||
}
|
||||
wchar* sep=strstr(arg[1],":");
|
||||
if (sep==0)
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value", arg[1]);
|
||||
return rv;
|
||||
}
|
||||
wchar* value=strstr(sep+1,"=");
|
||||
if (value==0)
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value", arg[1]);
|
||||
return rv;
|
||||
}
|
||||
|
||||
*sep++=0;
|
||||
*value++=0;
|
||||
|
||||
wchar* sect=trim_ws(arg[1]);
|
||||
wchar* key=trim_ws(sep);
|
||||
value=trim_ws(value);
|
||||
|
||||
if (sect==0 || key==0)
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid parameter, format is section:key=value");
|
||||
return rv;
|
||||
}
|
||||
|
||||
const wchar* constval = value;
|
||||
if (constval==0)
|
||||
constval="";
|
||||
INFO_LOG(COMMON, "Virtual cfg %s:%s=%s", sect, key, value);
|
||||
|
||||
cfgSetVirtual(sect,key,value);
|
||||
rv++;
|
||||
|
||||
if (cl>=3 && stricmp(arg[2],",")==0)
|
||||
{
|
||||
cl-=2;
|
||||
arg+=2;
|
||||
rv++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int showhelp(wchar** arg,int cl)
|
||||
{
|
||||
NOTICE_LOG(COMMON, "Available commands:");
|
||||
|
||||
NOTICE_LOG(COMMON, "-config section:key=value [, ..]: add a virtual config value\n Virtual config values won't be saved to the .cfg file\n unless a different value is written to em\nNote :\n You can specify many settings in the xx:yy=zz , gg:hh=jj , ...\n format.The spaces between the values and ',' are needed.");
|
||||
NOTICE_LOG(COMMON, "-help: show help info");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ParseCommandLine(int argc,wchar* argv[])
|
||||
{
|
||||
cfgSetVirtual("config", "image", "");
|
||||
int cl=argc-2;
|
||||
wchar** arg=argv+1;
|
||||
while(cl>=0)
|
||||
{
|
||||
if (stricmp(*arg,"-help")==0 || stricmp(*arg,"--help")==0)
|
||||
{
|
||||
showhelp(arg,cl);
|
||||
return true;
|
||||
}
|
||||
else if (stricmp(*arg,"-config")==0 || stricmp(*arg,"--config")==0)
|
||||
{
|
||||
int as=setconfig(arg,cl);
|
||||
cl-=as;
|
||||
arg+=as;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* extension = strrchr(*arg, '.');
|
||||
|
||||
if (extension
|
||||
&& (stricmp(extension, ".cdi") == 0 || stricmp(extension, ".chd") == 0
|
||||
|| stricmp(extension, ".gdi") == 0 || stricmp(extension, ".cue") == 0))
|
||||
{
|
||||
INFO_LOG(COMMON, "Using '%s' as cd image", *arg);
|
||||
cfgSetVirtual("config", "image", *arg);
|
||||
}
|
||||
else if (extension && stricmp(extension, ".elf") == 0)
|
||||
{
|
||||
INFO_LOG(COMMON, "Using '%s' as reios elf file", *arg);
|
||||
cfgSetVirtual("config", "bios.UseReios", "yes");
|
||||
cfgSetVirtual("reios", "ElfFile", *arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(COMMON, "Using '%s' as rom", *arg);
|
||||
cfgSetVirtual("config", "image", *arg);
|
||||
}
|
||||
}
|
||||
arg++;
|
||||
cl--;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
+262
-281
@@ -1,322 +1,303 @@
|
||||
/*
|
||||
Copyright 2025 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "ini.h"
|
||||
#include "types.h"
|
||||
#include "stdclass.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace config {
|
||||
wchar* trim_ws(wchar* str);
|
||||
|
||||
const IniFile::Entry *IniFile::getEntry(const std::string& sectionName, const std::string& name) const
|
||||
namespace emucfg {
|
||||
|
||||
/* ConfigEntry */
|
||||
|
||||
const string& ConfigEntry::get_string() const
|
||||
{
|
||||
auto it = sections.find(sectionName);
|
||||
if (it == sections.end())
|
||||
return nullptr;
|
||||
const Section& section = it->second;
|
||||
auto it2 = section.entries.find(name);
|
||||
if (it2 == section.entries.end())
|
||||
return nullptr;
|
||||
return &it2->second;
|
||||
return this->value;
|
||||
}
|
||||
|
||||
const std::string *IniFile::getRaw(const std::string& section, const std::string& name) const
|
||||
int ConfigEntry::get_int()
|
||||
{
|
||||
const Entry *entry = getEntry(section, name);
|
||||
if (entry == nullptr)
|
||||
return nullptr;
|
||||
if (entry->transient)
|
||||
return &entry->transientValue;
|
||||
if (strstr(this->value.c_str(), "0x") != NULL)
|
||||
{
|
||||
return strtol(this->value.c_str(), NULL, 16);
|
||||
}
|
||||
else
|
||||
return &entry->value;
|
||||
{
|
||||
return atoi(this->value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& IniFile::getString(const std::string& section, const std::string& entry, const std::string& defaultValue) const
|
||||
bool ConfigEntry::get_bool()
|
||||
{
|
||||
const std::string *pValue = getRaw(section, entry);
|
||||
if (pValue == nullptr)
|
||||
return defaultValue;
|
||||
else
|
||||
return *pValue;
|
||||
}
|
||||
|
||||
bool IniFile::getBool(const std::string& section, const std::string& entry, bool defaultValue) const
|
||||
{
|
||||
const std::string *pValue = getRaw(section, entry);
|
||||
if (pValue == nullptr)
|
||||
return defaultValue;
|
||||
if (!stricmp("yes", pValue->c_str())
|
||||
|| !stricmp("true", pValue->c_str())
|
||||
|| !stricmp("on", pValue->c_str())
|
||||
|| !stricmp("1", pValue->c_str()))
|
||||
if (stricmp(this->value.c_str(), "yes") == 0 ||
|
||||
stricmp(this->value.c_str(), "true") == 0 ||
|
||||
stricmp(this->value.c_str(), "on") == 0 ||
|
||||
stricmp(this->value.c_str(), "1") == 0)
|
||||
{
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasHexPrefix(const std::string& s)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while (pos < s.length() - 1 && std::isspace((int8_t)s[pos]))
|
||||
pos++;
|
||||
if (pos + 1 >= s.length())
|
||||
return false;
|
||||
return (s[pos] == '0' && (s[pos + 1] == 'x' || s[pos + 1] == 'X'));
|
||||
}
|
||||
|
||||
int IniFile::getInt(const std::string& section, const std::string& entry, int defaultValue) const
|
||||
{
|
||||
const std::string *pValue = getRaw(section, entry);
|
||||
if (pValue == nullptr)
|
||||
return defaultValue;
|
||||
std::istringstream ss(*pValue);
|
||||
ss.imbue(std::locale::classic());
|
||||
if (hasHexPrefix(*pValue))
|
||||
ss.setf(std::ios_base::hex, std::ios_base::basefield);
|
||||
else
|
||||
ss.setf(std::ios_base::dec, std::ios_base::basefield);
|
||||
unsigned value = defaultValue;
|
||||
ss >> value;
|
||||
return (int)value;
|
||||
}
|
||||
|
||||
int64_t IniFile::getInt64(const std::string& section, const std::string& entry, int64_t defaultValue) const
|
||||
{
|
||||
const std::string *pValue = getRaw(section, entry);
|
||||
if (pValue == nullptr)
|
||||
return defaultValue;
|
||||
std::istringstream ss(*pValue);
|
||||
ss.imbue(std::locale::classic());
|
||||
if (hasHexPrefix(*pValue))
|
||||
ss.setf(std::ios_base::hex, std::ios_base::basefield);
|
||||
else
|
||||
ss.setf(std::ios_base::dec, std::ios_base::basefield);
|
||||
int64_t value = defaultValue;
|
||||
ss >> value;
|
||||
return value;
|
||||
}
|
||||
|
||||
float IniFile::getFloat(const std::string& section, const std::string& entry, float defaultValue) const
|
||||
{
|
||||
const std::string *pValue = getRaw(section, entry);
|
||||
if (pValue == nullptr || pValue->empty())
|
||||
return defaultValue;
|
||||
std::istringstream ss(*pValue);
|
||||
ss.imbue(std::locale::classic());
|
||||
float value = defaultValue;
|
||||
ss >> value;
|
||||
return value;
|
||||
}
|
||||
|
||||
void IniFile::setRaw(const std::string& sectionName, const std::string& entryName, const std::string& value, bool transient)
|
||||
{
|
||||
Section& section = sections[sectionName];
|
||||
Entry& entry = section.entries[entryName];
|
||||
if (entry.name.empty())
|
||||
entry.name = entryName;
|
||||
if (transient) {
|
||||
entry.transientValue = value;
|
||||
entry.transient = true;
|
||||
}
|
||||
else {
|
||||
entry.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string handleEscapeSeq(const std::string& s)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(s.length());
|
||||
for (size_t i = 0; i < s.length(); i++)
|
||||
else
|
||||
{
|
||||
char c = s[i];
|
||||
if (c == '\\' && i < s.length() - 1)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ConfigSection */
|
||||
|
||||
bool ConfigSection::has_entry(const std::string& name)
|
||||
{
|
||||
return (this->entries.count(name) == 1);
|
||||
}
|
||||
|
||||
ConfigEntry* ConfigSection::get_entry(const std::string& name)
|
||||
{
|
||||
if(this->has_entry(name))
|
||||
{
|
||||
return &this->entries[name];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ConfigSection::set(const std::string& name, const std::string& value)
|
||||
{
|
||||
ConfigEntry new_entry = { value };
|
||||
this->entries[name] = new_entry;
|
||||
}
|
||||
|
||||
void ConfigSection::delete_entry(const std::string& name)
|
||||
{
|
||||
this->entries.erase(name);
|
||||
}
|
||||
|
||||
/* ConfigFile */
|
||||
|
||||
ConfigSection* ConfigFile::add_section(const std::string& name, bool is_virtual)
|
||||
{
|
||||
ConfigSection new_section;
|
||||
if (is_virtual)
|
||||
{
|
||||
this->virtual_sections.insert(std::make_pair(name, new_section));
|
||||
return &this->virtual_sections[name];
|
||||
}
|
||||
this->sections.insert(std::make_pair(name, new_section));
|
||||
return &this->sections[name];
|
||||
}
|
||||
|
||||
bool ConfigFile::has_section(const std::string& name)
|
||||
{
|
||||
return (this->virtual_sections.count(name) == 1 || this->sections.count(name) == 1);
|
||||
}
|
||||
|
||||
bool ConfigFile::has_entry(const std::string& section_name, const std::string& entry_name)
|
||||
{
|
||||
ConfigSection* section = this->get_section(section_name, true);
|
||||
if ((section != NULL) && section->has_entry(entry_name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
section = this->get_section(section_name, false);
|
||||
return ((section != NULL) && section->has_entry(entry_name));
|
||||
}
|
||||
|
||||
ConfigSection* ConfigFile::get_section(const std::string& name, bool is_virtual)
|
||||
{
|
||||
if(is_virtual)
|
||||
{
|
||||
if (this->virtual_sections.count(name) == 1)
|
||||
{
|
||||
switch (s[++i])
|
||||
{
|
||||
case 'n':
|
||||
ret += '\n';
|
||||
break;
|
||||
case 't':
|
||||
ret += '\t';
|
||||
break;
|
||||
case 'r':
|
||||
ret += '\r';
|
||||
break;
|
||||
case 'f':
|
||||
ret += '\f';
|
||||
break;
|
||||
case '\\':
|
||||
ret += '\\';
|
||||
break;
|
||||
default:
|
||||
WARN_LOG(COMMON, "Unrecognized escape sequence [\\%c] in [%s]", s[i], s.c_str());
|
||||
ret += '\\';
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret += c;
|
||||
return &this->virtual_sections[name];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
else
|
||||
{
|
||||
if (this->sections.count(name) == 1)
|
||||
{
|
||||
return &this->sections[name];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void IniFile::load(const std::string& data, bool cEscape)
|
||||
ConfigEntry* ConfigFile::get_entry(const std::string& section_name, const std::string& entry_name)
|
||||
{
|
||||
std::istringstream fss(data);
|
||||
int cline = 1;
|
||||
std::string curSection;
|
||||
for (std::string line; std::getline(fss, line); cline++)
|
||||
ConfigSection* section = this->get_section(section_name, true);
|
||||
if(section != NULL && section->has_entry(entry_name))
|
||||
{
|
||||
size_t pos = 0;
|
||||
while (pos < line.length() && std::isspace((int8_t)line[pos]))
|
||||
pos++;
|
||||
if (pos == line.length() || line[pos] == ';')
|
||||
// Ignore empty lines and comments
|
||||
return section->get_entry(entry_name);
|
||||
}
|
||||
|
||||
section = this->get_section(section_name, false);
|
||||
if(section != NULL)
|
||||
{
|
||||
return section->get_entry(entry_name);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
string ConfigFile::get(const std::string& section_name, const std::string& entry_name, const std::string& default_value)
|
||||
{
|
||||
ConfigEntry* entry = this->get_entry(section_name, entry_name);
|
||||
if (entry == NULL)
|
||||
{
|
||||
return default_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return entry->get_string();
|
||||
}
|
||||
}
|
||||
|
||||
int ConfigFile::get_int(const std::string& section_name, const std::string& entry_name, int default_value)
|
||||
{
|
||||
ConfigEntry* entry = this->get_entry(section_name, entry_name);
|
||||
if (entry == NULL)
|
||||
{
|
||||
return default_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return entry->get_int();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigFile::get_bool(const std::string& section_name, const std::string& entry_name, bool default_value)
|
||||
{
|
||||
ConfigEntry* entry = this->get_entry(section_name, entry_name);
|
||||
if (entry == NULL)
|
||||
{
|
||||
return default_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return entry->get_bool();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigFile::set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual)
|
||||
{
|
||||
ConfigSection* section = this->get_section(section_name, is_virtual);
|
||||
if(section == NULL)
|
||||
{
|
||||
section = this->add_section(section_name, is_virtual);
|
||||
}
|
||||
section->set(entry_name, value);
|
||||
}
|
||||
|
||||
void ConfigFile::set_int(const std::string& section_name, const std::string& entry_name, int value, bool is_virtual)
|
||||
{
|
||||
std::stringstream str_value;
|
||||
str_value << value;
|
||||
this->set(section_name, entry_name, str_value.str(), is_virtual);
|
||||
}
|
||||
|
||||
void ConfigFile::set_bool(const std::string& section_name, const std::string& entry_name, bool value, bool is_virtual)
|
||||
{
|
||||
string str_value = (value ? "yes" : "no");
|
||||
this->set(section_name, entry_name, str_value, is_virtual);
|
||||
}
|
||||
|
||||
void ConfigFile::parse(FILE* file)
|
||||
{
|
||||
if(file == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
char line[512];
|
||||
char current_section[512] = { '\0' };
|
||||
int cline = 0;
|
||||
while(file && !feof(file))
|
||||
{
|
||||
if (fgets(line, 512, file) == NULL || feof(file))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cline++;
|
||||
|
||||
if (strlen(line) < 3)
|
||||
{
|
||||
continue;
|
||||
if (line[pos] == '[')
|
||||
}
|
||||
|
||||
if (line[strlen(line)-1] == '\r' ||
|
||||
line[strlen(line)-1] == '\n')
|
||||
{
|
||||
// Section
|
||||
size_t end = line.find(']', ++pos);
|
||||
if (end == line.npos) {
|
||||
WARN_LOG(COMMON, "Missing ']' character - ignoring line %d: %s", cline, line.c_str());
|
||||
continue;
|
||||
}
|
||||
curSection = line.substr(pos, end - pos);
|
||||
if (cEscape)
|
||||
curSection = handleEscapeSeq(curSection);
|
||||
line[strlen(line)-1] = '\0';
|
||||
}
|
||||
|
||||
char* tl = trim_ws(line);
|
||||
|
||||
if (tl[0] == '[' && tl[strlen(tl)-1] == ']')
|
||||
{
|
||||
tl[strlen(tl)-1] = '\0';
|
||||
|
||||
// FIXME: Data loss if buffer is too small
|
||||
strncpy(current_section, tl+1, sizeof(current_section));
|
||||
current_section[sizeof(current_section) - 1] = '\0';
|
||||
|
||||
trim_ws(current_section);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Entry
|
||||
pos = line.find('=', pos);
|
||||
if (pos == line.npos) {
|
||||
WARN_LOG(COMMON, "Malformed entry - ignoring line %d: %s", cline, line.c_str());
|
||||
continue;
|
||||
}
|
||||
std::string entry = trim_ws(line.substr(0, pos), " \t");
|
||||
if (entry.empty()) {
|
||||
WARN_LOG(COMMON, "Invalid empty entry name - ignoring line %d: %s", cline, line.c_str());
|
||||
continue;
|
||||
}
|
||||
std::string value = line.substr(pos + 1);
|
||||
// Space characters before and after the equal sign are for readability, and not part of the entry name or value
|
||||
if (!value.empty() && value[0] == ' ')
|
||||
value = value.substr(1);
|
||||
if (!value.empty() && value[0] == '"')
|
||||
if (strlen(current_section) == 0)
|
||||
{
|
||||
// quoted value
|
||||
size_t end = value.rfind('"');
|
||||
if (end == value.npos || end == 0)
|
||||
end = value.length();
|
||||
value = value.substr(1, end - 1);
|
||||
continue; //no open section
|
||||
}
|
||||
if (cEscape) {
|
||||
entry = handleEscapeSeq(entry);
|
||||
value = handleEscapeSeq(value);
|
||||
}
|
||||
set(curSection, entry, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IniFile::load(hostfs::File *file, bool cEscape)
|
||||
{
|
||||
std::string data;
|
||||
for (;;)
|
||||
{
|
||||
char buffer[4096];
|
||||
int n = file->read(buffer, 1, sizeof(buffer));
|
||||
if (n <= 0)
|
||||
break;
|
||||
data += std::string(buffer, buffer + n);
|
||||
}
|
||||
load(data, cEscape);
|
||||
}
|
||||
char* separator = strstr(tl, "=");
|
||||
|
||||
void IniFile::save(std::string& data) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
for (const auto& [sectionName, section] : sections)
|
||||
{
|
||||
bool sectionSaved = sectionName.empty();
|
||||
for (const auto& [name, entry] : section.entries)
|
||||
{
|
||||
if (entry.transient && entry.value.empty())
|
||||
// Don't save transient-only values
|
||||
if (!separator)
|
||||
{
|
||||
WARN_LOG(COMMON, "Malformed entry on config - ignoring @ %d(%s)", cline, tl);
|
||||
continue;
|
||||
if (!sectionSaved) {
|
||||
ss << '[' << sectionName << ']' << std::endl;
|
||||
sectionSaved = true;
|
||||
}
|
||||
ss << name << " = " << entry.value << std::endl;
|
||||
|
||||
*separator = '\0';
|
||||
|
||||
char* name = trim_ws(tl);
|
||||
char* value = trim_ws(separator + 1);
|
||||
if (name == NULL || value == NULL)
|
||||
{
|
||||
//printf("Malformed entry on config - ignoring @ %d(%s)\n",cline, tl);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->set(string(current_section), string(name), string(value));
|
||||
}
|
||||
}
|
||||
ss << std::endl;
|
||||
}
|
||||
data = ss.str();
|
||||
}
|
||||
|
||||
void IniFile::save(hostfs::File *file) const
|
||||
void ConfigFile::save(FILE* file)
|
||||
{
|
||||
std::string data;
|
||||
save(data);
|
||||
file->write(data.c_str(), 1, data.length());
|
||||
}
|
||||
|
||||
bool IniFile::hasSection(const std::string& section) const {
|
||||
return sections.count(section) != 0;
|
||||
}
|
||||
bool IniFile::hasEntry(const std::string& section, const std::string& name) const {
|
||||
return getEntry(section, name) != nullptr;
|
||||
}
|
||||
bool IniFile::isTransient(const std::string& section, const std::string& name) const
|
||||
{
|
||||
const Entry *entry = getEntry(section, name);
|
||||
if (entry == nullptr)
|
||||
return false;
|
||||
else
|
||||
return entry->transient;
|
||||
}
|
||||
|
||||
void IniFile::deleteSection(const std::string& section) {
|
||||
sections.erase(section);
|
||||
}
|
||||
void IniFile::deleteEntry(const std::string& sectionName, const std::string& entry)
|
||||
{
|
||||
auto it = sections.find(sectionName);
|
||||
if (it == sections.end())
|
||||
return;
|
||||
Section& section = it->second;
|
||||
section.entries.erase(entry);
|
||||
}
|
||||
|
||||
std::vector<std::string> IniFile::getEntryNames(const std::string& sectionName) const
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto it = sections.find(sectionName);
|
||||
if (it != sections.end())
|
||||
for (const auto& section_it : this->sections)
|
||||
{
|
||||
const Section& section = it->second;
|
||||
for (const auto& [name, entry] : section.entries)
|
||||
v.push_back(name);
|
||||
const string& section_name = section_it.first;
|
||||
const ConfigSection& section = section_it.second;
|
||||
|
||||
fprintf(file, "[%s]\n", section_name.c_str());
|
||||
|
||||
for (const auto& entry_it : section.entries)
|
||||
{
|
||||
const string& entry_name = entry_it.first;
|
||||
const ConfigEntry& entry = entry_it.second;
|
||||
fprintf(file, "%s = %s\n", entry_name.c_str(), entry.get_string().c_str());
|
||||
}
|
||||
|
||||
fputs("\n", file);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
void ConfigFile::delete_section(const std::string& section_name) {
|
||||
sections.erase(section_name);
|
||||
virtual_sections.erase(section_name);
|
||||
}
|
||||
|
||||
void ConfigFile::delete_entry(const std::string& section_name, const std::string& entry_name)
|
||||
{
|
||||
ConfigSection *section = get_section(section_name, false);
|
||||
if (section != NULL)
|
||||
section->delete_entry(entry_name);
|
||||
}
|
||||
|
||||
} // namespace emucfg
|
||||
|
||||
+45
-99
@@ -1,106 +1,52 @@
|
||||
/*
|
||||
Copyright 2025 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "oslib/storage.h"
|
||||
#include <cstdio>
|
||||
#include "types.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
namespace config {
|
||||
namespace emucfg {
|
||||
|
||||
class IniFile
|
||||
{
|
||||
public:
|
||||
void load(hostfs::File *file, bool cEscape = false);
|
||||
void load(const std::string& data, bool cEscape = false);
|
||||
void save(hostfs::File *file) const;
|
||||
void save(std::string& data) const;
|
||||
bool hasSection(const std::string& section) const;
|
||||
bool hasEntry(const std::string& section, const std::string& name) const;
|
||||
bool isTransient(const std::string& section, const std::string& name) const;
|
||||
void deleteSection(const std::string& section);
|
||||
void deleteEntry(const std::string& section, const std::string& name);
|
||||
|
||||
template<typename T>
|
||||
void set(const std::string& section, const std::string& entry, T value, bool transient = false) {
|
||||
setRaw(section, entry, std::to_string(value), transient);
|
||||
}
|
||||
|
||||
const std::string& getString(const std::string& section, const std::string& entry, const std::string& defaultValue = {}) const;
|
||||
const std::string& get(const std::string& section, const std::string& entry, const std::string& defaultValue = {}) const {
|
||||
return getString(section, entry, defaultValue);
|
||||
}
|
||||
bool getBool(const std::string& section, const std::string& entry, bool defaultValue = false) const;
|
||||
int getInt(const std::string& section, const std::string& entry, int defaultValue = 0) const;
|
||||
int64_t getInt64(const std::string& section, const std::string& entry, int64_t defaultValue = 0ll) const;
|
||||
float getFloat(const std::string& section, const std::string& entry, float defaultValue = 0.f) const;
|
||||
|
||||
std::vector<std::string> getEntryNames(const std::string& section) const;
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
std::string transientValue;
|
||||
bool transient = false;
|
||||
};
|
||||
struct Section
|
||||
{
|
||||
std::string name;
|
||||
std::map<std::string, Entry> entries;
|
||||
};
|
||||
const Entry *getEntry(const std::string& section, const std::string& entry) const;
|
||||
const std::string *getRaw(const std::string& section, const std::string& name) const;
|
||||
void setRaw(const std::string& section, const std::string& name, const std::string& value, bool transient);
|
||||
|
||||
std::map<std::string, Section> sections;
|
||||
struct ConfigEntry {
|
||||
std::string value;
|
||||
const std::string& get_string() const;
|
||||
int get_int();
|
||||
bool get_bool();
|
||||
};
|
||||
|
||||
template<>
|
||||
inline void IniFile::set(const std::string& section, const std::string& entry, const std::string& value, bool transient) {
|
||||
setRaw(section, entry, value, transient);
|
||||
}
|
||||
template<>
|
||||
inline void IniFile::set(const std::string& section, const std::string& entry, std::string value, bool transient) {
|
||||
setRaw(section, entry, value, transient);
|
||||
}
|
||||
template<>
|
||||
inline void IniFile::set(const std::string& section, const std::string& entry, const char *value, bool transient) {
|
||||
setRaw(section, entry, value, transient);
|
||||
}
|
||||
template<>
|
||||
inline void IniFile::set(const std::string& section, const std::string& entry, bool value, bool transient) {
|
||||
setRaw(section, entry, value ? "yes" : "no", transient);
|
||||
}
|
||||
template<>
|
||||
inline void IniFile::set(const std::string& section, const std::string& entry, float value, bool transient)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss.precision(7);
|
||||
ss << value;
|
||||
setRaw(section, entry, ss.str(), transient);
|
||||
}
|
||||
struct ConfigSection {
|
||||
std::map<std::string, ConfigEntry> entries;
|
||||
bool has_entry(const std::string& name);
|
||||
void set(const std::string& name, const std::string& value);
|
||||
void delete_entry(const std::string& name);
|
||||
ConfigEntry* get_entry(const std::string& name);
|
||||
};
|
||||
|
||||
struct ConfigFile {
|
||||
private:
|
||||
std::map<std::string, ConfigSection> sections;
|
||||
std::map<std::string, ConfigSection> virtual_sections;
|
||||
ConfigSection* add_section(const std::string& name, bool is_virtual);
|
||||
ConfigSection* get_section(const std::string& name, bool is_virtual);
|
||||
ConfigEntry* get_entry(const std::string& section_name, const std::string& entry_name);
|
||||
|
||||
|
||||
public:
|
||||
bool has_section(const std::string& name);
|
||||
bool has_entry(const std::string& section_name, const std::string& entry_name);
|
||||
|
||||
void parse(FILE* fd);
|
||||
void save(FILE* fd);
|
||||
|
||||
/* getting values */
|
||||
string get(const std::string& section_name, const std::string& entry_name, const std::string& default_value = "");
|
||||
int get_int(const std::string& section_name, const std::string& entry_name, int default_value = 0);
|
||||
bool get_bool(const std::string& section_name, const std::string& entry_name, bool default_value = false);
|
||||
/* setting values */
|
||||
void set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual = false);
|
||||
void set_int(const std::string& section_name, const std::string& entry_name, int value, bool is_virtual = false);
|
||||
void set_bool(const std::string& section_name, const std::string& entry_name, bool value, bool is_virtual = false);
|
||||
|
||||
void delete_section(const std::string& section_name);
|
||||
void delete_entry(const std::string& section_name, const std::string& entry_name);
|
||||
};
|
||||
|
||||
} // namespace emucfg
|
||||
|
||||
} // namespace config
|
||||
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "option.h"
|
||||
#include "network/net_handshake.h"
|
||||
#include "debug/gdb_server.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
// Dynarec
|
||||
|
||||
Option<bool> DynarecEnabled("Dynarec.Enabled", true);
|
||||
Option<int> Sh4Clock("Sh4Clock", 200);
|
||||
|
||||
// General
|
||||
|
||||
Option<int> Cable("Dreamcast.Cable", 3); // TV Composite
|
||||
Option<int> Region("Dreamcast.Region", 1); // USA
|
||||
Option<int> Broadcast("Dreamcast.Broadcast", 0); // NTSC
|
||||
Option<int> Language("Dreamcast.Language", 1); // English
|
||||
OptionString UILanguage("UILanguage", "");
|
||||
Option<bool> AutoLoadState("Dreamcast.AutoLoadState");
|
||||
Option<bool> AutoSaveState("Dreamcast.AutoSaveState");
|
||||
Option<int, false> SavestateSlot("Dreamcast.SavestateSlot");
|
||||
Option<bool> ForceFreePlay("ForceFreePlay", true);
|
||||
Option<bool, false> FetchBoxart("FetchBoxart", true);
|
||||
Option<bool, false> BoxartDisplayMode("BoxartDisplayMode", true);
|
||||
Option<int, false> UIScaling("UIScaling", 100);
|
||||
Option<int, false> UITheme("UITheme", 0);
|
||||
|
||||
// Sound
|
||||
|
||||
Option<bool> DSPEnabled("aica.DSPEnabled", false);
|
||||
#if HOST_CPU == CPU_ARM
|
||||
Option<int> AudioBufferSize("aica.BufferSize", 5644); // 128 ms
|
||||
#else
|
||||
Option<int> AudioBufferSize("aica.BufferSize", 2822); // 64 ms
|
||||
#endif
|
||||
Option<bool> AutoLatency("aica.AutoLatency",
|
||||
#ifdef __ANDROID__
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
);
|
||||
|
||||
OptionString AudioBackend("backend", "auto", "audio");
|
||||
AudioVolumeOption AudioVolume;
|
||||
Option<bool> VmuSound("VmuSound", false, "audio");
|
||||
|
||||
// Rendering
|
||||
|
||||
RendererOption RendererType;
|
||||
Option<bool> UseMipmaps("rend.UseMipmaps", true);
|
||||
Option<bool> Widescreen("rend.WideScreen");
|
||||
Option<bool> SuperWidescreen("rend.SuperWideScreen");
|
||||
Option<bool> ShowFPS("rend.ShowFPS");
|
||||
Option<bool> RenderToTextureBuffer("rend.RenderToTextureBuffer");
|
||||
Option<bool> TranslucentPolygonDepthMask("rend.TranslucentPolygonDepthMask");
|
||||
Option<bool> ModifierVolumes("rend.ModifierVolumes", true);
|
||||
Option<int> TextureUpscale("rend.TextureUpscale2", 1);
|
||||
Option<int> MaxFilteredTextureSize("rend.MaxFilteredTextureSize", 256);
|
||||
Option<float> ExtraDepthScale("rend.ExtraDepthScale", 1.f);
|
||||
Option<bool> CustomTextures("rend.CustomTextures");
|
||||
Option<bool> PreloadCustomTextures("rend.PreloadCustomTextures");
|
||||
Option<bool> DumpTextures("rend.DumpTextures");
|
||||
Option<bool> DumpUniqueTextures("rend.DumpUniqueTextures");
|
||||
Option<bool> DumpReplacedTextures("rend.DumpReplacedTextures");
|
||||
Option<int> ScreenStretching("rend.ScreenStretching", 100);
|
||||
Option<bool> Fog("rend.Fog", true);
|
||||
Option<bool> FloatVMUs("rend.FloatVMUs");
|
||||
Option<bool> Rotate90("rend.Rotate90");
|
||||
Option<bool> PerStripSorting("rend.PerStripSorting");
|
||||
#ifdef __APPLE__
|
||||
Option<bool> DelayFrameSwapping("rend.DelayFrameSwapping", false);
|
||||
#else
|
||||
Option<bool> DelayFrameSwapping("rend.DelayFrameSwapping", true);
|
||||
#endif
|
||||
Option<bool> WidescreenGameHacks("rend.WidescreenGameHacks");
|
||||
std::array<Option<int>, 4> CrosshairColor {
|
||||
Option<int>("rend.CrossHairColor1"),
|
||||
Option<int>("rend.CrossHairColor2"),
|
||||
Option<int>("rend.CrossHairColor3"),
|
||||
Option<int>("rend.CrossHairColor4"),
|
||||
};
|
||||
Option<int> CrosshairSize("rend.CrosshairSize", 40);
|
||||
Option<int> SkipFrame("ta.skip");
|
||||
Option<int> MaxThreads("pvr.MaxThreads", 3);
|
||||
Option<int> AutoSkipFrame("pvr.AutoSkipFrame", 0);
|
||||
Option<int> RenderResolution("rend.Resolution", 480);
|
||||
Option<bool> IntegerScale("rend.IntegerScale", false);
|
||||
Option<bool> LinearInterpolation("rend.LinearInterpolation", true);
|
||||
Option<bool> VSync("rend.vsync", true);
|
||||
Option<int64_t> PixelBufferSize("rend.PixelBufferSize", 512_MB);
|
||||
Option<int> AnisotropicFiltering("rend.AnisotropicFiltering", 1);
|
||||
Option<int> TextureFiltering("rend.TextureFiltering", 0); // Default
|
||||
Option<bool> ThreadedRendering("rend.ThreadedRendering", true);
|
||||
Option<bool> VblankPacing("");
|
||||
Option<bool> DupeFrames("rend.DupeFrames", false);
|
||||
Option<int> PerPixelLayers("rend.PerPixelLayers", 32);
|
||||
#ifdef TARGET_UWP
|
||||
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", true);
|
||||
#else
|
||||
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", false);
|
||||
#endif
|
||||
Option<bool> EmulateFramebuffer("rend.EmulateFramebuffer", false);
|
||||
Option<bool> FixUpscaleBleedingEdge("rend.FixUpscaleBleedingEdge", true);
|
||||
Option<bool> CustomGpuDriver("rend.CustomGpuDriver", false);
|
||||
Option<bool> FramePacing("rend.FramePacing", true);
|
||||
#ifdef VIDEO_ROUTING
|
||||
Option<bool, false> VideoRouting("rend.VideoRouting", false);
|
||||
Option<bool, false> VideoRoutingScale("rend.VideoRoutingScale", false);
|
||||
Option<int, false> VideoRoutingVRes("rend.VideoRoutingVRes", 720);
|
||||
#endif
|
||||
|
||||
// Misc
|
||||
|
||||
Option<bool> SerialConsole("Debug.SerialConsoleEnabled");
|
||||
Option<bool> SerialPTY("Debug.SerialPTY");
|
||||
Option<bool> GDB("Debug.GDBEnabled");
|
||||
Option<int> GDBPort("Debug.GDBPort", debugger::DEFAULT_PORT);
|
||||
Option<bool> GDBWaitForConnection("Debug.GDBWaitForConnection");
|
||||
Option<bool> UseReios("UseReios");
|
||||
Option<bool> FastGDRomLoad("FastGDRomLoad", false);
|
||||
Option<bool> RamMod32MB("Dreamcast.RamMod32MB", false);
|
||||
|
||||
Option<bool> OpenGlChecks("OpenGlChecks", false, "validate");
|
||||
|
||||
Option<std::vector<std::string>, false> ContentPath("Dreamcast.ContentPath");
|
||||
Option<std::vector<std::string>, false> BiosPath("Dreamcast.BiosPath");
|
||||
Option<std::string, false> VMUPath("Dreamcast.VMUPath");
|
||||
Option<std::vector<std::string>, false> SavestatePath("Dreamcast.SavestatePath");
|
||||
Option<std::string, false> SavePath("Dreamcast.SavePath");
|
||||
Option<std::vector<std::string>, false> TexturePath("Dreamcast.TexturePath");
|
||||
Option<std::string, false> TextureDumpPath("Dreamcast.TextureDumpPath");
|
||||
Option<std::string, false> BoxartPath("Dreamcast.BoxartPath");
|
||||
Option<std::vector<std::string>, false> MappingsPath("Dreamcast.MappingsPath");
|
||||
Option<std::vector<std::string>, false> CheatPath("Dreamcast.CheatPath");
|
||||
Option<bool, false> HideLegacyNaomiRoms("Dreamcast.HideLegacyNaomiRoms", true);
|
||||
Option<bool, false> UploadCrashLogs("UploadCrashLogs", true);
|
||||
Option<bool, false> DiscordPresence("DiscordPresence", true);
|
||||
#if defined(__ANDROID__) && !defined(LIBRETRO)
|
||||
Option<bool, false> UseSafFilePicker("UseSafFilePicker", true);
|
||||
#endif
|
||||
OptionString LogServer("LogServer", "", "log");
|
||||
|
||||
// Profiler
|
||||
Option<bool> ProfilerEnabled("Profiler.Enabled");
|
||||
Option<bool> ProfilerDrawToGUI("Profiler.DrawGUI");
|
||||
Option<bool> ProfilerOutputTTY("Profiler.OutputTTY");
|
||||
Option<float> ProfilerFrameWarningTime("Profiler.FrameWarningTime", 1.0f / 55.0f);
|
||||
|
||||
// Network
|
||||
|
||||
Option<bool> NetworkEnable("Enable", false, "network");
|
||||
Option<bool> ActAsServer("ActAsServer", false, "network");
|
||||
Option<bool> NaomiSatellite("NaomiSatellite", false, "network");
|
||||
OptionString DNS("DNS", "dns.flyca.st", "network");
|
||||
OptionString NetworkServer("server", "", "network");
|
||||
Option<int> LocalPort("LocalPort", defaultNaomiServerPort(), "network");
|
||||
Option<bool> EmulateBBA("EmulateBBA", false, "network");
|
||||
Option<bool> EnableUPnP("EnableUPnP", true, "network");
|
||||
Option<bool> GGPOEnable("GGPO", false, "network");
|
||||
Option<int> GGPODelay("GGPODelay", 0, "network");
|
||||
Option<bool> NetworkStats("Stats", true, "network");
|
||||
Option<int> GGPOAnalogAxes("GGPOAnalogAxes", 0, "network");
|
||||
Option<bool> GGPOChat("GGPOChat", true, "network");
|
||||
Option<bool> GGPOChatTimeoutToggle("GGPOChatTimeoutToggle", true, "network");
|
||||
Option<int> GGPOChatTimeout("GGPOChatTimeout", 10, "network");
|
||||
Option<bool> NetworkOutput("NetworkOutput", false, "network");
|
||||
Option<int> MultiboardSlaves("MultiboardSlaves", 1, "network");
|
||||
Option<bool> BattleCableEnable("BattleCable", false, "network");
|
||||
Option<bool> UseDCNet("DCNet", true, "network");
|
||||
OptionString ISPUsername("ISPUsername", "flycast1", "network");
|
||||
|
||||
#ifdef USE_OMX
|
||||
Option<int> OmxAudioLatency("audio_latency", 100, "omx");
|
||||
Option<bool> OmxAudioHdmi("audio_hdmi", true, "omx");
|
||||
#endif
|
||||
|
||||
// Maple
|
||||
|
||||
Option<int> MouseSensitivity("MouseSensitivity", 100, "input");
|
||||
Option<int> VirtualGamepadVibration("VirtualGamepadVibration", 20, "input");
|
||||
Option<int> VirtualGamepadTransparency("VirtualGamepadTransparency", 37, "input");
|
||||
|
||||
std::array<Option<MapleDeviceType>, 4> MapleMainDevices {
|
||||
Option<MapleDeviceType>("device1", MDT_SegaController, "input"),
|
||||
Option<MapleDeviceType>("device2", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device3", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device4", MDT_None, "input"),
|
||||
};
|
||||
std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices {{
|
||||
{{Option<MapleDeviceType>("device1.1", MDT_SegaVMU, "input"),
|
||||
Option<MapleDeviceType>("device1.2", MDT_SegaVMU, "input")}},
|
||||
|
||||
{{Option<MapleDeviceType>("device2.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device2.2", MDT_None, "input")}},
|
||||
|
||||
{{Option<MapleDeviceType>("device3.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device3.2", MDT_None, "input")}},
|
||||
|
||||
{{Option<MapleDeviceType>("device4.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device4.2", MDT_None, "input")}},
|
||||
}};
|
||||
|
||||
std::array<std::array<Option<int>, 2>, 4> NetworkExpansionDevices{{
|
||||
{{Option<int>("device1.1.net", 0, "input"),
|
||||
Option<int>("device1.2.net", 0, "input")}},
|
||||
|
||||
{{Option<int>("device2.1.net", 0, "input"),
|
||||
Option<int>("device2.2.net", 0, "input")}},
|
||||
|
||||
{{Option<int>("device3.1.net", 0, "input"),
|
||||
Option<int>("device3.2.net", 0, "input")}},
|
||||
|
||||
{{Option<int>("device4.1.net", 0, "input"),
|
||||
Option<int>("device4.2.net", 0, "input")}},
|
||||
}};
|
||||
Option<bool> PerGameVmu("PerGameVmu", true, "config");
|
||||
#ifdef _WIN32
|
||||
Option<bool, false> UseRawInput("RawInput", false, "input");
|
||||
#endif
|
||||
Option<bool> UsePhysicalVmuMemory("UsePhysicalVmuMemory", false);
|
||||
|
||||
#ifdef USE_LUA
|
||||
Option<std::string, false> LuaFileName("LuaFileName", "flycast.lua");
|
||||
#endif
|
||||
|
||||
// RetroAchievements
|
||||
|
||||
Option<bool> EnableAchievements("Enabled", false, "achievements");
|
||||
Option<bool> AchievementsHardcoreMode("HardcoreMode", false, "achievements");
|
||||
OptionString AchievementsUserName("UserName", "", "achievements");
|
||||
OptionString AchievementsToken("Token", "", "achievements");
|
||||
OptionString AchievementsHostUrl("HostUrl", "", "achievements");
|
||||
|
||||
} // namespace config
|
||||
@@ -1,580 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include "cfg.h"
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#ifdef LIBRETRO
|
||||
#include <libretro.h>
|
||||
#endif
|
||||
|
||||
namespace config {
|
||||
|
||||
class BaseOption {
|
||||
public:
|
||||
virtual ~BaseOption() = default;
|
||||
virtual void save() const = 0;
|
||||
virtual void load() = 0;
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
|
||||
#ifdef LIBRETRO
|
||||
#include "option_lr.h"
|
||||
#else
|
||||
|
||||
class Settings {
|
||||
public:
|
||||
void reset() {
|
||||
for (const auto& o : options)
|
||||
o->reset();
|
||||
gameId.clear();
|
||||
perGameConfig = false;
|
||||
}
|
||||
|
||||
void load(bool gameSpecific) {
|
||||
if (gameSpecific)
|
||||
{
|
||||
if (gameId.empty())
|
||||
return;
|
||||
if (!hasSection(gameId))
|
||||
return;
|
||||
perGameConfig = true;
|
||||
}
|
||||
for (const auto& o : options)
|
||||
o->load();
|
||||
}
|
||||
|
||||
void save() {
|
||||
setAutoSave(false);
|
||||
for (const auto& o : options)
|
||||
o->save();
|
||||
setAutoSave(true);
|
||||
}
|
||||
|
||||
void setGameId(const std::string& gameId) {
|
||||
this->gameId = gameId;
|
||||
}
|
||||
|
||||
bool hasPerGameConfig() const {
|
||||
return perGameConfig;
|
||||
}
|
||||
void setPerGameConfig(bool perGameConfig) {
|
||||
this->perGameConfig = perGameConfig;
|
||||
if (!perGameConfig) {
|
||||
if (!gameId.empty())
|
||||
deleteSection(gameId);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
static Settings& instance() {
|
||||
static Settings *_instance = new Settings();
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<BaseOption *> options;
|
||||
std::string gameId;
|
||||
bool perGameConfig = false;
|
||||
|
||||
template<typename T, bool PerGameOption>
|
||||
friend class Option;
|
||||
};
|
||||
|
||||
template<typename T, bool PerGameOption = true>
|
||||
class Option : public BaseOption {
|
||||
public:
|
||||
Option(const std::string& name, T defaultValue = T(), const std::string& section = "config")
|
||||
: section(section), name(name), value(defaultValue), defaultValue(defaultValue),
|
||||
settings(Settings::instance())
|
||||
{
|
||||
settings.options.push_back(this);
|
||||
}
|
||||
|
||||
void reset() override {
|
||||
set(defaultValue);
|
||||
overridden = false;
|
||||
}
|
||||
|
||||
void load() override {
|
||||
if (PerGameOption && settings.hasPerGameConfig())
|
||||
set(doLoad(settings.gameId, section + "." + name));
|
||||
else
|
||||
{
|
||||
set(doLoad(section, name));
|
||||
if (isTransient(section, name))
|
||||
override(value);
|
||||
}
|
||||
}
|
||||
|
||||
void save() const override
|
||||
{
|
||||
if (overridden) {
|
||||
if (value == overriddenDefault)
|
||||
return;
|
||||
if (!settings.hasPerGameConfig())
|
||||
// overridden options can only be saved in per-game settings
|
||||
return;
|
||||
}
|
||||
else if (PerGameOption && settings.hasPerGameConfig())
|
||||
{
|
||||
if (value == doLoad(section, name))
|
||||
{
|
||||
// delete existing per-game option if any
|
||||
deleteEntry(settings.gameId, section + "." + name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (PerGameOption && settings.hasPerGameConfig())
|
||||
doSave(settings.gameId, section + "." + name);
|
||||
else
|
||||
doSave(section, name);
|
||||
}
|
||||
|
||||
T& get() { return value; }
|
||||
void set(T v) { value = v; }
|
||||
|
||||
void override(T v)
|
||||
{
|
||||
overriddenDefault = v;
|
||||
overridden = true;
|
||||
value = v;
|
||||
}
|
||||
bool isReadOnly() const {
|
||||
return overridden && !settings.hasPerGameConfig();
|
||||
}
|
||||
|
||||
explicit operator T() const { return value; }
|
||||
operator T&() { return value; }
|
||||
T& operator=(const T& v) { set(v); return value; }
|
||||
|
||||
protected:
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<U, bool>, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
return loadBool(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same<U, int64_t>::value, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
return loadInt64(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<(std::is_integral_v<U> || std::is_enum_v<U>)
|
||||
&& !std::is_same_v<U, bool> && !std::is_same_v<U, int64_t>, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
return (T)loadInt(section, name, (int)value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<U, std::string>, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
return loadStr(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<float, U>, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
return loadFloat(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<std::vector<std::string>, U>, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
std::string paths = loadStr(section, name);
|
||||
if (paths.empty())
|
||||
return value;
|
||||
std::string::size_type start = 0;
|
||||
std::vector<std::string> newValue;
|
||||
while (true)
|
||||
{
|
||||
if (paths[start] == '"')
|
||||
{
|
||||
std::string v;
|
||||
start++;
|
||||
while (true)
|
||||
{
|
||||
if (paths[start] == '"')
|
||||
{
|
||||
if (start + 1 >= paths.size())
|
||||
{
|
||||
newValue.push_back(v);
|
||||
return newValue;
|
||||
}
|
||||
if (paths[start + 1] == '"')
|
||||
{
|
||||
v += paths[start++];
|
||||
start++;
|
||||
}
|
||||
else if (paths[start + 1] == ';')
|
||||
{
|
||||
newValue.push_back(v);
|
||||
start += 2;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
v += paths[start++];
|
||||
}
|
||||
}
|
||||
else
|
||||
v += paths[start++];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string::size_type end = paths.find(';', start);
|
||||
if (end == std::string::npos)
|
||||
end = paths.size();
|
||||
if (start != end)
|
||||
newValue.push_back(paths.substr(start, end - start));
|
||||
if (end == paths.size())
|
||||
break;
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<U, bool>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
saveBool(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same<U, int64_t>::value>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
saveInt64(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<(std::is_integral_v<U> || std::is_enum_v<U>)
|
||||
&& !std::is_same_v<U, bool> && !std::is_same_v<U, int64_t>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
saveInt(section, name, (int)value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<U, std::string>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
saveStr(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<float, U>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
saveFloat(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
std::enable_if_t<std::is_same_v<std::vector<std::string>, U>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
std::string s;
|
||||
for (const auto& v : value)
|
||||
{
|
||||
if (!s.empty())
|
||||
s += ';';
|
||||
if (v.find(';') != std::string::npos || (!v.empty() && v[0] == '"'))
|
||||
{
|
||||
s += '"';
|
||||
std::string v2 = v;
|
||||
while (true)
|
||||
{
|
||||
auto pos = v2.find('"');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
s += v2.substr(0, pos + 1) + '"';
|
||||
v2 = v2.substr(pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
s += v2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
s += '"';
|
||||
}
|
||||
else
|
||||
s += v;
|
||||
}
|
||||
saveStr(section, name, s);
|
||||
}
|
||||
|
||||
std::string section;
|
||||
std::string name;
|
||||
T value;
|
||||
T defaultValue;
|
||||
T overriddenDefault = T();
|
||||
bool overridden = false;
|
||||
Settings& settings;
|
||||
};
|
||||
#endif
|
||||
|
||||
using OptionString = Option<std::string>;
|
||||
|
||||
// Dynarec
|
||||
|
||||
extern Option<bool> DynarecEnabled;
|
||||
#ifndef LIBRETRO
|
||||
extern Option<int> Sh4Clock;
|
||||
#endif
|
||||
|
||||
// General
|
||||
|
||||
extern Option<int> Cable; // 0 -> VGA, 1 -> VGA, 2 -> RGB, 3 -> TV Composite
|
||||
extern Option<int> Region; // 0 -> JP, 1 -> USA, 2 -> EU, 3 -> default
|
||||
extern Option<int> Broadcast; // 0 -> NTSC, 1 -> PAL, 2 -> PAL/M, 3 -> PAL/N, 4 -> default
|
||||
extern Option<int> Language; // 0 -> JP, 1 -> EN, 2 -> DE, 3 -> FR, 4 -> SP, 5 -> IT, 6 -> default
|
||||
extern OptionString UILanguage;
|
||||
extern Option<bool> AutoLoadState;
|
||||
extern Option<bool> AutoSaveState;
|
||||
extern Option<int, false> SavestateSlot;
|
||||
extern Option<bool> ForceFreePlay;
|
||||
extern Option<bool, false> FetchBoxart;
|
||||
extern Option<bool, false> BoxartDisplayMode;
|
||||
extern Option<int, false> UIScaling;
|
||||
extern Option<int, false> UITheme; // 0 -> Dark, 1 -> Light, 2 -> Dreamcast, 3 -> High Contrast, 4 -> Nintendo, 5 -> Aqua Chill
|
||||
|
||||
// Sound
|
||||
|
||||
constexpr bool LimitFPS = true;
|
||||
extern Option<bool> DSPEnabled;
|
||||
extern Option<int> AudioBufferSize; //In samples ,*4 for bytes
|
||||
extern Option<bool> AutoLatency;
|
||||
|
||||
extern OptionString AudioBackend;
|
||||
|
||||
class AudioVolumeOption : public Option<int> {
|
||||
public:
|
||||
AudioVolumeOption() : Option<int>("aica.Volume", 100) {};
|
||||
float logarithmic_volume_scale = 1.0;
|
||||
|
||||
void load() override {
|
||||
Option<int>::load();
|
||||
calcDbPower();
|
||||
}
|
||||
|
||||
float dbPower()
|
||||
{
|
||||
return logarithmic_volume_scale;
|
||||
}
|
||||
void calcDbPower()
|
||||
{
|
||||
// dB scaling calculation: https://www.dr-lex.be/info-stuff/volumecontrols.html
|
||||
logarithmic_volume_scale = std::min(std::exp(4.605f * float(value) / 100.f) / 100.f, 1.f);
|
||||
if (value < 10)
|
||||
logarithmic_volume_scale *= value / 10.f;
|
||||
}
|
||||
};
|
||||
extern AudioVolumeOption AudioVolume;
|
||||
extern Option<bool> VmuSound;
|
||||
|
||||
// Rendering
|
||||
|
||||
class RendererOption : public Option<RenderType> {
|
||||
public:
|
||||
#ifdef LIBRETRO
|
||||
RendererOption() : Option<RenderType>("",
|
||||
#else
|
||||
RendererOption() : Option<RenderType>("pvr.rend",
|
||||
#endif
|
||||
#if defined(USE_DX11)
|
||||
RenderType::DirectX11
|
||||
#elif defined(USE_DX9)
|
||||
RenderType::DirectX9
|
||||
#elif !defined(USE_OPENGL)
|
||||
RenderType::Vulkan
|
||||
#else
|
||||
RenderType::OpenGL
|
||||
#endif
|
||||
) {}
|
||||
|
||||
RenderType& operator=(const RenderType& v) { set(v); return value; }
|
||||
|
||||
void reset() override {
|
||||
// don't reset the value to avoid quick switching when starting a game
|
||||
overridden = false;
|
||||
}
|
||||
};
|
||||
extern RendererOption RendererType;
|
||||
extern Option<bool> UseMipmaps;
|
||||
extern Option<bool> Widescreen;
|
||||
extern Option<bool> SuperWidescreen;
|
||||
extern Option<bool> ShowFPS;
|
||||
extern Option<bool> RenderToTextureBuffer;
|
||||
extern Option<bool> TranslucentPolygonDepthMask;
|
||||
extern Option<bool> ModifierVolumes;
|
||||
constexpr bool Clipping = true;
|
||||
#ifndef LIBRETRO
|
||||
extern Option<int> TextureUpscale;
|
||||
extern Option<int> MaxFilteredTextureSize;
|
||||
extern Option<int> PerPixelLayers;
|
||||
#endif
|
||||
extern Option<float> ExtraDepthScale;
|
||||
extern Option<bool> CustomTextures;
|
||||
extern Option<bool> PreloadCustomTextures;
|
||||
extern Option<bool> DumpTextures;
|
||||
extern Option<bool> DumpUniqueTextures;
|
||||
extern Option<bool> DumpReplacedTextures;
|
||||
extern Option<int> ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3
|
||||
extern Option<bool> Fog;
|
||||
extern Option<bool> FloatVMUs;
|
||||
extern Option<bool> Rotate90;
|
||||
extern Option<bool> PerStripSorting;
|
||||
extern Option<bool> DelayFrameSwapping; // Delay swapping frame until FB_R_SOF matches FB_W_SOF
|
||||
extern Option<bool> WidescreenGameHacks;
|
||||
extern std::array<Option<int>, 4> CrosshairColor;
|
||||
extern Option<int> CrosshairSize;
|
||||
extern Option<int> SkipFrame;
|
||||
extern Option<int> MaxThreads;
|
||||
extern Option<int> AutoSkipFrame; // 0: none, 1: some, 2: more
|
||||
extern Option<int> RenderResolution;
|
||||
extern Option<bool> IntegerScale;
|
||||
extern Option<bool> LinearInterpolation;
|
||||
extern Option<bool> VSync;
|
||||
extern Option<int64_t> PixelBufferSize;
|
||||
extern Option<int> AnisotropicFiltering;
|
||||
extern Option<int> TextureFiltering; // 0: default, 1: force nearest, 2: force linear
|
||||
extern Option<bool> ThreadedRendering;
|
||||
extern Option<bool> VblankPacing;
|
||||
extern Option<bool> DupeFrames;
|
||||
extern Option<bool> NativeDepthInterpolation;
|
||||
extern Option<bool> EmulateFramebuffer;
|
||||
extern Option<bool> FixUpscaleBleedingEdge;
|
||||
extern Option<bool> CustomGpuDriver;
|
||||
extern Option<bool> FramePacing;
|
||||
#ifdef VIDEO_ROUTING
|
||||
extern Option<bool, false> VideoRouting;
|
||||
extern Option<bool, false> VideoRoutingScale;
|
||||
extern Option<int, false> VideoRoutingVRes;
|
||||
#endif
|
||||
|
||||
// Misc
|
||||
|
||||
extern Option<bool> SerialConsole;
|
||||
extern Option<bool> SerialPTY;
|
||||
extern Option<bool> GDB;
|
||||
extern Option<int> GDBPort;
|
||||
extern Option<bool> GDBWaitForConnection;
|
||||
extern Option<bool> UseReios;
|
||||
extern Option<bool> FastGDRomLoad;
|
||||
extern Option<bool> RamMod32MB;
|
||||
|
||||
extern Option<bool> OpenGlChecks;
|
||||
|
||||
extern Option<std::vector<std::string>, false> ContentPath;
|
||||
extern Option<std::vector<std::string>, false> BiosPath;
|
||||
extern Option<std::string, false> VMUPath;
|
||||
extern Option<std::vector<std::string>, false> SavestatePath;
|
||||
extern Option<std::string, false> SavePath;
|
||||
extern Option<std::vector<std::string>, false> TexturePath;
|
||||
extern Option<std::string, false> TextureDumpPath;
|
||||
extern Option<std::string, false> BoxartPath;
|
||||
extern Option<std::vector<std::string>, false> MappingsPath;
|
||||
extern Option<std::vector<std::string>, false> CheatPath;
|
||||
extern Option<bool, false> HideLegacyNaomiRoms;
|
||||
extern Option<bool, false> UploadCrashLogs;
|
||||
extern Option<bool, false> DiscordPresence;
|
||||
#if defined(__ANDROID__) && !defined(LIBRETRO)
|
||||
extern Option<bool, false> UseSafFilePicker;
|
||||
#endif
|
||||
extern OptionString LogServer;
|
||||
|
||||
// Profiling
|
||||
extern Option<bool> ProfilerEnabled;
|
||||
extern Option<bool> ProfilerDrawToGUI;
|
||||
extern Option<bool> ProfilerOutputTTY;
|
||||
extern Option<float> ProfilerFrameWarningTime;
|
||||
|
||||
// Network
|
||||
|
||||
extern Option<bool> NetworkEnable;
|
||||
extern Option<bool> ActAsServer;
|
||||
extern Option<bool> NaomiSatellite;
|
||||
extern OptionString DNS;
|
||||
extern OptionString NetworkServer;
|
||||
extern Option<int> LocalPort;
|
||||
extern Option<bool> EmulateBBA;
|
||||
extern Option<bool> EnableUPnP;
|
||||
extern Option<bool> GGPOEnable;
|
||||
extern Option<int> GGPODelay;
|
||||
extern Option<bool> NetworkStats;
|
||||
extern Option<int> GGPOAnalogAxes;
|
||||
extern Option<bool> GGPOChat;
|
||||
extern Option<bool> GGPOChatTimeoutToggle;
|
||||
extern Option<int> GGPOChatTimeout;
|
||||
extern Option<bool> NetworkOutput;
|
||||
extern Option<int> MultiboardSlaves;
|
||||
extern Option<bool> BattleCableEnable;
|
||||
extern Option<bool> UseDCNet;
|
||||
extern OptionString ISPUsername;
|
||||
|
||||
#ifdef USE_OMX
|
||||
extern Option<int> OmxAudioLatency;
|
||||
extern Option<bool> OmxAudioHdmi;
|
||||
#endif
|
||||
|
||||
// Maple
|
||||
|
||||
extern Option<int> MouseSensitivity;
|
||||
extern Option<int> VirtualGamepadVibration;
|
||||
extern Option<int> VirtualGamepadTransparency;
|
||||
extern std::array<Option<MapleDeviceType>, 4> MapleMainDevices;
|
||||
extern std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices;
|
||||
extern std::array<std::array<Option<int>, 2>, 4> NetworkExpansionDevices;
|
||||
extern Option<bool> PerGameVmu;
|
||||
#ifdef _WIN32
|
||||
extern Option<bool, false> UseRawInput;
|
||||
#else
|
||||
constexpr bool UseRawInput = false;
|
||||
#endif
|
||||
extern Option<bool> UsePhysicalVmuMemory;
|
||||
|
||||
#ifdef USE_LUA
|
||||
extern Option<std::string, false> LuaFileName;
|
||||
#endif
|
||||
|
||||
// RetroAchievements
|
||||
|
||||
extern Option<bool> EnableAchievements;
|
||||
extern Option<bool> AchievementsHardcoreMode;
|
||||
extern OptionString AchievementsUserName;
|
||||
extern OptionString AchievementsToken;
|
||||
extern OptionString AchievementsHostUrl;
|
||||
|
||||
} // namespace config
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <libchdr/chd.h>
|
||||
|
||||
#include "oslib/storage.h"
|
||||
|
||||
namespace CHD
|
||||
{
|
||||
namespace Callbacks
|
||||
{
|
||||
// Moved out here to avoid being duplicated by the template.
|
||||
inline uint64_t fsize(struct chd_core_file *core_file)
|
||||
{
|
||||
auto file = static_cast<hostfs::File*>(core_file->argp);
|
||||
return file->size();
|
||||
}
|
||||
|
||||
inline size_t fread(void* buffer, size_t size, size_t count, struct chd_core_file *core_file)
|
||||
{
|
||||
auto file = static_cast<hostfs::File*>(core_file->argp);
|
||||
return file->read(buffer, size, count);
|
||||
}
|
||||
|
||||
inline int fclose(struct chd_core_file *core_file)
|
||||
{
|
||||
auto file = static_cast<hostfs::File*>(core_file->argp);
|
||||
delete file;
|
||||
delete core_file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int fseek(struct chd_core_file *core_file, int64_t offset, int whence)
|
||||
{
|
||||
auto file = static_cast<hostfs::File*>(core_file->argp);
|
||||
return file->seek(offset, whence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
inline auto chd_open_file(hostfs::File *file, Ts &&...args)
|
||||
{
|
||||
auto core_file = new struct chd_core_file();
|
||||
|
||||
core_file->argp = file;
|
||||
core_file->fsize = CHD::Callbacks::fsize;
|
||||
core_file->fread = CHD::Callbacks::fread;
|
||||
core_file->fclose = CHD::Callbacks::fclose;
|
||||
core_file->fseek = CHD::Callbacks::fseek;
|
||||
|
||||
return chd_open_core_file(core_file, std::forward<Ts>(args)...);
|
||||
}
|
||||
+240
-1038
File diff suppressed because it is too large.
Load diff
+10
-71
@@ -3,102 +3,41 @@
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
This file is part of reicast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
reicast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
reicast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct WidescreenCheat
|
||||
struct Cheat
|
||||
{
|
||||
const char *game_id;
|
||||
const char *area_or_version;
|
||||
u32 addresses[16];
|
||||
u32 values[16];
|
||||
u32 origValues[16];
|
||||
};
|
||||
|
||||
struct Cheat
|
||||
{
|
||||
enum class Type {
|
||||
disabled,
|
||||
setValue,
|
||||
increase,
|
||||
decrease,
|
||||
runNextIfEq,
|
||||
runNextIfNeq,
|
||||
runNextIfGt,
|
||||
runNextIfLt,
|
||||
copy
|
||||
};
|
||||
Type type;
|
||||
std::string description;
|
||||
bool enabled;
|
||||
u32 size;
|
||||
u32 address;
|
||||
u32 value;
|
||||
u8 valueMask = 0;
|
||||
u32 repeatCount = 1;
|
||||
u32 repeatValueIncrement = 0;
|
||||
u32 repeatAddressIncrement = 0;
|
||||
u32 destAddress = 0;
|
||||
bool builtIn;
|
||||
|
||||
Cheat(Type type = Type::disabled, const std::string& description = "", bool enabled = false,
|
||||
u32 size = 0, u32 address = 0, u32 value = 0, bool builtIn = false)
|
||||
: type(type), description(description), enabled(enabled),
|
||||
size(size), address(address), value(value), builtIn(builtIn)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class CheatManager
|
||||
{
|
||||
public:
|
||||
~CheatManager() {
|
||||
reset("");
|
||||
}
|
||||
void reset(const std::string& gameId);
|
||||
void apply();
|
||||
size_t cheatCount() const { return cheats.size(); }
|
||||
const std::string& cheatDescription(size_t index) const { return cheats[index].description; }
|
||||
bool cheatEnabled(size_t index) const { return cheats[index].enabled; }
|
||||
void enableCheat(size_t index, bool enabled) { cheats[index].enabled = enabled; }
|
||||
void loadCheatFile(const std::string& filename);
|
||||
void saveCheatFile(const std::string& filename);
|
||||
// Returns true if using 16:9 anamorphic screen ratio
|
||||
bool isWidescreen() const { return widescreen_cheat != nullptr; }
|
||||
void addGameSharkCheat(const std::string& name, const std::string& s);
|
||||
|
||||
CheatManager() : _widescreen_cheat(nullptr) {}
|
||||
bool Reset(); // Returns true if using 16:9 anamorphic screen ratio
|
||||
void Apply();
|
||||
private:
|
||||
u32 readRam(u32 addr, u32 bits);
|
||||
void writeRam(u32 addr, u32 value, u32 bits);
|
||||
void setActive(bool active);
|
||||
|
||||
static const WidescreenCheat widescreen_cheats[];
|
||||
static const WidescreenCheat naomi_widescreen_cheats[];
|
||||
const WidescreenCheat *widescreen_cheat = nullptr;
|
||||
bool active = false;
|
||||
std::vector<Cheat> cheats;
|
||||
std::string gameId;
|
||||
|
||||
friend class CheatManagerTest_TestLoad_Test;
|
||||
friend class CheatManagerTest_TestGameShark_Test;
|
||||
friend class CheatManagerTest_TestSave_Test;
|
||||
static const Cheat _widescreen_cheats[];
|
||||
const Cheat *_widescreen_cheat;
|
||||
};
|
||||
|
||||
extern CheatManager cheatManager;
|
||||
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#LOCAL_PATH:=
|
||||
|
||||
#MFLAGS := -marm -march=armv7-a -mtune=cortex-a8 -mfpu=vfpv3-d16 -mfloat-abi=softfp
|
||||
#ASFLAGS := -march=armv7-a -mfpu=vfp-d16 -mfloat-abi=softfp
|
||||
#LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common
|
||||
|
||||
RZDCY_SRC_DIR ?= $(call my-dir)
|
||||
VERSION_HEADER := $(RZDCY_SRC_DIR)/version.h
|
||||
|
||||
RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ \
|
||||
hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \
|
||||
hw/extdev/ hw/arm/ hw/naomi/ imgread/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ \
|
||||
deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/libpng/ deps/xbrz/ \
|
||||
deps/libzip/ deps/imgui/ archive/ input/ log/
|
||||
|
||||
ifndef NOT_ARM
|
||||
RZDCY_MODULES += rec-ARM/
|
||||
endif
|
||||
|
||||
ifdef X86_REC
|
||||
RZDCY_MODULES += rec-x86/ emitter/
|
||||
endif
|
||||
|
||||
ifdef X64_REC
|
||||
RZDCY_MODULES += rec-x64/
|
||||
endif
|
||||
|
||||
ifdef CPP_REC
|
||||
RZDCY_MODULES += rec-cpp/
|
||||
endif
|
||||
|
||||
ifdef ARM64_REC
|
||||
RZDCY_MODULES += rec-ARM64/ deps/vixl/ deps/vixl/aarch64/
|
||||
endif
|
||||
|
||||
ifndef NO_REND
|
||||
RZDCY_MODULES += rend/gles/
|
||||
ifndef USE_GLES
|
||||
ifndef USE_DISPMANX
|
||||
RZDCY_MODULES += rend/gl4/
|
||||
endif
|
||||
endif
|
||||
else
|
||||
RZDCY_MODULES += rend/norend/
|
||||
endif
|
||||
|
||||
ifndef NO_NIXPROF
|
||||
RZDCY_MODULES += linux/nixprof/
|
||||
endif
|
||||
|
||||
ifdef FOR_ANDROID
|
||||
RZDCY_MODULES += android/ deps/libandroid/ linux/
|
||||
endif
|
||||
|
||||
ifdef USE_SDL
|
||||
RZDCY_MODULES += sdl/
|
||||
endif
|
||||
|
||||
ifdef FOR_LINUX
|
||||
ifndef UNIT_TESTS
|
||||
RZDCY_MODULES += linux-dist/
|
||||
endif
|
||||
RZDCY_MODULES += linux/
|
||||
endif
|
||||
|
||||
ifdef FOR_WINDOWS
|
||||
RZDCY_MODULES += windows/
|
||||
endif
|
||||
|
||||
ifdef FOR_PANDORA
|
||||
RZDCY_CFLAGS := \
|
||||
$(CFLAGS) -c -O3 \
|
||||
-DRELEASE -DPANDORA\
|
||||
-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp \
|
||||
-frename-registers -fsingle-precision-constant -ffast-math \
|
||||
-ftree-vectorize -fomit-frame-pointer
|
||||
RZDCY_CFLAGS += -march=armv7-a -mtune=cortex-a8 -mfpu=neon
|
||||
RZDCY_CFLAGS += -DTARGET_LINUX_ARMELv7
|
||||
else
|
||||
ifdef FOR_ANDROID
|
||||
RZDCY_CFLAGS := \
|
||||
$(CFLAGS) -c -O3 \
|
||||
-D_ANDROID -DRELEASE \
|
||||
-frename-registers -fsingle-precision-constant -ffast-math \
|
||||
-ftree-vectorize -fomit-frame-pointer
|
||||
|
||||
ifndef NOT_ARM
|
||||
RZDCY_CFLAGS += -march=armv7-a -mtune=cortex-a9 -mfpu=vfpv3-d16
|
||||
RZDCY_CFLAGS += -DTARGET_LINUX_ARMELv7
|
||||
else
|
||||
ifdef ISARM64
|
||||
RZDCY_CFLAGS += -march=armv8-a
|
||||
RZDCY_CFLAGS += -DTARGET_LINUX_ARMv8
|
||||
else
|
||||
ifdef ISMIPS
|
||||
RZDCY_CFLAGS += -DTARGET_LINUX_MIPS
|
||||
else
|
||||
RZDCY_CFLAGS += -DTARGET_LINUX_x86
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
else
|
||||
RZDCY_CFLAGS :=
|
||||
endif
|
||||
endif
|
||||
|
||||
RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/rend/gles -I$(RZDCY_SRC_DIR)/deps \
|
||||
-I$(RZDCY_SRC_DIR)/deps/vixl -I$(RZDCY_SRC_DIR)/khronos
|
||||
|
||||
ifdef USE_MODEM
|
||||
RZDCY_CFLAGS += -DENABLE_MODEM -I$(RZDCY_SRC_DIR)/deps/picotcp/include -I$(RZDCY_SRC_DIR)/deps/picotcp/modules
|
||||
RZDCY_MODULES += hw/modem/ deps/picotcp/modules/ deps/picotcp/stack/
|
||||
endif
|
||||
|
||||
ifdef NO_REC
|
||||
RZDCY_CFLAGS += -DTARGET_NO_REC
|
||||
else
|
||||
RZDCY_MODULES += hw/sh4/dyna/
|
||||
endif
|
||||
|
||||
ifdef USE_GLES
|
||||
RZDCY_CFLAGS += -DGLES -fPIC
|
||||
endif
|
||||
|
||||
ifdef HAS_SOFTREND
|
||||
RZDCY_CFLAGS += -DTARGET_SOFTREND
|
||||
RZDCY_MODULES += rend/soft/
|
||||
endif
|
||||
|
||||
ifdef CHD5_FLAC
|
||||
RZDCY_CFLAGS += -DCHD5_FLAC -I$(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/include/ -I$(RZDCY_SRC_DIR)/deps/flac/include
|
||||
RZDCY_CFLAGS += -DHAVE_CONFIG_H
|
||||
RZDCY_MODULES += deps/flac/src/libFLAC/
|
||||
endif
|
||||
|
||||
# 7-Zip/LZMA settings (CHDv5)
|
||||
ifdef CHD5_LZMA
|
||||
RZDCY_MODULES += deps/lzma/
|
||||
RZDCY_CFLAGS += -D_7ZIP_ST -DCHD5_LZMA
|
||||
endif
|
||||
|
||||
RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR)/deps/libpng -I$(RZDCY_SRC_DIR)/deps/zlib
|
||||
RZDCY_CFLAGS += -DXXH_INLINE_ALL -I$(RZDCY_SRC_DIR)/deps/xxhash
|
||||
|
||||
RZDCY_CXXFLAGS := $(RZDCY_CFLAGS) -fno-exceptions -fno-rtti -std=gnu++11
|
||||
|
||||
RZDCY_FILES := $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cpp))
|
||||
RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cc))
|
||||
RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.c))
|
||||
RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S))
|
||||
|
||||
$(VERSION_HEADER):
|
||||
echo "#define REICAST_VERSION \"`git describe --tags --always`\"" > $(VERSION_HEADER)
|
||||
echo "#define GIT_HASH \"`git rev-parse --short HEAD`\"" >> $(VERSION_HEADER)
|
||||
echo "#define BUILD_DATE \"`date '+%Y-%m-%d %H:%M:%S %Z'`\"" >> $(VERSION_HEADER)
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "emulator.h"
|
||||
#include "hw/sh4/sh4_if.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "hw/sh4/dyna/shil.h"
|
||||
#include "cfg/option.h"
|
||||
#include <array>
|
||||
#include <signal.h>
|
||||
#include <map>
|
||||
|
||||
#ifndef SIGTRAP
|
||||
#define SIGTRAP 5
|
||||
#endif
|
||||
#ifndef SIGBUS
|
||||
#define SIGBUS 7
|
||||
#endif
|
||||
|
||||
const std::array<Sh4RegType, 59> Sh4RegList {
|
||||
reg_r0,
|
||||
reg_r1,
|
||||
reg_r2,
|
||||
reg_r3,
|
||||
reg_r4,
|
||||
reg_r5,
|
||||
reg_r6,
|
||||
reg_r7,
|
||||
reg_r8,
|
||||
reg_r9,
|
||||
reg_r10,
|
||||
reg_r11,
|
||||
reg_r12,
|
||||
reg_r13,
|
||||
reg_r14,
|
||||
reg_r15,
|
||||
reg_nextpc,
|
||||
reg_pr,
|
||||
reg_gbr,
|
||||
reg_vbr,
|
||||
reg_mach,
|
||||
reg_macl,
|
||||
reg_sr_status,
|
||||
|
||||
reg_fpul,
|
||||
reg_fpscr,
|
||||
reg_fr_0, reg_fr_1, reg_fr_2, reg_fr_3, reg_fr_4, reg_fr_5, reg_fr_6, reg_fr_7,
|
||||
reg_fr_8, reg_fr_9, reg_fr_10, reg_fr_11, reg_fr_12, reg_fr_13, reg_fr_14, reg_fr_15,
|
||||
reg_ssr,
|
||||
reg_spc,
|
||||
// correct?
|
||||
reg_r0, reg_r1, reg_r2, reg_r3, reg_r4, reg_r5, reg_r6, reg_r7,
|
||||
reg_r0_Bank, reg_r1_Bank, reg_r2_Bank, reg_r3_Bank, reg_r4_Bank, reg_r5_Bank, reg_r6_Bank, reg_r7_Bank,
|
||||
};
|
||||
|
||||
class DebugAgent
|
||||
{
|
||||
public:
|
||||
struct Breakpoint {
|
||||
enum Type
|
||||
{
|
||||
BP_TYPE_SOFTWARE_BREAK,
|
||||
BP_TYPE_HARDWARE_BREAK,
|
||||
BP_TYPE_WRITE_WATCHPOINT,
|
||||
BP_TYPE_READ_WATCHPOINT,
|
||||
BP_TYPE_ACCESS_WATCHPOINT,
|
||||
BP_TYPE_COUNT
|
||||
};
|
||||
|
||||
Breakpoint() = default;
|
||||
Breakpoint(u16 type, u32 addr) : addr(addr), type(type) { }
|
||||
u32 addr = 0;
|
||||
u16 type = 0;
|
||||
u16 savedOp = 0;
|
||||
};
|
||||
|
||||
void doContinue(u32 pc = 1)
|
||||
{
|
||||
if (pc != 1)
|
||||
Sh4cntx.pc = pc;
|
||||
emu.start();
|
||||
}
|
||||
|
||||
void step()
|
||||
{
|
||||
bool restoreBreakpoint = removeMatchpoint(Breakpoint::BP_TYPE_SOFTWARE_BREAK, Sh4cntx.pc, 2);
|
||||
u32 savedPc = Sh4cntx.pc;
|
||||
emu.step();
|
||||
if (restoreBreakpoint)
|
||||
insertMatchpoint(Breakpoint::BP_TYPE_SOFTWARE_BREAK, savedPc, 2);
|
||||
}
|
||||
|
||||
void stepRange(u32 from, u32 to)
|
||||
{
|
||||
bool restoreBreakpoint = removeMatchpoint(Breakpoint::BP_TYPE_SOFTWARE_BREAK, Sh4cntx.pc, 2);
|
||||
u32 savedPc = Sh4cntx.pc;
|
||||
emu.stepRange(from, to);
|
||||
if (restoreBreakpoint)
|
||||
insertMatchpoint(Breakpoint::BP_TYPE_SOFTWARE_BREAK, savedPc, 2);
|
||||
}
|
||||
|
||||
int readAllRegs(u32 **regs)
|
||||
{
|
||||
static std::array<u32, Sh4RegList.size()> allregs;
|
||||
for (u32 i = 0; i < Sh4RegList.size(); i++)
|
||||
{
|
||||
if (Sh4RegList[i] == reg_sr_status)
|
||||
allregs[i] = Sh4cntx.sr.getFull();
|
||||
else if (Sh4RegList[i] != NoReg)
|
||||
allregs[i] = *GetRegPtr(Sh4cntx, Sh4RegList[i]);
|
||||
}
|
||||
*regs = &allregs[0];
|
||||
return allregs.size();
|
||||
}
|
||||
|
||||
void writeAllRegs(const std::vector<u32>& regs)
|
||||
{
|
||||
for (u32 i = 0; i < Sh4RegList.size(); i++)
|
||||
if (Sh4RegList[i] != NoReg)
|
||||
*GetRegPtr(Sh4cntx, Sh4RegList[i]) = regs[i];
|
||||
}
|
||||
|
||||
u32 readReg(u32 regNum)
|
||||
{
|
||||
if (regNum >= Sh4RegList.size())
|
||||
return 0;
|
||||
Sh4RegType reg = Sh4RegList[regNum];
|
||||
if (reg == reg_sr_status)
|
||||
return Sh4cntx.sr.getFull();
|
||||
if (reg != NoReg)
|
||||
return *GetRegPtr(Sh4cntx, reg);
|
||||
return 0;
|
||||
}
|
||||
void writeReg(u32 regNum, u32 value)
|
||||
{
|
||||
if (regNum >= Sh4RegList.size())
|
||||
return;
|
||||
Sh4RegType reg = Sh4RegList[regNum];
|
||||
if (reg == reg_sr_status)
|
||||
Sh4cntx.sr.setFull(value);
|
||||
else if (reg != NoReg)
|
||||
*GetRegPtr(Sh4cntx, reg) = value;
|
||||
}
|
||||
|
||||
const u8 *readMem(u32 addr, u32 len)
|
||||
{
|
||||
static std::vector<u8> data;
|
||||
data.resize(len);
|
||||
u8 *p = &data[0];
|
||||
while (len > 0)
|
||||
{
|
||||
if (len >= 4 && (addr & 3) == 0)
|
||||
{
|
||||
*(u32 *)p = ReadMem32_nommu(addr);
|
||||
addr += 4;
|
||||
len -= 4;
|
||||
p += 4;
|
||||
}
|
||||
else if (len >= 2 && (addr & 1) == 0)
|
||||
{
|
||||
*(u16 *)p = ReadMem16_nommu(addr);
|
||||
addr += 2;
|
||||
len -= 2;
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = ReadMem8_nommu(addr);
|
||||
addr++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
return &data[0];
|
||||
}
|
||||
void writeMem(u32 addr, const std::vector<u8>& data)
|
||||
{
|
||||
const u8 *p = &data[0];
|
||||
u32 len = data.size();
|
||||
while (len > 0)
|
||||
{
|
||||
if (len >= 4 && (addr & 3) == 0)
|
||||
{
|
||||
WriteMem32_nommu(addr, *(u32 *)p);
|
||||
addr += 4;
|
||||
len -= 4;
|
||||
p += 4;
|
||||
}
|
||||
else if (len >= 2 && (addr & 3) == 0)
|
||||
{
|
||||
WriteMem16_nommu(addr, *(u16 *)p);
|
||||
addr += 2;
|
||||
len -= 2;
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMem8_nommu(addr, *p++);
|
||||
addr++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool insertMatchpoint(Breakpoint::Type type, u32 addr, u32 len)
|
||||
{
|
||||
if (type == Breakpoint::BP_TYPE_SOFTWARE_BREAK && len != 2) {
|
||||
WARN_LOG(COMMON, "insertMatchpoint: length != 2: %d", len);
|
||||
return false;
|
||||
}
|
||||
// TODO other matchpoint types
|
||||
if (breakpoints[type].find(addr) != breakpoints[type].end())
|
||||
return true;
|
||||
breakpoints[type][addr] = Breakpoint(type, addr);
|
||||
breakpoints[type][addr].savedOp = ReadMem16_nommu(addr);
|
||||
WriteMem16_nommu(addr, 0xC308); // trapa #0x20
|
||||
return true;
|
||||
}
|
||||
bool removeMatchpoint(Breakpoint::Type type, u32 addr, u32 len)
|
||||
{
|
||||
if (type == Breakpoint::BP_TYPE_SOFTWARE_BREAK && len != 2) {
|
||||
WARN_LOG(COMMON, "removeMatchpoint: length != 2: %d", len);
|
||||
return false;
|
||||
}
|
||||
auto it = breakpoints[type].find(addr);
|
||||
if (it == breakpoints[type].end())
|
||||
return false;
|
||||
WriteMem16_nommu(addr, it->second.savedOp);
|
||||
breakpoints[type].erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 interrupt()
|
||||
{
|
||||
config::DynarecEnabled = false;
|
||||
exception = SIGINT;
|
||||
emu.stop();
|
||||
return exception;
|
||||
}
|
||||
|
||||
// called on the emu thread
|
||||
void debugTrap(u32 event)
|
||||
{
|
||||
exception = findException(event);
|
||||
Sh4cntx.pc -= 2; // FIXME delay slot
|
||||
}
|
||||
|
||||
void postDebugTrap()
|
||||
{
|
||||
// needed to join the emu thread since debugTrap() is called by it
|
||||
emu.stop();
|
||||
}
|
||||
|
||||
u32 currentException()
|
||||
{
|
||||
return exception;
|
||||
}
|
||||
|
||||
void restart()
|
||||
{
|
||||
emu.unloadGame();
|
||||
emu.loadGame(settings.content.path.c_str());
|
||||
emu.start();
|
||||
}
|
||||
|
||||
void detach()
|
||||
{
|
||||
emu.start();
|
||||
}
|
||||
|
||||
void kill()
|
||||
{
|
||||
dc_exit();
|
||||
}
|
||||
|
||||
void resetAgent()
|
||||
{
|
||||
stack.clear();
|
||||
}
|
||||
|
||||
int findException(u32 event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case 0x0A0: // Instruction/data TLB protection violation exception (read)
|
||||
case 0x0C0: // Data TLB protection violation exception (write)
|
||||
return SIGSEGV;
|
||||
case 0x0E0: // Instruction/data address error (read)
|
||||
case 0x100: // data address error (write)
|
||||
return SIGBUS;
|
||||
case 0x160: // trapa
|
||||
case 0x1E0: // User break before/after instruction execution
|
||||
return SIGTRAP;
|
||||
case 0x180: // General illegal instruction exception
|
||||
case 0x1A0: // Slot illegal instruction exception
|
||||
return SIGILL;
|
||||
case 0x120: // FPU exception
|
||||
case 0x800: // General FPU disable exception
|
||||
case 0x820: // Slot FPU disable exception
|
||||
return SIGFPE;
|
||||
default:
|
||||
return SIGABRT;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 *getStack(u32& size)
|
||||
{
|
||||
verify(!Sh4cntx.CpuRunning);
|
||||
size = stack.size() * 8;
|
||||
if (!stack.empty())
|
||||
return (const u32 *)&stack[0];
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void subroutineCall()
|
||||
{
|
||||
subroutineReturn();
|
||||
stack.push_back(std::make_pair(Sh4cntx.pc, Sh4cntx.r[15]));
|
||||
}
|
||||
|
||||
void subroutineReturn()
|
||||
{
|
||||
while (!stack.empty() && Sh4cntx.r[15] >= stack.back().second)
|
||||
stack.pop_back();
|
||||
}
|
||||
|
||||
u32 exception = 0;
|
||||
|
||||
std::map<u32, Breakpoint> breakpoints[Breakpoint::Type::BP_TYPE_COUNT];
|
||||
std::vector<std::pair<u32, u32>> stack;
|
||||
};
|
||||
@@ -1,989 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "types.h"
|
||||
|
||||
#ifdef GDB_SERVER
|
||||
#include "gdb_server.h"
|
||||
#include "debug_agent.h"
|
||||
#include "cfg/option.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include "util/shared_this.h"
|
||||
#include <asio.hpp>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace debugger {
|
||||
|
||||
constexpr u32 MAX_PACKET_LEN = 4096;
|
||||
|
||||
static u8 unpack(char c)
|
||||
{
|
||||
c = std::tolower(c);
|
||||
if (c <= '9')
|
||||
return c - '0';
|
||||
else
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
|
||||
u32 unpack(const char *s, int l)
|
||||
{
|
||||
u32 r = 0;
|
||||
for (int i = 0; i < l && *s != '\0'; i += 2, s += 2) {
|
||||
r |= (unpack(s[0]) << 4 | unpack(s[1])) << (i * 4);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
class GdbServer;
|
||||
|
||||
class Connection : public SharedThis<Connection>
|
||||
{
|
||||
public:
|
||||
asio::ip::tcp::socket& getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
void start() {
|
||||
asio::async_read_until(socket, asio::dynamic_string_buffer(message, MAX_PACKET_LEN), packetMatcher,
|
||||
std::bind(&Connection::handlePacket, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void send(const std::string& msg)
|
||||
{
|
||||
if (!msg.empty())
|
||||
{
|
||||
if (!outMessage.empty()) {
|
||||
WARN_LOG(COMMON, "Message dropped: %s. Already sending: %s", msg.c_str(), outMessage.c_str());
|
||||
return;
|
||||
}
|
||||
outMessage = msg;
|
||||
asio::async_write(socket, asio::buffer(outMessage),
|
||||
std::bind(&Connection::writeDone, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Connection(GdbServer& server, asio::io_context& io_context)
|
||||
: server(server), io_context(io_context), socket(io_context) {
|
||||
}
|
||||
|
||||
using iterator = asio::buffers_iterator<asio::const_buffers_1>;
|
||||
|
||||
std::pair<iterator, bool>
|
||||
static packetMatcher(iterator begin, iterator end)
|
||||
{
|
||||
if (begin == end)
|
||||
return std::make_pair(begin, false);
|
||||
iterator i = begin;
|
||||
if (*i == '\03')
|
||||
// break
|
||||
return std::make_pair(i + 1, true);
|
||||
if (*i != '$') {
|
||||
// unexpected, or ack/nack ('+', '-')
|
||||
return std::make_pair(i + 1, true);
|
||||
}
|
||||
++i;
|
||||
while (i != end && *i != '#')
|
||||
++i;
|
||||
if (i + 3 <= end)
|
||||
// 2 chars for CRC
|
||||
return std::make_pair(i + 3, true);
|
||||
|
||||
return std::make_pair(begin, false);
|
||||
}
|
||||
|
||||
void handlePacket(const std::error_code& ec, size_t len);
|
||||
|
||||
void writeDone(const std::error_code& ec, size_t len)
|
||||
{
|
||||
if (ec)
|
||||
WARN_LOG(COMMON, "Write error: %s", ec.message().c_str());
|
||||
outMessage.clear();
|
||||
}
|
||||
|
||||
GdbServer& server;
|
||||
asio::io_context& io_context;
|
||||
asio::ip::tcp::socket socket;
|
||||
std::string message;
|
||||
std::string outMessage;
|
||||
friend super;
|
||||
};
|
||||
|
||||
class TcpAcceptor
|
||||
{
|
||||
public:
|
||||
TcpAcceptor(GdbServer& server, asio::io_context& io_context, u16 port)
|
||||
: server(server), io_context(io_context),
|
||||
acceptor(asio::ip::tcp::acceptor(io_context,
|
||||
asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)))
|
||||
{
|
||||
asio::socket_base::reuse_address option(true);
|
||||
acceptor.set_option(option);
|
||||
start();
|
||||
}
|
||||
|
||||
private:
|
||||
void start()
|
||||
{
|
||||
Connection::Ptr newConnection = Connection::create(server, io_context);
|
||||
|
||||
acceptor.async_accept(newConnection->getSocket(),
|
||||
std::bind(&TcpAcceptor::handleAccept, this, newConnection, asio::placeholders::error));
|
||||
}
|
||||
void handleAccept(Connection::Ptr newConnection, const std::error_code& error);
|
||||
|
||||
GdbServer& server;
|
||||
asio::io_context& io_context;
|
||||
asio::ip::tcp::acceptor acceptor;
|
||||
};
|
||||
|
||||
class GdbServer
|
||||
{
|
||||
public:
|
||||
struct Error : public std::runtime_error {
|
||||
Error(const char *reason) : std::runtime_error(reason) {}
|
||||
};
|
||||
|
||||
void init(int port)
|
||||
{
|
||||
this->port = port;
|
||||
EventManager::listen(Event::Resume, emuEventCallback, this);
|
||||
EventManager::listen(Event::Terminate, emuEventCallback, this);
|
||||
}
|
||||
|
||||
void term()
|
||||
{
|
||||
EventManager::unlisten(Event::Resume, emuEventCallback, this);
|
||||
EventManager::unlisten(Event::Terminate, emuEventCallback, this);
|
||||
stop();
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (thread.joinable())
|
||||
return;
|
||||
DEBUG_LOG(COMMON, "GdbServer starting");
|
||||
io_context = std::make_unique<asio::io_context>();
|
||||
thread = std::thread(&GdbServer::serverThread, this);
|
||||
if (config::GDBWaitForConnection)
|
||||
{
|
||||
DEBUG_LOG(COMMON, "Waiting for GDB connection...");
|
||||
agentInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (thread.joinable())
|
||||
{
|
||||
DEBUG_LOG(COMMON, "GdbServer stopping");
|
||||
agent.resetAgent();
|
||||
io_context->stop();
|
||||
thread.join();
|
||||
io_context.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool isRunning() const {
|
||||
return thread.joinable();
|
||||
}
|
||||
|
||||
// called on the emu thread
|
||||
void debugTrap(u32 event)
|
||||
{
|
||||
if (!attached)
|
||||
return;
|
||||
agent.debugTrap(event);
|
||||
std::string pkt = handleCommand("?"); // report exception
|
||||
if (connection)
|
||||
io_context->post([this, pkt]() { connection->send(pkt); });
|
||||
postDebugTrapNeeded = true;
|
||||
throw Stop();
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 EXCEPT_NONE = 1;
|
||||
|
||||
void serverThread()
|
||||
{
|
||||
ThreadName _("GdbServer");
|
||||
try
|
||||
{
|
||||
TcpAcceptor server(*this, *io_context, port);
|
||||
io_context->run();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
ERROR_LOG(COMMON, "Gdb server exception: %s", e.what());
|
||||
}
|
||||
attached = false;
|
||||
connection.reset();
|
||||
}
|
||||
|
||||
std::string handleCommand(const std::string& packet)
|
||||
{
|
||||
try {
|
||||
if (postDebugTrapNeeded)
|
||||
{
|
||||
postDebugTrapNeeded = false;
|
||||
try {
|
||||
agent.postDebugTrap();
|
||||
} catch (const FlycastException& e) {
|
||||
throw Error(e.what());
|
||||
}
|
||||
}
|
||||
if (packet.empty())
|
||||
return "";
|
||||
|
||||
DEBUG_LOG(NETWORK, "gdb: recv %s", packet.c_str());
|
||||
std::vector<std::string> replies;
|
||||
switch (packet[0])
|
||||
{
|
||||
case '!': // Enable extended mode
|
||||
replies.push_back("OK");
|
||||
break;
|
||||
case '?': // Sent when connection is first established to query the reason the target halted
|
||||
replies.push_back(reportException());
|
||||
break;
|
||||
case 'A': // Initialized argv[] array passed into program. not supported
|
||||
replies.push_back("E01");
|
||||
break;
|
||||
case 'b': // Change the serial line speed to baud. deprecated
|
||||
break;
|
||||
case 'B': // Set or clear a breakpoint at addr. deprecated
|
||||
break;
|
||||
case 'c': // Continue at addr, which is the address to resume.
|
||||
// If addr is omitted, resume at current address
|
||||
doContinue(packet);
|
||||
break;
|
||||
case 'C': // Continue with signal sig
|
||||
doContinue(packet);
|
||||
break;
|
||||
case 'd': // Toggle debug flag. deprecated
|
||||
break;
|
||||
case 'D': // Detach GDB from the remote system
|
||||
replies.push_back("OK");
|
||||
agent.detach();
|
||||
break;
|
||||
case 'F': // File-I/O protocol extension not currently supported
|
||||
break;
|
||||
case 'g': // Read general registers
|
||||
replies.push_back(readAllRegs());
|
||||
break;
|
||||
case 'G': // Write general registers
|
||||
replies.push_back(writeAllRegs(packet));
|
||||
break;
|
||||
case 'H': // Set thread for subsequent operations
|
||||
replies.push_back("OK");
|
||||
break;
|
||||
case 'i': // Step the remote target by a single clock cycle
|
||||
case 'I': // Signal, then cycle step
|
||||
// not supported
|
||||
replies.push_back("");
|
||||
break;
|
||||
case 'k': // Kill request. Stop process/system
|
||||
agent.kill();
|
||||
break;
|
||||
case 'm': // Read length addressable memory units
|
||||
replies.push_back(readMem(packet));
|
||||
break;
|
||||
case 'M': // Write length addressable memory units
|
||||
replies.push_back(writeMem(packet));
|
||||
break;
|
||||
case 'p': // Read the value of register
|
||||
replies.push_back(readReg(packet));
|
||||
break;
|
||||
case 'P': // Write register
|
||||
replies.push_back(writeReg(packet));
|
||||
break;
|
||||
case 'q': // General query packets
|
||||
{
|
||||
auto v = query(packet);
|
||||
replies.insert(replies.end(), v.begin(), v.end());
|
||||
}
|
||||
break;
|
||||
case 'Q': // General set packets
|
||||
{
|
||||
auto v = set(packet);
|
||||
replies.insert(replies.end(), v.begin(), v.end());
|
||||
}
|
||||
break;
|
||||
case 'r': // Reset the entire system. Deprecated (use 'R' instead)
|
||||
break;
|
||||
case 'R': // Restart the program being debugged.
|
||||
restart();
|
||||
break;
|
||||
case 's': // Single step
|
||||
replies.push_back(step(EXCEPT_NONE));
|
||||
break;
|
||||
case 'S': // Step with signal
|
||||
replies.push_back(step());
|
||||
break;
|
||||
case 't': // Search backwards. unsupported
|
||||
break;
|
||||
case 'T': // Find out if the thread is alive
|
||||
replies.push_back("OK");
|
||||
break;
|
||||
case 'v': // 'v' packets to control execution
|
||||
{
|
||||
auto v = vpacket(packet);
|
||||
replies.insert(replies.end(), v.begin(), v.end());
|
||||
}
|
||||
break;
|
||||
case 'X': // Write binary data to memory
|
||||
replies.push_back(writeMemBin(packet));
|
||||
break;
|
||||
case 'z': // Remove a breakpoint/watchpoint.
|
||||
replies.push_back(removeMatchpoint(packet));
|
||||
break;
|
||||
case 'Z': // Insert a breakpoint/watchpoint.
|
||||
replies.push_back(insertMatchpoint(packet));
|
||||
break;
|
||||
case 3:
|
||||
replies.push_back(interrupt());
|
||||
break;
|
||||
default:
|
||||
// Unknown commands are ignored
|
||||
WARN_LOG(COMMON, "Unknown gdb command: %s", packet.c_str());
|
||||
break;
|
||||
}
|
||||
std::string data;
|
||||
for (const std::string& pkt : replies)
|
||||
{
|
||||
data.push_back('$');
|
||||
u8 checksum = 0;
|
||||
for (char c : pkt)
|
||||
{
|
||||
if (c == '$' || c == '#' || c == '*' || c == '}')
|
||||
{
|
||||
c ^= 0x20;
|
||||
checksum += (u8)'}';
|
||||
data.push_back('}');
|
||||
}
|
||||
checksum += (u8)c;
|
||||
data.push_back(c);
|
||||
}
|
||||
data.push_back('#');
|
||||
char s[9];
|
||||
snprintf(s, sizeof(s), "%02x", checksum);
|
||||
data += s;
|
||||
}
|
||||
DEBUG_LOG(NETWORK, "gdb: sent %s", data.c_str());
|
||||
|
||||
return data;
|
||||
} catch (const Error& e) {
|
||||
ERROR_LOG(COMMON, "%s", e.what());
|
||||
attached = false;
|
||||
connection.reset();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
std::string reportException()
|
||||
{
|
||||
char s[4];
|
||||
snprintf(s, sizeof(s), "S%02X", agent.currentException());
|
||||
return s;
|
||||
}
|
||||
|
||||
void doContinue(const std::string& pkt)
|
||||
{
|
||||
if (pkt[0] != 'c') {
|
||||
WARN_LOG(COMMON, "Continue with signal not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pkt == "c")
|
||||
agent.doContinue();
|
||||
else
|
||||
{
|
||||
// Get the pc at which to resume
|
||||
u32 addr;
|
||||
if (sscanf(pkt.c_str(), "c%x", &addr) != 1)
|
||||
{
|
||||
WARN_LOG(COMMON, "Continue address invalid %s", pkt.c_str());
|
||||
return;
|
||||
}
|
||||
agent.doContinue(addr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string readAllRegs()
|
||||
{
|
||||
u32 *regs;
|
||||
int c = agent.readAllRegs(®s);
|
||||
std::string outpkt;
|
||||
for (int i = 0; i < c; i++)
|
||||
outpkt += pack(regs[i]);
|
||||
return outpkt;
|
||||
}
|
||||
|
||||
std::string writeAllRegs(const std::string& pkt)
|
||||
{
|
||||
std::vector<u32> regs;
|
||||
for (auto it = pkt.begin() + 1; it <= pkt.end() - 8; it += 8)
|
||||
regs.push_back(unpack(&*it, 8));
|
||||
|
||||
agent.writeAllRegs(regs);
|
||||
return "OK";
|
||||
}
|
||||
|
||||
std::string readMem(const std::string& pkt)
|
||||
{
|
||||
u32 addr;
|
||||
u32 len;
|
||||
if (sscanf(pkt.c_str(), "m%x,%x:", &addr, &len) != 2)
|
||||
{
|
||||
WARN_LOG(COMMON, "readMem: invalid packet %s", pkt.c_str());
|
||||
return "E01";
|
||||
}
|
||||
const u8 *mem = agent.readMem(addr, len);
|
||||
std::string outpkt;
|
||||
for (u32 i = 0; i < len; i++)
|
||||
{
|
||||
char s[3];
|
||||
snprintf(s, sizeof(s), "%02x", mem[i]);
|
||||
outpkt += s;
|
||||
}
|
||||
return outpkt;
|
||||
}
|
||||
|
||||
std::string writeMem(const std::string& pkt)
|
||||
{
|
||||
u32 addr;
|
||||
u32 len;
|
||||
if (sscanf(pkt.c_str(), "M%x,%x:", &addr, &len) != 2)
|
||||
{
|
||||
WARN_LOG(COMMON, "writeMem: invalid packet %s", pkt.c_str());
|
||||
return "E01";
|
||||
}
|
||||
std::vector<u8> data(len);
|
||||
const char *p = &pkt[pkt.find(':')] + 1;
|
||||
for (u32 i = 0; i < len; i++, p += 2)
|
||||
{
|
||||
u32 b;
|
||||
sscanf(p,"%2x", &b);
|
||||
data[i] = (u8)b;
|
||||
}
|
||||
agent.writeMem(addr, data);
|
||||
return "OK";
|
||||
}
|
||||
|
||||
std::string writeMemBin(const std::string& pkt)
|
||||
{
|
||||
u32 addr;
|
||||
u32 len;
|
||||
if (sscanf(pkt.c_str(), "X%x,%x:", &addr, &len) != 2)
|
||||
{
|
||||
WARN_LOG(COMMON, "writeMemBin invalid command: %s", pkt.c_str());
|
||||
return "E01";
|
||||
}
|
||||
const char *p = &pkt[pkt.find(':')] + 1;
|
||||
std::vector<u8> data;
|
||||
data.reserve(pkt.length() - (p - &pkt[0]));
|
||||
for (; p <= &pkt.back(); p++)
|
||||
{
|
||||
u8 b = *p;
|
||||
if (b == '}')
|
||||
{
|
||||
b = *++p ^ 0x20;
|
||||
}
|
||||
data.push_back(b);
|
||||
}
|
||||
agent.writeMem(addr, data);
|
||||
return "OK";
|
||||
}
|
||||
|
||||
std::string readReg(const std::string& pkt)
|
||||
{
|
||||
u32 regNum;
|
||||
if (sscanf(pkt.c_str(), "p%x", ®Num) != 1)
|
||||
{
|
||||
WARN_LOG(COMMON, "readReg: invalid packet %s", pkt.c_str());
|
||||
return "E01";
|
||||
}
|
||||
u32 v = agent.readReg(regNum);
|
||||
return pack(v);
|
||||
}
|
||||
|
||||
std::string writeReg(const std::string& pkt)
|
||||
{
|
||||
u32 regNum;
|
||||
char vstr[9];
|
||||
if (sscanf(pkt.c_str(), "P%x=%8s", ®Num, vstr) != 2)
|
||||
{
|
||||
WARN_LOG(COMMON, "writeReg: invalid packet %s", pkt.c_str());
|
||||
return "E01";
|
||||
}
|
||||
agent.writeReg(regNum, unpack(vstr, 8));
|
||||
return "OK";
|
||||
}
|
||||
|
||||
std::vector<std::string> query(const std::string& pkt)
|
||||
{
|
||||
if (pkt == "qC")
|
||||
// Return the current thread ID. 0 is "any thread"
|
||||
return { "QC0.01" };
|
||||
else if (pkt.rfind("qCRC", 0) == 0)
|
||||
{
|
||||
WARN_LOG(COMMON, "CRC compute not supported %s", pkt.c_str());
|
||||
return { "E01" };
|
||||
}
|
||||
else if (pkt == "qfThreadInfo")
|
||||
// Obtain a list of all active thread IDs (first call)
|
||||
return { "m0" };
|
||||
else if (pkt == "qsThreadInfo")
|
||||
// Obtain a list of all active thread IDs (subsequent calls -> 'l' == end of list)
|
||||
return { "l" };
|
||||
else if (pkt.rfind("qGetTLSAddr:", 0) == 0)
|
||||
// Fetch the address associated with thread local storage
|
||||
return { "" };
|
||||
else if (pkt.rfind("qL", 0) == 0)
|
||||
// Obtain thread information. deprecated
|
||||
return { "qM001" };
|
||||
else if (pkt == "qOffsets")
|
||||
// Get section offsets. Not supported
|
||||
return { "" };
|
||||
else if (pkt.rfind("qP", 0) == 0)
|
||||
// Returns information on thread. deprecated
|
||||
return { "" };
|
||||
else if (pkt.rfind("qRcmd,", 0) == 0)
|
||||
{
|
||||
std::string customCmd;
|
||||
for (const char *p = pkt.c_str() + 6; *p != '\0'; p += 2)
|
||||
customCmd += (char)unpack(p, 2);
|
||||
DEBUG_LOG(COMMON, "query: custom command %s", customCmd.c_str());
|
||||
if (customCmd == "reset")
|
||||
restart();
|
||||
else if (customCmd == "stack")
|
||||
{
|
||||
u32 len;
|
||||
const u32 *data = agent.getStack(len);
|
||||
len /= 4;
|
||||
|
||||
#if _MSC_VER // Non-const array size is a GCC extension
|
||||
assert((len * 9 * 2 + 1) < MAX_PACKET_LEN);
|
||||
char reply[MAX_PACKET_LEN];
|
||||
#else
|
||||
char reply[len * 9 * 2 + 1];
|
||||
#endif
|
||||
char *r = reply;
|
||||
for (u32 i = 0; i < len; i++)
|
||||
{
|
||||
char n[10];
|
||||
snprintf(n, sizeof(n), "%08x ", *data++);
|
||||
for (char *p = n; *p != 0; p++)
|
||||
{
|
||||
*r++ = packnb((*p >> 4) & 0xf);
|
||||
*r++ = packnb(*p & 0xf);
|
||||
}
|
||||
}
|
||||
*r = 0;
|
||||
return { reply };
|
||||
}
|
||||
else
|
||||
return { "" };
|
||||
}
|
||||
else if (pkt.rfind("qSupported", 0) == 0)
|
||||
{
|
||||
// Tell the remote stub about features supported by GDB,
|
||||
// and query the stub for features it supports
|
||||
char qsupported[128];
|
||||
snprintf(qsupported, 128, "PacketSize=%i;vContSupported+", MAX_PACKET_LEN);
|
||||
return { qsupported };
|
||||
}
|
||||
else if (pkt.rfind("qSymbol:", 0) == 0)
|
||||
// Notify the target that GDB is prepared to serve symbol lookup requests
|
||||
return { "OK" };
|
||||
else if (pkt.rfind("qThreadExtraInfo,", 0) == 0)
|
||||
{
|
||||
// Obtain from the target OS a printable string description of thread attributes
|
||||
char s[19];
|
||||
snprintf(s, sizeof(s), "%02x%02x%02x%02x%02x%02x%02x%02x%02x", 'R', 'u', 'n', 'n', 'a', 'b', 'l', 'e', 0);
|
||||
return { std::string(s, 18) };
|
||||
}
|
||||
else if (pkt.rfind("qXfer:", 0) == 0)
|
||||
// Read uninterpreted bytes from the target’s special data area identified by the keyword object
|
||||
return { "" };
|
||||
else if (pkt.rfind("qAttached", 0) == 0)
|
||||
// Return an indication of whether the remote server attached to an existing process
|
||||
// or created a new process
|
||||
return { "1" }; // existing process
|
||||
else if (pkt.rfind("qTfV", 0) == 0)
|
||||
// request data about trace state variables
|
||||
return { "" };
|
||||
else if (pkt.rfind("qTfP", 0) == 0)
|
||||
// request data about tracepoints
|
||||
return { "" };
|
||||
else if (pkt.rfind("qTStatus", 0) == 0)
|
||||
// Ask the stub if there is a trace experiment running right now
|
||||
return { "" };
|
||||
WARN_LOG(COMMON, "query not supported %s", pkt.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> set(const std::string& pkt)
|
||||
{
|
||||
if (pkt.rfind("QPassSignals:", 0) == 0)
|
||||
// Passing signals not supported
|
||||
return { "" };
|
||||
else if (pkt.rfind("QTDP", 0) == 0
|
||||
|| pkt.rfind("QFrame", 0) == 0
|
||||
|| pkt.rfind("QTStart", 0) == 0
|
||||
|| pkt.rfind("QTStop", 0) == 0
|
||||
|| pkt.rfind("QTinit", 0) == 0
|
||||
|| pkt.rfind("QTro", 0) == 0)
|
||||
// No tracepoint feature supported
|
||||
return { "" };
|
||||
WARN_LOG(COMMON, "set not supported %s", pkt.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> vpacket(const std::string& pkt)
|
||||
{
|
||||
if (pkt.rfind("vAttach;", 0) == 0)
|
||||
return { "S05" };
|
||||
else if (pkt.rfind("vCont?", 0) == 0)
|
||||
// supported vCont actions - (c)ontinue, (s)tep, (r)ange-step
|
||||
return { "vCont;c;s;r" };
|
||||
else if (pkt.rfind("vCont", 0) == 0)
|
||||
{
|
||||
std::string vContCmd = pkt.substr(strlen("vCont;"));
|
||||
switch (vContCmd[0])
|
||||
{
|
||||
case 'c':
|
||||
doContinue(vContCmd.substr(0, 1));
|
||||
return {};
|
||||
case 's':
|
||||
return { step(EXCEPT_NONE) };
|
||||
case 'r':
|
||||
{
|
||||
std::vector<std::string> replies;
|
||||
u32 from, to;
|
||||
if (sscanf(vContCmd.c_str(), "r%x,%x", &from, &to) == 2)
|
||||
{
|
||||
auto v = stepRange(from, to);
|
||||
replies.insert(replies.end(), v.begin(), v.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(COMMON, "Unsupported vCont:r format %s", pkt.c_str());
|
||||
doContinue("c");
|
||||
}
|
||||
return replies;
|
||||
}
|
||||
default:
|
||||
WARN_LOG(COMMON, "vCont action not supported %s", pkt.c_str());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (pkt.rfind("vFile:", 0) == 0)
|
||||
// not supported
|
||||
return { "" };
|
||||
else if (pkt.rfind("vFlashErase:", 0) == 0)
|
||||
// not supported
|
||||
return { "E01" };
|
||||
else if (pkt.rfind("vFlashWrite:", 0) == 0)
|
||||
// not supported
|
||||
return { "E01" };
|
||||
else if (pkt.rfind("vFlashDone:", 0) == 0)
|
||||
// not supported
|
||||
return { "E01" };
|
||||
else if (pkt.rfind("vRun;", 0) == 0)
|
||||
{
|
||||
if (pkt != "vRun;")
|
||||
WARN_LOG(COMMON, "unexpected vRun args ignored: %s", pkt.c_str());
|
||||
agent.restart();
|
||||
return { "S05" };
|
||||
}
|
||||
else if (pkt.rfind("vKill", 0) == 0)
|
||||
{
|
||||
agent.kill();
|
||||
return { "OK" };
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pkt != "vMustReplyEmpty")
|
||||
WARN_LOG(COMMON, "unknown v packet: %s", pkt.c_str());
|
||||
return { "" };
|
||||
}
|
||||
}
|
||||
|
||||
void restart() {
|
||||
agent.restart();
|
||||
}
|
||||
|
||||
std::string step(u32 what = 0)
|
||||
{
|
||||
try {
|
||||
agent.step();
|
||||
return "S05";
|
||||
} catch (const FlycastException& e) {
|
||||
throw Error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> stepRange(u32 from, u32 to)
|
||||
{
|
||||
try {
|
||||
if (from + 2 >= to)
|
||||
agent.step();
|
||||
else
|
||||
agent.stepRange(from, to);
|
||||
return { "OK", "S05" };
|
||||
} catch (const FlycastException& e) {
|
||||
throw Error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::string insertMatchpoint(const std::string& pkt)
|
||||
{
|
||||
u32 type;
|
||||
u32 addr;
|
||||
u32 len;
|
||||
if (sscanf(pkt.c_str(), "Z%1d,%x,%1d", &type, &addr, &len) != 3) {
|
||||
WARN_LOG(COMMON, "insertMatchpoint: unknown packet: %s", pkt.c_str());
|
||||
return "E01";
|
||||
}
|
||||
switch (type) {
|
||||
case DebugAgent::Breakpoint::BP_TYPE_SOFTWARE_BREAK: // soft bp
|
||||
if (agent.insertMatchpoint(DebugAgent::Breakpoint::BP_TYPE_SOFTWARE_BREAK,
|
||||
addr, len))
|
||||
return "OK";
|
||||
else
|
||||
return "E01";
|
||||
break;
|
||||
case DebugAgent::Breakpoint::BP_TYPE_HARDWARE_BREAK: // hardware bp
|
||||
return "";
|
||||
break;
|
||||
case DebugAgent::Breakpoint::BP_TYPE_WRITE_WATCHPOINT: // write watchpoint
|
||||
return "";
|
||||
break;
|
||||
case DebugAgent::Breakpoint::BP_TYPE_READ_WATCHPOINT: // read watchpoint
|
||||
return "";
|
||||
break;
|
||||
case DebugAgent::Breakpoint::BP_TYPE_ACCESS_WATCHPOINT: // access watchpoint
|
||||
return "";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string removeMatchpoint(const std::string& pkt)
|
||||
{
|
||||
u32 type;
|
||||
u32 addr;
|
||||
u32 len;
|
||||
if (sscanf(pkt.c_str(), "z%1d,%x,%1d", &type, &addr, &len) != 3) {
|
||||
WARN_LOG(COMMON, "removeMatchpoint: unknown packet: %s", pkt.c_str());
|
||||
return "E01";
|
||||
}
|
||||
switch (type) {
|
||||
case 0: // soft bp
|
||||
if (agent.removeMatchpoint(DebugAgent::Breakpoint::BP_TYPE_SOFTWARE_BREAK,
|
||||
addr, len))
|
||||
return "OK";
|
||||
else
|
||||
return "E01";
|
||||
break;
|
||||
case 1: // hardware bp
|
||||
return "";
|
||||
break;
|
||||
case 2: // write watchpoint
|
||||
return "";
|
||||
break;
|
||||
case 3: // read watchpoint
|
||||
return "";
|
||||
break;
|
||||
case 4: // access watchpoint
|
||||
return "";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string interrupt()
|
||||
{
|
||||
u32 signal = agentInterrupt();
|
||||
char s[10];
|
||||
snprintf(s, sizeof(s), "S%02x", signal);
|
||||
return s;
|
||||
}
|
||||
|
||||
char packnb(u8 b)
|
||||
{
|
||||
if (b < 10)
|
||||
return '0' + b;
|
||||
else
|
||||
return 'a' + b - 10;
|
||||
}
|
||||
|
||||
std::string packb(u8 v)
|
||||
{
|
||||
std::string s(1, packnb((v >> 4) & 0xf));
|
||||
s += packnb(v & 0xf);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string pack(u32 v) {
|
||||
return packb(v & 0xff) + packb((v >> 8) & 0xff)
|
||||
+ packb((v >> 16) & 0xff) + packb((v >> 24) & 0xff);
|
||||
}
|
||||
|
||||
u32 agentInterrupt()
|
||||
{
|
||||
try {
|
||||
return agent.interrupt();
|
||||
} catch (const FlycastException& e) {
|
||||
throw Error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void clientConnected(Connection::Ptr connection) {
|
||||
attached = true;
|
||||
this->connection = connection;
|
||||
agentInterrupt();
|
||||
}
|
||||
|
||||
static void emuEventCallback(Event event, void *arg)
|
||||
{
|
||||
GdbServer *gdbServer = static_cast<GdbServer*>(arg);
|
||||
switch (event)
|
||||
{
|
||||
case Event::Resume:
|
||||
try {
|
||||
if (!gdbServer->isRunning())
|
||||
gdbServer->run();
|
||||
} catch (const GdbServer::Error& e) {
|
||||
ERROR_LOG(COMMON, "%s", e.what());
|
||||
}
|
||||
break;
|
||||
case Event::Terminate:
|
||||
gdbServer->stop();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool attached = false;
|
||||
bool postDebugTrapNeeded = false;
|
||||
std::thread thread;
|
||||
std::unique_ptr<asio::io_context> io_context;
|
||||
int port = DEFAULT_PORT;
|
||||
Connection::Ptr connection;
|
||||
friend class TcpAcceptor;
|
||||
friend class Connection;
|
||||
|
||||
public:
|
||||
DebugAgent agent;
|
||||
};
|
||||
|
||||
static GdbServer gdbServer;
|
||||
|
||||
void TcpAcceptor::handleAccept(Connection::Ptr newConnection, const std::error_code& error)
|
||||
{
|
||||
if (!error) {
|
||||
server.clientConnected(newConnection);
|
||||
newConnection->start();
|
||||
}
|
||||
start();
|
||||
}
|
||||
|
||||
void Connection::handlePacket(const std::error_code& ec, size_t len)
|
||||
{
|
||||
std::string msg = message.substr(0, len);
|
||||
message = message.substr(len);
|
||||
if (ec || len == 0)
|
||||
{
|
||||
// terminate the connection
|
||||
if (ec != asio::error::eof && ec != asio::error::operation_aborted)
|
||||
WARN_LOG(NETWORK, "Read error %s", ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (msg[0] == '\03') { // break
|
||||
send(server.handleCommand(msg));
|
||||
}
|
||||
else if (msg[0] != '$') {
|
||||
// Ignore unexpected chars
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 cksum = 0;
|
||||
for (unsigned i = 1; i < len - 3; i++)
|
||||
cksum += (u8)msg[i];
|
||||
if (cksum != (unpack(msg[len - 2]) << 4 | unpack(msg[len - 1])))
|
||||
{
|
||||
// Invalid checksum
|
||||
WARN_LOG(COMMON, "Connection::handlePacket: invalid checksum: [%s]", msg.c_str());
|
||||
send("-");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Positive ack
|
||||
std::string reply = "+";
|
||||
reply += server.handleCommand(msg.substr(1, msg.length() - 4));
|
||||
send(reply);
|
||||
}
|
||||
}
|
||||
start();
|
||||
} catch (...) {
|
||||
// terminate the connection
|
||||
}
|
||||
}
|
||||
|
||||
void init(int port)
|
||||
{
|
||||
gdbServer.init(port);
|
||||
}
|
||||
|
||||
void term()
|
||||
{
|
||||
gdbServer.term();
|
||||
}
|
||||
|
||||
void debugTrap(u32 event)
|
||||
{
|
||||
gdbServer.debugTrap(event);
|
||||
}
|
||||
|
||||
void subroutineCall()
|
||||
{
|
||||
gdbServer.agent.subroutineCall();
|
||||
}
|
||||
|
||||
void subroutineReturn()
|
||||
{
|
||||
gdbServer.agent.subroutineReturn();
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace debugger {
|
||||
|
||||
// exception thrown in response to trap
|
||||
struct Stop { };
|
||||
|
||||
static const int DEFAULT_PORT = 3263;
|
||||
|
||||
#ifdef GDB_SERVER
|
||||
|
||||
void init(int port);
|
||||
void term();
|
||||
void run();
|
||||
void debugTrap(u32 event);
|
||||
void subroutineCall();
|
||||
void subroutineReturn();
|
||||
|
||||
#else
|
||||
static inline void init(int port) {}
|
||||
static inline void term() {}
|
||||
static inline void run() {}
|
||||
static inline void debugTrap(u32 event) {}
|
||||
static inline void subroutineCall() {}
|
||||
static inline void subroutineReturn() {}
|
||||
#endif
|
||||
}
|
||||
Submodule core/deps/DreamPicoPort-API deleted from e06d9f6c59.
Submodule core/deps/SDL deleted from 00d4f1c2c4.
Submodule core/deps/Spout deleted from 2110a918e7.
Submodule core/deps/Syphon deleted from 53ec412a78.
Submodule core/deps/Vulkan-Headers deleted from b5c8f99619.
Submodule core/deps/VulkanMemoryAllocator deleted from 1d8f600fd4.
Submodule core/deps/asio deleted from d3402006e8.
Submodule core/deps/breakpad deleted from 1ab24bcc81.
+74
-78
@@ -2,31 +2,41 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "deps/coreio/coreio.h"
|
||||
#include "cdipsr.h"
|
||||
|
||||
// Global variables
|
||||
|
||||
unsigned long temp_value;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsigned long ask_type(hostfs::File *fsource, long header_position)
|
||||
#define FILE core_file
|
||||
#define fread(buff,sz,cnt,fc) core_fread(fc,buff,sz*cnt)
|
||||
#define fseek core_fseek
|
||||
#define ftell core_ftell
|
||||
|
||||
unsigned long ask_type(FILE *fsource, long header_position)
|
||||
{
|
||||
|
||||
unsigned char filename_length;
|
||||
unsigned long track_mode;
|
||||
uint32_t temp_value = 0;
|
||||
|
||||
fsource->seek(header_position, SEEK_SET);
|
||||
fsource->read(&temp_value, 4, 1);
|
||||
fseek(fsource, header_position, SEEK_SET);
|
||||
fread(&temp_value, 4, 1, fsource);
|
||||
if (temp_value != 0)
|
||||
fsource->seek(8, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
fsource->seek(24, SEEK_CUR);
|
||||
fsource->read(&filename_length, 1, 1);
|
||||
fsource->seek(filename_length, SEEK_CUR);
|
||||
fsource->seek(19, SEEK_CUR);
|
||||
fsource->read(&temp_value, 4, 1);
|
||||
fseek(fsource, 8, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
fseek(fsource, 24, SEEK_CUR);
|
||||
fread(&filename_length, 1, 1, fsource);
|
||||
fseek(fsource, filename_length, SEEK_CUR);
|
||||
fseek(fsource, 19, SEEK_CUR);
|
||||
fread(&temp_value, 4, 1, fsource);
|
||||
if (temp_value == 0x80000000)
|
||||
fsource->seek(8, SEEK_CUR); // DJ4
|
||||
fsource->seek(16, SEEK_CUR);
|
||||
fsource->read(&track_mode, 4, 1);
|
||||
fsource->seek(header_position, SEEK_SET);
|
||||
fseek(fsource, 8, SEEK_CUR); // DJ4
|
||||
fseek(fsource, 16, SEEK_CUR);
|
||||
fread(&track_mode, 4, 1, fsource);
|
||||
fseek(fsource, header_position, SEEK_SET);
|
||||
return (track_mode);
|
||||
}
|
||||
|
||||
@@ -34,48 +44,41 @@ uint32_t temp_value = 0;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
bool CDI_read_track (hostfs::File *fsource, image_s *image, track_s *track)
|
||||
void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
{
|
||||
|
||||
unsigned char TRACK_START_MARK[10] = { 0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
unsigned char current_start_mark[10];
|
||||
uint32_t temp_value = 0;
|
||||
|
||||
fsource->read(&temp_value, 4, 1);
|
||||
fread(&temp_value, 4, 1, fsource);
|
||||
if (temp_value != 0)
|
||||
fsource->seek(8, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
fseek(fsource, 8, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
|
||||
fsource->read(¤t_start_mark, 10, 1);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
|
||||
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
|
||||
return false;
|
||||
}
|
||||
fread(¤t_start_mark, 10, 1, fsource);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
|
||||
|
||||
fsource->read(¤t_start_mark, 10, 1);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
|
||||
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
|
||||
return false;
|
||||
}
|
||||
fread(¤t_start_mark, 10, 1, fsource);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
|
||||
|
||||
fsource->seek(4, SEEK_CUR);
|
||||
fsource->read(&track->filename_length, 1, 1);
|
||||
fsource->seek(track->filename_length, SEEK_CUR);
|
||||
fsource->seek(11, SEEK_CUR);
|
||||
fsource->seek(4, SEEK_CUR);
|
||||
fsource->seek(4, SEEK_CUR);
|
||||
fsource->read(&temp_value, 4, 1);
|
||||
fseek(fsource, 4, SEEK_CUR);
|
||||
fread(&track->filename_length, 1, 1, fsource);
|
||||
fseek(fsource, track->filename_length, SEEK_CUR);
|
||||
fseek(fsource, 11, SEEK_CUR);
|
||||
fseek(fsource, 4, SEEK_CUR);
|
||||
fseek(fsource, 4, SEEK_CUR);
|
||||
fread(&temp_value, 4, 1, fsource);
|
||||
if (temp_value == 0x80000000)
|
||||
fsource->seek(8, SEEK_CUR); // DJ4
|
||||
fsource->seek(2, SEEK_CUR);
|
||||
fsource->read(&track->pregap_length, 4, 1);
|
||||
fsource->read(&track->length, 4, 1);
|
||||
fsource->seek(6, SEEK_CUR);
|
||||
fsource->read(&track->mode, 4, 1);
|
||||
fsource->seek(12, SEEK_CUR);
|
||||
fsource->read(&track->start_lba, 4, 1);
|
||||
fsource->read(&track->total_length, 4, 1);
|
||||
fsource->seek(16, SEEK_CUR);
|
||||
fsource->read(&track->sector_size_value, 4, 1);
|
||||
fseek(fsource, 8, SEEK_CUR); // DJ4
|
||||
fseek(fsource, 2, SEEK_CUR);
|
||||
fread(&track->pregap_length, 4, 1, fsource);
|
||||
fread(&track->length, 4, 1, fsource);
|
||||
fseek(fsource, 6, SEEK_CUR);
|
||||
fread(&track->mode, 4, 1, fsource);
|
||||
fseek(fsource, 12, SEEK_CUR);
|
||||
fread(&track->start_lba, 4, 1, fsource);
|
||||
fread(&track->total_length, 4, 1, fsource);
|
||||
fseek(fsource, 16, SEEK_CUR);
|
||||
fread(&track->sector_size_value, 4, 1, fsource);
|
||||
|
||||
switch(track->sector_size_value)
|
||||
{
|
||||
@@ -83,44 +86,38 @@ bool CDI_read_track (hostfs::File *fsource, image_s *image, track_s *track)
|
||||
case 1 : track->sector_size = 2336; break;
|
||||
case 2 : track->sector_size = 2352; break;
|
||||
case 4 : track->sector_size = 2448; break;
|
||||
default:
|
||||
printf("CDI_read_track: Unsupported sector size. value %ld\n", track->sector_size_value);
|
||||
return false;
|
||||
default: printf("Unsupported sector size. value %ld\n", track->sector_size_value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (track->mode > 2) {
|
||||
printf("CDI_read_track: Unsupported format: Track mode not supported\n");
|
||||
return false;
|
||||
}
|
||||
if (track->mode > 2) printf( "Unsupported format: Track mode not supported");
|
||||
|
||||
fsource->seek(29, SEEK_CUR);
|
||||
fseek(fsource, 29, SEEK_CUR);
|
||||
if (image->version != CDI_V2)
|
||||
{
|
||||
fsource->seek(5, SEEK_CUR);
|
||||
fsource->read(&temp_value, 4, 1);
|
||||
{
|
||||
fseek(fsource, 5, SEEK_CUR);
|
||||
fread(&temp_value, 4, 1, fsource);
|
||||
if (temp_value == 0xffffffff)
|
||||
fsource->seek(78, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
}
|
||||
return true;
|
||||
fseek(fsource, 78, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CDI_skip_next_session (hostfs::File *fsource, image_s *image)
|
||||
void CDI_skip_next_session (FILE *fsource, image_s *image)
|
||||
{
|
||||
fsource->seek(4, SEEK_CUR);
|
||||
fsource->seek(8, SEEK_CUR);
|
||||
if (image->version != CDI_V2) fsource->seek(1, SEEK_CUR);
|
||||
fseek(fsource, 4, SEEK_CUR);
|
||||
fseek(fsource, 8, SEEK_CUR);
|
||||
if (image->version != CDI_V2) fseek(fsource, 1, SEEK_CUR);
|
||||
}
|
||||
|
||||
bool CDI_get_tracks (hostfs::File *fsource, image_s *image)
|
||||
void CDI_get_tracks (FILE *fsource, image_s *image)
|
||||
{
|
||||
return fsource->read(&image->tracks, 2, 1) == 1;
|
||||
fread(&image->tracks, 2, 1, fsource);
|
||||
}
|
||||
|
||||
bool CDI_init (hostfs::File *fsource, image_s *image, const char *fsourcename)
|
||||
bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
|
||||
{
|
||||
fsource->seek(0, SEEK_END);
|
||||
image->length = fsource->tell();
|
||||
image->length = core_fsize(fsource);
|
||||
|
||||
if (image->length < 8)
|
||||
{
|
||||
@@ -128,10 +125,9 @@ bool CDI_init (hostfs::File *fsource, image_s *image, const char *fsourcename)
|
||||
return false;
|
||||
}
|
||||
|
||||
fsource->seek(image->length-8, SEEK_SET);
|
||||
if (fsource->read(&image->version, 4, 1) != 1
|
||||
|| fsource->read(&image->header_offset, 4, 1) != 1)
|
||||
return false;
|
||||
fseek(fsource, image->length-8, SEEK_SET);
|
||||
fread(&image->version, 4, 1, fsource);
|
||||
fread(&image->header_offset, 4, 1, fsource);
|
||||
|
||||
if ((image->version != CDI_V2 && image->version != CDI_V3 && image->version != CDI_V35)
|
||||
|| image->header_offset == 0)
|
||||
@@ -142,17 +138,17 @@ bool CDI_init (hostfs::File *fsource, image_s *image, const char *fsourcename)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDI_get_sessions (hostfs::File *fsource, image_s *image)
|
||||
void CDI_get_sessions (FILE *fsource, image_s *image)
|
||||
{
|
||||
#ifndef DEBUG_CDI
|
||||
if (image->version == CDI_V35)
|
||||
fsource->seek((image->length - image->header_offset), SEEK_SET);
|
||||
fseek(fsource, (image->length - image->header_offset), SEEK_SET);
|
||||
else
|
||||
fsource->seek(image->header_offset, SEEK_SET);
|
||||
fseek(fsource, image->header_offset, SEEK_SET);
|
||||
|
||||
#else
|
||||
fsource->seek(0L, SEEK_SET);
|
||||
fseek(fsource, 0L, SEEK_SET);
|
||||
#endif
|
||||
return fsource->read(&image->sessions, 2, 1) == 1;
|
||||
fread(&image->sessions, 2, 1, fsource);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef __CDI_H__
|
||||
#define __CDI_H__
|
||||
|
||||
#include "../../oslib/storage.h"
|
||||
#include "deps/coreio/coreio.h"
|
||||
|
||||
/* Basic structures */
|
||||
|
||||
@@ -38,11 +38,11 @@ typedef struct track_s
|
||||
#define CDI_V3 0x80000005
|
||||
#define CDI_V35 0x80000006
|
||||
|
||||
unsigned long ask_type(hostfs::File *fsource, long header_position);
|
||||
bool CDI_init(hostfs::File *fsource, image_s *image, const char *fsourcename);
|
||||
bool CDI_get_sessions(hostfs::File *fsource, image_s *image);
|
||||
bool CDI_get_tracks(hostfs::File *fsource, image_s *image);
|
||||
bool CDI_read_track(hostfs::File *fsource, image_s *image, track_s *track);
|
||||
void CDI_skip_next_session(hostfs::File *fsource, image_s *image);
|
||||
unsigned long ask_type(core_file *fsource, long header_position);
|
||||
bool CDI_init(core_file *fsource, image_s *image, const char *fsourcename);
|
||||
void CDI_get_sessions(core_file *fsource, image_s *image);
|
||||
void CDI_get_tracks(core_file *fsource, image_s *image);
|
||||
void CDI_read_track(core_file *fsource, image_s *image, track_s *track);
|
||||
void CDI_skip_next_session(core_file *fsource, image_s *image);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
/* license:BSD-3-Clause
|
||||
* copyright-holders:Aaron Giles
|
||||
***************************************************************************
|
||||
|
||||
bitstream.c
|
||||
|
||||
Helper classes for reading/writing at the bit level.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "bitstream.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/***************************************************************************
|
||||
* INLINE FUNCTIONS
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); }
|
||||
|
||||
/*-------------------------------------------------
|
||||
* create_bitstream - constructor
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
struct bitstream* create_bitstream(const void *src, uint32_t srclength)
|
||||
{
|
||||
struct bitstream* bitstream = (struct bitstream*)malloc(sizeof(struct bitstream));
|
||||
bitstream->buffer = 0;
|
||||
bitstream->bits = 0;
|
||||
bitstream->read = (const uint8_t*)src;
|
||||
bitstream->doffset = 0;
|
||||
bitstream->dlength = srclength;
|
||||
return bitstream;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* bitstream_peek - fetch the requested number of bits
|
||||
* but don't advance the input pointer
|
||||
*-----------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t bitstream_peek(struct bitstream* bitstream, int numbits)
|
||||
{
|
||||
if (numbits == 0)
|
||||
return 0;
|
||||
|
||||
/* fetch data if we need more */
|
||||
if (numbits > bitstream->bits)
|
||||
{
|
||||
while (bitstream->bits <= 24)
|
||||
{
|
||||
if (bitstream->doffset < bitstream->dlength)
|
||||
bitstream->buffer |= bitstream->read[bitstream->doffset] << (24 - bitstream->bits);
|
||||
bitstream->doffset++;
|
||||
bitstream->bits += 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* return the data */
|
||||
return bitstream->buffer >> (32 - numbits);
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* bitstream_remove - advance the input pointer by the
|
||||
* specified number of bits
|
||||
*-----------------------------------------------------
|
||||
*/
|
||||
|
||||
void bitstream_remove(struct bitstream* bitstream, int numbits)
|
||||
{
|
||||
bitstream->buffer <<= numbits;
|
||||
bitstream->bits -= numbits;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* bitstream_read - fetch the requested number of bits
|
||||
*-----------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t bitstream_read(struct bitstream* bitstream, int numbits)
|
||||
{
|
||||
uint32_t result = bitstream_peek(bitstream, numbits);
|
||||
bitstream_remove(bitstream, numbits);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
* read_offset - return the current read offset
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t bitstream_read_offset(struct bitstream* bitstream)
|
||||
{
|
||||
uint32_t result = bitstream->doffset;
|
||||
int bits = bitstream->bits;
|
||||
while (bits >= 8)
|
||||
{
|
||||
result--;
|
||||
bits -= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
* flush - flush to the nearest byte
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t bitstream_flush(struct bitstream* bitstream)
|
||||
{
|
||||
while (bitstream->bits >= 8)
|
||||
{
|
||||
bitstream->doffset--;
|
||||
bitstream->bits -= 8;
|
||||
}
|
||||
bitstream->bits = bitstream->buffer = 0;
|
||||
return bitstream->doffset;
|
||||
}
|
||||
|
||||
@@ -39,4 +39,5 @@ uint32_t bitstream_peek(struct bitstream* bitstream, int numbits);
|
||||
void bitstream_remove(struct bitstream* bitstream, int numbits);
|
||||
uint32_t bitstream_flush(struct bitstream* bitstream);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,412 @@
|
||||
/* license:BSD-3-Clause
|
||||
* copyright-holders:Aaron Giles
|
||||
***************************************************************************
|
||||
|
||||
cdrom.c
|
||||
|
||||
Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this
|
||||
|
||||
****************************************************************************
|
||||
|
||||
IMPORTANT:
|
||||
"physical" block addresses are the actual addresses on the emulated CD.
|
||||
"chd" block addresses are the block addresses in the CHD file.
|
||||
Because we pad each track to a 4-frame boundary, these addressing
|
||||
schemes will differ after track 1!
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cdrom.h"
|
||||
|
||||
/***************************************************************************
|
||||
DEBUGGING
|
||||
***************************************************************************/
|
||||
|
||||
/** @brief The verbose. */
|
||||
#define VERBOSE (0)
|
||||
#if VERBOSE
|
||||
|
||||
/**
|
||||
* @def LOG(x) do
|
||||
*
|
||||
* @brief A macro that defines log.
|
||||
*
|
||||
* @param x The void to process.
|
||||
*/
|
||||
|
||||
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
||||
|
||||
/**
|
||||
* @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
|
||||
*
|
||||
* @brief Logerrors the given text.
|
||||
*
|
||||
* @param text The text.
|
||||
*
|
||||
* @return A CLIB_DECL.
|
||||
*/
|
||||
|
||||
void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
|
||||
#else
|
||||
|
||||
/**
|
||||
* @def LOG(x);
|
||||
*
|
||||
* @brief A macro that defines log.
|
||||
*
|
||||
* @param x The void to process.
|
||||
*/
|
||||
|
||||
#define LOG(x)
|
||||
#endif
|
||||
|
||||
/***************************************************************************
|
||||
CONSTANTS
|
||||
***************************************************************************/
|
||||
|
||||
/** @brief offset within sector. */
|
||||
#define SYNC_OFFSET 0x000
|
||||
/** @brief 12 bytes. */
|
||||
#define SYNC_NUM_BYTES 12
|
||||
|
||||
/** @brief offset within sector. */
|
||||
#define MODE_OFFSET 0x00f
|
||||
|
||||
/** @brief offset within sector. */
|
||||
#define ECC_P_OFFSET 0x81c
|
||||
/** @brief 2 lots of 86. */
|
||||
#define ECC_P_NUM_BYTES 86
|
||||
/** @brief 24 bytes each. */
|
||||
#define ECC_P_COMP 24
|
||||
|
||||
/** @brief The ECC q offset. */
|
||||
#define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES)
|
||||
/** @brief 2 lots of 52. */
|
||||
#define ECC_Q_NUM_BYTES 52
|
||||
/** @brief 43 bytes each. */
|
||||
#define ECC_Q_COMP 43
|
||||
|
||||
/**
|
||||
* @brief -------------------------------------------------
|
||||
* ECC lookup tables pre-calculated tables for ECC data calcs
|
||||
* -------------------------------------------------.
|
||||
*/
|
||||
|
||||
static const uint8_t ecclow[256] =
|
||||
{
|
||||
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
|
||||
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
|
||||
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
|
||||
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
|
||||
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
|
||||
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
|
||||
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
|
||||
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
|
||||
0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03,
|
||||
0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23,
|
||||
0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43,
|
||||
0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63,
|
||||
0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83,
|
||||
0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3,
|
||||
0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3,
|
||||
0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3
|
||||
};
|
||||
|
||||
/** @brief The ecchigh[ 256]. */
|
||||
static const uint8_t ecchigh[256] =
|
||||
{
|
||||
0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
|
||||
0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
|
||||
0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee,
|
||||
0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15,
|
||||
0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce,
|
||||
0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35,
|
||||
0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25,
|
||||
0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde,
|
||||
0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e,
|
||||
0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75,
|
||||
0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65,
|
||||
0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e,
|
||||
0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45,
|
||||
0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe,
|
||||
0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae,
|
||||
0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief -------------------------------------------------
|
||||
* poffsets - each row represents the addresses used to calculate a byte of the ECC P
|
||||
* data 86 (*2) ECC P bytes, 24 values represented by each
|
||||
* -------------------------------------------------.
|
||||
*/
|
||||
|
||||
static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
|
||||
{
|
||||
{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
|
||||
{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
|
||||
{ 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc },
|
||||
{ 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd },
|
||||
{ 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be },
|
||||
{ 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf },
|
||||
{ 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 },
|
||||
{ 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 },
|
||||
{ 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 },
|
||||
{ 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 },
|
||||
{ 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 },
|
||||
{ 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 },
|
||||
{ 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 },
|
||||
{ 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 },
|
||||
{ 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 },
|
||||
{ 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 },
|
||||
{ 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca },
|
||||
{ 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb },
|
||||
{ 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc },
|
||||
{ 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd },
|
||||
{ 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce },
|
||||
{ 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf },
|
||||
{ 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 },
|
||||
{ 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 },
|
||||
{ 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 },
|
||||
{ 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 },
|
||||
{ 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 },
|
||||
{ 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 },
|
||||
{ 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 },
|
||||
{ 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 },
|
||||
{ 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 },
|
||||
{ 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 },
|
||||
{ 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da },
|
||||
{ 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db },
|
||||
{ 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc },
|
||||
{ 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd },
|
||||
{ 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de },
|
||||
{ 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df },
|
||||
{ 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 },
|
||||
{ 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 },
|
||||
{ 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 },
|
||||
{ 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 },
|
||||
{ 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 },
|
||||
{ 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 },
|
||||
{ 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 },
|
||||
{ 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 },
|
||||
{ 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 },
|
||||
{ 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 },
|
||||
{ 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea },
|
||||
{ 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb },
|
||||
{ 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec },
|
||||
{ 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed },
|
||||
{ 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee },
|
||||
{ 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef },
|
||||
{ 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 },
|
||||
{ 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 },
|
||||
{ 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 },
|
||||
{ 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 },
|
||||
{ 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 },
|
||||
{ 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 },
|
||||
{ 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 },
|
||||
{ 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 },
|
||||
{ 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 },
|
||||
{ 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 },
|
||||
{ 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa },
|
||||
{ 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb },
|
||||
{ 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc },
|
||||
{ 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd },
|
||||
{ 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe },
|
||||
{ 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff },
|
||||
{ 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 },
|
||||
{ 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 },
|
||||
{ 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 },
|
||||
{ 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 },
|
||||
{ 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 },
|
||||
{ 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 },
|
||||
{ 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 },
|
||||
{ 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 },
|
||||
{ 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 },
|
||||
{ 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 },
|
||||
{ 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a },
|
||||
{ 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b },
|
||||
{ 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c },
|
||||
{ 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d },
|
||||
{ 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e },
|
||||
{ 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief -------------------------------------------------
|
||||
* qoffsets - each row represents the addresses used to calculate a byte of the ECC Q
|
||||
* data 52 (*2) ECC Q bytes, 43 values represented by each
|
||||
* -------------------------------------------------.
|
||||
*/
|
||||
|
||||
static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
|
||||
{
|
||||
{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
|
||||
{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
|
||||
{ 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a },
|
||||
{ 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b },
|
||||
{ 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 },
|
||||
{ 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 },
|
||||
{ 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 },
|
||||
{ 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 },
|
||||
{ 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c },
|
||||
{ 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d },
|
||||
{ 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 },
|
||||
{ 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 },
|
||||
{ 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 },
|
||||
{ 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 },
|
||||
{ 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e },
|
||||
{ 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f },
|
||||
{ 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 },
|
||||
{ 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 },
|
||||
{ 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba },
|
||||
{ 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb },
|
||||
{ 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 },
|
||||
{ 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 },
|
||||
{ 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa },
|
||||
{ 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab },
|
||||
{ 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 },
|
||||
{ 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 },
|
||||
{ 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 },
|
||||
{ 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 },
|
||||
{ 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac },
|
||||
{ 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad },
|
||||
{ 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 },
|
||||
{ 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 },
|
||||
{ 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 },
|
||||
{ 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 },
|
||||
{ 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae },
|
||||
{ 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af },
|
||||
{ 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 },
|
||||
{ 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 },
|
||||
{ 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a },
|
||||
{ 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b },
|
||||
{ 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 },
|
||||
{ 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 },
|
||||
{ 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 },
|
||||
{ 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 },
|
||||
{ 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c },
|
||||
{ 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d },
|
||||
{ 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 },
|
||||
{ 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 },
|
||||
{ 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 },
|
||||
{ 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 },
|
||||
{ 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e },
|
||||
{ 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f }
|
||||
};
|
||||
|
||||
/*-------------------------------------------------
|
||||
* ecc_source_byte - return data from the sector
|
||||
* at the given offset, masking anything
|
||||
* particular to a mode
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
static inline uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
|
||||
{
|
||||
/* in mode 2 always treat these as 0 bytes */
|
||||
return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_compute_bytes - calculate an ECC value (P or Q)
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param sector The sector.
|
||||
* @param row The row.
|
||||
* @param rowlen The rowlen.
|
||||
* @param [in,out] val1 The first value.
|
||||
* @param [in,out] val2 The second value.
|
||||
*/
|
||||
|
||||
void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2)
|
||||
{
|
||||
int component;
|
||||
*val1 = *val2 = 0;
|
||||
for (component = 0; component < rowlen; component++)
|
||||
{
|
||||
*val1 ^= ecc_source_byte(sector, row[component]);
|
||||
*val2 ^= ecc_source_byte(sector, row[component]);
|
||||
*val1 = ecclow[*val1];
|
||||
}
|
||||
*val1 = ecchigh[ecclow[*val1] ^ *val2];
|
||||
*val2 ^= *val1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn int ecc_verify(const uint8_t *sector)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_verify - verify the P and Q ECC codes in a sector
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param sector The sector.
|
||||
*
|
||||
* @return true if it succeeds, false if it fails.
|
||||
*/
|
||||
|
||||
int ecc_verify(const uint8_t *sector)
|
||||
{
|
||||
int byte;
|
||||
/* first verify P bytes */
|
||||
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
|
||||
{
|
||||
uint8_t val1, val2;
|
||||
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2);
|
||||
if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* then verify Q bytes */
|
||||
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
|
||||
{
|
||||
uint8_t val1, val2;
|
||||
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2);
|
||||
if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void ecc_generate(uint8_t *sector)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_generate - generate the P and Q ECC codes for a sector, overwriting any
|
||||
* existing codes
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param [in,out] sector If non-null, the sector.
|
||||
*/
|
||||
|
||||
void ecc_generate(uint8_t *sector)
|
||||
{
|
||||
int byte;
|
||||
/* first verify P bytes */
|
||||
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
|
||||
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, §or[ECC_P_OFFSET + byte], §or[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]);
|
||||
|
||||
/* then verify Q bytes */
|
||||
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
|
||||
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, §or[ECC_Q_OFFSET + byte], §or[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn void ecc_clear(uint8_t *sector)
|
||||
*
|
||||
* @brief -------------------------------------------------
|
||||
* ecc_clear - erase the ECC P and Q cods to 0 within a sector
|
||||
* -------------------------------------------------.
|
||||
*
|
||||
* @param [in,out] sector If non-null, the sector.
|
||||
*/
|
||||
|
||||
void ecc_clear(uint8_t *sector)
|
||||
{
|
||||
memset(§or[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
|
||||
memset(§or[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/* license:BSD-3-Clause
|
||||
* copyright-holders:Aaron Giles
|
||||
***************************************************************************
|
||||
|
||||
cdrom.h
|
||||
|
||||
Generic MAME cd-rom implementation
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __CDROM_H__
|
||||
#define __CDROM_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
/***************************************************************************
|
||||
CONSTANTS
|
||||
***************************************************************************/
|
||||
|
||||
#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */
|
||||
#define CD_MAX_SECTOR_DATA (2352)
|
||||
#define CD_MAX_SUBCODE_DATA (96)
|
||||
|
||||
#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA)
|
||||
#define CD_FRAMES_PER_HUNK (8)
|
||||
|
||||
#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6))
|
||||
|
||||
enum
|
||||
{
|
||||
CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */
|
||||
CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */
|
||||
CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */
|
||||
CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */
|
||||
CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */
|
||||
CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */
|
||||
CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */
|
||||
CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */
|
||||
|
||||
CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */
|
||||
CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */
|
||||
CD_SUB_NONE /* no subcode data stored */
|
||||
};
|
||||
|
||||
#define CD_FLAG_GDROM 0x00000001 /* disc is a GD-ROM, all tracks should be stored with GD-ROM metadata */
|
||||
#define CD_FLAG_GDROMLE 0x00000002 /* legacy GD-ROM, with little-endian CDDA data */
|
||||
|
||||
/***************************************************************************
|
||||
FUNCTION PROTOTYPES
|
||||
***************************************************************************/
|
||||
|
||||
/* ECC utilities */
|
||||
int ecc_verify(const uint8_t *sector);
|
||||
void ecc_generate(uint8_t *sector);
|
||||
void ecc_clear(uint8_t *sector);
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
INLINE FUNCTIONS
|
||||
***************************************************************************/
|
||||
|
||||
static inline uint32_t msf_to_lba(uint32_t msf)
|
||||
{
|
||||
return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0);
|
||||
}
|
||||
|
||||
static inline uint32_t lba_to_msf(uint32_t lba)
|
||||
{
|
||||
uint8_t m, s, f;
|
||||
|
||||
m = lba / (60 * 75);
|
||||
lba -= m * (60 * 75);
|
||||
s = lba / 75;
|
||||
f = lba % 75;
|
||||
|
||||
return ((m / 10) << 20) | ((m % 10) << 16) |
|
||||
((s / 10) << 12) | ((s % 10) << 8) |
|
||||
((f / 10) << 4) | ((f % 10) << 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* segacd needs it like this.. investigate
|
||||
* Angelo also says PCE tracks often start playing at the
|
||||
* wrong address.. related?
|
||||
**/
|
||||
static inline uint32_t lba_to_msf_alt(int lba)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
ret |= ((lba / (60 * 75))&0xff)<<16;
|
||||
ret |= (((lba / 75) % 60)&0xff)<<8;
|
||||
ret |= ((lba % 75)&0xff)<<0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __CDROM_H__ */
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 4466 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user