mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-08 01:48:31 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eadfb388e | ||
|
|
83f6d818b5 | ||
|
|
bbae3de9a4 | ||
|
|
c2f66018d8 | ||
|
|
82ca2db61a | ||
|
|
b4d83c0d12 | ||
|
|
1aba1b467e | ||
|
|
5be5eeb905 | ||
|
|
2f842238a5 | ||
|
|
3de74672ce | ||
|
|
b76b1c7275 | ||
|
|
392a429e8b | ||
|
|
0491f2e6a5 | ||
|
|
33d9a58cf9 | ||
|
|
401332d520 | ||
|
|
8511d1ad3c | ||
|
|
7ce50e5229 | ||
|
|
99fc194dd0 | ||
|
|
d794b53512 | ||
|
|
bd8f7c8a75 | ||
|
|
594d82b223 | ||
|
|
fd72eebbe3 | ||
|
|
fea8699884 | ||
|
|
d00b5d4c17 | ||
|
|
74cbee908f | ||
|
|
1652143f5f | ||
|
|
4886dcb1c2 | ||
|
|
75bca2a166 | ||
|
|
13df86c520 | ||
|
|
16fc172ffc | ||
|
|
dfb2ae8987 | ||
|
|
4529a31372 | ||
|
|
5a29ac489f | ||
|
|
c4c3c2ae93 | ||
|
|
8a228e4e16 | ||
|
|
0a3c68f7cc | ||
|
|
4905dc21c8 | ||
|
|
98dfb4c32a | ||
|
|
df2bdfc9bf | ||
|
|
e86c1c992f | ||
|
|
5b6cc33a75 | ||
|
|
e37518524f | ||
|
|
994f72f954 | ||
|
|
5978b3a058 | ||
|
|
bb12d5c72d | ||
|
|
b127c96cf3 | ||
|
|
0d3beb1c6d | ||
|
|
9bd3179745 | ||
|
|
5833fb3a47 | ||
|
|
49ec396e24 | ||
|
|
5607b8eebd | ||
|
|
35888406d3 | ||
|
|
6b3bbd6f85 | ||
|
|
ef689fd35d | ||
|
|
6a78f27bef | ||
|
|
97de155fd8 | ||
|
|
f183f30262 | ||
|
|
065c91f9ad | ||
|
|
f65907a256 | ||
|
|
161d057d4c | ||
|
|
a7ee2e9a66 | ||
|
|
e08ec86962 | ||
|
|
7653b1c811 | ||
|
|
1475812a94 | ||
|
|
c8a724ad63 | ||
|
|
2cfce747c5 | ||
|
|
37a9077304 | ||
|
|
f8bdf6c35e | ||
|
|
f4c2d001bd | ||
|
|
f3ddec5f1e | ||
|
|
13c7ddc6c9 | ||
|
|
c24d7fb0fe | ||
|
|
3ebc79a5ce | ||
|
|
27a913fc02 | ||
|
|
97442c0411 | ||
|
|
5b12f9ca68 | ||
|
|
6a298289a9 | ||
|
|
59ef8579d2 | ||
|
|
6989a0aa2c | ||
|
|
bd24812d0c | ||
|
|
15889616dd | ||
|
|
e60efed8b5 | ||
|
|
11fcadf37e | ||
|
|
88b8a0b51e | ||
|
|
8e9a0f9c1f | ||
|
|
dd50fcf81c | ||
|
|
7b8656b944 | ||
|
|
00ae6c42a2 | ||
|
|
8c4c463c38 | ||
|
|
14f759eeb7 | ||
|
|
329b6a1e34 | ||
|
|
9c28800384 | ||
|
|
602982fb56 | ||
|
|
cb830e0bd3 | ||
|
|
ec3d85a96c | ||
|
|
d3bc72b0d0 | ||
|
|
4170e783b9 | ||
|
|
083ccb984a | ||
|
|
0220207545 | ||
|
|
aee4cd34fc | ||
|
|
a99cce27bb | ||
|
|
b2941a8593 | ||
|
|
3ad072f9aa | ||
|
|
14374084d5 | ||
|
|
5c7a241897 | ||
|
|
7fcc5fa2f7 | ||
|
|
cf2fe027f7 | ||
|
|
b13685f0bb | ||
|
|
cf7b6bdcb0 | ||
|
|
230d971d4d | ||
|
|
894c61ed64 | ||
|
|
82cadfde22 | ||
|
|
1586cbaea9 | ||
|
|
2113e4e249 | ||
|
|
5a150f4121 | ||
|
|
f178e5d76e | ||
|
|
9222fa8442 | ||
|
|
77359cefb2 | ||
|
|
a6dc38a35b | ||
|
|
eab952fcbe | ||
|
|
1666eb0875 | ||
|
|
5bce763f66 | ||
|
|
0f4fca415b | ||
|
|
f2fe918379 | ||
|
|
a9be4d111c | ||
|
|
ce07a7fbf5 | ||
|
|
e44c03fa0e | ||
|
|
1dac3695fd | ||
|
|
997f6179dd | ||
|
|
bed524f0d0 | ||
|
|
410b37b041 | ||
|
|
ead4b51d6f | ||
|
|
043bb5ee99 | ||
|
|
4747e61908 | ||
|
|
548cf6fe2e | ||
|
|
339d288dfb | ||
|
|
8f7e4bf2d3 | ||
|
|
922ca3e54b | ||
|
|
4b09de2fc1 | ||
|
|
98db7a6cd5 | ||
|
|
3405b5b2a6 | ||
|
|
4320685ff8 | ||
|
|
cd3fe7393c | ||
|
|
2da0d392fd | ||
|
|
8d1bf3382c | ||
|
|
b1489d665e | ||
|
|
fc56bd283e | ||
|
|
807e4a20ed | ||
|
|
943311752e | ||
|
|
e7ad12b031 | ||
|
|
b2bc6251c7 | ||
|
|
6f38dc3210 | ||
|
|
4bb32466bd | ||
|
|
e26d356265 | ||
|
|
265c5c6c3d | ||
|
|
2b1eb55daa | ||
|
|
bd136b1856 | ||
|
|
bd8adf1da1 | ||
|
|
11625176c2 | ||
|
|
c64441a02f | ||
|
|
8e9eee246d | ||
|
|
d0ec876936 | ||
|
|
b19cbcbf29 | ||
|
|
bf2bd7efed | ||
|
|
d15d5be44a | ||
|
|
70c8d1d22b | ||
|
|
3917c0181b | ||
|
|
6f58d204b6 | ||
|
|
d450c84a14 | ||
|
|
3b7036d14a | ||
|
|
5d628f8167 | ||
|
|
d1ed878446 | ||
|
|
28ea3dc69c | ||
|
|
031a645ffc | ||
|
|
0a526d7c94 | ||
|
|
3d594054eb | ||
|
|
3b651463dc | ||
|
|
4e6b5b270b | ||
|
|
fd8f2e24b8 | ||
|
|
82b4c55f09 | ||
|
|
8176646c5a | ||
|
|
3f0dd626c1 | ||
|
|
8edd8fc447 | ||
|
|
0c7dca9cb7 | ||
|
|
086e8bc20c | ||
|
|
11880bca30 | ||
|
|
37187467e7 | ||
|
|
7b0c106fc2 | ||
|
|
fd654614ae | ||
|
|
4ee24ca612 | ||
|
|
4b3704953f | ||
|
|
6ea9a34e8a | ||
|
|
c018d46d00 | ||
|
|
400da2bdfe | ||
|
|
3b5536dd43 | ||
|
|
34e2f426ac | ||
|
|
a246964a99 | ||
|
|
b4b4b3ab1e | ||
|
|
af5f67c15d | ||
|
|
909ea496ac | ||
|
|
3347dc1a46 | ||
|
|
dd5e148e2b | ||
|
|
1f818ca750 | ||
|
|
ab6b95ff4d | ||
|
|
760c62a3da | ||
|
|
b2d8ad633d | ||
|
|
f9b47b6f91 | ||
|
|
372ef91211 | ||
|
|
3375f6c476 | ||
|
|
c5396a041e | ||
|
|
851dd22664 | ||
|
|
ac32a80352 | ||
|
|
eff11d6a07 | ||
|
|
b6c135b0c6 | ||
|
|
0fe7073496 | ||
|
|
d5c5839a92 | ||
|
|
7ac45a7e46 | ||
|
|
0243f81c26 | ||
|
|
2dfa647445 | ||
|
|
9c5408a6d3 | ||
|
|
d3dcb13ef9 | ||
|
|
2cb2ac23e0 | ||
|
|
e38b0ac01f | ||
|
|
09b27ddd5c | ||
|
|
33833cfd1e | ||
|
|
3c5c1e0b66 | ||
|
|
3f446a5835 | ||
|
|
df3c676a04 | ||
|
|
9ff3ab618c | ||
|
|
ba33b8c3f7 | ||
|
|
eab13e85bc | ||
|
|
a1f1c9825a | ||
|
|
43a9c1384a | ||
|
|
b4ab21a768 | ||
|
|
30365d13ef | ||
|
|
f7eae0ff9e | ||
|
|
e26f170558 | ||
|
|
40e400ab08 | ||
|
|
5743502cdd | ||
|
|
8f33b63153 | ||
|
|
6fca05f00e | ||
|
|
8c07a2e1d6 | ||
|
|
ffbe266413 | ||
|
|
5d3b7fae6b | ||
|
|
738b95d2be | ||
|
|
48c58dbd18 | ||
|
|
54b06a5c34 | ||
|
|
d3a46a1e33 | ||
|
|
4fe2f4760f | ||
|
|
c0809d771d | ||
|
|
54ed92e2b0 | ||
|
|
6ea61b9084 | ||
|
|
f3e0f09ca9 | ||
|
|
301b1a0f00 | ||
|
|
b787427446 | ||
|
|
5df5c70994 | ||
|
|
75defb590b | ||
|
|
fbeaf8ba16 | ||
|
|
3f79b6baed | ||
|
|
34f095a91e | ||
|
|
09247b4503 | ||
|
|
109e084e18 | ||
|
|
af043c55cd | ||
|
|
04999d8969 | ||
|
|
26b8684e78 | ||
|
|
eefcb0e05d | ||
|
|
449d256995 | ||
|
|
0897f0ed8a | ||
|
|
9bdb90e605 | ||
|
|
5b75975237 | ||
|
|
c73336a86f | ||
|
|
27318443a7 | ||
|
|
df93c069f2 | ||
|
|
7358f29556 | ||
|
|
8801b2ae16 | ||
|
|
b10db5a078 | ||
|
|
71341a27e4 | ||
|
|
7e7f645f37 | ||
|
|
146e90dae6 | ||
|
|
bb5bd71437 | ||
|
|
e8217ca0c5 | ||
|
|
8f033723a1 | ||
|
|
b097aa3331 | ||
|
|
64b9e51af0 | ||
|
|
9b91430513 | ||
|
|
d9ec0cb3ab | ||
|
|
39d68ec7fa | ||
|
|
a1a782df38 | ||
|
|
acc106e988 | ||
|
|
11de0eda13 | ||
|
|
a7148ad7ae | ||
|
|
27f19a5b54 | ||
|
|
b973453317 | ||
|
|
ffaada9e9d | ||
|
|
1953eb0eea | ||
|
|
c3275d3c6f | ||
|
|
5e769d5cd2 | ||
|
|
f26e10e44a | ||
|
|
3d60fc8c60 | ||
|
|
7adeb53aee | ||
|
|
eacadf24a6 | ||
|
|
8e14c7f394 | ||
|
|
a842edb09e | ||
|
|
e2247e4872 | ||
|
|
eb662da389 | ||
|
|
2b79f93d6b | ||
|
|
ffc32d2d86 | ||
|
|
892cdb7cea | ||
|
|
90ed30f70c | ||
|
|
34f8ab292f | ||
|
|
e1ab9af89d | ||
|
|
3330225de4 | ||
|
|
58ce9a4159 | ||
|
|
1b5c8678a4 | ||
|
|
c20ec680a8 | ||
|
|
f62582a4bd | ||
|
|
1273c06d4b | ||
|
|
332be9c8aa | ||
|
|
77776be20b | ||
|
|
f7676faa68 | ||
|
|
10a5ed6cec | ||
|
|
b9ba3f1e63 | ||
|
|
34fda43d9c | ||
|
|
b602cc9c57 | ||
|
|
63534c5d56 | ||
|
|
0267de4997 |
No files matched your search
@@ -1,6 +1,10 @@
|
||||
name: Android CI
|
||||
name: Android
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -11,20 +15,20 @@ jobs:
|
||||
- name: Set up build environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache libcurl4-openssl-dev ninja-build
|
||||
sudo apt-get -y install ccache cmake libcurl4-openssl-dev ninja-build
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: android-ccache-${{ github.sha }}
|
||||
@@ -47,7 +51,7 @@ jobs:
|
||||
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
|
||||
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: flycast-release.apk
|
||||
path: shell/android-studio/flycast/build/outputs/apk/release/flycast-release.apk
|
||||
@@ -102,4 +106,4 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
if: ${{ env.SENTRY_TOKEN != '' }}
|
||||
if: ${{ env.SENTRY_TOKEN != '' && github.event_name == 'push' }}
|
||||
+17
-12
@@ -1,6 +1,10 @@
|
||||
name: BSD CI
|
||||
name: BSD
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -11,32 +15,33 @@ jobs:
|
||||
matrix:
|
||||
operating_system: [ freebsd, netbsd, openbsd ]
|
||||
architecture: [ arm64, x86-64 ]
|
||||
libretro: [ OFF, ON ]
|
||||
include:
|
||||
- operating_system: freebsd
|
||||
version: '14.2'
|
||||
pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
|
||||
version: '15.0'
|
||||
pkginstall: sudo pkg update && sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
|
||||
- operating_system: netbsd
|
||||
version: '10.1'
|
||||
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 libcdio && export PATH=/usr/pkg/gcc12/bin:$PATH
|
||||
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache clang cmake git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 libcdio
|
||||
- operating_system: openbsd
|
||||
version: '7.6'
|
||||
pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
|
||||
version: '7.8'
|
||||
pkginstall: sudo pkg_add -u && sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
|
||||
exclude:
|
||||
- architecture: arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-
|
||||
|
||||
- uses: cross-platform-actions/action@v0.27.0
|
||||
- uses: cross-platform-actions/action@v0.31.0
|
||||
with:
|
||||
operating_system: ${{ matrix.operating_system }}
|
||||
architecture: ${{ matrix.architecture }}
|
||||
@@ -44,5 +49,5 @@ jobs:
|
||||
environment_variables: CCACHE_DIR
|
||||
run: |
|
||||
${{ matrix.pkginstall }}
|
||||
cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja -DLIBRETRO=${{ matrix.libretro }}
|
||||
cmake --build build --config Release
|
||||
+15
-14
@@ -1,6 +1,10 @@
|
||||
name: C/C++ CI
|
||||
name: Linux, macOS, Windows
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -19,7 +23,6 @@ jobs:
|
||||
config:
|
||||
- {name: i686-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release}
|
||||
- {name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON, destDir: osx, buildType: RelWithDebInfo}
|
||||
- {name: apple-ios, os: macos-latest, shell: sh, cmakeArgs: -DCMAKE_SYSTEM_NAME=iOS -G Xcode, destDir: ios, buildType: Release}
|
||||
- {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-latest, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release}
|
||||
- {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: win, buildType: RelWithDebInfo}
|
||||
@@ -34,7 +37,7 @@ jobs:
|
||||
brew update || :
|
||||
brew install libao ldid pulseaudio
|
||||
brew uninstall --ignore-dependencies zstd
|
||||
VULKAN_VER=1.3.261.1 && aria2c https://sdk.lunarg.com/sdk/download/$VULKAN_VER/mac/vulkansdk-macos-$VULKAN_VER.dmg
|
||||
VULKAN_VER=1.3.290.0 && aria2c https://sdk.lunarg.com/sdk/download/$VULKAN_VER/mac/vulkansdk-macos-$VULKAN_VER.dmg
|
||||
hdiutil attach ./vulkansdk-macos-*.dmg -mountpoint /Volumes/VulkanSDK
|
||||
sudo /Volumes/VulkanSDK/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $HOME/VulkanSDK --accept-licenses --default-answer --confirm-command install
|
||||
hdiutil detach /Volumes/VulkanSDK
|
||||
@@ -45,8 +48,10 @@ jobs:
|
||||
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 ninja-build libcurl4-openssl-dev libcdio-dev libfuse2
|
||||
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 ninja-build 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)
|
||||
@@ -67,20 +72,16 @@ jobs:
|
||||
arch: ${{ matrix.config.arch }}
|
||||
if: matrix.config.shell == 'cmd'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Compile a universal OpenMP (macOS)
|
||||
run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
|
||||
run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- name: Compile OpenMP.xcframework (iOS)
|
||||
run: ./shell/apple/emulator-ios/OpenMP/build_ios_openmp.sh --disableSimulator
|
||||
if: matrix.config.name == 'apple-ios'
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.config.name }}-${{ github.sha }}
|
||||
@@ -126,7 +127,7 @@ jobs:
|
||||
rm artifact/bin/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: flycast-${{ matrix.config.name }}
|
||||
path: |
|
||||
@@ -176,5 +177,5 @@ jobs:
|
||||
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') }}
|
||||
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' }}
|
||||
|
||||
@@ -15,18 +15,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Upload Source
|
||||
shell: bash
|
||||
|
||||
@@ -12,18 +12,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
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.
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
git commit -m "Fetch translations & Recreate libretro_core_options_intl.h"
|
||||
|
||||
- name: GitHub Push
|
||||
uses: ad-m/github-push-action@v0.8.0
|
||||
uses: ad-m/github-push-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ github.ref }}
|
||||
@@ -1,6 +1,10 @@
|
||||
name: Nintendo Switch CI
|
||||
name: Nintendo Switch
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -18,17 +22,17 @@ jobs:
|
||||
- name: Set up build environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache
|
||||
sudo apt-get -y install ccache cmake
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
submodules: recursive
|
||||
|
||||
- name: Mark git repository as safe
|
||||
run: git config --global --add safe.directory $PWD
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-switch-${{ matrix.config.name }}-${{ github.sha }}
|
||||
@@ -39,10 +43,12 @@ jobs:
|
||||
$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@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: flycast-switch-${{ matrix.config.name }}
|
||||
path: artifact/bin
|
||||
path: |
|
||||
artifact/bin
|
||||
artifact/lib/libretro
|
||||
|
||||
- name: Package app
|
||||
run: zip -m artifact/bin/flycast.nro.zip artifact/bin/flycast.nro
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
name: Windows UWP CI
|
||||
name: Xbox, UWP
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'l10n_**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: UWP
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [ Win32, x64, ARM64 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
submodules: recursive
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.22000.0
|
||||
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
|
||||
|
||||
@@ -24,7 +32,7 @@ jobs:
|
||||
- name: Package app
|
||||
run: |
|
||||
mkdir build\artifact
|
||||
cd build\AppPackages\flycast\flycast_*_x64_Test
|
||||
cd build\AppPackages\flycast\flycast_*_Test
|
||||
mkdir tmp
|
||||
ren *.msix flycast.msix
|
||||
makeappx.exe unpack /p .\flycast.msix /d tmp
|
||||
@@ -35,9 +43,9 @@ jobs:
|
||||
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@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: flycast-uwp
|
||||
name: flycast-uwp-${{ matrix.arch }}
|
||||
path: build/artifact
|
||||
|
||||
- name: Setup Rclone
|
||||
@@ -48,4 +56,4 @@ jobs:
|
||||
- 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'
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.arch == 'x64'
|
||||
+17
-7
@@ -9,39 +9,46 @@
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CORENAME: flycast
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release
|
||||
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 -DCMAKE_BUILD_TYPE=Release
|
||||
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_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64
|
||||
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 -DCMAKE_BUILD_TYPE=Release
|
||||
CORE_ARGS: -DLIBRETRO=ON -G Xcode
|
||||
EXTRA_PATH: Release
|
||||
|
||||
.core-defs-ios-arm64:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_OPENMP=OFF -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0
|
||||
IOS_MINVER: 13.0
|
||||
MINVER: 13.0
|
||||
|
||||
.core-defs-android:
|
||||
extends: .core-defs
|
||||
script:
|
||||
- cmake -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release -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 "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
|
||||
- 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:
|
||||
API_LEVEL: 18
|
||||
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:
|
||||
@@ -108,12 +115,14 @@ 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
|
||||
|
||||
# MacOS 64-bit
|
||||
libretro-build-osx-x64:
|
||||
@@ -171,3 +180,4 @@ libretro-build-tvos-arm64:
|
||||
extends:
|
||||
- .libretro-tvos-cmake-arm64
|
||||
- .core-defs-ios-arm64
|
||||
|
||||
+15
@@ -44,3 +44,18 @@
|
||||
[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
|
||||
+136
-489
@@ -1,4 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.10.2)
|
||||
if(APPLE AND NOT LIBRETRO)
|
||||
# Requirement from SDL
|
||||
# Minimum version for $<LINK_LIBRARY:feature,library-list>
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
endif()
|
||||
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
@@ -10,6 +16,11 @@ set(SENTRY_UPLOAD_URL "" CACHE STRING "Sentry upload URL")
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/shell/cmake")
|
||||
|
||||
# Set WINDOWS_STORE flag for UWP builds
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(WINDOWS_STORE TRUE)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL iOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum iOS deployment version")
|
||||
@@ -18,7 +29,7 @@ if(APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum tvOS deployment version")
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
|
||||
else()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum macOS deployment version")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum macOS deployment version")
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
|
||||
endif()
|
||||
set(ZLIB_LIBRARY "-lz" CACHE STRING "Use generic linker flag for Xcode to support multiple SDKs")
|
||||
@@ -32,7 +43,7 @@ endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
if(NINTENDO_SWITCH)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "NetBSD|Haiku" OR NINTENDO_SWITCH)
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
else()
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
@@ -44,8 +55,13 @@ if(NOT DEFINED ARCHITECTURE)
|
||||
endif()
|
||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
|
||||
if(CMAKE_CXX_COMPILER MATCHES "webos" OR
|
||||
CMAKE_CXX_COMPILER MATCHES "starfish")
|
||||
set(WEBOS ON)
|
||||
endif()
|
||||
|
||||
set(USE_HOST_SDL_DEFAULT OFF)
|
||||
if(NOT APPLE AND (NOT UNIX OR CMAKE_SYSTEM_NAME MATCHES "(FreeBSD|OpenBSD|NetBSD)"))
|
||||
if(NOT APPLE AND (NOT UNIX OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|OpenBSD|NetBSD|Haiku"))
|
||||
set(USE_HOST_SDL_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
@@ -83,11 +99,18 @@ if(IOS AND NOT LIBRETRO)
|
||||
set(USE_VULKAN OFF CACHE BOOL "Force vulkan off" FORCE)
|
||||
endif()
|
||||
|
||||
if(WEBOS)
|
||||
set(USE_GLES2 ON CACHE BOOL "Use GLES2 API" FORCE)
|
||||
set(USE_OPENMP OFF CACHE BOOL "Force openmp off" FORCE)
|
||||
set(USE_VULKAN OFF CACHE BOOL "Force vulkan off" FORCE)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeRC)
|
||||
|
||||
if(ENABLE_CTEST)
|
||||
include(CTest)
|
||||
add_compile_definitions(FLYCAST_TEST_FILES="${CMAKE_CURRENT_SOURCE_DIR}/tests/files")
|
||||
endif()
|
||||
|
||||
if(IOS AND NOT LIBRETRO)
|
||||
@@ -153,7 +176,8 @@ if(NINTENDO_SWITCH)
|
||||
ASIO_DISABLE_SERIAL_PORT
|
||||
ESHUTDOWN=110
|
||||
SA_RESTART=0
|
||||
SA_NOCLDWAIT=0)
|
||||
SA_NOCLDWAIT=0
|
||||
IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS)
|
||||
|
||||
elseif(LIBRETRO)
|
||||
add_library(${PROJECT_NAME} SHARED core/emulator.cpp)
|
||||
@@ -163,7 +187,9 @@ elseif(LIBRETRO)
|
||||
endif()
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/shell/libretro/libretro.osx.def")
|
||||
else()
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
|
||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
|
||||
endif()
|
||||
endif()
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "flycast_libretro"
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
|
||||
@@ -177,7 +203,7 @@ elseif(LIBRETRO)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${FOUNDATION})
|
||||
endif()
|
||||
if(ANDROID OR USE_GLES)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3 HAVE_OPENGLES HAVE_OPENGLES3)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3 HAVE_OPENGLES HAVE_OPENGLES3 HAVE_OIT)
|
||||
elseif(IOS)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3 HAVE_OPENGLES HAVE_OPENGLES3)
|
||||
find_library(OPENGLES OpenGLES)
|
||||
@@ -193,6 +219,14 @@ elseif(LIBRETRO)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE "-lGLESv2")
|
||||
endif()
|
||||
elseif(USE_OPENGL)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD")
|
||||
find_package(OpenGL REQUIRED)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.29.0)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENGL_INCLUDE_DIR})
|
||||
else()
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENGL_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_OPENGL)
|
||||
if(APPLE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-framework,OpenGL")
|
||||
@@ -265,6 +299,8 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
$<$<BOOL:${MSVC}>:_WINSOCK_DEPRECATED_NO_WARNING>
|
||||
$<$<BOOL:${MSVC}>:NOMINMAX>
|
||||
$<$<BOOL:${TEST_AUTOMATION}>:TEST_AUTOMATION>
|
||||
$<$<BOOL:${WINDOWS_STORE}>:WINDOWS_STORE>
|
||||
$<$<BOOL:${WINDOWS_STORE}>:TARGET_UWP>
|
||||
$<$<BOOL:${WINDOWS_STORE}>:NOCRYPT>
|
||||
$<$<BOOL:${WINDOWS_STORE}>:_WIN32_WINNT=0x0A00>
|
||||
$<$<OR:$<BOOL:${MINGW}>,$<BOOL:${MSVC}>>:_USE_MATH_DEFINES>)
|
||||
@@ -286,6 +322,10 @@ if(NINTENDO_SWITCH)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE shell/switch)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
target_link_options(${PROJECT_NAME} PRIVATE "-Wl,--no-execute-only")
|
||||
endif()
|
||||
|
||||
if(USE_BREAKPAD AND NOT LIBRETRO)
|
||||
find_program(SH_EXECUTABLE sh)
|
||||
if((ANDROID AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") OR (MINGW AND SH_EXECUTABLE) OR (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||
@@ -373,6 +413,10 @@ if(USE_BREAKPAD AND NOT LIBRETRO)
|
||||
endif()
|
||||
|
||||
if(USE_OPENMP)
|
||||
if(APPLE AND IS_DIRECTORY "/opt/homebrew/opt/libomp")
|
||||
list(PREPEND CMAKE_PREFIX_PATH "/opt/homebrew/opt/libomp")
|
||||
message(STATUS "Found and using Homebrew libomp hint.")
|
||||
endif()
|
||||
find_package(OpenMP)
|
||||
if(OpenMP_CXX_FOUND)
|
||||
if(MINGW)
|
||||
@@ -387,22 +431,6 @@ if(USE_OPENMP)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_CXX)
|
||||
endif()
|
||||
endif()
|
||||
if(IOS)
|
||||
message("-- Finding OpenMP.xcframework for iOS")
|
||||
set(OPENMP_XCFRAMEWORK "${CMAKE_CURRENT_SOURCE_DIR}/shell/apple/emulator-ios/OpenMP/OpenMP.xcframework")
|
||||
if(IS_DIRECTORY ${OPENMP_XCFRAMEWORK})
|
||||
# CMake does not support linking xcframework directly atm. (see https://gitlab.kitware.com/cmake/cmake/-/issues/21752)
|
||||
set(OPENMP_FRAMEWORK ${OPENMP_XCFRAMEWORK}/ios-arm64_arm64e/OpenMP.framework/Versions/A/libomp.a)
|
||||
if(EXISTS ${OPENMP_FRAMEWORK})
|
||||
message("-- Found OpenMP.xcframework: ${OPENMP_FRAMEWORK} (Linking static library before CMake support linking xcframework)")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENMP_FRAMEWORK})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENMP_XCFRAMEWORK}/ios-arm64_arm64e/OpenMP.framework/Versions/A/Headers)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Xclang -fopenmp>)
|
||||
endif()
|
||||
else()
|
||||
message("-- Could NOT find OpenMP.xcframework, compile it using shell/apple/emulator-ios/OpenMP/build_ios_openmp.sh")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library" OFF)
|
||||
@@ -419,14 +447,12 @@ if(USE_VULKAN)
|
||||
find_package(glslang REQUIRED)
|
||||
else()
|
||||
option(BUILD_EXTERNAL "Build external dependencies in /External" OFF)
|
||||
option(SKIP_GLSLANG_INSTALL "Skip installation" ON)
|
||||
option(ENABLE_SPVREMAPPER "Enables building of SPVRemapper" OFF)
|
||||
option(ENABLE_GLSLANG_BINARIES "Builds glslang and spirv-remap" OFF)
|
||||
option(ENABLE_HLSL "Enables HLSL input support" OFF)
|
||||
option(ENABLE_OPT "Enables spirv-opt capability if present" OFF)
|
||||
option(ENABLE_PCH "Enables Precompiled header" OFF)
|
||||
add_subdirectory(core/deps/glslang EXCLUDE_FROM_ALL)
|
||||
add_library(glslang::glslang-default-resource-limits ALIAS glslang-default-resource-limits)
|
||||
add_library(glslang::SPIRV ALIAS SPIRV)
|
||||
endif()
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE glslang::glslang-default-resource-limits glslang::SPIRV)
|
||||
endif()
|
||||
@@ -450,6 +476,32 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID AND NOT IOS)
|
||||
if (NOT NINTENDO_SWITCH)
|
||||
# DreamLink enabled for non-mobile Linux, MacOS, and Windows
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DREAMLINK_DEVICES=1)
|
||||
set(USE_DREAMLINK_DEVICES ON) # Must be set before adding core/sdl
|
||||
|
||||
if (WINDOWS_STORE)
|
||||
# Use winrt implementation of the DreamPicoPort-API
|
||||
option(DREAMPICOPORT_WITH_LIBUSB "Use libusb to access DreamPicoPort-API" OFF)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|Haiku")
|
||||
# A version of libusb is installed in FreeBSD
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
|
||||
|
||||
set(DREAMPICOPORT_LIBUSB_LIBRARIES "${LIBUSB_LIBRARIES}" CACHE STRING "Name of the libusb library to link against")
|
||||
set(DREAMPICOPORT_LIBUSB_INCLUDE_DIRS "${LIBUSB_INCLUDE_DIRS}" CACHE STRING "Path to libusb include directory, when applicable")
|
||||
else()
|
||||
# libusb
|
||||
add_subdirectory(core/deps/DreamPicoPort-API/ext/libusb-cmake)
|
||||
endif()
|
||||
|
||||
# DreamPicoPort-API
|
||||
option(DREAMPICOPORT_ADD_LIBUSB "Add internal libusb library" OFF) # Already included above, when applicable
|
||||
add_subdirectory(core/deps/DreamPicoPort-API EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dream_pico_port_api)
|
||||
endif()
|
||||
|
||||
if(USE_HOST_SDL)
|
||||
find_package(SDL2 2.0.9)
|
||||
endif()
|
||||
@@ -475,18 +527,7 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_SDL USE_SDL_AUDIO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/sdl/sdl.cpp
|
||||
core/sdl/sdl.h
|
||||
core/sdl/sdl_gamepad.h
|
||||
core/sdl/sdl_keyboard.h
|
||||
core/sdl/sdl_keyboard_mac.h
|
||||
core/sdl/dreamlink.cpp
|
||||
core/sdl/dreamlink.h
|
||||
core/sdl/dreampicoport.cpp
|
||||
core/sdl/dreampicoport.h
|
||||
core/sdl/dreamconn.cpp
|
||||
core/sdl/dreamconn.h)
|
||||
add_subdirectory(core/sdl)
|
||||
|
||||
if((UNIX AND NOT APPLE) OR NINTENDO_SWITCH)
|
||||
find_package(CURL REQUIRED)
|
||||
@@ -726,6 +767,37 @@ if(WIN32)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent)
|
||||
endif()
|
||||
|
||||
if(NOT LIBRETRO AND NOT NINTENDO_SWITCH)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_ICE)
|
||||
# libjuice
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
option(NO_SERVER "Disable server support" ON)
|
||||
option(NO_TESTS "Disable tests build" ON)
|
||||
add_subdirectory(core/deps/libjuice EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE LibJuice::LibJuiceStatic)
|
||||
|
||||
# websocket++
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/websocketpp)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE ASIO_STANDALONE)
|
||||
if (MSVC)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
_WEBSOCKETPP_CPP11_FUNCTIONAL_
|
||||
_WEBSOCKETPP_CPP11_SYSTEM_ERROR_
|
||||
_WEBSOCKETPP_CPP11_RANDOM_DEVICE_
|
||||
_WEBSOCKETPP_CPP11_MEMORY_
|
||||
_WEBSOCKETPP_CPP11_TYPE_TRAITS_)
|
||||
elseif(MINGW)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE _WEBSOCKETPP_CPP11_THREAD_)
|
||||
else()
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE _WEBSOCKETPP_CPP11_STL_)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(TINYGETTEXT_WO_ICONV ON)
|
||||
add_subdirectory(core/deps/tinygettext EXCLUDE_FROM_ALL)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/tinygettext/include)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE tinygettext::tinygettext)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/picotcp/include core/deps/picotcp/modules)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/deps/picotcp/include/arch/pico_arm9.h
|
||||
@@ -791,7 +863,10 @@ target_sources(${PROJECT_NAME} PRIVATE core/deps/lzma/7zArcIn.c core/deps/lzma/7
|
||||
add_subdirectory(core/deps/libelf)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE elf)
|
||||
if(NOT LIBRETRO)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE IMGUI_DISABLE_DEMO_WINDOWS IMGUI_DEFINE_MATH_OPERATORS)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
IMGUI_DISABLE_DEMO_WINDOWS
|
||||
IMGUI_DISABLE_DEBUG_TOOLS
|
||||
IMGUI_DEFINE_MATH_OPERATORS)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/imgui core/deps/imgui/backends)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/deps/imgui/imgui.cpp
|
||||
@@ -819,10 +894,8 @@ if(USE_DISCORD AND NOT LIBRETRO)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/discord-rpc/include)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE discord-rpc)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DISCORD)
|
||||
target_sources(${PROJECT_NAME} PRIVATE core/ui/discord.cpp)
|
||||
endif()
|
||||
|
||||
cmrc_add_resource_library(flycast-resources ALIAS flycast::res NAMESPACE flycast)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE flycast::res)
|
||||
|
||||
if(LIBRETRO)
|
||||
@@ -869,274 +942,23 @@ if(LIBRETRO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/archive/7zArchive.cpp
|
||||
core/archive/7zArchive.h
|
||||
core/archive/archive.cpp
|
||||
core/archive/archive.h
|
||||
core/archive/rzip.cpp
|
||||
core/archive/rzip.h
|
||||
core/archive/ZipArchive.cpp
|
||||
core/archive/ZipArchive.h
|
||||
core/cfg/option.h)
|
||||
add_subdirectory(core/archive)
|
||||
add_subdirectory(core/cfg)
|
||||
add_subdirectory(core/hw)
|
||||
add_subdirectory(core/imgread)
|
||||
add_subdirectory(core/input)
|
||||
add_subdirectory(core/log)
|
||||
add_subdirectory(core/network)
|
||||
add_subdirectory(core/oslib)
|
||||
add_subdirectory(core/audio)
|
||||
add_subdirectory(core/reios)
|
||||
add_subdirectory(core/achievements)
|
||||
add_subdirectory(core/ui)
|
||||
add_subdirectory(core/lua)
|
||||
add_subdirectory(core/profiler)
|
||||
add_subdirectory(core/wsi)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/cfg/cfg.cpp
|
||||
core/cfg/cfg.h
|
||||
core/cfg/cl.cpp
|
||||
core/cfg/ini.cpp
|
||||
core/cfg/ini.h
|
||||
core/cfg/option.cpp)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/hw/hwreg.h
|
||||
core/hw/aica/aica.cpp
|
||||
core/hw/aica/aica.h
|
||||
core/hw/aica/aica_if.cpp
|
||||
core/hw/aica/aica_if.h
|
||||
core/hw/aica/aica_mem.cpp
|
||||
core/hw/aica/aica_mem.h
|
||||
core/hw/aica/dsp.cpp
|
||||
core/hw/aica/dsp.h
|
||||
core/hw/aica/dsp_arm32.cpp
|
||||
core/hw/aica/dsp_arm64.cpp
|
||||
core/hw/aica/dsp_interp.cpp
|
||||
core/hw/aica/dsp_x64.cpp
|
||||
core/hw/aica/dsp_x86.cpp
|
||||
core/hw/aica/sgc_if.cpp
|
||||
core/hw/aica/sgc_if.h
|
||||
core/hw/arm7/arm7.cpp
|
||||
core/hw/arm7/arm7.h
|
||||
core/hw/arm7/arm_mem.cpp
|
||||
core/hw/arm7/arm_mem.h
|
||||
core/hw/arm7/arm7_rec_arm32.cpp
|
||||
core/hw/arm7/arm7_rec_arm64.cpp
|
||||
core/hw/arm7/arm7_rec_x64.cpp
|
||||
core/hw/arm7/arm7_rec.cpp
|
||||
core/hw/arm7/arm7_rec.h
|
||||
core/hw/arm7/arm-new.h
|
||||
core/hw/bba/bba.h
|
||||
core/hw/bba/bba.cpp
|
||||
core/hw/bba/rtl8139c.h
|
||||
core/hw/bba/rtl8139c.cpp
|
||||
core/hw/flashrom/at93cxx.cpp
|
||||
core/hw/flashrom/at93cxx.h
|
||||
core/hw/flashrom/flashrom.cpp
|
||||
core/hw/flashrom/flashrom.h
|
||||
core/hw/flashrom/nvmem.cpp
|
||||
core/hw/flashrom/nvmem.h
|
||||
core/hw/flashrom/x76f100.cpp
|
||||
core/hw/flashrom/x76f100.h
|
||||
core/hw/gdrom/gdrom_if.h
|
||||
core/hw/gdrom/gdrom_response.cpp
|
||||
core/hw/gdrom/gdromv3.cpp
|
||||
core/hw/gdrom/gdromv3.h
|
||||
core/hw/holly/holly_intc.cpp
|
||||
core/hw/holly/holly_intc.h
|
||||
core/hw/holly/sb.cpp
|
||||
core/hw/holly/sb.h
|
||||
core/hw/holly/sb_mem.cpp
|
||||
core/hw/holly/sb_mem.h
|
||||
core/hw/maple/maple_cfg.cpp
|
||||
core/hw/maple/maple_cfg.h
|
||||
core/hw/maple/maple_devs.cpp
|
||||
core/hw/maple/maple_devs.h
|
||||
core/hw/maple/maple_helper.cpp
|
||||
core/hw/maple/maple_helper.h
|
||||
core/hw/maple/maple_if.cpp
|
||||
core/hw/maple/maple_if.h
|
||||
core/hw/maple/maple_jvs.cpp
|
||||
core/hw/mem/addrspace.cpp
|
||||
core/hw/mem/addrspace.h
|
||||
core/hw/mem/mem_watch.cpp
|
||||
core/hw/mem/mem_watch.h
|
||||
core/hw/modem/modem.cpp
|
||||
core/hw/modem/modem.h
|
||||
core/hw/modem/modem_regs.h
|
||||
core/hw/naomi/awave_regs.h
|
||||
core/hw/naomi/awcartridge.cpp
|
||||
core/hw/naomi/awcartridge.h
|
||||
core/hw/naomi/decrypt.cpp
|
||||
core/hw/naomi/decrypt.h
|
||||
core/hw/naomi/gdcartridge.cpp
|
||||
core/hw/naomi/gdcartridge.h
|
||||
core/hw/naomi/m1cartridge.cpp
|
||||
core/hw/naomi/m1cartridge.h
|
||||
core/hw/naomi/m4cartridge.cpp
|
||||
core/hw/naomi/m4cartridge.h
|
||||
core/hw/naomi/multiboard.h
|
||||
core/hw/naomi/multiboard.cpp
|
||||
core/hw/naomi/naomi_cart.cpp
|
||||
core/hw/naomi/naomi_cart.h
|
||||
core/hw/naomi/naomi_flashrom.cpp
|
||||
core/hw/naomi/naomi_flashrom.h
|
||||
core/hw/naomi/naomi_m3comm.cpp
|
||||
core/hw/naomi/naomi_m3comm.h
|
||||
core/hw/naomi/naomi.cpp
|
||||
core/hw/naomi/naomi.h
|
||||
core/hw/naomi/naomi_regs.h
|
||||
core/hw/naomi/naomi_roms.cpp
|
||||
core/hw/naomi/naomi_roms.h
|
||||
core/hw/naomi/naomi_roms_input.h
|
||||
core/hw/naomi/netdimm.cpp
|
||||
core/hw/naomi/netdimm.h
|
||||
core/hw/naomi/card_reader.h
|
||||
core/hw/naomi/card_reader.cpp
|
||||
core/hw/naomi/touchscreen.h
|
||||
core/hw/naomi/touchscreen.cpp
|
||||
core/hw/naomi/printer.h
|
||||
core/hw/naomi/printer.cpp
|
||||
core/hw/naomi/systemsp.h
|
||||
core/hw/naomi/systemsp.cpp
|
||||
core/hw/naomi/hopper.h
|
||||
core/hw/naomi/hopper.cpp
|
||||
core/hw/pvr/elan.cpp
|
||||
core/hw/pvr/elan.h
|
||||
core/hw/pvr/elan_struct.h
|
||||
core/hw/pvr/pvr.cpp
|
||||
core/hw/pvr/pvr.h
|
||||
core/hw/pvr/pvr_mem.cpp
|
||||
core/hw/pvr/pvr_mem.h
|
||||
core/hw/pvr/pvr_regs.cpp
|
||||
core/hw/pvr/pvr_regs.h
|
||||
core/hw/pvr/pvr_sb_regs.cpp
|
||||
core/hw/pvr/pvr_sb_regs.h
|
||||
core/hw/pvr/Renderer_if.cpp
|
||||
core/hw/pvr/Renderer_if.h
|
||||
core/hw/pvr/spg.cpp
|
||||
core/hw/pvr/spg.h
|
||||
core/hw/pvr/ta_const_df.h
|
||||
core/hw/pvr/ta.cpp
|
||||
core/hw/pvr/ta_ctx.cpp
|
||||
core/hw/pvr/ta_ctx.h
|
||||
core/hw/pvr/ta.h
|
||||
core/hw/pvr/ta_structs.h
|
||||
core/hw/pvr/ta_util.cpp
|
||||
core/hw/pvr/ta_vtx.cpp
|
||||
core/hw/sh4/dyna/blockmanager.cpp
|
||||
core/hw/sh4/dyna/blockmanager.h
|
||||
core/hw/sh4/dyna/decoder.cpp
|
||||
core/hw/sh4/dyna/decoder.h
|
||||
core/hw/sh4/dyna/decoder_opcodes.h
|
||||
core/hw/sh4/dyna/driver.cpp
|
||||
core/hw/sh4/dyna/ngen.h
|
||||
core/hw/sh4/dyna/shil_canonical.h
|
||||
core/hw/sh4/dyna/shil.cpp
|
||||
core/hw/sh4/dyna/shil.h
|
||||
core/hw/sh4/dyna/ssa.cpp
|
||||
core/hw/sh4/dyna/ssa.h
|
||||
core/hw/sh4/dyna/ssa_regalloc.h
|
||||
core/hw/sh4/fsca-table.h
|
||||
core/hw/sh4/interpr/sh4_fpu.cpp
|
||||
core/hw/sh4/interpr/sh4_interpreter.cpp
|
||||
core/hw/sh4/interpr/sh4_opcodes.cpp
|
||||
core/hw/sh4/interpr/sh4_opcodes.h
|
||||
core/hw/sh4/modules/bsc.cpp
|
||||
core/hw/sh4/modules/ccn.cpp
|
||||
core/hw/sh4/modules/ccn.h
|
||||
core/hw/sh4/modules/cpg.cpp
|
||||
core/hw/sh4/modules/dmac.cpp
|
||||
core/hw/sh4/modules/dmac.h
|
||||
core/hw/sh4/modules/fastmmu.cpp
|
||||
core/hw/sh4/modules/intc.cpp
|
||||
core/hw/sh4/modules/mmu.cpp
|
||||
core/hw/sh4/modules/mmu.h
|
||||
core/hw/sh4/modules/modules.h
|
||||
core/hw/sh4/modules/rtc.cpp
|
||||
core/hw/sh4/modules/serial.cpp
|
||||
core/hw/sh4/modules/tmu.cpp
|
||||
core/hw/sh4/modules/ubc.cpp
|
||||
core/hw/sh4/modules/wince.h
|
||||
core/hw/sh4/sh4_cache.h
|
||||
core/hw/sh4/sh4_cycles.cpp
|
||||
core/hw/sh4/sh4_cycles.h
|
||||
core/hw/sh4/sh4_core.h
|
||||
core/hw/sh4/sh4_core_regs.cpp
|
||||
core/hw/sh4/sh4_if.h
|
||||
core/hw/sh4/sh4_interpreter.h
|
||||
core/hw/sh4/sh4_interrupts.cpp
|
||||
core/hw/sh4/sh4_interrupts.h
|
||||
core/hw/sh4/sh4_mem.cpp
|
||||
core/hw/sh4/sh4_mem.h
|
||||
core/hw/sh4/sh4_mmr.cpp
|
||||
core/hw/sh4/sh4_mmr.h
|
||||
core/hw/sh4/sh4_opcode_list.cpp
|
||||
core/hw/sh4/sh4_opcode_list.h
|
||||
core/hw/sh4/sh4_rom.cpp
|
||||
core/hw/sh4/sh4_rom.h
|
||||
core/hw/sh4/sh4_sched.cpp
|
||||
core/hw/sh4/sh4_sched.h
|
||||
core/hw/sh4/storeq.cpp)
|
||||
|
||||
cmrc_add_resources(flycast-resources
|
||||
WHENCE resources
|
||||
resources/flash/alienfnt.nvmem.zip # network settings
|
||||
resources/flash/gunsur2.nvmem.zip
|
||||
resources/flash/mazan.nvmem.zip # free play, calibrated
|
||||
resources/flash/otrigger.nvmem.zip
|
||||
resources/flash/wldkicks.nvmem.zip # free play
|
||||
resources/flash/wldkicksj.nvmem.zip # free play
|
||||
resources/flash/wldkicksu.nvmem.zip # free play
|
||||
resources/flash/f355.nvmem.zip # printer on
|
||||
resources/flash/f355twin.nvmem.zip # printer on
|
||||
resources/flash/f355twn2.nvmem.zip # printer on
|
||||
resources/flash/dirtypig.nvmem.zip # 4 players
|
||||
resources/flash/dirtypig.nvmem2.zip
|
||||
resources/flash/vf4.nvmem.zip # card all day, stage select
|
||||
resources/flash/vf4evob.nvmem.zip # card all day, stage select
|
||||
resources/flash/vf4tuned.nvmem.zip # card all day, stage select, 45 sec time limit, 3 match
|
||||
resources/flash/magicpop.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/ochaken.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/puyomedal.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/unomedal.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/westdrmg.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/picture/f355_print_template.png)
|
||||
|
||||
cmrc_add_resources(flycast-resources
|
||||
fonts/printer_ascii8x16.bin.zip
|
||||
fonts/printer_ascii12x24.bin.zip
|
||||
fonts/printer_kanji16x16.bin.zip
|
||||
fonts/printer_kanji24x24.bin.zip)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
cmrc_add_resources(flycast-resources
|
||||
fonts/Roboto-Medium.ttf.zip
|
||||
fonts/Roboto-Regular.ttf.zip
|
||||
fonts/fa-solid-900.ttf.zip)
|
||||
if(ANDROID OR IOS)
|
||||
cmrc_add_resources(flycast-resources
|
||||
WHENCE resources
|
||||
resources/picture/buttons.png
|
||||
resources/picture/buttons-arcade.png)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/imgread/cdi.cpp
|
||||
core/imgread/cdio.cpp
|
||||
core/imgread/chd.cpp
|
||||
core/imgread/common.cpp
|
||||
core/imgread/common.h
|
||||
core/imgread/cue.cpp
|
||||
core/imgread/gdi.cpp
|
||||
core/imgread/ImgReader.cpp
|
||||
core/imgread/iso9660.h
|
||||
core/imgread/isofs.cpp
|
||||
core/imgread/isofs.h)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/input/gamepad.h
|
||||
core/input/gamepad_device.cpp
|
||||
core/input/gamepad_device.h
|
||||
core/input/keyboard_device.h
|
||||
core/input/mapping.cpp
|
||||
core/input/mapping.h
|
||||
core/input/mouse.cpp
|
||||
core/input/mouse.h)
|
||||
endif()
|
||||
include(resources/resources.cmake)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
@@ -1151,14 +973,7 @@ else()
|
||||
core/linux/posix_vmem.cpp
|
||||
core/linux/unwind_info.cpp)
|
||||
if(NINTENDO_SWITCH)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
shell/switch/libnx_vmem.cpp
|
||||
shell/switch/stubs.c
|
||||
shell/switch/context_switch.S
|
||||
shell/switch/nswitch.h
|
||||
shell/switch/switch_gamepad.h
|
||||
shell/switch/switch_main.cpp
|
||||
shell/switch/ucontext.h)
|
||||
add_subdirectory(shell/switch)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1172,109 +987,6 @@ if(NOT LIBRETRO)
|
||||
core/linux-dist/x11_keyboard.h)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/log/BitSet.h
|
||||
core/log/Log.h
|
||||
core/log/StringUtil.h)
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/log/ConsoleListener.h
|
||||
core/log/ConsoleListenerDroid.cpp
|
||||
core/log/ConsoleListenerNix.cpp
|
||||
core/log/ConsoleListenerWin.cpp
|
||||
core/log/LogManager.cpp
|
||||
core/log/LogManager.h)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/network/alienfnt_modem.cpp
|
||||
core/network/alienfnt_modem.h
|
||||
core/network/dns.cpp
|
||||
core/network/ggpo.cpp
|
||||
core/network/ggpo.h
|
||||
core/network/miniupnp.cpp
|
||||
core/network/miniupnp.h
|
||||
core/network/naomi_network.cpp
|
||||
core/network/naomi_network.h
|
||||
core/network/net_handshake.cpp
|
||||
core/network/net_handshake.h
|
||||
core/network/net_platform.h
|
||||
core/network/output.cpp
|
||||
core/network/output.h
|
||||
core/network/picoppp.cpp
|
||||
core/network/picoppp.h
|
||||
core/network/netservice.cpp
|
||||
core/network/netservice.h
|
||||
core/network/dcnet.cpp
|
||||
core/network/dcnet.h)
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/network/ifaddrs.c)
|
||||
endif()
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/audio/audiobackend_alsa.cpp
|
||||
core/audio/audiobackend_coreaudio.cpp
|
||||
core/audio/audiobackend_directsound.cpp
|
||||
core/audio/audiobackend_libao.cpp
|
||||
core/audio/audiobackend_null.cpp
|
||||
core/audio/audiobackend_oboe.cpp
|
||||
core/audio/audiobackend_omx.cpp
|
||||
core/audio/audiobackend_oss.cpp
|
||||
core/audio/audiobackend_pulseaudio.cpp
|
||||
core/audio/audiobackend_sdl2.cpp
|
||||
core/audio/audiostream.cpp
|
||||
core/oslib/http_client.cpp
|
||||
core/oslib/http_client.h
|
||||
core/oslib/oslib.cpp)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/audio/audiostream.h
|
||||
core/oslib/directory.h
|
||||
core/oslib/host_context.h
|
||||
core/oslib/oslib.h
|
||||
core/oslib/resources.cpp
|
||||
core/oslib/resources.h
|
||||
core/oslib/storage.cpp
|
||||
core/oslib/storage.h
|
||||
core/oslib/unwind_info.h
|
||||
core/oslib/virtmem.h
|
||||
core/lua/lua.cpp
|
||||
core/lua/lua.h)
|
||||
|
||||
if (ENABLE_DC_PROFILER)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/profiler/dc_profiler.cpp
|
||||
core/profiler/dc_profiler.h)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE DC_PROFILER)
|
||||
endif()
|
||||
|
||||
if (ENABLE_FC_PROFILER)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/profiler/fc_profiler.cpp
|
||||
core/profiler/fc_profiler.h)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE FC_PROFILER)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/reios/descrambl.cpp
|
||||
core/reios/descrambl.h
|
||||
core/reios/gdrom_hle.cpp
|
||||
core/reios/gdrom_hle.h
|
||||
core/reios/reios.cpp
|
||||
core/reios/reios.h
|
||||
core/reios/reios_elf.cpp
|
||||
core/reios/reios_elf.h)
|
||||
cmrc_add_resources(flycast-resources fonts/biosfont.bin.zip)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/achievements/achievements.cpp
|
||||
core/achievements/achievements.h)
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/deps/rcheevos/src/rc_client_raintegration.c
|
||||
@@ -1310,23 +1022,8 @@ if(NOT LIBRETRO)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_RACHIEVEMENTS RC_DISABLE_LUA)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/wsi/context.h
|
||||
core/wsi/libretro.cpp
|
||||
core/wsi/libretro.h
|
||||
core/wsi/switcher.cpp)
|
||||
|
||||
if(USE_OPENGL)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_OPENGL)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/wsi/egl.cpp
|
||||
core/wsi/egl.h
|
||||
core/wsi/gl_context.cpp
|
||||
core/wsi/gl_context.h
|
||||
core/wsi/osx.cpp
|
||||
core/wsi/osx.h
|
||||
core/wsi/sdl.cpp
|
||||
core/wsi/sdl.h)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/rend/gles/glcache.h
|
||||
@@ -1348,7 +1045,7 @@ if(USE_OPENGL)
|
||||
core/rend/gles/opengl_driver.h)
|
||||
endif()
|
||||
|
||||
if(NOT (APPLE OR ANDROID OR USE_GLES OR USE_GLES2))
|
||||
if(NOT (APPLE OR USE_GLES2))
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/rend/gl4/abuffer.cpp
|
||||
core/rend/gl4/gl4.h
|
||||
@@ -1385,29 +1082,6 @@ target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/rend/texconv.cpp
|
||||
core/rend/texconv.h
|
||||
core/rend/norend/norend.cpp)
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/ui/game_scanner.cpp
|
||||
core/ui/game_scanner.h
|
||||
core/ui/imgui_driver.h
|
||||
core/ui/gui.cpp
|
||||
core/ui/gui.h
|
||||
core/ui/gui_achievements.cpp
|
||||
core/ui/vgamepad.cpp
|
||||
core/ui/vgamepad.h
|
||||
core/ui/gui_chat.h
|
||||
core/ui/gui_cheats.cpp
|
||||
core/ui/gui_util.cpp
|
||||
core/ui/gui_util.h
|
||||
core/ui/mainui.cpp
|
||||
core/ui/mainui.h
|
||||
core/ui/boxart/boxart.cpp
|
||||
core/ui/boxart/boxart.h
|
||||
core/ui/boxart/gamesdb.cpp
|
||||
core/ui/boxart/gamesdb.h
|
||||
core/ui/boxart/scraper.cpp
|
||||
core/ui/boxart/scraper.h)
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_ENABLE_BETA_EXTENSIONS VK_NO_PROTOTYPES)
|
||||
@@ -1696,17 +1370,9 @@ if(NOT LIBRETRO)
|
||||
|
||||
if(ANDROID)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
shell/android-studio/flycast/src/main/jni/src/Android.cpp
|
||||
shell/android-studio/flycast/src/main/jni/src/android_gamepad.h
|
||||
shell/android-studio/flycast/src/main/jni/src/android_storage.h
|
||||
shell/android-studio/flycast/src/main/jni/src/http_client.h
|
||||
shell/android-studio/flycast/src/main/jni/src/jni_util.h
|
||||
shell/android-studio/flycast/src/main/jni/src/android_input.cpp
|
||||
shell/android-studio/flycast/src/main/jni/src/android_keyboard.h)
|
||||
|
||||
add_subdirectory(shell/android-studio/flycast/src/main/jni/src)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE android log)
|
||||
|
||||
elseif(APPLE)
|
||||
string(TIMESTAMP YEAR "%Y")
|
||||
string(REGEX MATCH "^[^-]+" TAG_VERSION ${GIT_VERSION})
|
||||
@@ -1867,7 +1533,6 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/windows/clock.c
|
||||
core/windows/rawinput.cpp
|
||||
core/windows/rawinput.h)
|
||||
if(NOT BUILD_TESTING)
|
||||
@@ -1907,32 +1572,14 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE shell/windows/flycast.rc)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dsound winmm ws2_32 wsock32 xinput9_1_0 cfgmgr32 wininet psapi)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dsound winmm ws2_32 wsock32 xinput9_1_0 cfgmgr32 wininet psapi setupapi)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
option(BUILD_GMOCK "Builds the googlemock subproject" OFF)
|
||||
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" OFF)
|
||||
if(WIN32)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
add_subdirectory(core/deps/googletest EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest_main)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
tests/src/CheatManagerTest.cpp
|
||||
tests/src/ConfigFileTest.cpp
|
||||
tests/src/div32_test.cpp
|
||||
tests/src/test_stubs.cpp
|
||||
tests/src/serialize_test.cpp
|
||||
tests/src/AicaArmTest.cpp
|
||||
tests/src/Sh4InterpreterTest.cpp
|
||||
tests/src/MmuTest.cpp
|
||||
tests/src/util/PeriodicThreadTest.cpp
|
||||
tests/src/util/TsQueueTest.cpp
|
||||
tests/src/util/WorkerThreadTest.cpp)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(NINTENDO_SWITCH)
|
||||
|
||||
@@ -38,6 +38,10 @@ Install Flycast from [**Google Play**](https://play.google.com/store/apps/detail
|
||||
|
||||
`brew install --cask flycast`
|
||||
|
||||
### 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**.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
achievements.cpp
|
||||
achievements.h)
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "oslib/oslib.h"
|
||||
#include "emulator.h"
|
||||
#include "stdclass.h"
|
||||
#include "oslib/i18n.h"
|
||||
#include <cassert>
|
||||
#include <rc_client.h>
|
||||
#include <rc_hash.h>
|
||||
@@ -283,7 +284,8 @@ std::pair<std::string, bool> Achievements::getCachedImage(const char *url)
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream stream;
|
||||
std::ostringstream stream;
|
||||
stream.imbue(std::locale::classic());
|
||||
stream << std::hex << hash << ".png";
|
||||
return std::make_pair(cachePath + stream.str(), false);
|
||||
}
|
||||
@@ -303,7 +305,8 @@ std::string Achievements::getOrDownloadImage(const char *url)
|
||||
int rc = http::get(url, content, content_type);
|
||||
if (!http::success(rc))
|
||||
return {};
|
||||
std::stringstream stream;
|
||||
std::ostringstream stream;
|
||||
stream.imbue(std::locale::classic());
|
||||
stream << std::hex << hash << ".png";
|
||||
std::string localPath = cachePath + stream.str();
|
||||
FILE *f = nowide::fopen(localPath.c_str(), "wb");
|
||||
@@ -338,9 +341,10 @@ void Achievements::authenticationSuccess(const rc_client_user_t *user)
|
||||
int rc = rc_client_user_get_image_url(user, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
asyncTask([this, url]()
|
||||
{
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "User " + config::AchievementsUserName.get() + " authenticated";
|
||||
std::string text = strprintf(i18n::T("User %s authenticated"), config::AchievementsUserName.get().c_str());
|
||||
notifier.notify(Notification::Login, image, text);
|
||||
});
|
||||
}
|
||||
@@ -356,7 +360,7 @@ void Achievements::clientLoginWithTokenCallback(int result, const char *error_me
|
||||
if (result != RC_OK)
|
||||
{
|
||||
WARN_LOG(COMMON, "RA Login failed: %s", error_message);
|
||||
notifier.notify(Notification::Login, "", "RetroAchievements authentication failed", error_message);
|
||||
notifier.notify(Notification::Login, "", i18n::Ts("RetroAchievements authentication failed"), error_message);
|
||||
return;
|
||||
}
|
||||
achievements->authenticationSuccess(rc_client_get_user_info(client));
|
||||
@@ -391,7 +395,7 @@ void Achievements::clientLoginWithPasswordCallback(int result, const char *error
|
||||
if (!user || !user->token)
|
||||
{
|
||||
WARN_LOG(COMMON, "RA: rc_client_get_user_info() returned NULL");
|
||||
promise->set_exception(std::make_exception_ptr(FlycastException("No user token returned")));
|
||||
promise->set_exception(std::make_exception_ptr(FlycastException(i18n::Ts("No user token returned"))));
|
||||
delete promise;
|
||||
return;
|
||||
}
|
||||
@@ -543,11 +547,11 @@ void Achievements::clientEventHandler(const rc_client_event_t* event, rc_client_
|
||||
// TODO case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD:
|
||||
|
||||
case RC_CLIENT_EVENT_DISCONNECTED:
|
||||
notifyError("RetroAchievements disconnected");
|
||||
notifyError(i18n::Ts("RetroAchievements disconnected"));
|
||||
break;
|
||||
|
||||
case RC_CLIENT_EVENT_RECONNECTED:
|
||||
notifyError("RetroAchievements reconnected");
|
||||
notifyError(i18n::Ts("RetroAchievements reconnected"));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -577,7 +581,7 @@ void Achievements::handleUnlockEvent(const rc_client_event_t *event)
|
||||
{
|
||||
asyncTask([this, url, title, description]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "Achievement " + title + " unlocked!";
|
||||
std::string text = strprintf(i18n::T("Achievement %s unlocked!"), title.c_str());
|
||||
notifier.notify(Notification::Login, image, text, description);
|
||||
});
|
||||
}
|
||||
@@ -617,21 +621,21 @@ void Achievements::handleLeaderboardStarted(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
|
||||
INFO_LOG(COMMON, "RA: Leaderboard started: %s", leaderboard->title);
|
||||
std::string text = "Leaderboard " + std::string(leaderboard->title) + " started";
|
||||
std::string text = strprintf(i18n::T("Leaderboard %s started"), leaderboard->title);
|
||||
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
|
||||
}
|
||||
void Achievements::handleLeaderboardFailed(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
|
||||
INFO_LOG(COMMON, "RA: Leaderboard failed: %s", leaderboard->title);
|
||||
std::string text = "Leaderboard " + std::string(leaderboard->title) + " failed";
|
||||
std::string text = strprintf(i18n::T("Leaderboard %s failed"), leaderboard->title);
|
||||
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
|
||||
}
|
||||
void Achievements::handleLeaderboardSubmitted(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
|
||||
INFO_LOG(COMMON, "RA: Leaderboard submitted: %s", leaderboard->title);
|
||||
std::string text = "Leaderboard " + std::string(leaderboard->title) + " submitted";
|
||||
std::string text = strprintf(i18n::T("Leaderboard %s submitted"), leaderboard->title);
|
||||
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
|
||||
}
|
||||
void Achievements::handleShowLeaderboardTracker(const rc_client_event_t *event)
|
||||
@@ -656,12 +660,10 @@ void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t *event
|
||||
void Achievements::handleGameCompleted(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_game_t* game = rc_client_get_game_info(rc_client);
|
||||
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
|
||||
std::string text1 = strprintf(rc_client_get_hardcore_enabled(rc_client) ? i18n::T("Mastered %s") : i18n::T("Completed %s"), game->title);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::stringstream ss;
|
||||
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
|
||||
std::string text2(ss.str());
|
||||
std::string text2 = strprintf(i18n::T("%d achievements, %d points"), summary.num_unlocked_achievements, summary.points_unlocked);
|
||||
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_game_get_image_url(game, url.data(), url.size()) != RC_OK)
|
||||
@@ -907,15 +909,14 @@ void Achievements::gameLoaded(int result, const char *errorMessage)
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::string text2;
|
||||
if (summary.num_core_achievements > 0)
|
||||
text2 = "You have " + std::to_string(summary.num_unlocked_achievements)
|
||||
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
|
||||
text2 = strprintf(i18n::T("You have %d of %d achievements unlocked."), summary.num_unlocked_achievements, summary.num_core_achievements);
|
||||
else
|
||||
text2 = "This game has no achievements.";
|
||||
text2 = i18n::Ts("This game has no achievements.");
|
||||
asyncTask([this, url, text1, text2]() {
|
||||
std::string image;
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
std::string text3 = settings.raHardcoreMode ? "Hardcore Mode" : "";
|
||||
std::string text3 = settings.raHardcoreMode ? i18n::T("Hardcore Mode") : "";
|
||||
notifier.notify(Notification::Login, image, text1, text2, text3);
|
||||
});
|
||||
}
|
||||
@@ -946,14 +947,14 @@ void Achievements::diskChange()
|
||||
rc_client_begin_change_media_from_hash(rc_client, hash.c_str(), [](int result, const char *errorMessage, rc_client_t *client, void *userdata) {
|
||||
if (result == RC_HARDCORE_DISABLED) {
|
||||
settings.raHardcoreMode = false;
|
||||
notifier.notify(Notification::Login, "", "Hardcore mode disabled", "Unrecognized media inserted");
|
||||
notifier.notify(Notification::Login, "", i18n::Ts("Hardcore mode disabled"), i18n::Ts("Unrecognized media inserted"));
|
||||
}
|
||||
else if (result != RC_OK)
|
||||
{
|
||||
settings.raHardcoreMode = false;
|
||||
if (errorMessage == nullptr)
|
||||
errorMessage = rc_error_str(result);
|
||||
notifier.notify(Notification::Login, "", "Media change failed", errorMessage);
|
||||
notifier.notify(Notification::Login, "", i18n::Ts("Media change failed"), errorMessage);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "7zArchive.h"
|
||||
#include "deps/lzma/7z.h"
|
||||
#include "deps/lzma/7zCrc.h"
|
||||
#include "deps/lzma/Alloc.h"
|
||||
#include "lzma/7z.h"
|
||||
#include "lzma/7zCrc.h"
|
||||
#include "lzma/Alloc.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "archive.h"
|
||||
#include "deps/lzma/7z.h"
|
||||
#include "deps/lzma/7zFile.h"
|
||||
#include "lzma/7z.h"
|
||||
#include "lzma/7zFile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
7zArchive.cpp
|
||||
7zArchive.h
|
||||
archive.cpp
|
||||
archive.h
|
||||
rzip.cpp
|
||||
rzip.h
|
||||
ZipArchive.cpp
|
||||
ZipArchive.h)
|
||||
@@ -0,0 +1,17 @@
|
||||
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()
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
{
|
||||
snd_pcm_hw_params_t *params;
|
||||
|
||||
std::string device = cfgLoadStr("alsa", "device", "");
|
||||
std::string device = config::loadStr("alsa", "device");
|
||||
|
||||
int rc = -1;
|
||||
if (!device.empty() && device != "auto") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "audiostream.h"
|
||||
|
||||
#include "oslib/i18n.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
@@ -9,7 +9,7 @@ class NullAudioBackend : public AudioBackend
|
||||
|
||||
public:
|
||||
NullAudioBackend()
|
||||
: AudioBackend("null", "No Audio") {}
|
||||
: AudioBackend("null", Tnop("No Audio")) {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
@@ -48,6 +48,10 @@ public:
|
||||
return samples;
|
||||
}
|
||||
|
||||
std::string getName() const override {
|
||||
return i18n::Ts(name);
|
||||
}
|
||||
|
||||
private:
|
||||
the_clock::time_point last_time;
|
||||
};
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
#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>
|
||||
#include "stdclass.h"
|
||||
|
||||
class OboeBackend : AudioBackend
|
||||
{
|
||||
@@ -77,7 +78,7 @@ class OboeBackend : AudioBackend
|
||||
|
||||
public:
|
||||
OboeBackend()
|
||||
: AudioBackend("Oboe", "Automatic AAudio / OpenSL selection"), audioCallback(this), errorCallback(this) {}
|
||||
: AudioBackend("Oboe", Tnop("Automatic AAudio / OpenSL selection")), audioCallback(this), errorCallback(this) {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
@@ -195,6 +196,11 @@ public:
|
||||
}
|
||||
return std::max(0, result.value());
|
||||
}
|
||||
|
||||
|
||||
std::string getName() const override {
|
||||
return i18n::Ts(name);
|
||||
}
|
||||
};
|
||||
static OboeBackend oboeBackend;
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class SDLAudioBackend : AudioBackend
|
||||
|
||||
public:
|
||||
SDLAudioBackend()
|
||||
: AudioBackend("sdl2", "Simple DirectMedia Layer 2 Audio") {}
|
||||
: AudioBackend("sdl2", "Simple DirectMedia Layer 2") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ AudioBackend *AudioBackend::getBackend(const std::string& slug)
|
||||
autoBackend = sdlBackend;
|
||||
if (autoBackend == nullptr)
|
||||
autoBackend = backends->front();
|
||||
INFO_LOG(AUDIO, "Auto-selected audio backend \"%s\" (%s).", autoBackend->slug.c_str(), autoBackend->name.c_str());
|
||||
INFO_LOG(AUDIO, "Auto-selected audio backend \"%s\" (%s).", autoBackend->slug.c_str(), autoBackend->getName().c_str());
|
||||
|
||||
return autoBackend;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ void InitAudio()
|
||||
}
|
||||
if (currentBackend != nullptr)
|
||||
{
|
||||
INFO_LOG(AUDIO, "Initializing audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->name.c_str());
|
||||
INFO_LOG(AUDIO, "Initializing audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->getName().c_str());
|
||||
if (!currentBackend->init())
|
||||
{
|
||||
currentBackend = nullptr;
|
||||
@@ -114,7 +114,7 @@ void TermAudio()
|
||||
StopAudioRecording();
|
||||
audio_recording_started = rec_started;
|
||||
currentBackend->term();
|
||||
INFO_LOG(AUDIO, "Terminating audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->name.c_str());
|
||||
INFO_LOG(AUDIO, "Terminating audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->getName().c_str());
|
||||
currentBackend = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
virtual void termRecord() {}
|
||||
|
||||
std::string slug;
|
||||
std::string name;
|
||||
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]; }
|
||||
@@ -46,6 +46,7 @@ protected:
|
||||
: slug(slug), name(name) {
|
||||
registerAudioBackend(this);
|
||||
}
|
||||
std::string name;
|
||||
|
||||
private:
|
||||
static void registerAudioBackend(AudioBackend *backend)
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
|
||||
#define TARGET_UWP
|
||||
#define TARGET_UWP 1
|
||||
#endif
|
||||
#ifdef HAVE_D3D11
|
||||
#define USE_DX11
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
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()
|
||||
+59
-69
@@ -1,37 +1,33 @@
|
||||
#include "cfg.h"
|
||||
#include "ini.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
namespace config
|
||||
{
|
||||
|
||||
static std::string cfgPath;
|
||||
static bool save_config = true;
|
||||
static bool autoSave = true;
|
||||
static bool autoSaving = true;
|
||||
static IniFile cfgdb;
|
||||
|
||||
static emucfg::ConfigFile cfgdb;
|
||||
|
||||
static void saveConfigFile()
|
||||
static void saveFile()
|
||||
{
|
||||
FILE* cfgfile = nowide::fopen(cfgPath.c_str(), "wt");
|
||||
if (!cfgfile)
|
||||
{
|
||||
if (!cfgfile) {
|
||||
WARN_LOG(COMMON, "Error: Unable to open file '%s' for saving", cfgPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
cfgdb.save(cfgfile);
|
||||
std::fclose(cfgfile);
|
||||
}
|
||||
}
|
||||
void cfgSaveStr(const std::string& section, const std::string& key, const std::string& value)
|
||||
{
|
||||
cfgdb.set(section, key, value);
|
||||
|
||||
if (save_config && autoSave)
|
||||
saveConfigFile();
|
||||
static void autoSave() {
|
||||
if (autoSaving)
|
||||
saveFile();
|
||||
}
|
||||
|
||||
bool cfgOpen()
|
||||
bool open()
|
||||
{
|
||||
if (get_writable_config_path("").empty())
|
||||
// Config dir not set (android onboarding)
|
||||
@@ -41,9 +37,9 @@ bool cfgOpen()
|
||||
std::string config_path_read = get_readonly_config_path(filename);
|
||||
cfgPath = get_writable_config_path(filename);
|
||||
|
||||
FILE* cfgfile = nowide::fopen(config_path_read.c_str(), "r");
|
||||
if(cfgfile != NULL) {
|
||||
cfgdb.parse(cfgfile);
|
||||
FILE* cfgfile = nowide::fopen(config_path_read.c_str(), "rt");
|
||||
if (cfgfile != nullptr) {
|
||||
cfgdb.load(cfgfile);
|
||||
std::fclose(cfgfile);
|
||||
}
|
||||
else
|
||||
@@ -55,87 +51,81 @@ bool cfgOpen()
|
||||
{
|
||||
// Config file didn't exist
|
||||
INFO_LOG(COMMON, "Creating new empty config file at '%s'", cfgPath.c_str());
|
||||
saveConfigFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
// There was some other error (may be a permissions problem or something like that)
|
||||
save_config = false;
|
||||
saveFile();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string cfgLoadStr(const std::string& section, const std::string& key, const std::string& def)
|
||||
{
|
||||
return cfgdb.get(section, key, def);
|
||||
void saveStr(const std::string& section, const std::string& key, const std::string& value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
|
||||
void cfgSaveInt(const std::string& section, const std::string& key, s32 value)
|
||||
{
|
||||
cfgSaveStr(section, key, std::to_string(value));
|
||||
std::string loadStr(const std::string& section, const std::string& key, const std::string& def) {
|
||||
return cfgdb.getString(section, key, def);
|
||||
}
|
||||
|
||||
s32 cfgLoadInt(const std::string& section, const std::string& key, s32 def)
|
||||
{
|
||||
return cfgdb.get_int(section, key, def);
|
||||
void saveInt(const std::string& section, const std::string& key, int value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
|
||||
int64_t cfgLoadInt64(const std::string& section, const std::string& key, int64_t def) {
|
||||
return cfgdb.get_int64(section, key, def);
|
||||
}
|
||||
void cfgSaveInt64(const std::string& section, const std::string& key, int64_t value) {
|
||||
cfgdb.set_int64(section, key, value);
|
||||
int loadInt(const std::string& section, const std::string& key, int def) {
|
||||
return cfgdb.getInt(section, key, def);
|
||||
}
|
||||
|
||||
void cfgSaveBool(const std::string& section, const std::string& key, bool value)
|
||||
{
|
||||
cfgSaveStr(section, key, value ? "yes" : "no");
|
||||
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();
|
||||
}
|
||||
|
||||
bool cfgLoadBool(const std::string& section, const std::string& key, bool def)
|
||||
{
|
||||
return cfgdb.get_bool(section, key, def);
|
||||
void saveBool(const std::string& section, const std::string& key, bool value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
|
||||
void cfgSetVirtual(const std::string& section, const std::string& key, const std::string& value)
|
||||
{
|
||||
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 cfgIsVirtual(const std::string& section, const std::string& key)
|
||||
{
|
||||
return cfgdb.is_virtual(section, key);
|
||||
bool isTransient(const std::string& section, const std::string& key) {
|
||||
return cfgdb.isTransient(section, key);
|
||||
}
|
||||
|
||||
bool cfgHasSection(const std::string& section)
|
||||
{
|
||||
return cfgdb.has_section(section);
|
||||
bool hasSection(const std::string& section) {
|
||||
return cfgdb.hasSection(section);
|
||||
}
|
||||
|
||||
void cfgDeleteSection(const std::string& section)
|
||||
{
|
||||
cfgdb.delete_section(section);
|
||||
void deleteSection(const std::string& section) {
|
||||
cfgdb.deleteSection(section);
|
||||
}
|
||||
|
||||
void cfgDeleteEntry(const std::string& section, const std::string& key)
|
||||
{
|
||||
cfgdb.delete_entry(section, key);
|
||||
void deleteEntry(const std::string& section, const std::string& key) {
|
||||
cfgdb.deleteEntry(section, key);
|
||||
}
|
||||
|
||||
void cfgSetAutoSave(bool autoSave)
|
||||
void setAutoSave(bool autoSaving)
|
||||
{
|
||||
::autoSave = autoSave;
|
||||
if (autoSave)
|
||||
saveConfigFile();
|
||||
config::autoSaving = autoSaving;
|
||||
if (autoSaving)
|
||||
saveFile();
|
||||
}
|
||||
|
||||
void cfgSaveFloat(const std::string& section, const std::string& key, float value)
|
||||
{
|
||||
cfgdb.set_float(section, key, value);
|
||||
void saveFloat(const std::string& section, const std::string& key, float value) {
|
||||
cfgdb.set(section, key, value);
|
||||
autoSave();
|
||||
}
|
||||
float cfgLoadFloat(const std::string& section, const std::string& key, float def)
|
||||
{
|
||||
return cfgdb.get_float(section, key, def);
|
||||
float loadFloat(const std::string& section, const std::string& key, float def) {
|
||||
return cfgdb.getFloat(section, key, def);
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
+23
-19
@@ -1,24 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
bool cfgOpen();
|
||||
s32 cfgLoadInt(const std::string& section, const std::string& key, s32 def);
|
||||
void cfgSaveInt(const std::string& section, const std::string& key, s32 value);
|
||||
int64_t cfgLoadInt64(const std::string& section, const std::string& key, int64_t def);
|
||||
void cfgSaveInt64(const std::string& section, const std::string& key, int64_t value);
|
||||
std::string cfgLoadStr(const std::string& section, const std::string& key, const std::string& def);
|
||||
void cfgSaveStr(const std::string& section, const std::string& key, const std::string& value);
|
||||
void cfgSaveBool(const std::string& section, const std::string& key, bool value);
|
||||
bool cfgLoadBool(const std::string& section, const std::string& key, bool def);
|
||||
void cfgSaveFloat(const std::string& section, const std::string& key, float value);
|
||||
float cfgLoadFloat(const std::string& section, const std::string& key, float def);
|
||||
void cfgSetVirtual(const std::string& section, const std::string& key, const std::string& value);
|
||||
bool cfgIsVirtual(const std::string& section, const std::string& key);
|
||||
namespace config
|
||||
{
|
||||
|
||||
void ParseCommandLine(int argc, char *argv[]);
|
||||
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);
|
||||
|
||||
void cfgSetAutoSave(bool autoSave);
|
||||
bool cfgHasSection(const std::string& section);
|
||||
void cfgDeleteSection(const std::string& section);
|
||||
void cfgDeleteEntry(const std::string& section, const std::string& key);
|
||||
void parseCommandLine(int argc, const char * const 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);
|
||||
|
||||
}
|
||||
+91
-119
@@ -1,143 +1,115 @@
|
||||
/*
|
||||
Command line parsing
|
||||
*/
|
||||
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 "cfg/cfg.h"
|
||||
#include "stdclass.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "cfg/cfg.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
static int setconfig(char *arg[], int cl)
|
||||
namespace config
|
||||
{
|
||||
int rv=0;
|
||||
|
||||
if (cl < 1)
|
||||
static void usage(const char *exe)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
static void parseConfigOption(const std::string& str)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while (pos < str.length())
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid number of parameters, format is section:key=value,section:key=value,...");
|
||||
return rv;
|
||||
size_t comma = str.find(',', pos);
|
||||
if (comma == str.npos)
|
||||
comma = str.length();
|
||||
std::string option = str.substr(pos, comma - pos);
|
||||
size_t colpos = option.find(':');
|
||||
size_t eqpos = option.npos;
|
||||
if (colpos != option.npos)
|
||||
{
|
||||
std::string section = option.substr(0, colpos);
|
||||
eqpos = option.find('=', colpos);
|
||||
if (eqpos != option.npos)
|
||||
{
|
||||
std::string key = option.substr(colpos + 1, eqpos - (colpos + 1));
|
||||
std::string value = option.substr(eqpos + 1);
|
||||
setTransient(section, key, value);
|
||||
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
if (colpos == option.npos || eqpos == option.npos) {
|
||||
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
|
||||
return;
|
||||
}
|
||||
pos = comma + 1;
|
||||
}
|
||||
|
||||
std::string value(arg[1]);
|
||||
for(;;)
|
||||
{
|
||||
auto seppos = value.find(':');
|
||||
if (seppos == std::string::npos)
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value,section:key=value,...", value.c_str());
|
||||
break;
|
||||
}
|
||||
auto eqpos = value.find('=', seppos);
|
||||
if (eqpos == std::string::npos)
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value,section:key=value,...", value.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
auto commapos = value.find(',', eqpos);
|
||||
|
||||
std::string sect = trim_ws(value.substr(0, seppos));
|
||||
std::string key = trim_ws(value.substr(seppos + 1, eqpos - seppos - 1));
|
||||
auto endofcurrent = (commapos == std::string::npos) ? value.size() : commapos;
|
||||
std::string next = (commapos == std::string::npos) ? "" : trim_ws(value.substr(endofcurrent + 1, value.size() - endofcurrent));
|
||||
value = trim_ws(value.substr(eqpos + 1, endofcurrent - eqpos - 1));
|
||||
|
||||
if (sect.empty() || key.empty())
|
||||
{
|
||||
WARN_LOG(COMMON, "-config : invalid parameter, format is section:key=value,section:key=value,...");
|
||||
break;
|
||||
}
|
||||
|
||||
INFO_LOG(COMMON, "Virtual cfg %s:%s=%s", sect.c_str(), key.c_str(), value.c_str());
|
||||
|
||||
cfgSetVirtual(sect, key, value);
|
||||
|
||||
if (commapos == std::string::npos)
|
||||
rv++;
|
||||
|
||||
if (commapos != std::string::npos)
|
||||
{
|
||||
value = next;
|
||||
continue;
|
||||
}
|
||||
else if (cl>=3 && strcmp(arg[2],",")==0)
|
||||
{
|
||||
cl-=2;
|
||||
arg+=2;
|
||||
rv++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int showhelp()
|
||||
{
|
||||
printf("Usage: flycast [OPTION]... [CONTENT]\n\n");
|
||||
printf("Options:\n");
|
||||
printf("-config section:key=value add a virtual config value;\n");
|
||||
printf(" virtual config values won't be saved to the .cfg file\n");
|
||||
printf(" unless a different value is written to them\n");
|
||||
printf("-help display this help\n");
|
||||
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ParseCommandLine(int argc,char* argv[])
|
||||
void parseCommandLine(int argc, const char * const argv[])
|
||||
{
|
||||
settings.content.path.clear();
|
||||
int cl=argc-2;
|
||||
char** arg=argv+1;
|
||||
while(cl>=0)
|
||||
const char *exe = argv[0];
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (stricmp(*arg,"-help")==0 || stricmp(*arg,"--help")==0)
|
||||
{
|
||||
showhelp();
|
||||
if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
|
||||
usage(exe);
|
||||
exit(0);
|
||||
}
|
||||
else if (stricmp(*arg,"-config")==0 || stricmp(*arg,"--config")==0)
|
||||
if (!strcmp(argv[i], "-config") || !strcmp(argv[i], "--config"))
|
||||
{
|
||||
int as=setconfig(arg,cl);
|
||||
cl-=as;
|
||||
arg+=as;
|
||||
if (i < argc - 1)
|
||||
parseConfigOption(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
else if (!strncmp(*arg, "-NSDocumentRevisions", 20))
|
||||
// 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")
|
||||
{
|
||||
arg++;
|
||||
cl--;
|
||||
INFO_LOG(COMMON, "Using '%s' as CD image", argv[i]);
|
||||
settings.content.path = argv[i];
|
||||
}
|
||||
#endif
|
||||
else if ((*arg)[0] == '-')
|
||||
else if (extension == "elf")
|
||||
{
|
||||
WARN_LOG(COMMON, "Ignoring unknown command line option '%s'", *arg);
|
||||
INFO_LOG(COMMON, "Using '%s' as reios elf file", argv[i]);
|
||||
setTransient("config", "bios.UseReios", "yes");
|
||||
settings.content.path = argv[i];
|
||||
}
|
||||
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);
|
||||
settings.content.path = *arg;
|
||||
}
|
||||
else if (extension && stricmp(extension, ".elf") == 0)
|
||||
{
|
||||
INFO_LOG(COMMON, "Using '%s' as reios elf file", *arg);
|
||||
cfgSetVirtual("config", "bios.UseReios", "yes");
|
||||
settings.content.path = *arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(COMMON, "Using '%s' as rom", *arg);
|
||||
settings.content.path = *arg;
|
||||
}
|
||||
else {
|
||||
INFO_LOG(COMMON, "Using '%s' as rom", argv[i]);
|
||||
settings.content.path = argv[i];
|
||||
}
|
||||
arg++;
|
||||
cl--;
|
||||
if (i < argc - 1)
|
||||
WARN_LOG(COMMON, "Rest of command line ignored: '%s'...", argv[i + 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
+301
-298
@@ -1,319 +1,322 @@
|
||||
/*
|
||||
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 <sstream>
|
||||
#include "stdclass.h"
|
||||
|
||||
namespace emucfg {
|
||||
namespace config {
|
||||
|
||||
/* ConfigEntry */
|
||||
|
||||
const std::string& ConfigEntry::get_string() const
|
||||
const IniFile::Entry *IniFile::getEntry(const std::string& sectionName, const std::string& name) const
|
||||
{
|
||||
return this->value;
|
||||
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;
|
||||
}
|
||||
|
||||
int ConfigEntry::get_int()
|
||||
const std::string *IniFile::getRaw(const std::string& section, const std::string& name) const
|
||||
{
|
||||
if (strstr(this->value.c_str(), "0x") != NULL)
|
||||
{
|
||||
return (int)strtoul(this->value.c_str(), NULL, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
return atoi(this->value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int64_t ConfigEntry::get_int64()
|
||||
{
|
||||
int base = 10;
|
||||
if (strstr(this->value.c_str(), "0x") != nullptr)
|
||||
base = 16;
|
||||
return (int64_t)strtoll(this->value.c_str(), nullptr, base);
|
||||
}
|
||||
|
||||
bool ConfigEntry::get_bool()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
float ConfigEntry::get_float()
|
||||
{
|
||||
return atof(this->value.c_str());
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
return &this->virtual_sections[name];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->sections.count(name) == 1)
|
||||
{
|
||||
return &this->sections[name];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ConfigEntry* ConfigFile::get_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 section->get_entry(entry_name);
|
||||
}
|
||||
|
||||
section = this->get_section(section_name, false);
|
||||
if(section != NULL)
|
||||
{
|
||||
return section->get_entry(entry_name);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
std::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();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t ConfigFile::get_int64(const std::string& section_name, const std::string& entry_name, int64_t default_value)
|
||||
{
|
||||
ConfigEntry* entry = this->get_entry(section_name, entry_name);
|
||||
if (entry == NULL)
|
||||
{
|
||||
return default_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return entry->get_int64();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
float ConfigFile::get_float(const std::string& section_name, const std::string& entry_name, float default_value)
|
||||
{
|
||||
ConfigEntry* entry = get_entry(section_name, entry_name);
|
||||
const Entry *entry = getEntry(section, name);
|
||||
if (entry == nullptr)
|
||||
return default_value;
|
||||
return nullptr;
|
||||
if (entry->transient)
|
||||
return &entry->transientValue;
|
||||
else
|
||||
return entry->get_float();
|
||||
return &entry->value;
|
||||
}
|
||||
|
||||
void ConfigFile::set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual)
|
||||
const std::string& IniFile::getString(const std::string& section, const std::string& entry, const std::string& defaultValue) const
|
||||
{
|
||||
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);
|
||||
const std::string *pValue = getRaw(section, entry);
|
||||
if (pValue == nullptr)
|
||||
return defaultValue;
|
||||
else
|
||||
return *pValue;
|
||||
}
|
||||
|
||||
void ConfigFile::set_int(const std::string& section_name, const std::string& entry_name, int value, bool is_virtual)
|
||||
bool IniFile::getBool(const std::string& section, const std::string& entry, bool defaultValue) const
|
||||
{
|
||||
std::stringstream str_value;
|
||||
str_value << value;
|
||||
this->set(section_name, entry_name, str_value.str(), is_virtual);
|
||||
}
|
||||
|
||||
void ConfigFile::set_int64(const std::string& section_name, const std::string& entry_name, int64_t 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)
|
||||
{
|
||||
std::string str_value = (value ? "yes" : "no");
|
||||
this->set(section_name, entry_name, str_value, is_virtual);
|
||||
}
|
||||
|
||||
void ConfigFile::set_float(const std::string& section_name, const std::string& entry_name, float value, bool is_virtual)
|
||||
{
|
||||
std::stringstream str_value;
|
||||
str_value << value;
|
||||
this->set(section_name, entry_name, str_value.str(), is_virtual);
|
||||
}
|
||||
|
||||
void ConfigFile::parse(FILE* file)
|
||||
{
|
||||
if (file == nullptr)
|
||||
return;
|
||||
|
||||
char line[512];
|
||||
std::string section;
|
||||
int cline = 0;
|
||||
while (true)
|
||||
{
|
||||
if (std::fgets(line, sizeof(line), file) == nullptr)
|
||||
break;
|
||||
cline++;
|
||||
std::string s(line);
|
||||
s = trim_ws(s, " \r\n");
|
||||
if (s.empty())
|
||||
continue;
|
||||
if (s.length() >= 3 && s[0] == '[' && s[s.length() - 1] == ']')
|
||||
{
|
||||
section = s.substr(1, s.length() - 2);
|
||||
continue;
|
||||
}
|
||||
auto eqpos = s.find('=');
|
||||
if (eqpos == std::string::npos)
|
||||
{
|
||||
WARN_LOG(COMMON, "Malformed entry on config - ignoring line %d: %s", cline, s.c_str());
|
||||
continue;
|
||||
}
|
||||
std::string property = trim_ws(s.substr(0, eqpos));
|
||||
std::string value = trim_ws(s.substr(eqpos + 1));
|
||||
if (value.length() >= 2 && value[0] == '"' && value[value.length() - 1] == '"')
|
||||
value = value.substr(1, value.length() - 2);
|
||||
set(section, property, value);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigFile::save(FILE* file)
|
||||
{
|
||||
for (const auto& [section_name, section] : this->sections)
|
||||
{
|
||||
if (!section_name.empty())
|
||||
std::fprintf(file, "[%s]\n", section_name.c_str());
|
||||
|
||||
for (const auto& [entry_name, entry] : section.entries)
|
||||
{
|
||||
std::fprintf(file, "%s = %s\n", entry_name.c_str(), entry.get_string().c_str());
|
||||
}
|
||||
if (!section_name.empty())
|
||||
std::fputc('\n', file);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool ConfigFile::is_virtual(const std::string& section_name, const std::string& entry_name)
|
||||
{
|
||||
ConfigSection *section = get_section(section_name, true);
|
||||
if (section == nullptr)
|
||||
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()))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return section->has_entry(entry_name);
|
||||
}
|
||||
|
||||
} // namespace emucfg
|
||||
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++)
|
||||
{
|
||||
char c = s[i];
|
||||
if (c == '\\' && i < s.length() - 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 ret;
|
||||
}
|
||||
|
||||
void IniFile::load(const std::string& data, bool cEscape)
|
||||
{
|
||||
std::istringstream fss(data);
|
||||
int cline = 1;
|
||||
std::string curSection;
|
||||
for (std::string line; std::getline(fss, line); cline++)
|
||||
{
|
||||
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
|
||||
continue;
|
||||
if (line[pos] == '[')
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
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[0] == ' ')
|
||||
value = value.substr(1);
|
||||
if (value[0] == '"')
|
||||
{
|
||||
// quoted value
|
||||
size_t end = value.rfind('"');
|
||||
if (end == value.npos || end == 0)
|
||||
end = value.length();
|
||||
value = value.substr(1, end - 1);
|
||||
}
|
||||
if (cEscape) {
|
||||
entry = handleEscapeSeq(entry);
|
||||
value = handleEscapeSeq(value);
|
||||
}
|
||||
set(curSection, entry, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IniFile::load(FILE *file, bool cEscape)
|
||||
{
|
||||
std::string data;
|
||||
for (;;)
|
||||
{
|
||||
char buffer[4096];
|
||||
int n = fread(buffer, 1, sizeof(buffer), file);
|
||||
if (n <= 0)
|
||||
break;
|
||||
data += std::string(buffer, buffer + n);
|
||||
}
|
||||
load(data, cEscape);
|
||||
}
|
||||
|
||||
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
|
||||
continue;
|
||||
if (!sectionSaved) {
|
||||
ss << '[' << sectionName << ']' << std::endl;
|
||||
sectionSaved = true;
|
||||
}
|
||||
ss << name << " = " << entry.value << std::endl;
|
||||
}
|
||||
ss << std::endl;
|
||||
}
|
||||
data = ss.str();
|
||||
}
|
||||
|
||||
void IniFile::save(FILE *file) const
|
||||
{
|
||||
std::string data;
|
||||
save(data);
|
||||
fwrite(data.c_str(), 1, data.length(), file);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
const Section& section = it->second;
|
||||
for (const auto& [name, entry] : section.entries)
|
||||
v.push_back(name);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
+95
-52
@@ -1,62 +1,105 @@
|
||||
#pragma once
|
||||
/*
|
||||
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 <cstdio>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
namespace emucfg {
|
||||
namespace config {
|
||||
|
||||
struct ConfigEntry {
|
||||
std::string value;
|
||||
const std::string& get_string() const;
|
||||
int get_int();
|
||||
bool get_bool();
|
||||
int64_t get_int64();
|
||||
float get_float();
|
||||
class IniFile
|
||||
{
|
||||
public:
|
||||
void load(FILE *file, bool cEscape = false);
|
||||
void load(const std::string& data, bool cEscape = false);
|
||||
void save(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 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);
|
||||
bool is_virtual(const std::string& section_name, const std::string& entry_name);
|
||||
|
||||
void parse(FILE* file);
|
||||
void save(FILE* file);
|
||||
|
||||
/* getting values */
|
||||
std::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);
|
||||
int64_t get_int64(const std::string& section_name, const std::string& entry_name, int64_t default_value = 0);
|
||||
bool get_bool(const std::string& section_name, const std::string& entry_name, bool default_value = false);
|
||||
float get_float(const std::string& section_name, const std::string& entry_name, float default_value = 0.f);
|
||||
/* 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_int64(const std::string& section_name, const std::string& entry_name, int64_t 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 set_float(const std::string& section_name, const std::string& entry_name, float 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
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
+30
-2
@@ -40,6 +40,7 @@ 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
|
||||
|
||||
@@ -71,11 +72,13 @@ 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.TextureUpscale", 1);
|
||||
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> DumpReplacedTextures("rend.DumpReplacedTextures");
|
||||
Option<int> ScreenStretching("rend.ScreenStretching", 100);
|
||||
Option<bool> Fog("rend.Fog", true);
|
||||
Option<bool> FloatVMUs("rend.FloatVMUs");
|
||||
@@ -98,6 +101,8 @@ 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);
|
||||
@@ -133,6 +138,15 @@ 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);
|
||||
@@ -199,7 +213,21 @@ std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices {{
|
||||
{{Option<MapleDeviceType>("device4.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device4.2", MDT_None, "input")}},
|
||||
}};
|
||||
Option<bool> PerGameVmu("PerGameVmu", false, "config");
|
||||
|
||||
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
|
||||
|
||||
+33
-18
@@ -56,7 +56,7 @@ public:
|
||||
{
|
||||
if (gameId.empty())
|
||||
return;
|
||||
if (!cfgHasSection(gameId))
|
||||
if (!hasSection(gameId))
|
||||
return;
|
||||
perGameConfig = true;
|
||||
}
|
||||
@@ -65,10 +65,10 @@ public:
|
||||
}
|
||||
|
||||
void save() {
|
||||
cfgSetAutoSave(false);
|
||||
setAutoSave(false);
|
||||
for (const auto& o : options)
|
||||
o->save();
|
||||
cfgSetAutoSave(true);
|
||||
setAutoSave(true);
|
||||
}
|
||||
|
||||
void setGameId(const std::string& gameId) {
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
this->perGameConfig = perGameConfig;
|
||||
if (!perGameConfig) {
|
||||
if (!gameId.empty())
|
||||
cfgDeleteSection(gameId);
|
||||
deleteSection(gameId);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
else
|
||||
{
|
||||
set(doLoad(section, name));
|
||||
if (cfgIsVirtual(section, name))
|
||||
if (isTransient(section, name))
|
||||
override(value);
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
if (value == doLoad(section, name))
|
||||
{
|
||||
// delete existing per-game option if any
|
||||
cfgDeleteEntry(settings.gameId, section + "." + name);
|
||||
deleteEntry(settings.gameId, section + "." + name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -173,14 +173,14 @@ protected:
|
||||
std::enable_if_t<std::is_same_v<U, bool>, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
return cfgLoadBool(section, name, value);
|
||||
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 cfgLoadInt64(section, name, value);
|
||||
return loadInt64(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
@@ -188,28 +188,28 @@ protected:
|
||||
&& !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)cfgLoadInt(section, name, (int)value);
|
||||
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 cfgLoadStr(section, name, value);
|
||||
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 cfgLoadFloat(section, name, value);
|
||||
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 = cfgLoadStr(section, name, "");
|
||||
std::string paths = loadStr(section, name);
|
||||
if (paths.empty())
|
||||
return value;
|
||||
std::string::size_type start = 0;
|
||||
@@ -268,14 +268,14 @@ protected:
|
||||
std::enable_if_t<std::is_same_v<U, bool>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
cfgSaveBool(section, name, value);
|
||||
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
|
||||
{
|
||||
cfgSaveInt64(section, name, value);
|
||||
saveInt64(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
@@ -283,21 +283,21 @@ protected:
|
||||
&& !std::is_same_v<U, bool> && !std::is_same_v<U, int64_t>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
cfgSaveInt(section, name, (int)value);
|
||||
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
|
||||
{
|
||||
cfgSaveStr(section, name, value);
|
||||
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
|
||||
{
|
||||
cfgSaveFloat(section, name, value);
|
||||
saveFloat(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
@@ -332,7 +332,7 @@ protected:
|
||||
else
|
||||
s += v;
|
||||
}
|
||||
cfgSaveStr(section, name, s);
|
||||
saveStr(section, name, s);
|
||||
}
|
||||
|
||||
std::string section;
|
||||
@@ -367,6 +367,7 @@ 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
|
||||
|
||||
@@ -445,7 +446,9 @@ extern Option<int> PerPixelLayers;
|
||||
#endif
|
||||
extern Option<float> ExtraDepthScale;
|
||||
extern Option<bool> CustomTextures;
|
||||
extern Option<bool> PreloadCustomTextures;
|
||||
extern Option<bool> DumpTextures;
|
||||
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;
|
||||
@@ -459,6 +462,8 @@ 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;
|
||||
@@ -489,6 +494,15 @@ 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;
|
||||
@@ -537,6 +551,7 @@ 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;
|
||||
|
||||
+98
-52
@@ -28,6 +28,8 @@
|
||||
#include "cfg/option.h"
|
||||
#include "emulator.h"
|
||||
#include "oslib/storage.h"
|
||||
#include "oslib/i18n.h"
|
||||
using namespace i18n;
|
||||
|
||||
const WidescreenCheat CheatManager::widescreen_cheats[] =
|
||||
{
|
||||
@@ -169,6 +171,7 @@ const WidescreenCheat CheatManager::widescreen_cheats[] =
|
||||
// { "T-9502D-50", nullptr, { 0xBDE9B0, 0xBDE9C4 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (PAL)
|
||||
{ "MK-5110250", nullptr, { 0x87B5A4 }, { 0x43700000 } }, // Outtrigger (PAL)
|
||||
{ "HDR-0118", nullptr, { 0x83E284 }, { 0x43700000 } }, // Outtrigger (JP)
|
||||
{ "MK-51102", nullptr, { 0x83E284 }, { 0x43700000 } }, // Outtrigger (US)
|
||||
{ "T15103D 50", nullptr, { 0x1EEE78 }, { 0x3F400000 } }, // PenPen (PAL)
|
||||
{ "T17001M", nullptr, { 0x1C3828 }, { 0x3F400000 } }, // PenPen TriIcelon (JP)
|
||||
{ "MK-5110050", nullptr, { 0x548E04, 0x0923C0 }, { 0x43E80000, 0x3F966666 } }, // Phantasy Star Online (PAL) TODO
|
||||
@@ -394,18 +397,18 @@ void CheatManager::loadCheatFile(const std::string& filename)
|
||||
WARN_LOG(COMMON, "Cannot open cheat file '%s'", filename.c_str());
|
||||
return;
|
||||
}
|
||||
emucfg::ConfigFile cfg;
|
||||
cfg.parse(cheatfile);
|
||||
config::IniFile cfg;
|
||||
cfg.load(cheatfile);
|
||||
fclose(cheatfile);
|
||||
|
||||
int count = cfg.get_int("", "cheats", 0);
|
||||
int count = cfg.getInt("", "cheats", 0);
|
||||
cheats.clear();
|
||||
for (int i = 0; count == 0 || i < count; i++)
|
||||
{
|
||||
std::string prefix = "cheat" + std::to_string(i) + "_";
|
||||
Cheat cheat{};
|
||||
cheat.description = cfg.get("", prefix + "desc", "Cheat " + std::to_string(i + 1));
|
||||
cheat.address = cfg.get_int("", prefix + "address", -1);
|
||||
cheat.description = cfg.getString("", prefix + "desc", "Cheat " + std::to_string(i + 1));
|
||||
cheat.address = cfg.getInt("", prefix + "address", -1);
|
||||
if (count == 0 && cheat.address == (u32)-1)
|
||||
break;
|
||||
if (cheat.address >= RAM_SIZE)
|
||||
@@ -413,27 +416,27 @@ void CheatManager::loadCheatFile(const std::string& filename)
|
||||
WARN_LOG(COMMON, "Invalid address %x", cheat.address);
|
||||
continue;
|
||||
}
|
||||
cheat.type = (Cheat::Type)cfg.get_int("", prefix + "cheat_type", (int)Cheat::Type::disabled);
|
||||
cheat.size = 1 << cfg.get_int("", prefix + "memory_search_size", 0);
|
||||
cheat.value = cfg.get_int("", prefix + "value", cheat.value);
|
||||
cheat.repeatCount = cfg.get_int("", prefix + "repeat_count", cheat.repeatCount);
|
||||
cheat.repeatValueIncrement = cfg.get_int("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
|
||||
cheat.repeatAddressIncrement = cfg.get_int("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
|
||||
cheat.enabled = cfg.get_bool("", prefix + "enable", false);
|
||||
cheat.destAddress = cfg.get_int("", prefix + "dest_address", 0);
|
||||
cheat.type = (Cheat::Type)cfg.getInt("", prefix + "cheat_type", (int)Cheat::Type::disabled);
|
||||
cheat.size = 1 << cfg.getInt("", prefix + "memory_search_size", 0);
|
||||
cheat.value = cfg.getInt("", prefix + "value", cheat.value);
|
||||
cheat.repeatCount = cfg.getInt("", prefix + "repeat_count", cheat.repeatCount);
|
||||
cheat.repeatValueIncrement = cfg.getInt("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
|
||||
cheat.repeatAddressIncrement = cfg.getInt("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
|
||||
cheat.enabled = cfg.getBool("", prefix + "enable", false);
|
||||
cheat.destAddress = cfg.getInt("", prefix + "dest_address", 0);
|
||||
if (cheat.destAddress >= RAM_SIZE)
|
||||
{
|
||||
WARN_LOG(COMMON, "Invalid address %x", cheat.destAddress);
|
||||
continue;
|
||||
}
|
||||
cheat.valueMask = cfg.get_int("", prefix + "address_bit_position", 0);
|
||||
cheat.valueMask = cfg.getInt("", prefix + "address_bit_position", 0);
|
||||
if (cheat.type != Cheat::Type::disabled)
|
||||
cheats.push_back(cheat);
|
||||
}
|
||||
setActive(!cheats.empty());
|
||||
INFO_LOG(COMMON, "%d cheats loaded", (int)cheats.size());
|
||||
if (!cheats.empty())
|
||||
cfgSaveStr("cheats", gameId, filename);
|
||||
config::saveStr("cheats", gameId, filename);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -445,13 +448,42 @@ void CheatManager::reset(const std::string& gameId)
|
||||
cheats.clear();
|
||||
setActive(false);
|
||||
this->gameId = gameId;
|
||||
|
||||
#ifndef LIBRETRO
|
||||
if (!settings.raHardcoreMode)
|
||||
{
|
||||
std::string cheatFile = cfgLoadStr("cheats", gameId, "");
|
||||
std::string cheatFile = config::loadStr("cheats", gameId);
|
||||
if (!cheatFile.empty())
|
||||
loadCheatFile(cheatFile);
|
||||
else
|
||||
{
|
||||
// Try to auto-locate a cheat file in user-defined CheatPath
|
||||
std::string romName = settings.content.fileName;
|
||||
const char* exts[] = { ".cht", ".txt" };
|
||||
for (const auto& base : config::CheatPath.get())
|
||||
{
|
||||
if (base.empty())
|
||||
continue;
|
||||
for (const char* ext : exts)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string candidate = hostfs::storage().getSubPath(base, romName + ext);
|
||||
if (hostfs::storage().exists(candidate))
|
||||
{
|
||||
loadCheatFile(candidate);
|
||||
config::saveStr("cheats", gameId, candidate);
|
||||
goto found_cheats;
|
||||
}
|
||||
}
|
||||
catch (const hostfs::StorageException&)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
found_cheats:
|
||||
#endif
|
||||
size_t cheatCount = cheats.size();
|
||||
if (gameId == "Fixed BOOT strapper") // Extreme Hunting 2
|
||||
@@ -538,9 +570,23 @@ void CheatManager::reset(const std::string& gameId)
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x00800524, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x00800524, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "T40214N") { // The Next Tetris (US)
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass speed ifeq", true, 32, 0x0016d5d4, 0x2f862fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass speed check", true, 32, 0x0016d5d4, 0xe001000b, true);
|
||||
else if (gameId == "HDR-0118") { // Outtrigger (JP)
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x00139f54, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x00139f54, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "HDR-0113") // Power Smash
|
||||
{
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe SYN ifeq", true, 16, 0x14d258, 0xbbce, true); // bsr SendNormalSYN
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe SYN set", true, 16, 0x14d258, 0x0009, true); // nop
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe ACK ifeq", true, 16, 0x14af42, 0x430b, true); // jsr TCPInternalSendPacket
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe ACK set", true, 16, 0x14af42, 0x0009, true); // nop
|
||||
}
|
||||
else if (gameId == "HDR-0091") // Pro Yakyuu Team de Asobou Net!
|
||||
{
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe SYN ifeq", true, 16, 0xe13e0c, 0xbbce, true); // bsr SendNormalSYN
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe SYN set", true, 16, 0xe13e0c, 0x0009, true); // nop
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe ACK ifeq", true, 16, 0xe11af6, 0x430b, true); // jsr TCPInternalSendPacket
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe ACK set", true, 16, 0xe11af6, 0x0009, true); // nop
|
||||
}
|
||||
|
||||
if (cheats.size() > cheatCount)
|
||||
@@ -722,12 +768,12 @@ static std::vector<u32> parseCodes(const std::string& s)
|
||||
}
|
||||
}
|
||||
else if (!curCode.empty())
|
||||
throw FlycastException("Invalid cheat code");
|
||||
throw FlycastException(Ts("Invalid cheat code"));
|
||||
}
|
||||
if (!curCode.empty())
|
||||
{
|
||||
if (curCode.length() != 8)
|
||||
throw FlycastException("Invalid cheat code");
|
||||
throw FlycastException(Ts("Invalid cheat code"));
|
||||
codes.push_back(strtoul(curCode.c_str(), nullptr, 16));
|
||||
}
|
||||
|
||||
@@ -809,7 +855,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// 8/16/32-bit write
|
||||
if (i + 1 >= codes.size())
|
||||
throw FlycastException("Missing value");
|
||||
throw FlycastException(Ts("Missing value"));
|
||||
cheat.type = Cheat::Type::setValue;
|
||||
cheat.size = code == 0 ? 8 : code == 1 ? 16 : 32;
|
||||
cheat.address = codes[i] & 0x00ffffff;
|
||||
@@ -827,7 +873,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
// Group write
|
||||
int count = codes[i] & 0xffff;
|
||||
if (i + count + 1 >= codes.size())
|
||||
throw FlycastException("Missing values");
|
||||
throw FlycastException(Ts("Missing values"));
|
||||
cheat.type = Cheat::Type::setValue;
|
||||
cheat.size = 32;
|
||||
cheat.address = codes[++i] & 0x00ffffff;
|
||||
@@ -848,7 +894,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// 8-bit inc/decrement
|
||||
if (i + 1 >= codes.size())
|
||||
throw FlycastException("Missing value");
|
||||
throw FlycastException(Ts("Missing value"));
|
||||
cheat.type = subcode == 1 ? Cheat::Type::increase : Cheat::Type::decrease;
|
||||
cheat.size = 8;
|
||||
cheat.value = codes[i] & 0xff;
|
||||
@@ -861,7 +907,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// 16-bit inc/decrement
|
||||
if (i + 1 >= codes.size())
|
||||
throw FlycastException("Missing value");
|
||||
throw FlycastException(Ts("Missing value"));
|
||||
cheat.type = subcode == 3 ? Cheat::Type::increase : Cheat::Type::decrease;
|
||||
cheat.size = 16;
|
||||
cheat.value = codes[i] & 0xffff;
|
||||
@@ -874,7 +920,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// 32-bit inc/decrement
|
||||
if (i + 2 >= codes.size())
|
||||
throw FlycastException("Missing address or value");
|
||||
throw FlycastException(Ts("Missing address or value"));
|
||||
cheat.type = subcode == 5 ? Cheat::Type::increase : Cheat::Type::decrease;
|
||||
cheat.size = 32;
|
||||
cheat.address = codes[++i] & 0x00ffffff;
|
||||
@@ -883,7 +929,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw FlycastException("Unsupported cheat type");
|
||||
throw FlycastException(Ts("Unsupported cheat type"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -891,7 +937,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// 32-bit repeat write
|
||||
if (i + 2 >= codes.size())
|
||||
throw FlycastException("Missing count or value");
|
||||
throw FlycastException(Ts("Missing count or value"));
|
||||
cheat.type = Cheat::Type::setValue;
|
||||
cheat.size = 32;
|
||||
cheat.address = codes[i] & 0x00ffffff;
|
||||
@@ -905,7 +951,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// copy bytes
|
||||
if (i + 2 >= codes.size())
|
||||
throw FlycastException("Missing count or destination address");
|
||||
throw FlycastException(Ts("Missing count or destination address"));
|
||||
cheat.type = Cheat::Type::copy;
|
||||
cheat.size = 8;
|
||||
cheat.address = codes[i] & 0x00ffffff;
|
||||
@@ -917,7 +963,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
|
||||
case 7:
|
||||
// change decryption type: 071000XX (example: 07100005)
|
||||
throw FlycastException("Master codes aren't supported");
|
||||
throw FlycastException(Ts("Master codes aren't supported"));
|
||||
|
||||
// TODO 0xb delay applying codes: 0b0xxxxx
|
||||
// Delay putting on codes for xxxxx cycles. Default 1000 (0x3e7)
|
||||
@@ -929,7 +975,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// enable next code if eq/neq/lt/gt
|
||||
if (i + 1 >= codes.size())
|
||||
throw FlycastException("Missing count or destination address");
|
||||
throw FlycastException(Ts("Missing count or destination address"));
|
||||
cheat.size = 16;
|
||||
cheat.address = codes[i] & 0x00ffffff;
|
||||
switch (codes[++i] >> 16)
|
||||
@@ -947,7 +993,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
cheat.type = Cheat::Type::runNextIfGt;
|
||||
break;
|
||||
default:
|
||||
throw FlycastException("Unsupported conditional code");
|
||||
throw FlycastException(Ts("Unsupported conditional code"));
|
||||
}
|
||||
cheat.value = codes[i] & 0xffff;
|
||||
cheats.push_back(cheat);
|
||||
@@ -957,7 +1003,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
{
|
||||
// multiline enable codes if eq/neq/lt/gt
|
||||
if (i + 1 >= codes.size())
|
||||
throw FlycastException("Missing test address");
|
||||
throw FlycastException(Ts("Missing test address"));
|
||||
cheat.size = 16;
|
||||
cheat.value = codes[i] & 0xffff;
|
||||
conditionLimit = i + 1 + ((codes[i] >> 16) & 0xff);
|
||||
@@ -976,7 +1022,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
cheat.type = Cheat::Type::runNextIfGt;
|
||||
break;
|
||||
default:
|
||||
throw FlycastException("Unsupported conditional code");
|
||||
throw FlycastException(Ts("Unsupported conditional code"));
|
||||
}
|
||||
cheat.address = codes[i] & 0x00ffffff;
|
||||
conditionCheat = cheat;
|
||||
@@ -987,15 +1033,15 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
// 16-Bit Write Once Immediately. (Activator code)
|
||||
|
||||
default:
|
||||
throw FlycastException("Unsupported cheat type");
|
||||
throw FlycastException(Ts("Unsupported cheat type"));
|
||||
}
|
||||
}
|
||||
#ifndef LIBRETRO
|
||||
std::string path = cfgLoadStr("cheats", gameId, "");
|
||||
std::string path = config::loadStr("cheats", gameId);
|
||||
if (path == "")
|
||||
{
|
||||
path = get_game_save_prefix() + ".cht";
|
||||
cfgSaveStr("cheats", gameId, path);
|
||||
config::saveStr("cheats", gameId, path);
|
||||
}
|
||||
saveCheatFile(path);
|
||||
#endif
|
||||
@@ -1009,7 +1055,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
void CheatManager::saveCheatFile(const std::string& filename)
|
||||
{
|
||||
#ifndef LIBRETRO
|
||||
emucfg::ConfigFile cfg;
|
||||
config::IniFile cfg;
|
||||
|
||||
int i = 0;
|
||||
for (const Cheat& cheat : cheats)
|
||||
@@ -1017,15 +1063,15 @@ void CheatManager::saveCheatFile(const std::string& filename)
|
||||
if (cheat.builtIn)
|
||||
continue;
|
||||
std::string prefix = "cheat" + std::to_string(i) + "_";
|
||||
cfg.set_int("", prefix + "address", cheat.address);
|
||||
cfg.set_int("", prefix + "address_bit_position", cheat.valueMask);
|
||||
cfg.set_bool("", prefix + "big_endian", false);
|
||||
cfg.set_int("", prefix + "cheat_type", (int)cheat.type);
|
||||
cfg.set("", prefix + "address", cheat.address);
|
||||
cfg.set("", prefix + "address_bit_position", cheat.valueMask);
|
||||
cfg.set("", prefix + "big_endian", false);
|
||||
cfg.set("", prefix + "cheat_type", (int)cheat.type);
|
||||
cfg.set("", prefix + "code", "");
|
||||
cfg.set("", prefix + "desc", cheat.description);
|
||||
cfg.set_int("", prefix + "dest_address", cheat.destAddress);
|
||||
cfg.set_bool("", prefix + "enable", false); // force all cheats disabled at start
|
||||
cfg.set_int("", prefix + "handler", 1);
|
||||
cfg.set("", prefix + "dest_address", cheat.destAddress);
|
||||
cfg.set("", prefix + "enable", false); // force all cheats disabled at start
|
||||
cfg.set("", prefix + "handler", 1);
|
||||
int memSize;
|
||||
switch (cheat.size) {
|
||||
case 1:
|
||||
@@ -1048,17 +1094,17 @@ void CheatManager::saveCheatFile(const std::string& filename)
|
||||
memSize = 5;
|
||||
break;
|
||||
}
|
||||
cfg.set_int("", prefix + "memory_search_size", memSize);
|
||||
cfg.set_int("", prefix + "value", cheat.value);
|
||||
cfg.set_int("", prefix + "repeat_count", cheat.repeatCount);
|
||||
cfg.set_int("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
|
||||
cfg.set_int("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
|
||||
cfg.set("", prefix + "memory_search_size", memSize);
|
||||
cfg.set("", prefix + "value", cheat.value);
|
||||
cfg.set("", prefix + "repeat_count", cheat.repeatCount);
|
||||
cfg.set("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
|
||||
cfg.set("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
|
||||
i++;
|
||||
}
|
||||
cfg.set_int("", "cheats", i);
|
||||
cfg.set("", "cheats", i);
|
||||
FILE *fp = hostfs::storage().openFile(filename.c_str(), "w");
|
||||
if (fp == nullptr)
|
||||
throw FlycastException("Can't save cheat file");
|
||||
throw FlycastException(Ts("Can't save cheat file"));
|
||||
cfg.save(fp);
|
||||
fclose(fp);
|
||||
#endif
|
||||
|
||||
+57
-44
@@ -69,6 +69,22 @@ public:
|
||||
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) {
|
||||
@@ -101,30 +117,18 @@ private:
|
||||
|
||||
void handlePacket(const std::error_code& ec, size_t len);
|
||||
|
||||
void send(const std::string& msg)
|
||||
{
|
||||
if (msg.empty())
|
||||
start();
|
||||
else
|
||||
asio::async_write(socket, asio::buffer(msg),
|
||||
std::bind(&Connection::writeDone, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
|
||||
}
|
||||
|
||||
void writeDone(const std::error_code& ec, size_t len)
|
||||
{
|
||||
if (ec)
|
||||
WARN_LOG(COMMON, "Write error: %s", ec.message().c_str());
|
||||
else
|
||||
start();
|
||||
outMessage.clear();
|
||||
}
|
||||
|
||||
GdbServer& server;
|
||||
asio::io_context& io_context;
|
||||
asio::ip::tcp::socket socket;
|
||||
std::string message;
|
||||
std::string outMessage;
|
||||
friend super;
|
||||
};
|
||||
|
||||
@@ -213,7 +217,9 @@ public:
|
||||
if (!attached)
|
||||
return;
|
||||
agent.debugTrap(event);
|
||||
reportException();
|
||||
std::string pkt = handleCommand("?"); // report exception
|
||||
if (connection)
|
||||
io_context->post([this, pkt]() { connection->send(pkt); });
|
||||
postDebugTrapNeeded = true;
|
||||
throw Stop();
|
||||
}
|
||||
@@ -234,6 +240,7 @@ private:
|
||||
ERROR_LOG(COMMON, "Gdb server exception: %s", e.what());
|
||||
}
|
||||
attached = false;
|
||||
connection.reset();
|
||||
}
|
||||
|
||||
std::string handleCommand(const std::string& packet)
|
||||
@@ -390,6 +397,7 @@ private:
|
||||
} catch (const Error& e) {
|
||||
ERROR_LOG(COMMON, "%s", e.what());
|
||||
attached = false;
|
||||
connection.reset();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -659,25 +667,21 @@ private:
|
||||
if (pkt.rfind("vAttach;", 0) == 0)
|
||||
return { "S05" };
|
||||
else if (pkt.rfind("vCont?", 0) == 0)
|
||||
// supported vCont actions - (c)ontinue, (C)ontinue with signal, (s)tep, (S)tep with signal, (r)ange-step
|
||||
return { "vCont;c;C;s;S;t;r" };
|
||||
// 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;"));
|
||||
std::vector<std::string> replies;
|
||||
switch (vContCmd[0])
|
||||
{
|
||||
case 'c':
|
||||
case 'C':
|
||||
doContinue(vContCmd);
|
||||
doContinue(vContCmd.substr(0, 1));
|
||||
return {};
|
||||
case 's':
|
||||
return { step(EXCEPT_NONE) };
|
||||
case 'S':
|
||||
replies.push_back(step());
|
||||
[[fallthrough]];
|
||||
case 'r':
|
||||
{
|
||||
std::vector<std::string> replies;
|
||||
u32 from, to;
|
||||
if (sscanf(vContCmd.c_str(), "r%x,%x", &from, &to) == 2)
|
||||
{
|
||||
@@ -722,7 +726,8 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(COMMON, "unknown v packet: %s", pkt.c_str());
|
||||
if (pkt != "vMustReplyEmpty")
|
||||
WARN_LOG(COMMON, "unknown v packet: %s", pkt.c_str());
|
||||
return { "" };
|
||||
}
|
||||
}
|
||||
@@ -744,7 +749,10 @@ private:
|
||||
std::vector<std::string> stepRange(u32 from, u32 to)
|
||||
{
|
||||
try {
|
||||
agent.stepRange(from, to);
|
||||
if (from + 2 >= to)
|
||||
agent.step();
|
||||
else
|
||||
agent.stepRange(from, to);
|
||||
return { "OK", "S05" };
|
||||
} catch (const FlycastException& e) {
|
||||
throw Error(e.what());
|
||||
@@ -858,8 +866,9 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void clientConnected() {
|
||||
void clientConnected(Connection::Ptr connection) {
|
||||
attached = true;
|
||||
this->connection = connection;
|
||||
agentInterrupt();
|
||||
}
|
||||
|
||||
@@ -889,6 +898,7 @@ private:
|
||||
std::thread thread;
|
||||
std::unique_ptr<asio::io_context> io_context;
|
||||
int port = DEFAULT_PORT;
|
||||
Connection::Ptr connection;
|
||||
friend class TcpAcceptor;
|
||||
friend class Connection;
|
||||
|
||||
@@ -901,7 +911,7 @@ static GdbServer gdbServer;
|
||||
void TcpAcceptor::handleAccept(Connection::Ptr newConnection, const std::error_code& error)
|
||||
{
|
||||
if (!error) {
|
||||
server.clientConnected();
|
||||
server.clientConnected(newConnection);
|
||||
newConnection->start();
|
||||
}
|
||||
start();
|
||||
@@ -921,27 +931,30 @@ void Connection::handlePacket(const std::error_code& ec, size_t len)
|
||||
try {
|
||||
if (msg[0] == '\03') { // break
|
||||
send(server.handleCommand(msg));
|
||||
return;
|
||||
}
|
||||
if (msg[0] != '$') {
|
||||
else if (msg[0] != '$') {
|
||||
// Ignore unexpected chars
|
||||
send("");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
Submodule
+1
Submodule core/deps/DreamPicoPort-API added at e06d9f6c59.
+1
-1
Submodule core/deps/SDL updated: 2359383fc1...00d4f1c2c4.
+1
-1
Submodule core/deps/Syphon updated: 486dd8e4ef...53ec412a78.
@@ -56,7 +56,7 @@ public:
|
||||
*/
|
||||
#if defined(_WIN32)
|
||||
# include "platform_windows.h"
|
||||
#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
|
||||
#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__)
|
||||
# include "platform_linux.h"
|
||||
#else
|
||||
# error Unsupported platform
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Use of this software is governed by the MIT license that can be found
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__)
|
||||
|
||||
#include "platform_linux.h"
|
||||
#include <time.h>
|
||||
|
||||
+1
-1
Submodule core/deps/glslang updated: 76b52ebf77...fc9889c889.
@@ -222,7 +222,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
glcache.Scissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
|
||||
|
||||
// Bind texture, Draw
|
||||
glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
|
||||
glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
|
||||
}
|
||||
}
|
||||
@@ -255,7 +255,7 @@ static bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
|
||||
io.Fonts->SetTexID((ImTextureID)g_FontTexture);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -266,7 +266,6 @@ static void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
glcache.DeleteTextures(1, &g_FontTexture);
|
||||
io.Fonts->TexID = 0;
|
||||
g_FontTexture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,14 +586,14 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
|
||||
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||
|
||||
// Bind DescriptorSet with font or user texture
|
||||
VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId };
|
||||
VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID();
|
||||
if (sizeof(ImTextureID) < sizeof(ImU64))
|
||||
{
|
||||
// We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used.
|
||||
IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet);
|
||||
desc_set[0] = bd->FontDescriptorSet;
|
||||
IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet);
|
||||
desc_set = bd->FontDescriptorSet;
|
||||
}
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr);
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr);
|
||||
|
||||
// Draw
|
||||
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||
@@ -820,7 +820,6 @@ void ImGui_ImplVulkan_DestroyFontsTexture()
|
||||
{
|
||||
ImGui_ImplVulkan_RemoveTexture(bd->FontDescriptorSet);
|
||||
bd->FontDescriptorSet = VK_NULL_HANDLE;
|
||||
io.Fonts->SetTexID(0);
|
||||
}
|
||||
|
||||
if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; }
|
||||
|
||||
Regular → Executable
+26
-10
@@ -15,20 +15,21 @@
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
// - If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
// - Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
@@ -42,21 +43,29 @@
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
@@ -75,13 +84,16 @@
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience.
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
|
||||
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
@@ -119,6 +131,10 @@
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
|
||||
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
|
||||
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
|
||||
+4017
-1973
File diff suppressed because it is too large.
Load diff
+1309
-516
File diff suppressed because it is too large.
Load diff
Regular → Executable
+4078
-1949
File diff suppressed because it is too large.
Load diff
Regular → Executable
+2826
-1106
File diff suppressed because it is too large.
Load diff
+979
-627
File diff suppressed because it is too large.
Load diff
@@ -1,13 +1,26 @@
|
||||
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||
|
||||
// This is also an example of how you may wrap your own similar types.
|
||||
// TL;DR; this is using the ImGuiInputTextFlags_CallbackResize facility,
|
||||
// which also demonstrated in 'Dear ImGui Demo->Widgets->Text Input->Resize Callback'.
|
||||
|
||||
// Changelog:
|
||||
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||
|
||||
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||
// Usage:
|
||||
// {
|
||||
// #include "misc/cpp/imgui_stdlib.h"
|
||||
// #include "misc/cpp/imgui_stdlib.cpp" // <-- If you want to include implementation without messing with your project/build.
|
||||
// [...]
|
||||
// std::string my_string;
|
||||
// ImGui::InputText("my string", &my_string);
|
||||
// }
|
||||
|
||||
// See more C++ related extension (fmt, RAII, syntactic sugar) on Wiki:
|
||||
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_stdlib.h"
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
@@ -83,3 +96,5 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string*
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
@@ -1,14 +1,28 @@
|
||||
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||
|
||||
// This is also an example of how you may wrap your own similar types.
|
||||
// TL;DR; this is using the ImGuiInputTextFlags_CallbackResize facility,
|
||||
// which also demonstrated in 'Dear ImGui Demo->Widgets->Text Input->Resize Callback'.
|
||||
|
||||
// Changelog:
|
||||
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||
|
||||
// Usage:
|
||||
// {
|
||||
// #include "misc/cpp/imgui_stdlib.h"
|
||||
// #include "misc/cpp/imgui_stdlib.cpp" // <-- If you want to include implementation without messing with your project/build.
|
||||
// [...]
|
||||
// std::string my_string;
|
||||
// ImGui::InputText("my string", &my_string);
|
||||
// }
|
||||
|
||||
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ImGui
|
||||
@@ -19,3 +33,5 @@ namespace ImGui
|
||||
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
}
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
Regular → Executable
+244
-123
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.90.8
|
||||
// dear imgui, v1.92.5
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
@@ -24,9 +24,9 @@ Index of this file:
|
||||
*/
|
||||
|
||||
// Navigating this file:
|
||||
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||||
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
|
||||
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
|
||||
// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
|
||||
// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
|
||||
// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Commentary
|
||||
@@ -221,6 +221,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
|
||||
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
|
||||
#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
|
||||
@@ -229,9 +230,15 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||||
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
|
||||
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
|
||||
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
|
||||
#pragma GCC diagnostic ignored "-Wstrict-overflow"
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
|
||||
#endif
|
||||
|
||||
@@ -328,13 +335,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
|
||||
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
|
||||
const ImVec2 avail_size = GetContentRegionAvail();
|
||||
const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
|
||||
const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f));
|
||||
const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
|
||||
const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
|
||||
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
|
||||
{
|
||||
ItemSize(outer_rect);
|
||||
ItemAdd(outer_rect, id);
|
||||
g.NextWindowData.ClearFlags();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -369,6 +377,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->ColumnsCount = columns_count;
|
||||
table->IsLayoutLocked = false;
|
||||
table->InnerWidth = inner_width;
|
||||
table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;
|
||||
temp_data->UserOuterSize = outer_size;
|
||||
|
||||
// Instance data (for instance 0, TableID == TableInstanceID)
|
||||
@@ -409,11 +418,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
|
||||
// Reset scroll if we are reactivating it
|
||||
if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
|
||||
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
|
||||
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0)
|
||||
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
|
||||
|
||||
// Create scrolling region (without border and zero window padding)
|
||||
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);
|
||||
ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;
|
||||
ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None;
|
||||
if (flags & ImGuiTableFlags_ScrollX)
|
||||
child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags);
|
||||
table->InnerWindow = g.CurrentWindow;
|
||||
table->WorkRect = table->InnerWindow->WorkRect;
|
||||
table->OuterRect = table->InnerWindow->Rect();
|
||||
@@ -424,7 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)
|
||||
table->InnerWindow->SkipItems = false;
|
||||
|
||||
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)
|
||||
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned
|
||||
if (instance_no == 0)
|
||||
{
|
||||
table->HasScrollbarYPrev = table->HasScrollbarYCurr;
|
||||
@@ -438,6 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
|
||||
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
|
||||
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
|
||||
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking across a table
|
||||
}
|
||||
|
||||
// Push a standardized ID for both child-using and not-child-using tables
|
||||
@@ -450,6 +464,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->HostIndentX = inner_window->DC.Indent.x;
|
||||
table->HostClipRect = inner_window->ClipRect;
|
||||
table->HostSkipItems = inner_window->SkipItems;
|
||||
temp_data->WindowID = inner_window->ID;
|
||||
temp_data->HostBackupWorkRect = inner_window->WorkRect;
|
||||
temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect;
|
||||
temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;
|
||||
@@ -460,16 +475,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
|
||||
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
|
||||
|
||||
// Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)
|
||||
// Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
|
||||
// (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
|
||||
// limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
|
||||
// problem only affect scrolling tables in this case we can get away with doing it without extra cost).
|
||||
if (inner_window != outer_window)
|
||||
{
|
||||
// FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
|
||||
// it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
|
||||
// different x/y values to BeginChild().
|
||||
if (flags & ImGuiTableFlags_BordersOuterV)
|
||||
{
|
||||
table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
|
||||
if (inner_window->DecoOuterSizeX2 == 0.0f)
|
||||
table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
|
||||
}
|
||||
if (flags & ImGuiTableFlags_BordersOuterH)
|
||||
{
|
||||
table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
|
||||
if (inner_window->DecoOuterSizeY2 == 0.0f)
|
||||
table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Padding and Spacing
|
||||
@@ -497,7 +523,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
|
||||
table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width
|
||||
table->InnerClipRect.ClipWithFull(table->HostClipRect);
|
||||
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y;
|
||||
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y;
|
||||
|
||||
table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
|
||||
table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
|
||||
@@ -517,7 +543,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
|
||||
// Make table current
|
||||
g.CurrentTable = table;
|
||||
outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
|
||||
inner_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
|
||||
outer_window->DC.CurrentTableIdx = table_idx;
|
||||
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
|
||||
inner_window->DC.CurrentTableIdx = table_idx;
|
||||
@@ -555,6 +581,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// Initialize
|
||||
table->SettingsOffset = -1;
|
||||
table->IsSortSpecsDirty = true;
|
||||
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
|
||||
table->InstanceInteracted = -1;
|
||||
table->ContextPopupColumn = -1;
|
||||
table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;
|
||||
@@ -855,7 +882,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
|
||||
// Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
|
||||
// Combine width from regular rows + width from headers unless requested not to.
|
||||
if (!column->IsPreserveWidthAuto)
|
||||
if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)
|
||||
column->WidthAuto = TableGetColumnWidthAuto(table, column);
|
||||
|
||||
// Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
|
||||
@@ -920,7 +947,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very
|
||||
// large height (= first frame scrollbar display very off + clipper would skip lots of items).
|
||||
// This is merely making the side-effect less extreme, but doesn't properly fixes it.
|
||||
// FIXME: Move this to ->WidthGiven to avoid temporary lossyless?
|
||||
// FIXME: Move this to ->WidthGiven to avoid temporary lossyness?
|
||||
// FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.
|
||||
if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto)
|
||||
column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale?
|
||||
@@ -954,7 +981,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// [Part 4] Apply final widths based on requested widths
|
||||
const ImRect work_rect = table->WorkRect;
|
||||
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
|
||||
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920)
|
||||
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
|
||||
const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);
|
||||
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
|
||||
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
|
||||
@@ -1033,7 +1060,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
|
||||
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen
|
||||
// Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen
|
||||
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer);
|
||||
|
||||
if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
|
||||
{
|
||||
@@ -1059,16 +1087,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect hovered column
|
||||
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
|
||||
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
|
||||
|
||||
// Lock start position
|
||||
column->MinX = offset_x;
|
||||
|
||||
// Lock width based on start position and minimum/maximum width for this position
|
||||
float max_width = TableGetMaxColumnWidth(table, column_n);
|
||||
column->WidthGiven = ImMin(column->WidthGiven, max_width);
|
||||
column->WidthMax = TableCalcMaxColumnWidth(table, column_n);
|
||||
column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
|
||||
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
|
||||
column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
|
||||
|
||||
@@ -1117,8 +1141,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
column->Flags |= ImGuiTableColumnFlags_IsVisible;
|
||||
if (column->SortOrder != -1)
|
||||
column->Flags |= ImGuiTableColumnFlags_IsSorted;
|
||||
if (table->HoveredColumnBody == column_n)
|
||||
|
||||
// Detect hovered column
|
||||
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
|
||||
{
|
||||
column->Flags |= ImGuiTableColumnFlags_IsHovered;
|
||||
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
|
||||
}
|
||||
|
||||
// Alignment
|
||||
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
|
||||
@@ -1148,7 +1177,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
}
|
||||
|
||||
// Don't decrement auto-fit counters until container window got a chance to submit its items
|
||||
if (table->HostSkipItems == false)
|
||||
if (table->HostSkipItems == false && table->InstanceCurrent == 0)
|
||||
{
|
||||
column->AutoFitQueue >>= 1;
|
||||
column->CannotSkipItemsQueue >>= 1;
|
||||
@@ -1162,7 +1191,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
}
|
||||
|
||||
// In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.
|
||||
// Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar.
|
||||
// Else if give no chance to a clipper-savvy user to submit rows and therefore total contents height used by scrollbar.
|
||||
if (has_at_least_one_column_requesting_output == false)
|
||||
{
|
||||
table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true;
|
||||
@@ -1223,7 +1252,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
|
||||
// [Part 11] Default context menu
|
||||
// - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
|
||||
// - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().
|
||||
// - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
|
||||
// - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
|
||||
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
|
||||
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
|
||||
@@ -1249,7 +1278,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
else
|
||||
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
|
||||
inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
|
||||
}
|
||||
|
||||
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
|
||||
@@ -1319,7 +1348,11 @@ void ImGui::EndTable()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "EndTable() call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
// This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
|
||||
// cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
|
||||
@@ -1334,7 +1367,7 @@ void ImGui::EndTable()
|
||||
ImGuiWindow* inner_window = table->InnerWindow;
|
||||
ImGuiWindow* outer_window = table->OuterWindow;
|
||||
ImGuiTableTempData* temp_data = table->TempData;
|
||||
IM_ASSERT(inner_window == g.CurrentWindow);
|
||||
IM_ASSERT(inner_window == g.CurrentWindow && inner_window->ID == temp_data->WindowID);
|
||||
IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow);
|
||||
|
||||
if (table->IsInsideRow)
|
||||
@@ -1361,7 +1394,7 @@ void ImGui::EndTable()
|
||||
|
||||
// Setup inner scrolling range
|
||||
// FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,
|
||||
// but since the later is likely to be impossible to do we'd rather update both axises together.
|
||||
// but since the later is likely to be impossible to do we'd rather update both axes together.
|
||||
if (table->Flags & ImGuiTableFlags_ScrollX)
|
||||
{
|
||||
const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
|
||||
@@ -1471,12 +1504,15 @@ void ImGui::EndTable()
|
||||
if (inner_window != outer_window)
|
||||
{
|
||||
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
|
||||
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
|
||||
inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently.
|
||||
g.CurrentTable = NULL; // To avoid error recovery recursing
|
||||
EndChild();
|
||||
g.CurrentTable = table;
|
||||
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
table->InnerWindow->DC.TreeDepth--;
|
||||
ItemSize(table->OuterRect.GetSize());
|
||||
ItemAdd(table->OuterRect, 0);
|
||||
}
|
||||
@@ -1524,7 +1560,7 @@ void ImGui::EndTable()
|
||||
IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table);
|
||||
IM_ASSERT(g.TablesTempDataStacked > 0);
|
||||
temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL;
|
||||
g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
|
||||
g.CurrentTable = temp_data && (temp_data->WindowID == outer_window->ID) ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
|
||||
if (g.CurrentTable)
|
||||
{
|
||||
g.CurrentTable->TempData = temp_data;
|
||||
@@ -1534,14 +1570,43 @@ void ImGui::EndTable()
|
||||
NavUpdateCurrentWindowIsScrollPushableX();
|
||||
}
|
||||
|
||||
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
|
||||
// 'init_mask' specify which fields to initialize.
|
||||
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
|
||||
{
|
||||
ImGuiTableColumnFlags flags = column->Flags;
|
||||
if (init_mask & ImGuiTableFlags_Resizable)
|
||||
{
|
||||
float init_width_or_weight = column->InitStretchWeightOrWidth;
|
||||
column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
|
||||
column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f;
|
||||
if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
if (init_mask & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
|
||||
if (init_mask & ImGuiTableFlags_Hideable)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
|
||||
if (init_mask & ImGuiTableFlags_Sortable)
|
||||
{
|
||||
// Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
|
||||
column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1;
|
||||
column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None;
|
||||
}
|
||||
}
|
||||
|
||||
// See "COLUMNS SIZING POLICIES" comments at the top of this file
|
||||
// If (init_width_or_weight <= 0.0f) it is ignored
|
||||
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
|
||||
IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
|
||||
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
|
||||
if (table->DeclColumnsCount >= table->ColumnsCount)
|
||||
{
|
||||
@@ -1558,7 +1623,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column.");
|
||||
|
||||
// When passing a width automatically enforce WidthFixed policy
|
||||
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable)
|
||||
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)
|
||||
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
|
||||
if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
|
||||
flags |= ImGuiTableColumnFlags_WidthFixed;
|
||||
@@ -1576,27 +1641,10 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
column->InitStretchWeightOrWidth = init_width_or_weight;
|
||||
if (table->IsInitializing)
|
||||
{
|
||||
// Init width or weight
|
||||
ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
|
||||
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
|
||||
{
|
||||
if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f)
|
||||
column->WidthRequest = init_width_or_weight;
|
||||
if (flags & ImGuiTableColumnFlags_WidthStretch)
|
||||
column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
|
||||
|
||||
// Disable auto-fit if an explicit width/weight has been specified
|
||||
if (init_width_or_weight > 0.0f)
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
|
||||
// Init default visibility/sort state
|
||||
if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = false;
|
||||
if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0)
|
||||
{
|
||||
column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
|
||||
column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);
|
||||
}
|
||||
init_flags |= ImGuiTableFlags_Resizable;
|
||||
TableInitColumnDefaults(table, column, init_flags);
|
||||
}
|
||||
|
||||
// Store name (append with zero-terminator in contiguous buffer)
|
||||
@@ -1605,7 +1653,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
if (label != NULL && label[0] != 0)
|
||||
{
|
||||
column->NameOffset = (ImS16)table->ColumnsNames.size();
|
||||
table->ColumnsNames.append(label, label + strlen(label) + 1);
|
||||
table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1614,7 +1662,11 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
|
||||
IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);
|
||||
IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
|
||||
@@ -1691,9 +1743,11 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL);
|
||||
if (!table)
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above
|
||||
if (column_n < 0)
|
||||
column_n = table->CurrentColumn;
|
||||
@@ -1772,6 +1826,11 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(target != ImGuiTableBgTarget_None);
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (color == IM_COL32_DISABLE)
|
||||
color = 0;
|
||||
@@ -1900,7 +1959,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
IM_ASSERT(table->IsInsideRow);
|
||||
|
||||
if (table->CurrentColumn != -1)
|
||||
{
|
||||
TableEndCell(table);
|
||||
table->CurrentColumn = -1;
|
||||
}
|
||||
|
||||
// Logging
|
||||
if (g.LogEnabled)
|
||||
@@ -1993,37 +2055,41 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
}
|
||||
|
||||
// End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
|
||||
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
|
||||
// get the new cursor position.
|
||||
// - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark
|
||||
// end of row and get the new cursor position.
|
||||
if (unfreeze_rows_request)
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
|
||||
table->IsUnfrozenRows = true;
|
||||
IM_ASSERT(table->FreezeRowsRequest > 0);
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
table->Columns[column_n].NavLayerCurrent = table->NavLayer;
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
|
||||
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
|
||||
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
table->IsUnfrozenRows = true;
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
|
||||
@@ -2062,7 +2128,11 @@ bool ImGui::TableSetColumnIndex(int column_n)
|
||||
{
|
||||
if (table->CurrentColumn != -1)
|
||||
TableEndCell(table);
|
||||
IM_ASSERT(column_n >= 0 && table->ColumnsCount);
|
||||
if ((column_n >= 0 && column_n < table->ColumnsCount) == false)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(column_n >= 0 && column_n < table->ColumnsCount, "TableSetColumnIndex() invalid column index!");
|
||||
return false;
|
||||
}
|
||||
TableBeginCell(table, column_n);
|
||||
}
|
||||
|
||||
@@ -2133,6 +2203,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
|
||||
g.LastItemData.StatusFlags = 0;
|
||||
}
|
||||
|
||||
// Also see TablePushColumnChannel()
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
{
|
||||
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
|
||||
@@ -2192,8 +2263,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
|
||||
// Note that actual columns widths are computed in TableUpdateLayout().
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.
|
||||
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
|
||||
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
|
||||
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
|
||||
{
|
||||
const ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
float max_width = FLT_MAX;
|
||||
@@ -2255,7 +2326,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
|
||||
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
|
||||
IM_ASSERT(table->MinColumnWidth > 0.0f);
|
||||
const float min_width = table->MinColumnWidth;
|
||||
const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n));
|
||||
const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
|
||||
column_0_width = ImClamp(column_0_width, min_width, max_width);
|
||||
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
|
||||
return;
|
||||
@@ -2406,10 +2477,38 @@ void ImGui::TablePopBackgroundChannel()
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||||
|
||||
// Optimization: avoid PopClipRect() + SetCurrentChannel()
|
||||
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
|
||||
}
|
||||
|
||||
// Also see TableBeginCell()
|
||||
void ImGui::TablePushColumnChannel(int column_n)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
|
||||
// Optimization: avoid SetCurrentChannel() + PushClipRect()
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
return;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
const ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
}
|
||||
|
||||
void ImGui::TablePopColumnChannel()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
|
||||
// Optimization: avoid PopClipRect() + SetCurrentChannel()
|
||||
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
|
||||
return;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||||
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
}
|
||||
|
||||
@@ -2427,7 +2526,7 @@ void ImGui::TablePopBackgroundChannel()
|
||||
// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)
|
||||
// - Clip --> 2+D+N channels
|
||||
// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero)
|
||||
// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero)
|
||||
// - FreezeRows || FreezeColumns --> 3+D+N*2 (unless scrolling value is zero)
|
||||
// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
|
||||
void ImGui::TableSetupDrawChannels(ImGuiTable* table)
|
||||
{
|
||||
@@ -2720,8 +2819,13 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
continue;
|
||||
|
||||
// Draw in outer window so right-most column won't be clipped
|
||||
// Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling.
|
||||
float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head;
|
||||
float draw_y2 = draw_y2_head;
|
||||
if (is_frozen_separator)
|
||||
draw_y2 = draw_y2_body;
|
||||
else if ((table->Flags & ImGuiTableFlags_NoBordersInBodyUntilResize) != 0 && (is_hovered || is_resized))
|
||||
draw_y2 = draw_y2_body;
|
||||
else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0)
|
||||
draw_y2 = draw_y2_body;
|
||||
if (draw_y2 > draw_y1)
|
||||
inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);
|
||||
}
|
||||
@@ -2740,7 +2844,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
const ImU32 outer_col = table->BorderColorStrong;
|
||||
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
|
||||
{
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
|
||||
}
|
||||
else if (table->Flags & ImGuiTableFlags_BordersOuterV)
|
||||
{
|
||||
@@ -2784,9 +2888,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL);
|
||||
|
||||
if (!(table->Flags & ImGuiTableFlags_Sortable))
|
||||
if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable))
|
||||
return NULL;
|
||||
|
||||
// Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.
|
||||
@@ -3004,15 +3106,21 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
|
||||
// The intent is that advanced users willing to create customized headers would not need to use this helper
|
||||
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
|
||||
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
|
||||
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
|
||||
// This code is intentionally written to not make much use of internal functions, to give you better direction
|
||||
// if you need to write your own.
|
||||
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
|
||||
void ImGui::TableHeadersRow()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout)
|
||||
// Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
|
||||
// it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
|
||||
if (!table->IsLayoutLocked)
|
||||
TableUpdateLayout(table);
|
||||
|
||||
@@ -3029,8 +3137,7 @@ void ImGui::TableHeadersRow()
|
||||
if (!TableSetColumnIndex(column_n))
|
||||
continue;
|
||||
|
||||
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
|
||||
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
|
||||
// Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
|
||||
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
|
||||
PushID(column_n);
|
||||
TableHeader(name);
|
||||
@@ -3055,7 +3162,12 @@ void ImGui::TableHeader(const char* label)
|
||||
return;
|
||||
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
IM_ASSERT(table->CurrentColumn != -1);
|
||||
const int column_n = table->CurrentColumn;
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
@@ -3121,7 +3233,7 @@ void ImGui::TableHeader(const char* label)
|
||||
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
|
||||
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
|
||||
}
|
||||
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
|
||||
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
|
||||
if (held)
|
||||
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
|
||||
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
|
||||
@@ -3176,7 +3288,7 @@ void ImGui::TableHeader(const char* label)
|
||||
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
|
||||
// be merged into a single draw call.
|
||||
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
|
||||
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
|
||||
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
|
||||
|
||||
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
|
||||
if (text_clipped && hovered && g.ActiveId == 0)
|
||||
@@ -3230,7 +3342,11 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
ImDrawList* draw_list = window->DrawList;
|
||||
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
|
||||
|
||||
if (max_label_width == 0.0f)
|
||||
@@ -3269,13 +3385,13 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
|
||||
ButtonBehavior(row_r, row_id, NULL, NULL);
|
||||
KeepAliveID(row_id);
|
||||
|
||||
const float ascent_scaled = g.Font->Ascent * (g.FontSize / g.Font->FontSize); // FIXME: Standardize those scaling factors better
|
||||
const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
|
||||
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
|
||||
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
|
||||
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
|
||||
|
||||
// Draw background and labels in first pass, then all borders.
|
||||
float max_x = 0.0f;
|
||||
float max_x = -FLT_MAX;
|
||||
for (int pass = 0; pass < 2; pass++)
|
||||
for (int order_n = 0; order_n < data_count; order_n++)
|
||||
{
|
||||
@@ -3305,7 +3421,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
|
||||
|
||||
// Left<>Right alignment
|
||||
float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
|
||||
float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x;
|
||||
float line_off_for_align_x = ImFloor(ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x);
|
||||
line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
|
||||
|
||||
// Register header width
|
||||
@@ -3324,7 +3440,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
|
||||
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
|
||||
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
|
||||
PushStyleColor(ImGuiCol_Text, request->TextColor);
|
||||
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
|
||||
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
|
||||
PopStyleColor();
|
||||
int vtx_idx_end = draw_list->_VtxCurrentIdx;
|
||||
|
||||
@@ -3471,7 +3587,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
||||
Separator();
|
||||
want_separator = true;
|
||||
|
||||
PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
|
||||
PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
|
||||
for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
|
||||
{
|
||||
ImGuiTableColumn* other_column = &table->Columns[other_column_n];
|
||||
@@ -3661,6 +3777,14 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
|
||||
table->SettingsLoadedFlags = settings->SaveFlags;
|
||||
table->RefScale = settings->RefScale;
|
||||
|
||||
// Initialize default columns settings
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
TableInitColumnDefaults(table, column, ~0);
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
|
||||
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
|
||||
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
|
||||
ImU64 display_order_mask = 0;
|
||||
@@ -3677,14 +3801,12 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
|
||||
column->StretchWeight = column_settings->WidthOrWeight;
|
||||
else
|
||||
column->WidthRequest = column_settings->WidthOrWeight;
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = column_settings->DisplayOrder;
|
||||
else
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)column_n;
|
||||
display_order_mask |= (ImU64)1 << column->DisplayOrder;
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled;
|
||||
if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
|
||||
column->SortOrder = column_settings->SortOrder;
|
||||
column->SortDirection = column_settings->SortDirection;
|
||||
}
|
||||
@@ -3780,8 +3902,7 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle
|
||||
const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;
|
||||
const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
|
||||
const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;
|
||||
if (!save_size && !save_visible && !save_order && !save_sort)
|
||||
continue;
|
||||
// We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
|
||||
|
||||
buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
|
||||
buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount);
|
||||
@@ -3828,7 +3949,7 @@ void ImGui::TableSettingsAddSettingsHandler()
|
||||
// - TableGcCompactSettings() [Internal]
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Remove Table (currently only used by TestEngine)
|
||||
// Remove Table data (currently only used by TestEngine)
|
||||
void ImGui::TableRemove(ImGuiTable* table)
|
||||
{
|
||||
//IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID);
|
||||
@@ -3907,9 +4028,9 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
|
||||
bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*");
|
||||
if (!is_active) { PopStyleColor(); }
|
||||
if (IsItemHovered())
|
||||
GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
|
||||
if (IsItemVisible() && table->HoveredColumnBody != -1)
|
||||
GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
|
||||
if (!open)
|
||||
return;
|
||||
if (table->InstanceCurrent > 0)
|
||||
@@ -3963,7 +4084,7 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
|
||||
if (IsItemHovered())
|
||||
{
|
||||
ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y);
|
||||
GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));
|
||||
}
|
||||
}
|
||||
if (ImGuiTableSettings* settings = TableGetBoundSettings(table))
|
||||
@@ -4393,7 +4514,7 @@ void ImGui::EndColumns()
|
||||
{
|
||||
ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
|
||||
if (hovered || held)
|
||||
g.MouseCursor = ImGuiMouseCursor_ResizeEW;
|
||||
SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
|
||||
dragging_column = n;
|
||||
}
|
||||
@@ -4425,12 +4546,12 @@ void ImGui::EndColumns()
|
||||
NavUpdateCurrentWindowIsScrollPushableX();
|
||||
}
|
||||
|
||||
void ImGui::Columns(int columns_count, const char* id, bool border)
|
||||
void ImGui::Columns(int columns_count, const char* id, bool borders)
|
||||
{
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
IM_ASSERT(columns_count >= 1);
|
||||
|
||||
ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);
|
||||
ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
|
||||
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
|
||||
ImGuiOldColumns* columns = window->DC.CurrentColumns;
|
||||
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)
|
||||
|
||||
+2722
-963
File diff suppressed because it is too large.
Load diff
Regular → Executable
File mode changed.
Regular → Executable
+160
-74
@@ -3,7 +3,10 @@
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
// - Added name to struct or it may be forward declared in our code.
|
||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||
// - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||
// Grep for [DEAR IMGUI] to find some changes.
|
||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||
|
||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||
@@ -37,6 +40,7 @@
|
||||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
|
||||
// 1.14 (2021-07-11) page up/down, various fixes
|
||||
// 1.13 (2019-02-07) fix bug in undo size management
|
||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||
@@ -139,12 +143,14 @@
|
||||
// with previous char)
|
||||
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
||||
// (return type is int, -1 means not valid to insert)
|
||||
// (not supported if you want to use UTF-8, see below)
|
||||
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
|
||||
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
||||
// as manually wordwrapping for end-of-line positioning
|
||||
//
|
||||
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
|
||||
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
||||
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
|
||||
// returns number of characters actually inserted. [DEAR IMGUI]
|
||||
//
|
||||
// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
|
||||
//
|
||||
@@ -176,6 +182,13 @@
|
||||
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
|
||||
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
|
||||
//
|
||||
// To support UTF-8:
|
||||
//
|
||||
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
|
||||
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
|
||||
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
|
||||
// Instead, call stb_textedit_text() directly for text contents.
|
||||
//
|
||||
// Keyboard input must be encoded as a single integer value; e.g. a character code
|
||||
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
||||
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
||||
@@ -209,6 +222,7 @@
|
||||
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
|
||||
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
|
||||
//
|
||||
// Each of these functions potentially updates the string and updates the
|
||||
// state.
|
||||
@@ -243,7 +257,14 @@
|
||||
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
|
||||
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
|
||||
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
|
||||
// anything other type you wante before including.
|
||||
// anything other type you want before including.
|
||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||
// transformed into text and stb_textedit_text() is automatically called.
|
||||
//
|
||||
// text: (added 2025)
|
||||
// call this to directly send text input the textfield, which is required
|
||||
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
|
||||
// cannot infer text length.
|
||||
//
|
||||
//
|
||||
// When rendering, you can read the cursor position and selection state from
|
||||
@@ -318,7 +339,7 @@ typedef struct
|
||||
int undo_char_point, redo_char_point;
|
||||
} StbUndoState;
|
||||
|
||||
typedef struct
|
||||
typedef struct STB_TexteditState
|
||||
{
|
||||
/////////////////////
|
||||
//
|
||||
@@ -392,6 +413,16 @@ typedef struct
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -399,7 +430,7 @@ typedef struct
|
||||
//
|
||||
|
||||
// traverse the layout to locate the nearest character to a display position
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
@@ -409,6 +440,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
r.x0 = r.x1 = 0;
|
||||
r.ymin = r.ymax = 0;
|
||||
r.num_chars = 0;
|
||||
*out_side_on_line = 0;
|
||||
|
||||
// search rows to find one that straddles 'y'
|
||||
while (i < n) {
|
||||
@@ -428,7 +460,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
|
||||
// below all text, return 'after' last character
|
||||
if (i >= n)
|
||||
{
|
||||
*out_side_on_line = 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
// check if it's before the beginning of the line
|
||||
if (x < r.x0)
|
||||
@@ -438,13 +473,14 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
if (x < r.x1) {
|
||||
// search characters in row for one that straddles 'x'
|
||||
prev_x = r.x0;
|
||||
for (k=0; k < r.num_chars; ++k) {
|
||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||
if (x < prev_x+w) {
|
||||
*out_side_on_line = (k == 0) ? 0 : 1;
|
||||
if (x < prev_x+w/2)
|
||||
return k+i;
|
||||
else
|
||||
return k+i+1;
|
||||
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
|
||||
}
|
||||
prev_x += w;
|
||||
}
|
||||
@@ -452,6 +488,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
}
|
||||
|
||||
// if the last character is a newline, return that. otherwise return 'after' the last character
|
||||
*out_side_on_line = 1;
|
||||
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
|
||||
return i+r.num_chars-1;
|
||||
else
|
||||
@@ -463,6 +500,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
||||
{
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
int side_on_line;
|
||||
if( state->single_line )
|
||||
{
|
||||
StbTexteditRow r;
|
||||
@@ -470,16 +508,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
|
||||
y = r.ymin;
|
||||
}
|
||||
|
||||
state->cursor = stb_text_locate_coord(str, x, y);
|
||||
state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||
state->select_start = state->cursor;
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||
}
|
||||
|
||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
int p = 0;
|
||||
int side_on_line;
|
||||
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
@@ -493,8 +533,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
||||
if (state->select_start == state->select_end)
|
||||
state->select_start = state->cursor;
|
||||
|
||||
p = stb_text_locate_coord(str, x, y);
|
||||
p = stb_text_locate_coord(str, x, y, &side_on_line);
|
||||
state->cursor = state->select_end = p;
|
||||
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -544,6 +585,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||
if (n < i + r.num_chars)
|
||||
break;
|
||||
if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
|
||||
break;
|
||||
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
|
||||
break; // [DEAR IMGUI]
|
||||
prev_start = i;
|
||||
@@ -563,7 +606,7 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
|
||||
|
||||
// now scan to find xpos
|
||||
find->x = r.x0;
|
||||
for (i=0; first+i < n; ++i)
|
||||
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
|
||||
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
|
||||
}
|
||||
|
||||
@@ -640,6 +683,35 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
|
||||
}
|
||||
}
|
||||
|
||||
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
|
||||
#ifndef STB_TEXTEDIT_MOVELINESTART
|
||||
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||
{
|
||||
if (state->single_line)
|
||||
return 0;
|
||||
while (cursor > 0) {
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
|
||||
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
cursor = prev;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_MOVELINEEND
|
||||
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
|
||||
{
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->single_line)
|
||||
return n;
|
||||
while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
|
||||
return cursor;
|
||||
}
|
||||
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
|
||||
#endif
|
||||
|
||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
{
|
||||
@@ -649,9 +721,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
--c; // always move at least one character
|
||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||
--c;
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
|
||||
while (c >= 0 && !is_word_boundary(str, c))
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
|
||||
|
||||
if( c < 0 )
|
||||
c = 0;
|
||||
@@ -665,9 +737,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
|
||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||
++c; // always move at least one character
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
|
||||
while( c < len && !is_word_boundary( str, c ) )
|
||||
++c;
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
|
||||
|
||||
if( c > len )
|
||||
c = len;
|
||||
@@ -706,7 +778,8 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_delete_selection(str,state);
|
||||
// try to insert the characters
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {
|
||||
len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len);
|
||||
if (len) {
|
||||
stb_text_makeundo_insert(state, state->cursor, len);
|
||||
state->cursor += len;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -720,36 +793,47 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
||||
#define STB_TEXTEDIT_KEYTYPE int
|
||||
#endif
|
||||
|
||||
// API key: process text input
|
||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
// can't add newline in single-line mode
|
||||
if (text[0] == '\n' && state->single_line)
|
||||
return;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||
if (text_len) {
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
} else {
|
||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
|
||||
if (text_len) {
|
||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// API key: process a keyboard input
|
||||
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||
{
|
||||
retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||
// This is not suitable for UTF-8 support.
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
|
||||
|
||||
// can't add newline in single-line mode
|
||||
if (c == '\n' && state->single_line)
|
||||
break;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
} else {
|
||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, 1);
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||
stb_textedit_text(str, state, &ch, 1);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -775,7 +859,7 @@ retry:
|
||||
stb_textedit_move_to_first(state);
|
||||
else
|
||||
if (state->cursor > 0)
|
||||
--state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -784,7 +868,7 @@ retry:
|
||||
if (STB_TEXT_HAS_SELECTION(state))
|
||||
stb_textedit_move_to_last(str, state);
|
||||
else
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -794,7 +878,7 @@ retry:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection left
|
||||
if (state->select_end > 0)
|
||||
--state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -844,7 +928,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection right
|
||||
++state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -884,15 +968,16 @@ retry:
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// going down while being on the last line shouldn't bring us to that line end
|
||||
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
//if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
// break;
|
||||
|
||||
// now find character position down a row
|
||||
state->cursor = start;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -900,10 +985,13 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
if (state->cursor == find.first_char + find.length)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||
state->has_preferred_x = 1;
|
||||
state->preferred_x = goal_x;
|
||||
|
||||
@@ -953,8 +1041,9 @@ retry:
|
||||
state->cursor = find.prev_first;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -962,10 +1051,15 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
if (state->cursor == find.first_char)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Right;
|
||||
else if (state->cursor == find.prev_first)
|
||||
str->LastMoveDirectionLR = ImGuiDir_Left;
|
||||
state->has_preferred_x = 1;
|
||||
state->preferred_x = goal_x;
|
||||
|
||||
@@ -975,10 +1069,15 @@ retry:
|
||||
// go to previous line
|
||||
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
||||
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
|
||||
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
--prev_scan;
|
||||
while (prev_scan > 0)
|
||||
{
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
|
||||
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
prev_scan = prev;
|
||||
}
|
||||
find.first_char = find.prev_first;
|
||||
find.prev_first = prev_scan;
|
||||
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -990,7 +1089,7 @@ retry:
|
||||
else {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->cursor < n)
|
||||
stb_textedit_delete(str, state, state->cursor, 1);
|
||||
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1002,8 +1101,9 @@ retry:
|
||||
else {
|
||||
stb_textedit_clamp(str, state);
|
||||
if (state->cursor > 0) {
|
||||
stb_textedit_delete(str, state, state->cursor-1, 1);
|
||||
--state->cursor;
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
stb_textedit_delete(str, state, prev, state->cursor - prev);
|
||||
state->cursor = prev;
|
||||
}
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
@@ -1051,10 +1151,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINESTART:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_move_to_first(state);
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -1062,13 +1159,9 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINEEND2:
|
||||
#endif
|
||||
case STB_TEXTEDIT_K_LINEEND: {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_move_to_first(state);
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
stb_textedit_move_to_last(str, state);
|
||||
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
}
|
||||
@@ -1079,10 +1172,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1091,13 +1181,9 @@ retry:
|
||||
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
|
||||
#endif
|
||||
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1272,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// check type of recorded action:
|
||||
if (u.insert_length) {
|
||||
// easy case: was a deletion, so we need to insert n characters
|
||||
STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
||||
u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
|
||||
s->undo_char_point -= u.insert_length;
|
||||
}
|
||||
|
||||
@@ -1323,7 +1409,7 @@ static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
|
||||
if (r.insert_length) {
|
||||
// easy case: need to insert n characters
|
||||
STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
||||
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
|
||||
s->redo_char_point += r.insert_length;
|
||||
}
|
||||
|
||||
|
||||
Regular → Executable
+2
-2
@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
|
||||
q2[0] = (float)x2;
|
||||
q2[1] = (float)y2;
|
||||
if (equal(q0,q1) || equal(q1,q2)) {
|
||||
x0 = (int)verts[i-1].x;
|
||||
y0 = (int)verts[i-1].y;
|
||||
x0 = (int)verts[i-1].x; //-V1048
|
||||
y0 = (int)verts[i-1].y; //-V1048
|
||||
x1 = (int)verts[i ].x;
|
||||
y1 = (int)verts[i ].y;
|
||||
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
||||
|
||||
+1
-1
Submodule core/deps/libadrenotools updated: 5deac9f1ab...8fae8ce254.
Submodule
+1
Submodule core/deps/libjuice added at 77daa8befd.
+1
-1
Submodule core/deps/oboe updated: b15f5e39c0...a81bb9f87d.
@@ -319,7 +319,7 @@ static void pppdump(uint8_t *buf, int len, int egress)
|
||||
last_time_ms = PICO_TIME_MS();
|
||||
}
|
||||
|
||||
uint32_t delta = (PICO_TIME_MS() - last_time_ms) / 100;
|
||||
uint32_t delta = PICO_TIME_MS() / 100 - last_time_ms / 100;
|
||||
if (delta < 256)
|
||||
{
|
||||
fputc(6, dumpfp); // Time step (short)
|
||||
|
||||
+1
-1
Submodule core/deps/rcheevos updated: 563230b1c2...3106e6d3d2.
Submodule
+1
Submodule core/deps/tinygettext added at 41572a67f9.
Submodule
+1
Submodule core/deps/websocketpp added at 1b11fd3015.
@@ -1,6 +1,21 @@
|
||||
===========
|
||||
|Changelog|
|
||||
===========
|
||||
xBRZ 1.8 [2019-11-28]
|
||||
---------------------
|
||||
Consider ARGB outside area as transparent
|
||||
Fixed ARGB scaling issue on image borders
|
||||
|
||||
|
||||
xBRZ 1.7 [2019-07-04]
|
||||
---------------------
|
||||
Fixed asymmetric color distance
|
||||
New parameter: "Center direction bias"
|
||||
|
||||
|
||||
xBRZ 1.6 [2018-02-27]
|
||||
---------------------
|
||||
Added bilinear scaling
|
||||
Option to skip color buffer creation
|
||||
Updated license info
|
||||
|
||||
|
||||
xBRZ 1.5 [2017-08-07]
|
||||
---------------------
|
||||
|
||||
+439
-204
@@ -1,24 +1,25 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
// ****************************************************************************
|
||||
|
||||
#include "xbrz.h"
|
||||
#include "xbrz_tools.h"
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <cmath> //std::sqrt
|
||||
#include "xbrz_tools.h"
|
||||
|
||||
using namespace xbrz;
|
||||
|
||||
@@ -26,9 +27,9 @@ using namespace xbrz;
|
||||
namespace
|
||||
{
|
||||
template <unsigned int M, unsigned int N> inline
|
||||
uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
|
||||
uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
|
||||
{
|
||||
static_assert(0 < M && M < N && N <= 1000, "");
|
||||
static_assert(0 < M && M < N && N <= 1000);
|
||||
|
||||
auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; };
|
||||
|
||||
@@ -41,7 +42,7 @@ uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color wi
|
||||
template <unsigned int M, unsigned int N> inline
|
||||
uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!)
|
||||
{
|
||||
static_assert(0 < M && M < N && N <= 1000, "");
|
||||
static_assert(0 < M && M < N && N <= 1000);
|
||||
|
||||
const unsigned int weightFront = getAlpha(pixFront) * M;
|
||||
const unsigned int weightBack = getAlpha(pixBack) * (N - M);
|
||||
@@ -135,7 +136,7 @@ template <class T> inline
|
||||
T square(T value) { return value * value; }
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
inline
|
||||
double distRGB(uint32_t pix1, uint32_t pix2)
|
||||
{
|
||||
@@ -146,12 +147,13 @@ double distRGB(uint32_t pix1, uint32_t pix2)
|
||||
//euklidean RGB distance
|
||||
return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline
|
||||
double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
|
||||
{
|
||||
//http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
|
||||
//https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
|
||||
//YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
|
||||
const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication
|
||||
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
|
||||
@@ -186,9 +188,9 @@ double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
|
||||
|
||||
for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores)
|
||||
{
|
||||
const int r_diff = getByte<2>(i) * 2 - 0xFF;
|
||||
const int g_diff = getByte<1>(i) * 2 - 0xFF;
|
||||
const int b_diff = getByte<0>(i) * 2 - 0xFF;
|
||||
const int r_diff = static_cast<signed char>(getByte<2>(i)) * 2;
|
||||
const int g_diff = static_cast<signed char>(getByte<1>(i)) * 2;
|
||||
const int b_diff = static_cast<signed char>(getByte<0>(i)) * 2;
|
||||
|
||||
const double k_b = 0.0593; //ITU-R BT.2020 conversion
|
||||
const double k_r = 0.2627; //
|
||||
@@ -210,24 +212,32 @@ double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
|
||||
// return 0;
|
||||
//if (pix1 < pix2)
|
||||
// std::swap(pix1, pix2); -> 30% perf degradation!!!
|
||||
#if 1
|
||||
|
||||
const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2);
|
||||
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
|
||||
const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
|
||||
|
||||
return diffToDist[(((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
(((g_diff + 0xFF) / 2) << 8) |
|
||||
(( b_diff + 0xFF) / 2)];
|
||||
#else //not noticeably faster:
|
||||
const int r_diff_tmp = ((pix1 & 0xFF0000) + 0xFF0000 - (pix2 & 0xFF0000)) / 2;
|
||||
const int g_diff_tmp = ((pix1 & 0x00FF00) + 0x00FF00 - (pix2 & 0x00FF00)) / 2; //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
const int b_diff_tmp = ((pix1 & 0x0000FF) + 0x0000FF - (pix2 & 0x0000FF)) / 2;
|
||||
const size_t index = (static_cast<unsigned char>(r_diff / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
(static_cast<unsigned char>(g_diff / 2) << 8) |
|
||||
(static_cast<unsigned char>(b_diff / 2));
|
||||
|
||||
return diffToDist[(r_diff_tmp & 0xFF0000) | (g_diff_tmp & 0x00FF00) | (b_diff_tmp & 0x0000FF)];
|
||||
#if 0 //attention: the following calculation creates an asymmetric color distance!!! (e.g. r_diff=46 will be unpacked as 45, but r_diff=-46 unpacks to -47
|
||||
const size_t index = (((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
(((g_diff + 0xFF) / 2) << 8) |
|
||||
(( b_diff + 0xFF) / 2);
|
||||
#endif
|
||||
return diffToDist[index];
|
||||
}
|
||||
|
||||
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
const int debugPixelX = -1;
|
||||
const int debugPixelY = 58;
|
||||
|
||||
thread_local bool breakIntoDebugger = false;
|
||||
#endif
|
||||
|
||||
|
||||
enum BlendType
|
||||
{
|
||||
BLEND_NONE = 0,
|
||||
@@ -244,24 +254,32 @@ struct BlendResult
|
||||
};
|
||||
|
||||
|
||||
struct Kernel_3x3
|
||||
{
|
||||
uint32_t
|
||||
a, b, c,
|
||||
d, e, f,
|
||||
g, h, i;
|
||||
};
|
||||
|
||||
struct Kernel_4x4 //kernel for preprocessing step
|
||||
{
|
||||
uint32_t
|
||||
/**/a, b, c, d,
|
||||
/**/e, f, g, h,
|
||||
/**/i, j, k, l,
|
||||
/**/m, n, o, p;
|
||||
a, b, c, //
|
||||
e, f, g, // support reinterpret_cast from Kernel_4x4 => Kernel_3x3
|
||||
i, j, k, //
|
||||
m, n, o,
|
||||
d, h, l, p;
|
||||
};
|
||||
|
||||
/*
|
||||
input kernel area naming convention:
|
||||
/* input kernel area naming convention:
|
||||
-----------------
|
||||
| A | B | C | D |
|
||||
----|---|---|---|
|
||||
| E | F | G | H | //evaluate the four corners between F, G, J, K
|
||||
----|---|---|---| //input pixel is at position F
|
||||
|---|---|---|---|
|
||||
| E | F | G | H | evaluate the four corners between F, G, J, K
|
||||
|---|---|---|---| input pixel is at position F
|
||||
| I | J | K | L |
|
||||
----|---|---|---|
|
||||
|---|---|---|---|
|
||||
| M | N | O | P |
|
||||
-----------------
|
||||
*/
|
||||
@@ -269,6 +287,11 @@ template <class ColorDistance>
|
||||
FORCE_INLINE //detect blend direction
|
||||
BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType"
|
||||
{
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
if (breakIntoDebugger)
|
||||
__debugbreak(); //__asm int 3;
|
||||
#endif
|
||||
|
||||
BlendResult result = {};
|
||||
|
||||
if ((ker.f == ker.g &&
|
||||
@@ -279,9 +302,8 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg)
|
||||
|
||||
auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); };
|
||||
|
||||
const int weight = 4;
|
||||
double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g);
|
||||
double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k);
|
||||
double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + cfg.centerDirectionBias * dist(ker.j, ker.g);
|
||||
double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + cfg.centerDirectionBias * dist(ker.f, ker.k);
|
||||
|
||||
if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8
|
||||
{
|
||||
@@ -304,14 +326,6 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg)
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Kernel_3x3
|
||||
{
|
||||
uint32_t
|
||||
/**/a, b, c,
|
||||
/**/d, e, f,
|
||||
/**/g, h, i;
|
||||
};
|
||||
|
||||
#define DEF_GETTER(x) template <RotationDegree rotDeg> uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; }
|
||||
//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token
|
||||
DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c)
|
||||
@@ -339,17 +353,21 @@ DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g)
|
||||
|
||||
|
||||
//compress four blend types into a single byte
|
||||
inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
|
||||
//inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
|
||||
inline BlendType getTopR (unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 2)); }
|
||||
inline BlendType getBottomR(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 4)); }
|
||||
inline BlendType getBottomL(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 6)); }
|
||||
|
||||
inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing!
|
||||
inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); }
|
||||
inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); }
|
||||
inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); }
|
||||
inline void clearAddTopL(unsigned char& b, BlendType bt) { b = static_cast<unsigned char>(bt); }
|
||||
inline void addTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } //buffer is assumed to be initialized before preprocessing!
|
||||
inline void addBottomR (unsigned char& b, BlendType bt) { b |= (bt << 4); } //e.g. via clearAddTopL()
|
||||
inline void addBottomL (unsigned char& b, BlendType bt) { b |= (bt << 6); } //
|
||||
|
||||
inline bool blendingNeeded(unsigned char b) { return b != 0; }
|
||||
inline bool blendingNeeded(unsigned char b)
|
||||
{
|
||||
static_assert(BLEND_NONE == 0);
|
||||
return b != 0;
|
||||
}
|
||||
|
||||
template <RotationDegree rotDeg> inline
|
||||
unsigned char rotateBlendInfo(unsigned char b) { return b; }
|
||||
@@ -358,20 +376,12 @@ template <> inline unsigned char rotateBlendInfo<ROT_180>(unsigned char b) { ret
|
||||
template <> inline unsigned char rotateBlendInfo<ROT_270>(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; }
|
||||
|
||||
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
int debugPixelX = -1;
|
||||
int debugPixelY = 12;
|
||||
__declspec(thread) bool breakIntoDebugger = false;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
input kernel area naming convention:
|
||||
/* input kernel area naming convention:
|
||||
-------------
|
||||
| A | B | C |
|
||||
----|---|---|
|
||||
| D | E | F | //input pixel is at position E
|
||||
----|---|---|
|
||||
|---|---|---|
|
||||
| D | E | F | input pixel is at position E
|
||||
|---|---|---|
|
||||
| G | H | I |
|
||||
-------------
|
||||
*/
|
||||
@@ -382,7 +392,7 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
unsigned char blendInfo, //result of preprocessing all four corners of pixel "e"
|
||||
const xbrz::ScalerCfg& cfg)
|
||||
{
|
||||
#define a get_a<rotDeg>(ker)
|
||||
//#define a get_a<rotDeg>(ker)
|
||||
#define b get_b<rotDeg>(ker)
|
||||
#define c get_c<rotDeg>(ker)
|
||||
#define d get_d<rotDeg>(ker)
|
||||
@@ -410,7 +420,7 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
return true;
|
||||
|
||||
//make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes
|
||||
if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90� corners
|
||||
if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners
|
||||
return false;
|
||||
if (getBottomL(blend) != BLEND_NONE && !eq(e, c))
|
||||
return false;
|
||||
@@ -453,7 +463,7 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
Scaler::blendCorner(px, out);
|
||||
}
|
||||
|
||||
#undef a
|
||||
//#undef a
|
||||
#undef b
|
||||
#undef c
|
||||
#undef d
|
||||
@@ -465,7 +475,72 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
}
|
||||
|
||||
|
||||
template <class Scaler, class ColorDistance> //scaler policy: see "Scaler2x" reference implementation
|
||||
class OobReaderTransparent
|
||||
{
|
||||
public:
|
||||
OobReaderTransparent(const uint32_t* src, int srcWidth, int srcHeight, int y) :
|
||||
s_m1(0 <= y - 1 && y - 1 < srcHeight ? src + srcWidth * (y - 1) : nullptr),
|
||||
s_0 (0 <= y && y < srcHeight ? src + srcWidth * y : nullptr),
|
||||
s_p1(0 <= y + 1 && y + 1 < srcHeight ? src + srcWidth * (y + 1) : nullptr),
|
||||
s_p2(0 <= y + 2 && y + 2 < srcHeight ? src + srcWidth * (y + 2) : nullptr),
|
||||
srcWidth_(srcWidth) {}
|
||||
|
||||
void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
|
||||
{
|
||||
[[likely]] if (const int x_p2 = x + 2; 0 <= x_p2 && x_p2 < srcWidth_)
|
||||
{
|
||||
ker.d = s_m1 ? s_m1[x_p2] : 0;
|
||||
ker.h = s_0 ? s_0 [x_p2] : 0;
|
||||
ker.l = s_p1 ? s_p1[x_p2] : 0;
|
||||
ker.p = s_p2 ? s_p2[x_p2] : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ker.d = 0;
|
||||
ker.h = 0;
|
||||
ker.l = 0;
|
||||
ker.p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t* const s_m1;
|
||||
const uint32_t* const s_0;
|
||||
const uint32_t* const s_p1;
|
||||
const uint32_t* const s_p2;
|
||||
const int srcWidth_;
|
||||
};
|
||||
|
||||
|
||||
class OobReaderDuplicate
|
||||
{
|
||||
public:
|
||||
OobReaderDuplicate(const uint32_t* src, int srcWidth, int srcHeight, int y) :
|
||||
s_m1(src + srcWidth * std::clamp(y - 1, 0, srcHeight - 1)),
|
||||
s_0 (src + srcWidth * std::clamp(y, 0, srcHeight - 1)),
|
||||
s_p1(src + srcWidth * std::clamp(y + 1, 0, srcHeight - 1)),
|
||||
s_p2(src + srcWidth * std::clamp(y + 2, 0, srcHeight - 1)),
|
||||
srcWidth_(srcWidth) {}
|
||||
|
||||
void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
|
||||
{
|
||||
const int x_p2 = std::clamp(x + 2, 0, srcWidth_ - 1);
|
||||
ker.d = s_m1[x_p2];
|
||||
ker.h = s_0 [x_p2];
|
||||
ker.l = s_p1[x_p2];
|
||||
ker.p = s_p2[x_p2];
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t* const s_m1;
|
||||
const uint32_t* const s_0;
|
||||
const uint32_t* const s_p1;
|
||||
const uint32_t* const s_p2;
|
||||
const int srcWidth_;
|
||||
};
|
||||
|
||||
|
||||
template <class Scaler, class ColorDistance, class OobReader> //scaler policy: see "Scaler2x" reference implementation
|
||||
void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
|
||||
{
|
||||
yFirst = std::max(yFirst, 0);
|
||||
@@ -475,64 +550,72 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
|
||||
|
||||
const int trgWidth = srcWidth * Scaler::scale;
|
||||
|
||||
//"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of
|
||||
//"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing
|
||||
const int bufferSize = srcWidth;
|
||||
unsigned char* preProcBuffer = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - bufferSize;
|
||||
std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0');
|
||||
static_assert(BLEND_NONE == 0, "");
|
||||
//(ab)use space of "sizeof(uint32_t) * srcWidth * Scaler::scale" at the end of the image as temporary
|
||||
//buffer for "on the fly preprocessing" without risk of accidental overwriting before accessing
|
||||
unsigned char* const preProcBuf = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - srcWidth;
|
||||
|
||||
//initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending
|
||||
//this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition!
|
||||
if (yFirst > 0)
|
||||
{
|
||||
const int y = yFirst - 1;
|
||||
const OobReader oobReader(src, srcWidth, srcHeight, yFirst - 1);
|
||||
|
||||
const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
|
||||
const uint32_t* s_0 = src + srcWidth * y; //center line
|
||||
const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
|
||||
const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
|
||||
//initialize at position x = -1
|
||||
Kernel_4x4 ker4 = {};
|
||||
oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
|
||||
ker4.a = ker4.d;
|
||||
ker4.e = ker4.h;
|
||||
ker4.i = ker4.l;
|
||||
ker4.m = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -3);
|
||||
ker4.b = ker4.d;
|
||||
ker4.f = ker4.h;
|
||||
ker4.j = ker4.l;
|
||||
ker4.n = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -2);
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -1);
|
||||
|
||||
{
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
clearAddTopL(preProcBuf[0], res.blend_k); //set 1st known corner for (0, yFirst)
|
||||
}
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x)
|
||||
{
|
||||
const int x_m1 = std::max(x - 1, 0);
|
||||
const int x_p1 = std::min(x + 1, srcWidth - 1);
|
||||
const int x_p2 = std::min(x + 2, srcWidth - 1);
|
||||
ker4.a = ker4.b; //shift previous kernel to the left
|
||||
ker4.e = ker4.f; // -----------------
|
||||
ker4.i = ker4.j; // | A | B | C | D |
|
||||
ker4.m = ker4.n; // |---|---|---|---|
|
||||
/**/ // | E | F | G | H | (x, yFirst - 1) is at position F
|
||||
ker4.b = ker4.c; // |---|---|---|---|
|
||||
ker4.f = ker4.g; // | I | J | K | L |
|
||||
ker4.j = ker4.k; // |---|---|---|---|
|
||||
ker4.n = ker4.o; // | M | N | O | P |
|
||||
/**/ // -----------------
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
Kernel_4x4 ker = {}; //perf: initialization is negligible
|
||||
ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible
|
||||
ker.b = s_m1[x];
|
||||
ker.c = s_m1[x_p1];
|
||||
ker.d = s_m1[x_p2];
|
||||
oobReader.readDhlp(ker4, x);
|
||||
|
||||
ker.e = s_0[x_m1];
|
||||
ker.f = s_0[x];
|
||||
ker.g = s_0[x_p1];
|
||||
ker.h = s_0[x_p2];
|
||||
/* preprocessing blend result:
|
||||
---------
|
||||
| F | G | evaluate corner between F, G, J, K
|
||||
|---+---| current input pixel is at position F
|
||||
| J | K |
|
||||
--------- */
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
addTopR(preProcBuf[x], res.blend_j); //set 2nd known corner for (x, yFirst)
|
||||
|
||||
ker.i = s_p1[x_m1];
|
||||
ker.j = s_p1[x];
|
||||
ker.k = s_p1[x_p1];
|
||||
ker.l = s_p1[x_p2];
|
||||
|
||||
ker.m = s_p2[x_m1];
|
||||
ker.n = s_p2[x];
|
||||
ker.o = s_p2[x_p1];
|
||||
ker.p = s_p2[x_p2];
|
||||
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker, cfg);
|
||||
/*
|
||||
preprocessing blend result:
|
||||
---------
|
||||
| F | G | //evalute corner between F, G, J, K
|
||||
----|---| //input pixel is at position F
|
||||
| J | K |
|
||||
---------
|
||||
*/
|
||||
setTopR(preProcBuffer[x], res.blend_j);
|
||||
|
||||
if (x + 1 < bufferSize)
|
||||
setTopL(preProcBuffer[x + 1], res.blend_k);
|
||||
if (x + 1 < srcWidth)
|
||||
clearAddTopL(preProcBuf[x + 1], res.blend_k); //set 1st known corner for (x + 1, yFirst)
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
@@ -541,91 +624,92 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
|
||||
{
|
||||
uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access
|
||||
|
||||
const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
|
||||
const uint32_t* s_0 = src + srcWidth * y; //center line
|
||||
const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
|
||||
const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
|
||||
const OobReader oobReader(src, srcWidth, srcHeight, y);
|
||||
|
||||
//initialize at position x = -1
|
||||
Kernel_4x4 ker4 = {};
|
||||
oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
|
||||
ker4.a = ker4.d;
|
||||
ker4.e = ker4.h;
|
||||
ker4.i = ker4.l;
|
||||
ker4.m = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -3);
|
||||
ker4.b = ker4.d;
|
||||
ker4.f = ker4.h;
|
||||
ker4.j = ker4.l;
|
||||
ker4.n = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -2);
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -1);
|
||||
|
||||
unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position
|
||||
{
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (0, y + 1) and buffer for use on next column
|
||||
|
||||
addBottomL(preProcBuf[0], res.blend_g); //set 3rd known corner for (0, y)
|
||||
}
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x, out += Scaler::scale)
|
||||
{
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
breakIntoDebugger = debugPixelX == x && debugPixelY == y;
|
||||
#endif
|
||||
//all those bounds checks have only insignificant impact on performance!
|
||||
const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers!
|
||||
const int x_p1 = std::min(x + 1, srcWidth - 1);
|
||||
const int x_p2 = std::min(x + 2, srcWidth - 1);
|
||||
ker4.a = ker4.b; //shift previous kernel to the left
|
||||
ker4.e = ker4.f; // -----------------
|
||||
ker4.i = ker4.j; // | A | B | C | D |
|
||||
ker4.m = ker4.n; // |---|---|---|---|
|
||||
/**/ // | E | F | G | H | (x, y) is at position F
|
||||
ker4.b = ker4.c; // |---|---|---|---|
|
||||
ker4.f = ker4.g; // | I | J | K | L |
|
||||
ker4.j = ker4.k; // |---|---|---|---|
|
||||
ker4.n = ker4.o; // | M | N | O | P |
|
||||
/**/ // -----------------
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
Kernel_4x4 ker4 = {}; //perf: initialization is negligible
|
||||
|
||||
ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible
|
||||
ker4.b = s_m1[x];
|
||||
ker4.c = s_m1[x_p1];
|
||||
ker4.d = s_m1[x_p2];
|
||||
|
||||
ker4.e = s_0[x_m1];
|
||||
ker4.f = s_0[x];
|
||||
ker4.g = s_0[x_p1];
|
||||
ker4.h = s_0[x_p2];
|
||||
|
||||
ker4.i = s_p1[x_m1];
|
||||
ker4.j = s_p1[x];
|
||||
ker4.k = s_p1[x_p1];
|
||||
ker4.l = s_p1[x_p2];
|
||||
|
||||
ker4.m = s_p2[x_m1];
|
||||
ker4.n = s_p2[x];
|
||||
ker4.o = s_p2[x_p1];
|
||||
ker4.p = s_p2[x_p2];
|
||||
oobReader.readDhlp(ker4, x);
|
||||
|
||||
//evaluate the four corners on bottom-right of current pixel
|
||||
unsigned char blend_xy = 0; //for current (x, y) position
|
||||
unsigned char blend_xy = preProcBuf[x]; //for current (x, y) position
|
||||
{
|
||||
/* preprocessing blend result:
|
||||
---------
|
||||
| F | G | evaluate corner between F, G, J, K
|
||||
|---+---| current input pixel is at position F
|
||||
| J | K |
|
||||
--------- */
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
/*
|
||||
preprocessing blend result:
|
||||
---------
|
||||
| F | G | //evalute corner between F, G, J, K
|
||||
----|---| //current input pixel is at position F
|
||||
| J | K |
|
||||
---------
|
||||
*/
|
||||
blend_xy = preProcBuffer[x];
|
||||
setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
|
||||
addBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
|
||||
|
||||
setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
|
||||
preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row
|
||||
addTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
|
||||
preProcBuf[x] = blend_xy1; //store on current buffer position for use on next row
|
||||
|
||||
blend_xy1 = 0;
|
||||
setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
|
||||
[[likely]] if (x + 1 < srcWidth)
|
||||
{
|
||||
//blend_xy1 -> blend_x1y1
|
||||
clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
|
||||
|
||||
if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y)
|
||||
setBottomL(preProcBuffer[x + 1], res.blend_g);
|
||||
addBottomL(preProcBuf[x + 1], res.blend_g); //set 3rd known corner for (x + 1, y)
|
||||
}
|
||||
}
|
||||
|
||||
//fill block of size scale * scale with the given color
|
||||
fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale);
|
||||
//place *after* preprocessing step, to not overwrite the results while processing the the last pixel!
|
||||
//place *after* preprocessing step, to not overwrite the results while processing the last pixel!
|
||||
|
||||
//blend four corners of current pixel
|
||||
if (blendingNeeded(blend_xy)) //good 5% perf-improvement
|
||||
//blend all four corners of current pixel
|
||||
if (blendingNeeded(blend_xy))
|
||||
{
|
||||
Kernel_3x3 ker3 = {}; //perf: initialization is negligible
|
||||
|
||||
ker3.a = ker4.a;
|
||||
ker3.b = ker4.b;
|
||||
ker3.c = ker4.c;
|
||||
|
||||
ker3.d = ker4.e;
|
||||
ker3.e = ker4.f;
|
||||
ker3.f = ker4.g;
|
||||
|
||||
ker3.g = ker4.i;
|
||||
ker3.h = ker4.j;
|
||||
ker3.i = ker4.k;
|
||||
|
||||
const auto& ker3 = reinterpret_cast<const Kernel_3x3&>(ker4); //"The Things We Do for Perf"
|
||||
blendPixel<Scaler, ColorDistance, ROT_0 >(ker3, out, trgWidth, blend_xy, cfg);
|
||||
blendPixel<Scaler, ColorDistance, ROT_90 >(ker3, out, trgWidth, blend_xy, cfg);
|
||||
blendPixel<Scaler, ColorDistance, ROT_180>(ker3, out, trgWidth, blend_xy, cfg);
|
||||
@@ -1037,6 +1121,22 @@ struct ColorDistanceARGB
|
||||
};
|
||||
|
||||
|
||||
struct ColorDistanceUnbufferedARGB
|
||||
{
|
||||
static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
|
||||
{
|
||||
const double a1 = getAlpha(pix1) / 255.0 ;
|
||||
const double a2 = getAlpha(pix2) / 255.0 ;
|
||||
|
||||
const double d = distYCbCr(pix1, pix2, luminanceWeight);
|
||||
if (a1 < a2)
|
||||
return a1 * d + 255 * (a2 - a1);
|
||||
else
|
||||
return a2 * d + 255 * (a1 - a2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ColorGradientRGB
|
||||
{
|
||||
template <unsigned int M, unsigned int N>
|
||||
@@ -1059,37 +1159,60 @@ struct ColorGradientARGB
|
||||
|
||||
void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
|
||||
{
|
||||
if (factor == 1)
|
||||
{
|
||||
std::copy(src + yFirst * srcWidth, src + yLast * srcWidth, trg);
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(SCALE_FACTOR_MAX == 6);
|
||||
switch (colFmt)
|
||||
{
|
||||
case ColorFormat::ARGB:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorFormat::RGB:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorFormat::ARGB:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorFormat::ARGB_UNBUFFERED:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1101,21 +1224,133 @@ bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, doub
|
||||
{
|
||||
switch (colFmt)
|
||||
{
|
||||
case ColorFormat::ARGB:
|
||||
return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
|
||||
case ColorFormat::RGB:
|
||||
return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
case ColorFormat::ARGB:
|
||||
return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
case ColorFormat::ARGB_UNBUFFERED:
|
||||
return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
}
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
|
||||
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
|
||||
0, trgHeight, [](uint32_t pix) { return pix; });
|
||||
}
|
||||
|
||||
|
||||
void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
uint32_t* trg, int trgWidth, int trgHeight)
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
|
||||
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
|
||||
SliceType::TARGET, 0, trgHeight, [](uint32_t pix) { return pix; });
|
||||
0, trgHeight, [](uint32_t pix) { return pix; });
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
//#include <ppl.h>
|
||||
void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
const int TASK_GRANULARITY = 16;
|
||||
|
||||
concurrency::task_group tg;
|
||||
|
||||
for (int i = 0; i < trgHeight; i += TASK_GRANULARITY)
|
||||
tg.run([=]
|
||||
{
|
||||
const int iLast = std::min(i + TASK_GRANULARITY, trgHeight);
|
||||
xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
|
||||
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
|
||||
i, iLast, [](uint32_t pix) { return pix; });
|
||||
});
|
||||
tg.wait();
|
||||
}
|
||||
|
||||
|
||||
//Perf: AMP vs CPU: merely ~10% shorter runtime (scaling 1280x800 -> 1920x1080)
|
||||
//#include <amp.h>
|
||||
void bilinearScaleAmp(const uint32_t* src, int srcWidth, int srcHeight, //throw concurrency::runtime_exception
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
//C++ AMP reference: https://msdn.microsoft.com/en-us/library/hh289390.aspx
|
||||
//introduction to C++ AMP: https://msdn.microsoft.com/en-us/magazine/hh882446.aspx
|
||||
using namespace concurrency;
|
||||
//TODO: pitch
|
||||
|
||||
if (srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
const float scaleX = static_cast<float>(trgWidth ) / srcWidth;
|
||||
const float scaleY = static_cast<float>(trgHeight) / srcHeight;
|
||||
|
||||
array_view<const uint32_t, 2> srcView(srcHeight, srcWidth, src);
|
||||
array_view< uint32_t, 2> trgView(trgHeight, trgWidth, trg);
|
||||
trgView.discard_data();
|
||||
|
||||
parallel_for_each(trgView.extent, [=](index<2> idx) restrict(amp) //throw ?
|
||||
{
|
||||
const int y = idx[0];
|
||||
const int x = idx[1];
|
||||
//Perf notes:
|
||||
// -> float-based calculation is (almost) 2x as fas as double!
|
||||
// -> no noticeable improvement via tiling: https://msdn.microsoft.com/en-us/magazine/hh882447.aspx
|
||||
// -> no noticeable improvement with restrict(amp,cpu)
|
||||
// -> iterating over y-axis only is significantly slower!
|
||||
// -> pre-calculating x,y-dependent variables in a buffer + array_view<> is ~ 20 % slower!
|
||||
const int y1 = srcHeight * y / trgHeight;
|
||||
int y2 = y1 + 1;
|
||||
if (y2 == srcHeight) --y2;
|
||||
|
||||
const float yy1 = y / scaleY - y1;
|
||||
const float y2y = 1 - yy1;
|
||||
//-------------------------------------
|
||||
const int x1 = srcWidth * x / trgWidth;
|
||||
int x2 = x1 + 1;
|
||||
if (x2 == srcWidth) --x2;
|
||||
|
||||
const float xx1 = x / scaleX - x1;
|
||||
const float x2x = 1 - xx1;
|
||||
//-------------------------------------
|
||||
const float x2xy2y = x2x * y2y;
|
||||
const float xx1y2y = xx1 * y2y;
|
||||
const float x2xyy1 = x2x * yy1;
|
||||
const float xx1yy1 = xx1 * yy1;
|
||||
|
||||
auto interpolate = [=](int offset)
|
||||
{
|
||||
/*
|
||||
https://en.wikipedia.org/wiki/Bilinear_interpolation
|
||||
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
|
||||
(c12(x2 - x) + c22(x - x1)) * (y - y1)
|
||||
*/
|
||||
const auto c11 = (srcView(y1, x1) >> (8 * offset)) & 0xff;
|
||||
const auto c21 = (srcView(y1, x2) >> (8 * offset)) & 0xff;
|
||||
const auto c12 = (srcView(y2, x1) >> (8 * offset)) & 0xff;
|
||||
const auto c22 = (srcView(y2, x2) >> (8 * offset)) & 0xff;
|
||||
|
||||
return c11 * x2xy2y + c21 * xx1y2y +
|
||||
c12 * x2xyy1 + c22 * xx1yy1;
|
||||
};
|
||||
|
||||
const float bi = interpolate(0);
|
||||
const float gi = interpolate(1);
|
||||
const float ri = interpolate(2);
|
||||
const float ai = interpolate(3);
|
||||
|
||||
const auto b = static_cast<uint32_t>(bi + 0.5f);
|
||||
const auto g = static_cast<uint32_t>(gi + 0.5f);
|
||||
const auto r = static_cast<uint32_t>(ri + 0.5f);
|
||||
const auto a = static_cast<uint32_t>(ai + 0.5f);
|
||||
|
||||
trgView(y, x) = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
});
|
||||
trgView.synchronize(); //throw ?
|
||||
}
|
||||
#endif
|
||||
+13
-11
@@ -1,13 +1,14 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
@@ -21,10 +22,6 @@
|
||||
#include <limits>
|
||||
#include "xbrz_config.h"
|
||||
|
||||
// Guard against msvc stupidity
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace xbrz
|
||||
{
|
||||
@@ -47,11 +44,13 @@ enum class ColorFormat //from high bits -> low bits, 8 bit per channel
|
||||
{
|
||||
RGB, //8 bit for each red, green, blue, upper 8 bits unused
|
||||
ARGB, //including alpha channel, BGRA byte order on little-endian machines
|
||||
ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
|
||||
};
|
||||
|
||||
const int SCALE_FACTOR_MAX = 6;
|
||||
|
||||
/*
|
||||
-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only
|
||||
-> support for source/target pitch in bytes!
|
||||
-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image:
|
||||
Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis)
|
||||
CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition
|
||||
@@ -60,12 +59,15 @@ enum class ColorFormat //from high bits -> low bits, 8 bit per channel
|
||||
THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap!
|
||||
- there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows
|
||||
*/
|
||||
void scale(size_t factor, //valid range: 2 - 6
|
||||
void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
|
||||
const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
|
||||
ColorFormat colFmt,
|
||||
const ScalerCfg& cfg = ScalerCfg(),
|
||||
int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
|
||||
|
||||
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight);
|
||||
|
||||
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight);
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
@@ -24,6 +25,7 @@ struct ScalerCfg
|
||||
{
|
||||
double luminanceWeight = 1;
|
||||
double equalColorTolerance = 30;
|
||||
double centerDirectionBias = 4;
|
||||
double dominantDirectionThreshold = 3.6;
|
||||
double steepDirectionThreshold = 2.2;
|
||||
double newTestAttribute = 0; //unused; test new parameters
|
||||
|
||||
+182
-71
@@ -1,13 +1,14 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
@@ -25,14 +26,14 @@ namespace xbrz
|
||||
{
|
||||
template <uint32_t N> inline
|
||||
unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); }
|
||||
// Note that we use RGBA, not ARGB
|
||||
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
|
||||
inline unsigned char getRed (uint32_t pix) { return getByte<0>(pix); }
|
||||
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
|
||||
inline unsigned char getBlue (uint32_t pix) { return getByte<2>(pix); }
|
||||
|
||||
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (b << 16) | (g << 8) | r; }
|
||||
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return 0xFF000000 | (b << 16) | (g << 8) | r; }
|
||||
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
|
||||
inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); }
|
||||
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
|
||||
inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); }
|
||||
|
||||
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; }
|
||||
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; }
|
||||
|
||||
inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); }
|
||||
inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); }
|
||||
@@ -55,7 +56,7 @@ Pix* byteAdvance(Pix* ptr, int bytes)
|
||||
|
||||
//fill block with the given color
|
||||
template <class Pix> inline
|
||||
void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
|
||||
void fillBlock(Pix* trg, int pitch /*[bytes]*/, Pix col, int blockWidth, int blockHeight)
|
||||
{
|
||||
//for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
|
||||
// std::fill(trg, trg + blockWidth, col);
|
||||
@@ -66,16 +67,11 @@ void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
|
||||
}
|
||||
|
||||
|
||||
enum class SliceType
|
||||
{
|
||||
SOURCE,
|
||||
TARGET,
|
||||
};
|
||||
|
||||
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
|
||||
template <class PixSrc, class PixTrg, class PixConverter>
|
||||
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
|
||||
SliceType st, int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
|
||||
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
|
||||
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
|
||||
{
|
||||
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
|
||||
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
|
||||
@@ -89,65 +85,180 @@ void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int sr
|
||||
return;
|
||||
}
|
||||
|
||||
switch (st)
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, trgHeight);
|
||||
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
{
|
||||
case SliceType::SOURCE:
|
||||
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, srcHeight);
|
||||
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
|
||||
const int ySrc = srcHeight * y / trgHeight;
|
||||
const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch);
|
||||
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
const int xSrc = srcWidth * x / trgWidth;
|
||||
trgLine[x] = pixCvrt(srcLine[xSrc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
|
||||
template <class PixSrc, class PixTrg, class PixConverter>
|
||||
void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
|
||||
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
|
||||
{
|
||||
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
|
||||
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
|
||||
|
||||
static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
|
||||
|
||||
if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
|
||||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, srcHeight);
|
||||
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
{
|
||||
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
|
||||
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
|
||||
|
||||
//keep within for loop to support MT input slices!
|
||||
const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
|
||||
const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
|
||||
const int blockHeight = yTrgLast - yTrgFirst;
|
||||
|
||||
if (blockHeight > 0)
|
||||
{
|
||||
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
|
||||
/**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch);
|
||||
int xTrgFirst = 0;
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x)
|
||||
{
|
||||
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
|
||||
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
|
||||
|
||||
//keep within for loop to support MT input slices!
|
||||
const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
|
||||
const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
|
||||
const int blockHeight = yTrg_last - yTrg_first;
|
||||
|
||||
if (blockHeight > 0)
|
||||
const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
|
||||
const int blockWidth = xTrgLast - xTrgFirst;
|
||||
if (blockWidth > 0)
|
||||
{
|
||||
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
|
||||
/**/ PixTrg* trgLine = byteAdvance(trg, yTrg_first * trgPitch);
|
||||
int xTrg_first = 0;
|
||||
xTrgFirst = xTrgLast;
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x)
|
||||
{
|
||||
const int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
|
||||
const int blockWidth = xTrg_last - xTrg_first;
|
||||
if (blockWidth > 0)
|
||||
{
|
||||
xTrg_first = xTrg_last;
|
||||
|
||||
const auto trgPix = pixCvrt(srcLine[x]);
|
||||
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
|
||||
trgLine += blockWidth;
|
||||
}
|
||||
}
|
||||
const auto trgPix = pixCvrt(srcLine[x]);
|
||||
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
|
||||
trgLine += blockWidth;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case SliceType::TARGET:
|
||||
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, trgHeight);
|
||||
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
template <class PixTrg, class PixConverter>
|
||||
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
|
||||
int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/)
|
||||
{
|
||||
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
|
||||
static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format");
|
||||
|
||||
if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) ||
|
||||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, trgHeight);
|
||||
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
const double scaleX = static_cast<double>(trgWidth ) / srcWidth;
|
||||
const double scaleY = static_cast<double>(trgHeight) / srcHeight;
|
||||
|
||||
//perf notes:
|
||||
// -> double-based calculation is (slightly) faster than float
|
||||
// -> pre-calculation gives significant boost; std::vector<> memory allocation is negligible!
|
||||
struct CoeffsX
|
||||
{
|
||||
int x1 = 0;
|
||||
int x2 = 0;
|
||||
double xx1 = 0;
|
||||
double x2x = 0;
|
||||
};
|
||||
std::vector<CoeffsX> buf(trgWidth);
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
const int x1 = srcWidth * x / trgWidth;
|
||||
int x2 = x1 + 1;
|
||||
if (x2 == srcWidth) --x2;
|
||||
|
||||
const double xx1 = x / scaleX - x1;
|
||||
const double x2x = 1 - xx1;
|
||||
|
||||
buf[x] = { x1, x2, xx1, x2x };
|
||||
}
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
{
|
||||
const int y1 = srcHeight * y / trgHeight;
|
||||
int y2 = y1 + 1;
|
||||
if (y2 == srcHeight) --y2;
|
||||
|
||||
const double yy1 = y / scaleY - y1;
|
||||
const double y2y = 1 - yy1;
|
||||
|
||||
const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch);
|
||||
const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch);
|
||||
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
|
||||
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
//perf: do NOT "simplify" the variable layout without measurement!
|
||||
const int x1 = buf[x].x1;
|
||||
const int x2 = buf[x].x2;
|
||||
const double xx1 = buf[x].xx1;
|
||||
const double x2x = buf[x].x2x;
|
||||
|
||||
const double x2xy2y = x2x * y2y;
|
||||
const double xx1y2y = xx1 * y2y;
|
||||
const double x2xyy1 = x2x * yy1;
|
||||
const double xx1yy1 = xx1 * yy1;
|
||||
|
||||
auto interpolate = [=](int offset)
|
||||
{
|
||||
PixTrg* trgLine = byteAdvance(trg, y * trgPitch);
|
||||
const int ySrc = srcHeight * y / trgHeight;
|
||||
const PixSrc* srcLine = byteAdvance(src, ySrc * srcPitch);
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
const int xSrc = srcWidth * x / trgWidth;
|
||||
trgLine[x] = pixCvrt(srcLine[xSrc]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* https://en.wikipedia.org/wiki/Bilinear_interpolation
|
||||
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
|
||||
(c12(x2 - x) + c22(x - x1)) * (y - y1) */
|
||||
const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff;
|
||||
const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff;
|
||||
const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff;
|
||||
const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff;
|
||||
|
||||
return c11 * x2xy2y + c21 * xx1y2y +
|
||||
c12 * x2xyy1 + c22 * xx1yy1;
|
||||
};
|
||||
|
||||
const double bi = interpolate(0);
|
||||
const double gi = interpolate(1);
|
||||
const double ri = interpolate(2);
|
||||
const double ai = interpolate(3);
|
||||
|
||||
const auto b = static_cast<uint32_t>(bi + 0.5);
|
||||
const auto g = static_cast<uint32_t>(gi + 0.5);
|
||||
const auto r = static_cast<uint32_t>(ri + 0.5);
|
||||
const auto a = static_cast<uint32_t>(ai + 0.5);
|
||||
|
||||
const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
|
||||
trgLine[x] = pixCvrt(trgPix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Submodule
+1
Submodule core/deps/xbyak added at 0d67fd1530.
-11
@@ -1,11 +0,0 @@
|
||||
name: test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: sudo apt install nasm yasm g++-multilib tcsh
|
||||
- run: make test
|
||||
@@ -1 +0,0 @@
|
||||
/build* # cmake
|
||||
@@ -1,42 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.5...4.0.0)
|
||||
|
||||
project(xbyak CXX)
|
||||
|
||||
file(GLOB headers xbyak/*.h)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME} INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}-targets
|
||||
)
|
||||
|
||||
configure_file(
|
||||
cmake/config.cmake.in
|
||||
${PROJECT_NAME}Config.cmake
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(
|
||||
EXPORT ${PROJECT_NAME}-targets
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${headers}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xbyak
|
||||
)
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
Copyright (c) 2007 MITSUNARI Shigeo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Neither the name of the copyright owner nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-----------------------------------------------------------------------------
|
||||
ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満た
|
||||
す場合に限り、再頒布および使用が許可されます。
|
||||
|
||||
ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項
|
||||
を含めること。
|
||||
バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作
|
||||
権表示、本条件一覧、および下記免責条項を含めること。
|
||||
書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進
|
||||
に、著作権者の名前またはコントリビューターの名前を使用してはならない。
|
||||
本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供さ
|
||||
れており、明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性
|
||||
に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。
|
||||
著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを
|
||||
問わず、かつ責任の根拠が契約であるか厳格責任であるか(過失その他の)不法行為で
|
||||
あるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、
|
||||
本ソフトウェアの使用によって発生した(代替品または代用サービスの調達、使用の
|
||||
喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない)直接
|
||||
損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、
|
||||
一切責任を負わないものとします。
|
||||
@@ -1,24 +0,0 @@
|
||||
PREFIX?=/usr/local
|
||||
INSTALL_DIR=$(PREFIX)/include/xbyak
|
||||
|
||||
all:
|
||||
$(MAKE) -C sample
|
||||
|
||||
clean:
|
||||
$(MAKE) -C sample clean
|
||||
|
||||
install:
|
||||
mkdir -p $(INSTALL_DIR)
|
||||
cp -pR xbyak/*.h $(INSTALL_DIR)
|
||||
|
||||
uninstall:
|
||||
rm -i $(INSTALL_DIR)/*.h
|
||||
rmdir $(INSTALL_DIR)
|
||||
|
||||
update:
|
||||
$(MAKE) -C gen
|
||||
|
||||
test:
|
||||
$(MAKE) -C test test
|
||||
|
||||
.PHONY: test update
|
||||
@@ -1 +0,0 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
|
||||
@@ -1,26 +0,0 @@
|
||||
TARGET=../xbyak/xbyak_mnemonic.h
|
||||
BIN=sortline gen_code gen_avx512
|
||||
CFLAGS=-I../ -O2 -DXBYAK_NO_OP_NAMES -Wall -Wextra -Wno-missing-field-initializers
|
||||
all: $(TARGET)
|
||||
sortline: sortline.cpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
gen_code: gen_code.cpp ../xbyak/xbyak.h avx_type.hpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
gen_avx512: gen_avx512.cpp ../xbyak/xbyak.h avx_type.hpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
|
||||
$(TARGET): $(BIN)
|
||||
./gen_code | ./sortline > $@
|
||||
echo "#ifdef XBYAK_ENABLE_OMITTED_OPERAND" >> $@
|
||||
./gen_code omit | ./sortline >> $@
|
||||
echo "#endif" >>$@
|
||||
./gen_code fixed >> $@
|
||||
echo "#ifndef XBYAK_DISABLE_AVX512" >> $@
|
||||
./gen_avx512 | ./sortline >> $@
|
||||
echo "#ifdef XBYAK64" >> $@
|
||||
./gen_avx512 64 | ./sortline >> $@
|
||||
echo "#endif" >> $@
|
||||
echo "#endif" >> $@
|
||||
|
||||
clean:
|
||||
$(RM) $(BIN) $(TARGET)
|
||||
@@ -1,170 +0,0 @@
|
||||
#include <assert.h>
|
||||
// copy CodeGenerator::AVXtype
|
||||
enum AVXtype {
|
||||
// low 3 bit
|
||||
T_N1 = 1,
|
||||
T_N2 = 2,
|
||||
T_N4 = 3,
|
||||
T_N8 = 4,
|
||||
T_N16 = 5,
|
||||
T_N32 = 6,
|
||||
T_NX_MASK = 7,
|
||||
//
|
||||
T_N_VL = 1 << 3, // N * (1, 2, 4) for VL
|
||||
T_DUP = 1 << 4, // N = (8, 32, 64)
|
||||
T_66 = 1 << 5,
|
||||
T_F3 = 1 << 6,
|
||||
T_F2 = 1 << 7,
|
||||
T_0F = 1 << 8,
|
||||
T_0F38 = 1 << 9,
|
||||
T_0F3A = 1 << 10,
|
||||
T_L0 = 1 << 11,
|
||||
T_L1 = 1 << 12,
|
||||
T_W0 = 1 << 13,
|
||||
T_W1 = 1 << 14,
|
||||
T_EW0 = 1 << 15,
|
||||
T_EW1 = 1 << 16,
|
||||
T_YMM = 1 << 17, // support YMM, ZMM
|
||||
T_EVEX = 1 << 18,
|
||||
T_ER_X = 1 << 19, // xmm{er}
|
||||
T_ER_Y = 1 << 20, // ymm{er}
|
||||
T_ER_Z = 1 << 21, // zmm{er}
|
||||
T_SAE_X = 1 << 22, // xmm{sae}
|
||||
T_SAE_Y = 1 << 23, // ymm{sae}
|
||||
T_SAE_Z = 1 << 24, // zmm{sae}
|
||||
T_MUST_EVEX = 1 << 25, // contains T_EVEX
|
||||
T_B32 = 1 << 26, // m32bcst
|
||||
T_B64 = 1 << 27, // m64bcst
|
||||
T_M_K = 1 << 28, // mem{k}
|
||||
T_VSIB = 1 << 29,
|
||||
T_MEM_EVEX = 1 << 30, // use evex if mem
|
||||
T_XXX
|
||||
};
|
||||
|
||||
const int NONE = 256; // same as Xbyak::CodeGenerator::NONE
|
||||
|
||||
std::string type2String(int type)
|
||||
{
|
||||
std::string str;
|
||||
int low = type & T_NX_MASK;
|
||||
if (0 < low) {
|
||||
const char *tbl[8] = {
|
||||
"T_N1", "T_N2", "T_N4", "T_N8", "T_N16", "T_N32"
|
||||
};
|
||||
assert(low < int(sizeof(tbl) / sizeof(tbl[0])));
|
||||
str = tbl[low - 1];
|
||||
}
|
||||
if (type & T_N_VL) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_N_VL";
|
||||
}
|
||||
if (type & T_DUP) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_DUP";
|
||||
}
|
||||
if (type & T_66) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_66";
|
||||
}
|
||||
if (type & T_F3) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_F3";
|
||||
}
|
||||
if (type & T_F2) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_F2";
|
||||
}
|
||||
if (type & T_0F) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_0F";
|
||||
}
|
||||
if (type & T_0F38) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_0F38";
|
||||
}
|
||||
if (type & T_0F3A) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_0F3A";
|
||||
}
|
||||
if (type & T_L0) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "VEZ_L0";
|
||||
}
|
||||
if (type & T_L1) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "VEZ_L1";
|
||||
}
|
||||
if (type & T_W0) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_W0";
|
||||
}
|
||||
if (type & T_W1) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_W1";
|
||||
}
|
||||
if (type & T_EW0) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_EW0";
|
||||
}
|
||||
if (type & T_EW1) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_EW1";
|
||||
}
|
||||
if (type & T_YMM) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_YMM";
|
||||
}
|
||||
if (type & T_EVEX) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_EVEX";
|
||||
}
|
||||
if (type & T_ER_X) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_ER_X";
|
||||
}
|
||||
if (type & T_ER_Y) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_ER_Y";
|
||||
}
|
||||
if (type & T_ER_Z) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_ER_Z";
|
||||
}
|
||||
if (type & T_SAE_X) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_SAE_X";
|
||||
}
|
||||
if (type & T_SAE_Y) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_SAE_Y";
|
||||
}
|
||||
if (type & T_SAE_Z) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_SAE_Z";
|
||||
}
|
||||
if (type & T_MUST_EVEX) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_MUST_EVEX";
|
||||
}
|
||||
if (type & T_B32) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_B32";
|
||||
}
|
||||
if (type & T_B64) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_B64";
|
||||
}
|
||||
if (type & T_M_K) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_M_K";
|
||||
}
|
||||
if (type & T_VSIB) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_VSIB";
|
||||
}
|
||||
if (type & T_MEM_EVEX) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_MEM_EVEX";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
puts("enum {");
|
||||
for (int i = 0; i < 256; i++) {
|
||||
printf(" B");
|
||||
for (int j = 0; j < 8; j++) {
|
||||
putchar(i & (1 << (7 - j)) ? '1' : '0');
|
||||
}
|
||||
printf("= %d", i);
|
||||
if (i < 255) putchar(',');
|
||||
putchar('\n');
|
||||
}
|
||||
puts("};");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,750 +0,0 @@
|
||||
#define XBYAK_DONT_READ_LIST
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../xbyak/xbyak.h"
|
||||
#define NUM_OF_ARRAY(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
using namespace Xbyak;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996) // scanf
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
|
||||
#include "avx_type.hpp"
|
||||
|
||||
void putOpmask(bool only64bit)
|
||||
{
|
||||
if (only64bit) {
|
||||
puts("void kmovq(const Opmask& k, const Reg64& r) { opVex(k, 0, r, T_L0 | T_0F | T_F2 | T_W1, 0x92); }");
|
||||
puts("void kmovq(const Reg64& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_F2 | T_W1, 0x93); }");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
} tbl[] = {
|
||||
{ "kadd", 0x4A },
|
||||
{ "kand", 0x41 },
|
||||
{ "kandn", 0x42 },
|
||||
{ "kor", 0x45 },
|
||||
{ "kxnor", 0x46 },
|
||||
{ "kxor", 0x47 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
printf("void %sw(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sq(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sb(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_66 | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sd(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_66 | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
}
|
||||
printf("void kunpckbw(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_66 | T_W0, 0x4B); }\n");
|
||||
printf("void kunpckwd(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W0, 0x4B); }\n");
|
||||
printf("void kunpckdq(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W1, 0x4B); }\n");
|
||||
}
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
} tbl[] = {
|
||||
{ "knot", 0x44 },
|
||||
{ "kortest", 0x98 },
|
||||
{ "ktest", 0x99 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
printf("void %sw(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sq(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sb(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_66 | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sd(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_66 | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
}
|
||||
}
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
} tbl[] = {
|
||||
{ "kshiftl", 0x32 },
|
||||
{ "kshiftr", 0x30 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
printf("void %sw(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code);
|
||||
printf("void %sq(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code + 1);
|
||||
printf("void %sb(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code);
|
||||
printf("void %sd(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code + 1);
|
||||
}
|
||||
}
|
||||
puts("void kmovw(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_W0, 0x90); }");
|
||||
puts("void kmovq(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_W1, 0x90); }");
|
||||
puts("void kmovb(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W0, 0x90); }");
|
||||
puts("void kmovd(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W1, 0x90); }");
|
||||
|
||||
puts("void kmovw(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W0, 0x91); }");
|
||||
puts("void kmovq(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W1, 0x91); }");
|
||||
puts("void kmovb(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_66 | T_W0, 0x91); }");
|
||||
puts("void kmovd(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_66 | T_W1, 0x91); }");
|
||||
|
||||
puts("void kmovw(const Opmask& k, const Reg32& r) { opVex(k, 0, r, T_L0 | T_0F | T_W0, 0x92); }");
|
||||
puts("void kmovw(const Reg32& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_W0, 0x93); }");
|
||||
puts("void kmovb(const Opmask& k, const Reg32& r) { opVex(k, 0, r, T_L0 | T_0F | T_66 | T_W0, 0x92); }");
|
||||
puts("void kmovb(const Reg32& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_66 | T_W0, 0x93); }");
|
||||
puts("void kmovd(const Opmask& k, const Reg32& r) { opVex(k, 0, r, T_L0 | T_0F | T_F2 | T_W0, 0x92); }");
|
||||
puts("void kmovd(const Reg32& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_F2 | T_W0, 0x93); }");
|
||||
}
|
||||
|
||||
// vcmppd(k, x, op)
|
||||
void putVcmp()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
bool hasIMM;
|
||||
} tbl[] = {
|
||||
{ 0xC2, "vcmppd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_YMM | T_66 | T_B64, true },
|
||||
{ 0xC2, "vcmpps", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_YMM | T_B32, true },
|
||||
{ 0xC2, "vcmpsd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_F2 | T_N8, true },
|
||||
{ 0xC2, "vcmpss", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_F3 | T_N4, true },
|
||||
|
||||
{ 0x74, "vpcmpeqb", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x75, "vpcmpeqw", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x76, "vpcmpeqd", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_B32, false },
|
||||
{ 0x29, "vpcmpeqq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x64, "vpcmpgtb", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x65, "vpcmpgtw", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x66, "vpcmpgtd", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0x37, "vpcmpgtq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x3F, "vpcmpb", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0, true },
|
||||
{ 0x3E, "vpcmpub", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0, true },
|
||||
|
||||
{ 0x3F, "vpcmpw", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1, true },
|
||||
{ 0x3E, "vpcmpuw", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1, true },
|
||||
{ 0x1F, "vpcmpd", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, true },
|
||||
{ 0x1E, "vpcmpud", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, true },
|
||||
{ 0x1F, "vpcmpq", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, true },
|
||||
{ 0x1E, "vpcmpuq", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, true },
|
||||
|
||||
{ 0x26, "vptestmb", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, false },
|
||||
{ 0x26, "vptestmw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x27, "vptestmd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0x27, "vptestmq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x26, "vptestnmb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, false },
|
||||
{ 0x26, "vptestnmw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x27, "vptestnmd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0x27, "vptestnmq", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Opmask& k, const Xmm& x, const Operand& op%s) { opAVX_K_X_XM(k, x, op, %s, 0x%02X%s); }\n"
|
||||
, p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
|
||||
}
|
||||
}
|
||||
|
||||
// XM_X
|
||||
void putX_XM()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ 0x6F, "vmovdqa32", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqa64", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu8", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu16", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu32", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu64", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x7B, "vcvtpd2qq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_ER_Z },
|
||||
// putCvt
|
||||
{ 0x79, "vcvtpd2uqq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_ER_Z },
|
||||
{ 0x79, "vcvtps2udq", T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_ER_Z },
|
||||
{ 0xE6, "vcvtqq2pd", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_ER_Z },
|
||||
{ 0x7A, "vcvttpd2qq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z },
|
||||
{ 0x78, "vcvttpd2uqq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z },
|
||||
{ 0x78, "vcvttps2udq", T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z },
|
||||
{ 0x7A, "vcvtudq2ps", T_F2 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_ER_Z },
|
||||
{ 0x7A, "vcvtuqq2pd", T_F3 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z },
|
||||
|
||||
{ 0x88, "vexpandpd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
{ 0x88, "vexpandps", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
|
||||
{ 0x89, "vpexpandd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
{ 0x89, "vpexpandq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
{ 0x42, "vgetexppd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z },
|
||||
{ 0x42, "vgetexpps", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Xmm& x, const Operand& op) { opAVX_X_XM_IMM(x, op, %s, 0x%02X); }\n", p->name, type.c_str(), p->code);
|
||||
}
|
||||
puts("void vpabsq(const Xmm& x, const Operand& op) { opAVX_X_XM_IMM(x, op, T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_B64 | T_YMM, 0x1F); }");
|
||||
|
||||
puts("void vexp2pd(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, 0xC8); }");
|
||||
puts("void vexp2ps(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, 0xC8); }");
|
||||
puts("void vrcp28pd(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, 0xCA); }");
|
||||
puts("void vrcp28ps(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, 0xCA); }");
|
||||
|
||||
puts("void vrsqrt28pd(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, 0xCC); }");
|
||||
puts("void vrsqrt28ps(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, 0xCC); }");
|
||||
}
|
||||
|
||||
void putM_X()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ 0x7F, "vmovdqa32", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqa64", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu8", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu16", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu32", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu64", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Address& addr, const Xmm& x) { opAVX_X_XM_IMM(x, addr, %s, 0x%02X); }\n", p->name, type.c_str(), p->code);
|
||||
}
|
||||
}
|
||||
|
||||
void putXM_X()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ 0x8A, "vcompresspd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
{ 0x8A, "vcompressps", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
|
||||
{ 0x8B, "vpcompressd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
{ 0x8B, "vpcompressq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
|
||||
{ 0x63, "vcompressb", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N1 },
|
||||
{ 0x63, "vcompressw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N2 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Operand& op, const Xmm& x) { opAVX_X_XM_IMM(x, op, %s, 0x%02X); }\n", p->name, type.c_str(), p->code);
|
||||
}
|
||||
}
|
||||
|
||||
void putX_X_XM_IMM()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
bool hasIMM;
|
||||
} tbl[] = {
|
||||
{ 0x03, "valignd", T_MUST_EVEX | T_66 | T_0F3A | T_EW0 | T_YMM, true },
|
||||
{ 0x03, "valignq", T_MUST_EVEX | T_66 | T_0F3A | T_EW1 | T_YMM, true },
|
||||
{ 0xDB, "vpandd", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW0 | T_B32, false },
|
||||
{ 0xDB, "vpandq", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW1 | T_B64, false },
|
||||
{ 0xDF, "vpandnd", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW0 | T_B32, false },
|
||||
{ 0xDF, "vpandnq", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW1 | T_B64, false },
|
||||
{ 0x3D, "vpmaxsq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x3F, "vpmaxuq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x39, "vpminsq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x3B, "vpminuq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0xE2, "vpsraq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_N16, false },
|
||||
{ 0x46, "vpsravq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x11, "vpsravw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x12, "vpsllvw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x10, "vpsrlvw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0xEB, "vpord", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0xEB, "vporq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0xEF, "vpxord", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0xEF, "vpxorq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x40, "vpmullq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x8D, "vpermb", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, false },
|
||||
{ 0x8D, "vpermw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
|
||||
{ 0x65, "vblendmpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x65, "vblendmps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x66, "vpblendmb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, false },
|
||||
{ 0x66, "vpblendmw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, false },
|
||||
{ 0x64, "vpblendmd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x64, "vpblendmq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x7D, "vpermt2b", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, false },
|
||||
{ 0x7D, "vpermt2w", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, false },
|
||||
{ 0x7E, "vpermt2d", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x7E, "vpermt2q", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x7F, "vpermt2ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x7F, "vpermt2pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x75, "vpermi2b", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, false },
|
||||
{ 0x75, "vpermi2w", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, false },
|
||||
{ 0x76, "vpermi2d", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x76, "vpermi2q", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x77, "vpermi2ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x77, "vpermi2pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x25, "vpternlogd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, true },
|
||||
{ 0x25, "vpternlogq", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, true },
|
||||
|
||||
{ 0x43, "vgetexpsd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, false },
|
||||
{ 0x43, "vgetexpss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, false },
|
||||
{ 0x27, "vgetmantsd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, true },
|
||||
{ 0x27, "vgetmantss", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, true },
|
||||
|
||||
{ 0x54, "vfixupimmpd", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x54, "vfixupimmps", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
{ 0x55, "vfixupimmsd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_N8, true },
|
||||
{ 0x55, "vfixupimmss", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_N4, true },
|
||||
|
||||
{ 0x4D, "vrcp14sd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_N8, false },
|
||||
{ 0x4D, "vrcp14ss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_N4, false },
|
||||
|
||||
{ 0x4F, "vrsqrt14sd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, false },
|
||||
{ 0x4F, "vrsqrt14ss", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, false },
|
||||
|
||||
{ 0x0B, "vrndscalesd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_N8, true },
|
||||
{ 0x0A, "vrndscaless", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_N4, true },
|
||||
|
||||
{ 0x2C, "vscalefpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, false },
|
||||
{ 0x2C, "vscalefps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_ER_Z, false },
|
||||
{ 0x2D, "vscalefsd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_ER_X | T_N8, false },
|
||||
{ 0x2D, "vscalefss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_ER_X | T_N4, false },
|
||||
|
||||
{ 0x42, "vdbpsadbw", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0, true },
|
||||
{ 0x83, "vpmultishiftqb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x15, "vprolvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x15, "vprolvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x14, "vprorvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x14, "vprorvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0xCB, "vrcp28sd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_N8 | T_SAE_X, false },
|
||||
{ 0xCB, "vrcp28ss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_N4 | T_SAE_X, false },
|
||||
|
||||
{ 0xCD, "vrsqrt28sd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_N8 | T_SAE_X, false },
|
||||
{ 0xCD, "vrsqrt28ss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_N4 | T_SAE_X, false },
|
||||
|
||||
{ 0x50, "vrangepd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x50, "vrangeps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
{ 0x51, "vrangesd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, true },
|
||||
{ 0x51, "vrangess", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, true },
|
||||
|
||||
{ 0x57, "vreducesd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, true },
|
||||
{ 0x57, "vreducess", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, true },
|
||||
|
||||
{ 0xB4, "vpmadd52luq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0xB5, "vpmadd52huq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x70, "vpshldw", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, true },
|
||||
{ 0x71, "vpshldd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, true },
|
||||
{ 0x71, "vpshldq", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, true },
|
||||
|
||||
{ 0x70, "vpshldvw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, false },
|
||||
{ 0x71, "vpshldvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x71, "vpshldvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, false },
|
||||
|
||||
{ 0x72, "vpshrdw", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, true },
|
||||
{ 0x73, "vpshrdd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, true },
|
||||
{ 0x73, "vpshrdq", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, true },
|
||||
|
||||
{ 0x72, "vpshrdvw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, false },
|
||||
{ 0x73, "vpshrdvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x73, "vpshrdvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, false },
|
||||
|
||||
{ 0x72, "vcvtne2ps2bf16", T_F2 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x52, "vdpbf16ps", T_F3 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Xmm& x1, const Xmm& x2, const Operand& op%s) { opAVX_X_X_XM(x1, x2, op, %s, 0x%02X%s); }\n"
|
||||
, p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
|
||||
}
|
||||
}
|
||||
|
||||
void putShift()
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
int idx;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ "vpsraq", 0x72, 4, T_0F | T_66 | T_YMM | T_MUST_EVEX |T_EW1 | T_B64 },
|
||||
{ "vprold", 0x72, 1, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 },
|
||||
{ "vprolq", 0x72, 1, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 },
|
||||
{ "vprord", 0x72, 0, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 },
|
||||
{ "vprorq", 0x72, 0, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
printf("void %s(const Xmm& x, const Operand& op, uint8_t imm) { opAVX_X_X_XM(Xmm(x.getKind(), %d), x, op, %s, 0x%02X, imm); }\n", p.name, p.idx, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
|
||||
void putExtractInsert()
|
||||
{
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
int type;
|
||||
bool isZMM;
|
||||
} tbl[] = {
|
||||
{ "vextractf32x4", 0x19, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vextractf64x2", 0x19, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vextractf32x8", 0x1B, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vextractf64x4", 0x1B, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
|
||||
{ "vextracti32x4", 0x39, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vextracti64x2", 0x39, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vextracti32x8", 0x3B, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vextracti64x4", 0x3B, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
const char *kind = p.isZMM ? "Operand::MEM | Operand::YMM" : "Operand::MEM | Operand::XMM";
|
||||
printf("void %s(const Operand& op, const %s& r, uint8_t imm) { if (!op.is(%s)) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(r, 0, op, %s, 0x%2X, imm); }\n", p.name, p.isZMM ? "Zmm" : "Ymm", kind, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
int type;
|
||||
bool isZMM;
|
||||
} tbl[] = {
|
||||
{ "vinsertf32x4", 0x18, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vinsertf64x2", 0x18, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vinsertf32x8", 0x1A, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vinsertf64x4", 0x1A, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
|
||||
{ "vinserti32x4", 0x38, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vinserti64x2", 0x38, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vinserti32x8", 0x3A, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vinserti64x4", 0x3A, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
const char *x = p.isZMM ? "Zmm" : "Ymm";
|
||||
const char *cond = p.isZMM ? "op.is(Operand::MEM | Operand::YMM)" : "(r1.getKind() == r2.getKind() && op.is(Operand::MEM | Operand::XMM))";
|
||||
printf("void %s(const %s& r1, const %s& r2, const Operand& op, uint8_t imm) {"
|
||||
"if (!%s) XBYAK_THROW(ERR_BAD_COMBINATION) "
|
||||
"opVex(r1, &r2, op, %s, 0x%2X, imm); }\n", p.name, x, x, cond, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void putBroadcast(bool only64bit)
|
||||
{
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
int reg;
|
||||
} tbl[] = {
|
||||
{ 0x7A, "vpbroadcastb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 8 },
|
||||
{ 0x7B, "vpbroadcastw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 16 },
|
||||
{ 0x7C, "vpbroadcastd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 32 },
|
||||
{ 0x7C, "vpbroadcastq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, 64},
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
if ((only64bit && p.reg == 64) || (!only64bit && p.reg != 64)) {
|
||||
printf("void %s(const Xmm& x, const Reg%d& r) { opVex(x, 0, r, %s, 0x%02X); }\n", p.name, p.reg, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (only64bit) return;
|
||||
puts("void vbroadcastf32x2(const Ymm& y, const Operand& op) { opAVX_X_XM_IMM(y, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N8, 0x19); }");
|
||||
puts("void vbroadcastf32x4(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N16, 0x1A); }");
|
||||
puts("void vbroadcastf64x2(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N16, 0x1A); }");
|
||||
puts("void vbroadcastf64x4(const Zmm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N32, 0x1B); }");
|
||||
puts("void vbroadcastf32x8(const Zmm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N32, 0x1B); }");
|
||||
|
||||
puts("void vbroadcasti32x2(const Xmm& x, const Operand& op) { opAVX_X_XM_IMM(x, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N8, 0x59); }");
|
||||
puts("void vbroadcasti32x4(const Ymm& y, const Operand& op) { opAVX_X_XM_IMM(y, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N16, 0x5A); }");
|
||||
puts("void vbroadcasti64x2(const Ymm& y, const Operand& op) { opAVX_X_XM_IMM(y, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N16, 0x5A); }");
|
||||
puts("void vbroadcasti32x8(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N32, 0x5B); }");
|
||||
puts("void vbroadcasti64x4(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N32, 0x5B); }");
|
||||
}
|
||||
|
||||
void putCvt()
|
||||
{
|
||||
puts("void vcvtpd2udq(const Xmm& x, const Operand& op) { opCvt2(x, op, T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, 0x79); }");
|
||||
puts("void vcvtps2qq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_ER_Y, 0x7B); }");
|
||||
puts("void vcvtps2uqq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_ER_Y, 0x79); }");
|
||||
puts("void vcvtqq2ps(const Xmm& x, const Operand& op) { opCvt2(x, op, T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, 0x5B); }");
|
||||
puts("void vcvttpd2udq(const Xmm& x, const Operand& op) { opCvt2(x, op, T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, 0x78); }");
|
||||
puts("void vcvttps2qq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_SAE_Y, 0x7A); }");
|
||||
puts("void vcvttps2uqq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_SAE_Y, 0x78); }");
|
||||
puts("void vcvtudq2pd(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_F3 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL, 0x7A); }");
|
||||
|
||||
puts("void vcvtsd2usi(const Reg32e& r, const Operand& op) { int type = (T_F2 | T_0F | T_MUST_EVEX | T_N8 | T_ER_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x79); }");
|
||||
puts("void vcvtss2usi(const Reg32e& r, const Operand& op) { int type = (T_F3 | T_0F | T_MUST_EVEX | T_N4 | T_ER_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x79); }");
|
||||
puts("void vcvttsd2usi(const Reg32e& r, const Operand& op) { int type = (T_F2 | T_0F | T_MUST_EVEX | T_N8 | T_SAE_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x78); }");
|
||||
puts("void vcvttss2usi(const Reg32e& r, const Operand& op) { int type = (T_F3 | T_0F | T_MUST_EVEX | T_N4 | T_SAE_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x78); }");
|
||||
puts("void vcvtuqq2ps(const Xmm& x, const Operand& op) { opCvt2(x, op, T_F2 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, 0x7A); }");
|
||||
puts("void vcvtusi2sd(const Xmm& x1, const Xmm& x2, const Operand& op) { opCvt3(x1, x2, op, T_F2 | T_0F | T_MUST_EVEX, T_W1 | T_EW1 | T_ER_X | T_N8, T_W0 | T_EW0 | T_N4, 0x7B); }");
|
||||
puts("void vcvtusi2ss(const Xmm& x1, const Xmm& x2, const Operand& op) { opCvt3(x1, x2, op, T_F3 | T_0F | T_MUST_EVEX | T_ER_X, T_W1 | T_EW1 | T_N8, T_W0 | T_EW0 | T_N4, 0x7B); }");
|
||||
}
|
||||
|
||||
enum { // same as xbyak.h
|
||||
xx_yy_zz = 0,
|
||||
xx_yx_zy = 1,
|
||||
xx_xy_yz = 2,
|
||||
};
|
||||
void putGather()
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
int type;
|
||||
uint8_t code;
|
||||
int mode;
|
||||
} tbl[] = {
|
||||
{ "vpgatherdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x90, xx_yy_zz },
|
||||
{ "vpgatherdq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x90, xx_yx_zy },
|
||||
{ "vpgatherqd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x91, xx_xy_yz },
|
||||
{ "vpgatherqq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x91, xx_yy_zz },
|
||||
{ "vgatherdps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x92, xx_yy_zz },
|
||||
{ "vgatherdpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x92, xx_yx_zy },
|
||||
{ "vgatherqps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x93, xx_xy_yz },
|
||||
{ "vgatherqpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x93, xx_yy_zz },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type | T_VSIB);
|
||||
printf("void %s(const Xmm& x, const Address& addr) { opGather2(x, addr, %s, 0x%02X, %d); }\n", p.name, type.c_str(), p.code, p.mode);
|
||||
}
|
||||
}
|
||||
void putScatter()
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
int type;
|
||||
uint8_t code;
|
||||
int mode; // reverse of gather
|
||||
} tbl[] = {
|
||||
{ "vpscatterdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA0, xx_yy_zz },
|
||||
{ "vpscatterdq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA0, xx_yx_zy },
|
||||
{ "vpscatterqd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA1, xx_xy_yz },
|
||||
{ "vpscatterqq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA1, xx_yy_zz },
|
||||
|
||||
{ "vscatterdps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA2, xx_yy_zz },
|
||||
{ "vscatterdpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA2, xx_yx_zy },
|
||||
{ "vscatterqps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA3, xx_xy_yz },
|
||||
{ "vscatterqpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA3, xx_yy_zz },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type | T_VSIB);
|
||||
printf("void %s(const Address& addr, const Xmm& x) { opGather2(x, addr, %s, 0x%02X, %d); }\n", p.name, type.c_str(), p.code, p.mode);
|
||||
}
|
||||
}
|
||||
|
||||
void putShuff()
|
||||
{
|
||||
puts("void vshuff32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x23, imm); }");
|
||||
puts("void vshuff64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x23, imm); }");
|
||||
puts("void vshufi32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x43, imm); }");
|
||||
puts("void vshufi64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x43, imm); }");
|
||||
}
|
||||
|
||||
void putMov()
|
||||
{
|
||||
puts("void vpmovm2b(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x28); }");
|
||||
puts("void vpmovm2w(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x28); }");
|
||||
puts("void vpmovm2d(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x38); }");
|
||||
puts("void vpmovm2q(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x38); }");
|
||||
|
||||
puts("void vpmovb2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x29); }");
|
||||
puts("void vpmovw2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x29); }");
|
||||
puts("void vpmovd2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x39); }");
|
||||
puts("void vpmovq2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x39); }");
|
||||
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
int mode;
|
||||
} tbl[] = {
|
||||
{ 0x32, "vpmovqb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N2 | T_N_VL, false },
|
||||
{ 0x22, "vpmovsqb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N2 | T_N_VL, false },
|
||||
{ 0x12, "vpmovusqb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N2 | T_N_VL, false },
|
||||
|
||||
{ 0x34, "vpmovqw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x24, "vpmovsqw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x14, "vpmovusqw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
|
||||
{ 0x35, "vpmovqd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x25, "vpmovsqd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x15, "vpmovusqd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
|
||||
{ 0x31, "vpmovdb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x21, "vpmovsdb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x11, "vpmovusdb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
|
||||
{ 0x33, "vpmovdw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x23, "vpmovsdw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x13, "vpmovusdw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
|
||||
{ 0x30, "vpmovwb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x20, "vpmovswb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x10, "vpmovuswb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
printf("void %s(const Operand& op, const Xmm& x) { opVmov(op, x, %s, 0x%02X, %s); }\n", p.name, type.c_str(), p.code, p.mode ? "true" : "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void putX_XM_IMM()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
bool hasIMM;
|
||||
} tbl[] = {
|
||||
{ 0x26, "vgetmantpd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x26, "vgetmantps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
{ 0x4C, "vrcp14pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x4C, "vrcp14ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
|
||||
{ 0x4E, "vrsqrt14pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x4E, "vrsqrt14ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
|
||||
{ 0x09, "vrndscalepd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, true },
|
||||
{ 0x08, "vrndscaleps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, true },
|
||||
|
||||
{ 0xC4, "vpconflictd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0xC4, "vpconflictq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x44, "vplzcntd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x44, "vplzcntq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x56, "vreducepd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x56, "vreduceps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
|
||||
{ 0x54, "vpopcntb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z, false },
|
||||
{ 0x54, "vpopcntw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, false },
|
||||
{ 0x55, "vpopcntd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x55, "vpopcntq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, false },
|
||||
|
||||
{ 0x62, "vpexpandb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_N1, false },
|
||||
{ 0x62, "vpexpandw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_N2, false },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Xmm& x, const Operand& op%s) { opAVX_X_XM_IMM(x, op, %s, 0x%02X%s); }\n"
|
||||
, p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
|
||||
}
|
||||
}
|
||||
|
||||
void putMisc()
|
||||
{
|
||||
puts("void vpbroadcastmb2q(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, 0x2A); }");
|
||||
puts("void vpbroadcastmw2d(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 0x3A); }");
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
int zm;
|
||||
int type;
|
||||
uint8_t code;
|
||||
bool isZmm;
|
||||
} tbl[] = {
|
||||
{ "vgatherpf0dps", 1, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vgatherpf0qps", 1, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vgatherpf0dpd", 1, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vgatherpf0qpd", 1, T_EW1 | T_N8, 0xC7, true },
|
||||
|
||||
{ "vgatherpf1dps", 2, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vgatherpf1qps", 2, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vgatherpf1dpd", 2, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vgatherpf1qpd", 2, T_EW1 | T_N8, 0xC7, true },
|
||||
|
||||
{ "vscatterpf0dps", 5, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vscatterpf0qps", 5, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vscatterpf0dpd", 5, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vscatterpf0qpd", 5, T_EW1 | T_N8, 0xC7, true },
|
||||
|
||||
{ "vscatterpf1dps", 6, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vscatterpf1qps", 6, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vscatterpf1dpd", 6, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vscatterpf1qpd", 6, T_EW1 | T_N8, 0xC7, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type | T_66 | T_0F38 | T_MUST_EVEX | T_M_K | T_VSIB);
|
||||
printf("void %s(const Address& addr) { opGatherFetch(addr, zm%d, %s, 0x%2X, Operand::%s); }\n"
|
||||
, p.name, p.zm, type.c_str(), p.code, p.isZmm ? "ZMM" : "YMM");
|
||||
}
|
||||
}
|
||||
|
||||
puts("void vfpclasspd(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isBit(128|256|512)) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, 0x66, imm); }");
|
||||
puts("void vfpclassps(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isBit(128|256|512)) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, 0x66, imm); }");
|
||||
puts("void vfpclasssd(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isXMEM()) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_N8, 0x67, imm); }");
|
||||
puts("void vfpclassss(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isXMEM()) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_N4, 0x67, imm); }");
|
||||
|
||||
puts("void vpshufbitqmb(const Opmask& k, const Xmm& x, const Operand& op) { opVex(k, &x, op, T_66 | T_0F38 | T_EW0 | T_YMM | T_MUST_EVEX, 0x8F); }");
|
||||
puts("void vcvtneps2bf16(const Xmm& x, const Operand& op) { opCvt2(x, op, T_F3 | T_0F38 | T_EW0 | T_YMM | T_SAE_Z | T_MUST_EVEX | T_B32, 0x72); }");
|
||||
|
||||
puts("void vp2intersectd(const Opmask& k, const Xmm& x, const Operand& op) { if (k.getOpmaskIdx() != 0) XBYAK_THROW(ERR_OPMASK_IS_ALREADY_SET) opAVX_K_X_XM(k, x, op, T_F2 | T_0F38 | T_YMM | T_EVEX | T_EW0 | T_B32, 0x68); }");
|
||||
puts("void vp2intersectq(const Opmask& k, const Xmm& x, const Operand& op) { if (k.getOpmaskIdx() != 0) XBYAK_THROW(ERR_OPMASK_IS_ALREADY_SET) opAVX_K_X_XM(k, x, op, T_F2 | T_0F38 | T_YMM | T_EVEX | T_EW1 | T_B64, 0x68); }");
|
||||
}
|
||||
|
||||
void putV4FMA()
|
||||
{
|
||||
puts("void v4fmaddps(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0x9A); }");
|
||||
puts("void v4fnmaddps(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0xAA); }");
|
||||
puts("void v4fmaddss(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, T_0F38 | T_F2 | T_EW0 | T_MUST_EVEX | T_N16, 0x9B); }");
|
||||
puts("void v4fnmaddss(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, T_0F38 | T_F2 | T_EW0 | T_MUST_EVEX | T_N16, 0xAB); }");
|
||||
puts("void vp4dpwssd(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0x52); }");
|
||||
puts("void vp4dpwssds(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0x53); }");
|
||||
}
|
||||
|
||||
int main(int argc, char *[])
|
||||
{
|
||||
bool only64bit = argc == 2;
|
||||
putOpmask(only64bit);
|
||||
putBroadcast(only64bit);
|
||||
if (only64bit) {
|
||||
return 0;
|
||||
}
|
||||
putVcmp();
|
||||
putX_XM();
|
||||
putM_X();
|
||||
putXM_X();
|
||||
putX_X_XM_IMM();
|
||||
putShift();
|
||||
putExtractInsert();
|
||||
putCvt();
|
||||
putGather();
|
||||
putShuff();
|
||||
putMov();
|
||||
putX_XM_IMM();
|
||||
putMisc();
|
||||
putScatter();
|
||||
putV4FMA();
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,23 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
typedef std::set<std::string> StrSet;
|
||||
|
||||
int main()
|
||||
{
|
||||
StrSet ss;
|
||||
std::string line;
|
||||
while (std::getline(std::cin, line)) {
|
||||
if (!line.empty() && line[line.size() - 1] == '\n') {
|
||||
line.resize(line.size() - 1);
|
||||
}
|
||||
if (!line.empty()) {
|
||||
ss.insert(line);
|
||||
}
|
||||
}
|
||||
for (StrSet::const_iterator i = ss.begin(), ie = ss.end(); i != ie; ++i) {
|
||||
std::cout << *i << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
@echo off
|
||||
set OPT=/EHsc -I../ /W4 -D_CRT_SECURE_NO_WARNINGS
|
||||
set TARGET=..\\xbyak\\xbyak_mnemonic.h
|
||||
set SORT=sortline
|
||||
cl gen_code.cpp %OPT%
|
||||
gen_code | %SORT% > %TARGET%
|
||||
echo #ifdef XBYAK_ENABLE_OMITTED_OPERAND>> %TARGET%
|
||||
gen_code omit | %SORT% >> %TARGET%
|
||||
echo #endif>>%TARGET%
|
||||
gen_code fixed >> %TARGET%
|
||||
cl gen_avx512.cpp %OPT%
|
||||
echo #ifndef XBYAK_DISABLE_AVX512>> %TARGET%
|
||||
gen_avx512 | %SORT% >> %TARGET%
|
||||
echo #ifdef XBYAK64>> %TARGET%
|
||||
gen_avx512 64 | %SORT% >> %TARGET%
|
||||
echo #endif>> %TARGET%
|
||||
echo #endif>> %TARGET%
|
||||
@@ -1,617 +0,0 @@
|
||||
[](https://github.com/herumi/xbyak/actions/workflows/main.yml)
|
||||
|
||||
# Xbyak 5.992 ; JIT assembler for x86(IA32), x64(AMD64, x86-64) by C++
|
||||
|
||||
## Abstract
|
||||
|
||||
Xbyak is a C++ header library that enables dynamically to assemble x86(IA32), x64(AMD64, x86-64) mnemonic.
|
||||
|
||||
The pronunciation of Xbyak is `kəi-bja-k`.
|
||||
It is named from a Japanese word [開闢](https://translate.google.com/?hl=ja&sl=ja&tl=en&text=%E9%96%8B%E9%97%A2&op=translate), which means the beginning of the world.
|
||||
|
||||
## Feature
|
||||
* header file only
|
||||
* Intel/MASM like syntax
|
||||
* fully support AVX-512
|
||||
|
||||
**Note**:
|
||||
Use `and_()`, `or_()`, ... instead of `and()`, `or()`.
|
||||
If you want to use them, then specify `-fno-operator-names` option to gcc/clang.
|
||||
|
||||
### News
|
||||
- vnni instructions such as vpdpbusd supports vex encoding.
|
||||
- (break backward compatibility) `push(byte, imm)` (resp. `push(word, imm)`) forces to cast `imm` to 8(resp. 16) bit.
|
||||
- (Windows) `#include <winsock2.h>` has been removed from xbyak.h, so add it explicitly if you need it.
|
||||
- support exception-less mode see. [Exception-less mode](#exception-less-mode)
|
||||
- `XBYAK_USE_MMAP_ALLOCATOR` will be defined on Linux/macOS unless `XBYAK_DONT_USE_MMAP_ALLOCATOR` is defined.
|
||||
|
||||
### Supported OS
|
||||
|
||||
* Windows Xp, Vista, Windows 7, Windows 10(32bit, 64bit)
|
||||
* Linux(32bit, 64bit)
|
||||
* Intel macOS
|
||||
|
||||
### Supported Compilers
|
||||
|
||||
Almost C++03 or later compilers for x86/x64 such as Visual Studio, g++, clang++, Intel C++ compiler and g++ on mingw/cygwin.
|
||||
|
||||
## Install
|
||||
|
||||
The following files are necessary. Please add the path to your compile directory.
|
||||
|
||||
* xbyak.h
|
||||
* xbyak_mnemonic.h
|
||||
* xbyak_util.h
|
||||
|
||||
Linux:
|
||||
```
|
||||
make install
|
||||
```
|
||||
|
||||
These files are copied into `/usr/local/include/xbyak`.
|
||||
|
||||
## How to use it
|
||||
|
||||
Inherit `Xbyak::CodeGenerator` class and make the class method.
|
||||
```
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code(int x)
|
||||
{
|
||||
mov(eax, x);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
```
|
||||
Or you can pass the instance of CodeGenerator without inheriting.
|
||||
```
|
||||
void genCode(Xbyak::CodeGenerator& code, int x) {
|
||||
using namespace Xbyak::util;
|
||||
code.mov(eax, x);
|
||||
code.ret();
|
||||
}
|
||||
```
|
||||
|
||||
Make an instance of the class and get the function
|
||||
pointer by calling `getCode()` and call it.
|
||||
```
|
||||
Code c(5);
|
||||
int (*f)() = c.getCode<int (*)()>();
|
||||
printf("ret=%d\n", f()); // ret = 5
|
||||
```
|
||||
|
||||
## Syntax
|
||||
Similar to MASM/NASM syntax with parentheses.
|
||||
|
||||
```
|
||||
NASM Xbyak
|
||||
mov eax, ebx --> mov(eax, ebx);
|
||||
inc ecx inc(ecx);
|
||||
ret --> ret();
|
||||
```
|
||||
|
||||
## Addressing
|
||||
Use `qword`, `dword`, `word` and `byte` if it is necessary to specify the size of memory,
|
||||
otherwise use `ptr`.
|
||||
|
||||
```
|
||||
(ptr|qword|dword|word|byte) [base + index * (1|2|4|8) + displacement]
|
||||
[rip + 32bit disp] ; x64 only
|
||||
|
||||
NASM Xbyak
|
||||
mov eax, [ebx+ecx] --> mov(eax, ptr [ebx+ecx]);
|
||||
mov al, [ebx+ecx] --> mov(al, ptr [ebx + ecx]);
|
||||
test byte [esp], 4 --> test(byte [esp], 4);
|
||||
inc qword [rax] --> inc(qword [rax]);
|
||||
```
|
||||
**Note**: `qword`, ... are member variables, then don't use `dword` as unsigned int type.
|
||||
|
||||
### How to use Selector (Segment Register)
|
||||
```
|
||||
mov eax, [fs:eax] --> putSeg(fs);
|
||||
mov(eax, ptr [eax]);
|
||||
mov ax, cs --> mov(ax, cs);
|
||||
```
|
||||
**Note**: Segment class is not derived from `Operand`.
|
||||
|
||||
## AVX
|
||||
|
||||
```
|
||||
vaddps(xmm1, xmm2, xmm3); // xmm1 <- xmm2 + xmm3
|
||||
vaddps(xmm2, xmm3, ptr [rax]); // use ptr to access memory
|
||||
vgatherdpd(xmm1, ptr [ebp + 256 + xmm2*4], xmm3);
|
||||
```
|
||||
|
||||
**Note**:
|
||||
If `XBYAK_ENABLE_OMITTED_OPERAND` is defined, then you can use two operand version for backward compatibility.
|
||||
But the newer version will not support it.
|
||||
```
|
||||
vaddps(xmm2, xmm3); // xmm2 <- xmm2 + xmm3
|
||||
```
|
||||
|
||||
## AVX-512
|
||||
|
||||
```
|
||||
vaddpd zmm2, zmm5, zmm30 --> vaddpd(zmm2, zmm5, zmm30);
|
||||
vaddpd xmm30, xmm20, [rax] --> vaddpd(xmm30, xmm20, ptr [rax]);
|
||||
vaddps xmm30, xmm20, [rax] --> vaddps(xmm30, xmm20, ptr [rax]);
|
||||
vaddpd zmm2{k5}, zmm4, zmm2 --> vaddpd(zmm2 | k5, zmm4, zmm2);
|
||||
vaddpd zmm2{k5}{z}, zmm4, zmm2 --> vaddpd(zmm2 | k5 | T_z, zmm4, zmm2);
|
||||
vaddpd zmm2{k5}{z}, zmm4, zmm2,{rd-sae} --> vaddpd(zmm2 | k5 | T_z, zmm4, zmm2 | T_rd_sae);
|
||||
vaddpd(zmm2 | k5 | T_z | T_rd_sae, zmm4, zmm2); // the position of `|` is arbitrary.
|
||||
vcmppd k4{k3}, zmm1, zmm2, {sae}, 5 --> vcmppd(k4 | k3, zmm1, zmm2 | T_sae, 5);
|
||||
|
||||
vaddpd xmm1, xmm2, [rax+256] --> vaddpd(xmm1, xmm2, ptr [rax+256]);
|
||||
vaddpd xmm1, xmm2, [rax+256]{1to2} --> vaddpd(xmm1, xmm2, ptr_b [rax+256]);
|
||||
vaddpd ymm1, ymm2, [rax+256]{1to4} --> vaddpd(ymm1, ymm2, ptr_b [rax+256]);
|
||||
vaddpd zmm1, zmm2, [rax+256]{1to8} --> vaddpd(zmm1, zmm2, ptr_b [rax+256]);
|
||||
vaddps zmm1, zmm2, [rax+rcx*8+8]{1to16} --> vaddps(zmm1, zmm2, ptr_b [rax+rcx*8+8]);
|
||||
vmovsd [rax]{k1}, xmm4 --> vmovsd(ptr [rax] | k1, xmm4);
|
||||
|
||||
vcvtpd2dq xmm16, oword [eax+33] --> vcvtpd2dq(xmm16, xword [eax+33]); // use xword for m128 instead of oword
|
||||
vcvtpd2dq(xmm16, ptr [eax+33]); // default xword
|
||||
vcvtpd2dq xmm21, [eax+32]{1to2} --> vcvtpd2dq(xmm21, ptr_b [eax+32]);
|
||||
vcvtpd2dq xmm0, yword [eax+33] --> vcvtpd2dq(xmm0, yword [eax+33]); // use yword for m256
|
||||
vcvtpd2dq xmm19, [eax+32]{1to4} --> vcvtpd2dq(xmm19, yword_b [eax+32]); // use yword_b to broadcast
|
||||
|
||||
vfpclassps k5{k3}, zword [rax+64], 5 --> vfpclassps(k5|k3, zword [rax+64], 5); // specify m512
|
||||
vfpclasspd k5{k3}, [rax+64]{1to2}, 5 --> vfpclasspd(k5|k3, xword_b [rax+64], 5); // broadcast 64-bit to 128-bit
|
||||
vfpclassps k5{k3}, [rax+64]{1to4}, 5 --> vfpclassps(k5|k3, yword_b [rax+64], 5); // broadcast 64-bit to 256-bit
|
||||
|
||||
vpdpbusd(xm0, xm1, xm2); // default encoding is EVEX
|
||||
vpdpbusd(xm0, xm1, xm2, EvexEncoding); // same as the above
|
||||
vpdpbusd(xm0, xm1, xm2, VexEncoding); // VEX encoding
|
||||
```
|
||||
### Remark
|
||||
* `k1`, ..., `k7` are opmask registers.
|
||||
- `k0` is dealt as no mask.
|
||||
- e.g. `vmovaps(zmm0|k0, ptr[rax]);` and `vmovaps(zmm0|T_z, ptr[rax]);` are same to `vmovaps(zmm0, ptr[rax]);`.
|
||||
* use `| T_z`, `| T_sae`, `| T_rn_sae`, `| T_rd_sae`, `| T_ru_sae`, `| T_rz_sae` instead of `,{z}`, `,{sae}`, `,{rn-sae}`, `,{rd-sae}`, `,{ru-sae}`, `,{rz-sae}` respectively.
|
||||
* `k4 | k3` is different from `k3 | k4`.
|
||||
* use `ptr_b` for broadcast `{1toX}`. X is automatically determined.
|
||||
* specify `xword`/`yword`/`zword(_b)` for m128/m256/m512 if necessary.
|
||||
|
||||
## Label
|
||||
Two kinds of Label are supported. (String literal and Label class).
|
||||
|
||||
### String literal
|
||||
```
|
||||
L("L1");
|
||||
jmp("L1");
|
||||
|
||||
jmp("L2");
|
||||
...
|
||||
a few mnemonics (8-bit displacement jmp)
|
||||
...
|
||||
L("L2");
|
||||
|
||||
jmp("L3", T_NEAR);
|
||||
...
|
||||
a lot of mnemonics (32-bit displacement jmp)
|
||||
...
|
||||
L("L3");
|
||||
```
|
||||
|
||||
* Call `hasUndefinedLabel()` to verify your code has no undefined label.
|
||||
* you can use a label for immediate value of mov like as `mov(eax, "L2")`.
|
||||
|
||||
### Support `@@`, `@f`, `@b` like MASM
|
||||
|
||||
```
|
||||
L("@@"); // <A>
|
||||
jmp("@b"); // jmp to <A>
|
||||
jmp("@f"); // jmp to <B>
|
||||
L("@@"); // <B>
|
||||
jmp("@b"); // jmp to <B>
|
||||
mov(eax, "@b");
|
||||
jmp(eax); // jmp to <B>
|
||||
```
|
||||
|
||||
### Local label
|
||||
|
||||
Label symbols beginning with a period between `inLocalLabel()` and `outLocalLabel()`
|
||||
are treated as a local label.
|
||||
`inLocalLabel()` and `outLocalLabel()` can be nested.
|
||||
|
||||
```
|
||||
void func1()
|
||||
{
|
||||
inLocalLabel();
|
||||
L(".lp"); // <A> ; local label
|
||||
...
|
||||
jmp(".lp"); // jmp to <A>
|
||||
L("aaa"); // global label <C>
|
||||
outLocalLabel();
|
||||
|
||||
inLocalLabel();
|
||||
L(".lp"); // <B> ; local label
|
||||
func1();
|
||||
jmp(".lp"); // jmp to <B>
|
||||
inLocalLabel();
|
||||
jmp("aaa"); // jmp to <C>
|
||||
}
|
||||
```
|
||||
|
||||
### short and long jump
|
||||
Xbyak deals with jump mnemonics of an undefined label as short jump if no type is specified.
|
||||
So if the size between jmp and label is larger than 127 byte, then xbyak will cause an error.
|
||||
|
||||
```
|
||||
jmp("short-jmp"); // short jmp
|
||||
// small code
|
||||
L("short-jmp");
|
||||
|
||||
jmp("long-jmp");
|
||||
// long code
|
||||
L("long-jmp"); // throw exception
|
||||
```
|
||||
Then specify T_NEAR for jmp.
|
||||
```
|
||||
jmp("long-jmp", T_NEAR); // long jmp
|
||||
// long code
|
||||
L("long-jmp");
|
||||
```
|
||||
Or call `setDefaultJmpNEAR(true);` once, then the default type is set to T_NEAR.
|
||||
```
|
||||
jmp("long-jmp"); // long jmp
|
||||
// long code
|
||||
L("long-jmp");
|
||||
```
|
||||
|
||||
### Label class
|
||||
|
||||
`L()` and `jxx()` support Label class.
|
||||
|
||||
```
|
||||
Xbyak::Label label1, label2;
|
||||
L(label1);
|
||||
...
|
||||
jmp(label1);
|
||||
...
|
||||
jmp(label2);
|
||||
...
|
||||
L(label2);
|
||||
```
|
||||
|
||||
Use `putL` for jmp table
|
||||
```
|
||||
Label labelTbl, L0, L1, L2;
|
||||
mov(rax, labelTbl);
|
||||
// rdx is an index of jump table
|
||||
jmp(ptr [rax + rdx * sizeof(void*)]);
|
||||
L(labelTbl);
|
||||
putL(L0);
|
||||
putL(L1);
|
||||
putL(L2);
|
||||
L(L0);
|
||||
....
|
||||
L(L1);
|
||||
....
|
||||
```
|
||||
|
||||
`assignL(dstLabel, srcLabel)` binds dstLabel with srcLabel.
|
||||
|
||||
```
|
||||
Label label2;
|
||||
Label label1 = L(); // make label1 ; same to Label label1; L(label1);
|
||||
...
|
||||
jmp(label2); // label2 is not determined here
|
||||
...
|
||||
assignL(label2, label1); // label2 <- label1
|
||||
```
|
||||
The `jmp` in the above code jumps to label1 assigned by `assignL`.
|
||||
|
||||
**Note**:
|
||||
* srcLabel must be used in `L()`.
|
||||
* dstLabel must not be used in `L()`.
|
||||
|
||||
`Label::getAddress()` returns the address specified by the label instance and 0 if not specified.
|
||||
```
|
||||
// not AutoGrow mode
|
||||
Label label;
|
||||
assert(label.getAddress() == 0);
|
||||
L(label);
|
||||
assert(label.getAddress() == getCurr());
|
||||
```
|
||||
|
||||
### Rip ; relative addressing
|
||||
```
|
||||
Label label;
|
||||
mov(eax, ptr [rip + label]); // eax = 4
|
||||
...
|
||||
|
||||
L(label);
|
||||
dd(4);
|
||||
```
|
||||
```
|
||||
int x;
|
||||
...
|
||||
mov(eax, ptr[rip + &x]); // throw exception if the difference between &x and current position is larger than 2GiB
|
||||
```
|
||||
|
||||
## Code size
|
||||
The default max code size is 4096 bytes.
|
||||
Specify the size in constructor of `CodeGenerator()` if necessary.
|
||||
|
||||
```
|
||||
class Quantize : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
Quantize()
|
||||
: CodeGenerator(8192)
|
||||
{
|
||||
}
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
## User allocated memory
|
||||
|
||||
You can make jit code on prepared memory.
|
||||
|
||||
Call `setProtectModeRE` yourself to change memory mode if using the prepared memory.
|
||||
|
||||
```
|
||||
uint8_t alignas(4096) buf[8192]; // C++11 or later
|
||||
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code() : Xbyak::CodeGenerator(sizeof(buf), buf)
|
||||
{
|
||||
mov(rax, 123);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Code c;
|
||||
c.setProtectModeRE(); // set memory to Read/Exec
|
||||
printf("%d\n", c.getCode<int(*)()>()());
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: See [sample/test0.cpp](sample/test0.cpp).
|
||||
|
||||
### AutoGrow
|
||||
|
||||
The memory region for jit is automatically extended if necessary when `AutoGrow` is specified in a constructor of `CodeGenerator`.
|
||||
|
||||
Call `ready()` or `readyRE()` before calling `getCode()` to fix jump address.
|
||||
```
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code()
|
||||
: Xbyak::CodeGenerator(<default memory size>, Xbyak::AutoGrow)
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
Code c;
|
||||
// generate code for jit
|
||||
c.ready(); // mode = Read/Write/Exec
|
||||
```
|
||||
|
||||
**Note**:
|
||||
* Don't use the address returned by `getCurr()` before calling `ready()` because it may be invalid address.
|
||||
|
||||
### Read/Exec mode
|
||||
Xbyak set Read/Write/Exec mode to memory to run jit code.
|
||||
If you want to use Read/Exec mode for security, then specify `DontSetProtectRWE` for `CodeGenerator` and
|
||||
call `setProtectModeRE()` after generating jit code.
|
||||
|
||||
```
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code()
|
||||
: Xbyak::CodeGenerator(4096, Xbyak::DontSetProtectRWE)
|
||||
{
|
||||
mov(eax, 123);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
Code c;
|
||||
c.setProtectModeRE();
|
||||
...
|
||||
|
||||
```
|
||||
Call `readyRE()` instead of `ready()` when using `AutoGrow` mode.
|
||||
See [protect-re.cpp](sample/protect-re.cpp).
|
||||
|
||||
## Exception-less mode
|
||||
If `XBYAK_NO_EXCEPTION` is defined, then gcc/clang can compile xbyak with `-fno-exceptions`.
|
||||
In stead of throwing an exception, `Xbyak::GetError()` returns non-zero value (e.g. `ERR_BAD_ADDRESSING`) if there is something wrong.
|
||||
The status will not be changed automatically, then you should reset it by `Xbyak::ClearError()`.
|
||||
`CodeGenerator::reset()` calls `ClearError()`.
|
||||
|
||||
## Macro
|
||||
|
||||
* **XBYAK32** is defined on 32bit.
|
||||
* **XBYAK64** is defined on 64bit.
|
||||
* **XBYAK64_WIN** is defined on 64bit Windows(VC).
|
||||
* **XBYAK64_GCC** is defined on 64bit gcc, cygwin.
|
||||
* define **XBYAK_USE_OP_NAMES** on gcc with `-fno-operator-names` if you want to use `and()`, ....
|
||||
* define **XBYAK_ENABLE_OMITTED_OPERAND** if you use omitted destination such as `vaddps(xmm2, xmm3);`(deprecated in the future).
|
||||
* define **XBYAK_UNDEF_JNL** if Bessel function jnl is defined as macro.
|
||||
* define **XBYAK_NO_EXCEPTION** for a compiler option `-fno-exceptions`.
|
||||
|
||||
## Sample
|
||||
|
||||
* [test0.cpp](sample/test0.cpp) ; tiny sample (x86, x64)
|
||||
* [quantize.cpp](sample/quantize.cpp) ; JIT optimized quantization by fast division (x86 only)
|
||||
* [calc.cpp](sample/calc.cpp) ; assemble and estimate a given polynomial (x86, x64)
|
||||
* [bf.cpp](sample/bf.cpp) ; JIT brainfuck (x86, x64)
|
||||
|
||||
## License
|
||||
|
||||
modified new BSD License
|
||||
http://opensource.org/licenses/BSD-3-Clause
|
||||
|
||||
## History
|
||||
* 2021/May/09 ver 5.992 support endbr32 and endbr64
|
||||
* 2020/Nov/16 ver 5.991 disable constexpr for gcc-5 with -std=c++-14
|
||||
* 2020/Oct/19 ver 5.99 support VNNI instructions(Thanks to akharito)
|
||||
* 2020/Oct/17 ver 5.98 support the form of [scale * reg]
|
||||
* 2020/Sep/08 ver 5.97 replace uint32 with uint32_t etc.
|
||||
* 2020/Aug/28 ver 5.95 some constructors of register classes support constexpr if C++14 or later
|
||||
* 2020/Aug/04 ver 5.941 `CodeGenerator::reset()` calls `ClearError()`.
|
||||
* 2020/Jul/28 ver 5.94 remove #include <winsock2.h> (only windows)
|
||||
* 2020/Jul/21 ver 5.93 support exception-less mode
|
||||
* 2020/Jun/30 ver 5.92 support Intel AMX instruction set (Thanks to nshustrov)
|
||||
* 2020/Jun/22 ver 5.913 fix mov(r64, imm64) on 32-bit env with XBYAK64
|
||||
* 2020/Jun/19 ver 5.912 define MAP_JIT on macOS regardless of Xcode version (Thanks to rsdubtso)
|
||||
* 2020/May/10 ver 5.911 XBYAK_USE_MMAP_ALLOCATOR is defined unless XBYAK_DONT_USE_MMAP_ALLOCATOR is defined.
|
||||
* 2020/Apr/20 ver 5.91 accept mask register k0 (it means no mask)
|
||||
* 2020/Apr/09 ver 5.90 kmov{b,d,w,q} throws exception for an unsupported register
|
||||
* 2020/Feb/26 ver 5.891 fix typo of type
|
||||
* 2020/Jan/03 ver 5.89 fix error of vfpclasspd
|
||||
* 2019/Dec/20 ver 5.88 fix compile error on Windows
|
||||
* 2019/Dec/19 ver 5.87 add setDefaultJmpNEAR(), which deals with `jmp` of an undefined label as T_NEAR if no type is specified.
|
||||
* 2019/Dec/13 ver 5.86 [changed] revert to the behavior before v5.84 if -fno-operator-names is defined (and() is available)
|
||||
* 2019/Dec/07 ver 5.85 append MAP_JIT flag to mmap for macOS mojave or later
|
||||
* 2019/Nov/29 ver 5.84 [changed] XBYAK_NO_OP_NAMES is defined unless XBYAK_USE_OP_NAMES is defined
|
||||
* 2019/Oct/12 ver 5.83 exit(1) was removed
|
||||
* 2019/Sep/23 ver 5.82 support monitorx, mwaitx, clzero (thanks to @MagurosanTeam)
|
||||
* 2019/Sep/14 ver 5.81 support some generic mnemonics.
|
||||
* 2019/Aug/01 ver 5.802 fix detection of AVX512_BF16 (thanks to vpirogov)
|
||||
* 2019/May/27 support vp2intersectd, vp2intersectq (not tested)
|
||||
* 2019/May/26 ver 5.80 support vcvtne2ps2bf16, vcvtneps2bf16, vdpbf16ps
|
||||
* 2019/Apr/27 ver 5.79 vcmppd/vcmpps supports ptr_b(thanks to jkopinsky)
|
||||
* 2019/Apr/15 ver 5.78 rewrite Reg::changeBit() (thanks to MerryMage)
|
||||
* 2019/Mar/06 ver 5.77 fix number of cores that share LLC cache by densamoilov
|
||||
* 2019/Jan/17 ver 5.76 add Cpu::getNumCores() by shelleygoel
|
||||
* 2018/Oct/31 ver 5.751 recover Xbyak::CastTo for compatibility
|
||||
* 2018/Oct/29 ver 5.75 unlink LabelManager from Label when msg is destroyed
|
||||
* 2018/Oct/21 ver 5.74 support RegRip +/- int. Xbyak::CastTo is removed
|
||||
* 2018/Oct/15 util::AddressFrame uses push/pop instead of mov
|
||||
* 2018/Sep/19 ver 5.73 fix evex encoding of vpslld, vpslldq, vpsllw, etc for (reg, mem, imm8)
|
||||
* 2018/Sep/19 ver 5.72 fix the encoding of vinsertps for disp8N(Thanks to petercaday)
|
||||
* 2018/Sep/04 ver 5.71 L() returns a new label instance
|
||||
* 2018/Aug/27 ver 5.70 support setProtectMode() and DontUseProtect for read/exec setting
|
||||
* 2018/Aug/24 ver 5.68 fix wrong VSIB encoding with vector index >= 16(thanks to petercaday)
|
||||
* 2018/Aug/14 ver 5.67 remove mutable in Address ; fix setCacheHierarchy for cloud vm
|
||||
* 2018/Jul/26 ver 5.661 support mingw64
|
||||
* 2018/Jul/24 ver 5.66 add CodeArray::PROTECT_RE to mode of protect()
|
||||
* 2018/Jun/26 ver 5.65 fix push(qword [mem])
|
||||
* 2018/Mar/07 ver 5.64 fix zero division in Cpu() on some cpu
|
||||
* 2018/Feb/14 ver 5.63 fix Cpu::setCacheHierarchy() and fix EvexModifierZero for clang<3.9(thanks to mgouicem)
|
||||
* 2018/Feb/13 ver 5.62 Cpu::setCacheHierarchy() by mgouicem and rsdubtso
|
||||
* 2018/Feb/07 ver 5.61 vmov* supports mem{k}{z}(I forgot it)
|
||||
* 2018/Jan/24 ver 5.601 add xword, yword, etc. into Xbyak::util namespace
|
||||
* 2018/Jan/05 ver 5.60 support AVX-512 for Ice lake(319433-030.pdf)
|
||||
* 2017/Aug/22 ver 5.53 fix mpx encoding, add bnd() prefix
|
||||
* 2017/Aug/18 ver 5.52 fix align (thanks to MerryMage)
|
||||
* 2017/Aug/17 ver 5.51 add multi-byte nop and align() uses it(thanks to inolen)
|
||||
* 2017/Aug/08 ver 5.50 add mpx(thanks to magurosan)
|
||||
* 2017/Aug/08 ver 5.45 add sha(thanks to magurosan)
|
||||
* 2017/Aug/08 ver 5.44 add prefetchw(thanks to rsdubtso)
|
||||
* 2017/Jul/12 ver 5.432 reduce warnings of PVS studio
|
||||
* 2017/Jul/09 ver 5.431 fix hasRex() (no affect) (thanks to drillsar)
|
||||
* 2017/May/14 ver 5.43 fix CodeGenerator::resetSize() (thanks to gibbed)
|
||||
* 2017/May/13 ver 5.42 add movs{b,w,d,q}
|
||||
* 2017/Jan/26 ver 5.41 add prefetchwt1 and support for scale == 0(thanks to rsdubtso)
|
||||
* 2016/Dec/14 ver 5.40 add Label::getAddress() method to get the pointer specified by the label
|
||||
* 2016/Dec/09 ver 5.34 fix handling of negative offsets when encoding disp8N(thanks to rsdubtso)
|
||||
* 2016/Dec/08 ver 5.33 fix encoding of vpbroadcast{b,w,d,q}, vpinsr{b,w}, vpextr{b,w} for disp8N
|
||||
* 2016/Dec/01 ver 5.32 rename __xgetbv() to _xgetbv() to support clang for Visual Studio(thanks to freiro)
|
||||
* 2016/Nov/27 ver 5.31 rename AVX512_4VNNI to AVX512_4VNNIW
|
||||
* 2016/Nov/27 ver 5.30 add AVX512_4VNNI, AVX512_4FMAPS instructions(thanks to rsdubtso)
|
||||
* 2016/Nov/26 ver 5.20 add detection of AVX512_4VNNI and AVX512_4FMAPS(thanks to rsdubtso)
|
||||
* 2016/Nov/20 ver 5.11 lost vptest for ymm(thanks to gregory38)
|
||||
* 2016/Nov/20 ver 5.10 add addressing [rip+&var]
|
||||
* 2016/Sep/29 ver 5.03 fix detection ERR_INVALID_OPMASK_WITH_MEMORY(thanks to PVS-Studio)
|
||||
* 2016/Aug/15 ver 5.02 xbyak does not include xbyak_bin2hex.h
|
||||
* 2016/Aug/15 ver 5.011 fix detection of version of gcc 5.4
|
||||
* 2016/Aug/03 ver 5.01 disable omitted operand
|
||||
* 2016/Jun/24 ver 5.00 support avx-512 instruction set
|
||||
* 2016/Jun/13 avx-512 add mask instructions
|
||||
* 2016/May/05 ver 4.91 add detection of AVX-512 to Xbyak::util::Cpu
|
||||
* 2016/Mar/14 ver 4.901 comment to ready() function(thanks to skmp)
|
||||
* 2016/Feb/04 ver 4.90 add jcc(const void *addr);
|
||||
* 2016/Jan/30 ver 4.89 vpblendvb supports ymm reg(thanks to John Funnell)
|
||||
* 2016/Jan/24 ver 4.88 lea, cmov supports 16-bit register(thanks to whyisthisfieldhere)
|
||||
* 2015/Oct/05 ver 4.87 support segment selectors
|
||||
* 2015/Aug/18 ver 4.86 fix [rip + label] addressing with immediate value(thanks to whyisthisfieldhere)
|
||||
* 2015/Aug/10 ver 4.85 Address::operator==() is not correct(thanks to inolen)
|
||||
* 2015/Jun/22 ver 4.84 call() support variadic template if available(thanks to randomstuff)
|
||||
* 2015/Jun/16 ver 4.83 support movbe(thanks to benvanik)
|
||||
* 2015/May/24 ver 4.82 support detection of F16C
|
||||
* 2015/Apr/25 ver 4.81 fix the condition to throw exception for setSize(thanks to whyisthisfieldhere)
|
||||
* 2015/Apr/22 ver 4.80 rip supports label(thanks to whyisthisfieldhere)
|
||||
* 2015/Jar/28 ver 4.71 support adcx, adox, cmpxchg, rdseed, stac
|
||||
* 2014/Oct/14 ver 4.70 support MmapAllocator
|
||||
* 2014/Jun/13 ver 4.62 disable warning of VC2014
|
||||
* 2014/May/30 ver 4.61 support bt, bts, btr, btc
|
||||
* 2014/May/28 ver 4.60 support vcvtph2ps, vcvtps2ph
|
||||
* 2014/Apr/11 ver 4.52 add detection of rdrand
|
||||
* 2014/Mar/25 ver 4.51 remove state information of unreferenced labels
|
||||
* 2014/Mar/16 ver 4.50 support new Label
|
||||
* 2014/Mar/05 ver 4.40 fix wrong detection of BMI/enhanced rep on VirtualBox
|
||||
* 2013/Dec/03 ver 4.30 support Reg::cvt8(), cvt16(), cvt32(), cvt64()
|
||||
* 2013/Oct/16 ver 4.21 label support std::string
|
||||
* 2013/Jul/30 ver 4.20 [break backward compatibility] split Reg32e class into RegExp(base+index*scale+disp) and Reg32e(means Reg32 or Reg64)
|
||||
* 2013/Jul/04 ver 4.10 [break backward compatibility] change the type of Xbyak::Error from enum to a class
|
||||
* 2013/Jun/21 ver 4.02 add putL(LABEL) function to put the address of the label
|
||||
* 2013/Jun/21 ver 4.01 vpsllw, vpslld, vpsllq, vpsraw, vpsrad, vpsrlw, vpsrld, vpsrlq support (ymm, ymm, xmm). support vpbroadcastb, vpbroadcastw, vpbroadcastd, vpbroadcastq(thanks to Gabest).
|
||||
* 2013/May/30 ver 4.00 support AVX2, VEX-encoded GPR-instructions
|
||||
* 2013/Mar/27 ver 3.80 support mov(reg, "label");
|
||||
* 2013/Mar/13 ver 3.76 add cqo(), jcxz(), jecxz(), jrcxz()
|
||||
* 2013/Jan/15 ver 3.75 add setSize() to modify generated code
|
||||
* 2013/Jan/12 ver 3.74 add CodeGenerator::reset() ; add Allocator::useProtect()
|
||||
* 2013/Jan/06 ver 3.73 use unordered_map if possible
|
||||
* 2012/Dec/04 ver 3.72 eax, ebx, ... are member variables of CodeGenerator(revert), Xbyak::util::eax, ... are static const.
|
||||
* 2012/Nov/17 ver 3.71 and_(), or_(), xor_(), not_() are available if XBYAK_NO_OP_NAMES is not defined.
|
||||
* 2012/Nov/17 change eax, ebx, ptr and so on in CodeGenerator as static member and alias of them are defined in Xbyak::util.
|
||||
* 2012/Nov/09 ver 3.70 XBYAK_NO_OP_NAMES macro is added to use and_() instead of and() (thanks to Mattias)
|
||||
* 2012/Nov/01 ver 3.62 add fwait/fnwait/finit/fninit
|
||||
* 2012/Nov/01 ver 3.61 add fldcw/fstcw
|
||||
* 2012/May/03 ver 3.60 change interface of Allocator
|
||||
* 2012/Mar/23 ver 3.51 fix userPtr mode
|
||||
* 2012/Mar/19 ver 3.50 support AutoGrow mode
|
||||
* 2011/Nov/09 ver 3.05 fix bit property of rip addresing / support movsxd
|
||||
* 2011/Aug/15 ver 3.04 fix dealing with imm8 such as add(dword [ebp-8], 0xda); (thanks to lolcat)
|
||||
* 2011/Jun/16 ver 3.03 fix __GNUC_PREREQ macro for Mac gcc(thanks to t_teruya)
|
||||
* 2011/Apr/28 ver 3.02 do not use xgetbv on Mac gcc
|
||||
* 2011/May/24 ver 3.01 fix typo of OSXSAVE
|
||||
* 2011/May/23 ver 3.00 add vcmpeqps and so on
|
||||
* 2011/Feb/16 ver 2.994 beta add vmovq for 32-bit mode(I forgot it)
|
||||
* 2011/Feb/16 ver 2.993 beta remove cvtReg to avoid thread unsafe
|
||||
* 2011/Feb/10 ver 2.992 beta support one argument syntax for fadd like nasm
|
||||
* 2011/Feb/07 ver 2.991 beta fix pextrw reg, xmm, imm(Thanks to Gabest)
|
||||
* 2011/Feb/04 ver 2.99 beta support AVX
|
||||
* 2010/Dec/08 ver 2.31 fix ptr [rip + 32bit offset], support rdtscp
|
||||
* 2010/Oct/19 ver 2.30 support pclmulqdq, aesdec, aesdeclast, aesenc, aesenclast, aesimc, aeskeygenassist
|
||||
* 2010/Jun/07 ver 2.29 fix call(<label>)
|
||||
* 2010/Jun/17 ver 2.28 move some member functions to public
|
||||
* 2010/Jun/01 ver 2.27 support encoding of mov(reg64, imm) like yasm(not nasm)
|
||||
* 2010/May/24 ver 2.26 fix sub(rsp, 1000)
|
||||
* 2010/Apr/26 ver 2.25 add jc/jnc(I forgot to implement them...)
|
||||
* 2010/Apr/16 ver 2.24 change the prototype of rewrite() method
|
||||
* 2010/Apr/15 ver 2.23 fix align() and xbyak_util.h for Mac
|
||||
* 2010/Feb/16 ver 2.22 fix inLocalLabel()/outLocalLabel()
|
||||
* 2009/Dec/09 ver 2.21 support cygwin(gcc 4.3.2)
|
||||
* 2009/Nov/28 support a part of FPU
|
||||
* 2009/Jun/25 fix mov(qword[rax], imm); (thanks to Martin)
|
||||
* 2009/Mar/10 fix redundant REX.W prefix on jmp/call reg64
|
||||
* 2009/Feb/24 add movq reg64, mmx/xmm; movq mmx/xmm, reg64
|
||||
* 2009/Feb/13 movd(xmm7, dword[eax]) drops 0x66 prefix (thanks to Gabest)
|
||||
* 2008/Dec/30 fix call in short relative address(thanks to kato san)
|
||||
* 2008/Sep/18 support @@, @f, @b and localization of label(thanks to nobu-q san)
|
||||
* 2008/Sep/18 support (ptr[rip + 32bit offset]) (thanks to Dango-Chu san)
|
||||
* 2008/Jun/03 fix align(). mov(ptr[eax],1) throws ERR_MEM_SIZE_IS_NOT_SPECIFIED.
|
||||
* 2008/Jun/02 support memory interface allocated by user
|
||||
* 2008/May/26 fix protect() to avoid invalid setting(thanks to shinichiro_h san)
|
||||
* 2008/Apr/30 add cmpxchg16b, cdqe
|
||||
* 2008/Apr/29 support x64
|
||||
* 2008/Apr/14 code refactoring
|
||||
* 2008/Mar/12 add bsr/bsf
|
||||
* 2008/Feb/14 fix output of sub eax, 1234 (thanks to Robert)
|
||||
* 2007/Nov/5 support lock, xadd, xchg
|
||||
* 2007/Nov/2 support SSSE3/SSE4 (thanks to Dango-Chu san)
|
||||
* 2007/Feb/4 fix the bug that exception doesn't occur under the condition which the offset of jmp mnemonic without T_NEAR is over 127.
|
||||
* 2007/Jan/21 fix the bug to create address like [disp] select smaller representation for mov (eax|ax|al, [disp])
|
||||
* 2007/Jan/4 first version
|
||||
|
||||
## Author
|
||||
MITSUNARI Shigeo(herumi@nifty.com)
|
||||
|
||||
## Sponsors welcome
|
||||
[GitHub Sponsor](https://github.com/sponsors/herumi)
|
||||
@@ -1,560 +0,0 @@
|
||||
|
||||
C++用x86(IA-32), x64(AMD64, x86-64) JITアセンブラ Xbyak 5.992
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎概要
|
||||
|
||||
これはx86, x64(AMD64, x86-64)のマシン語命令を生成するC++のクラスライブラリです。
|
||||
プログラム実行時に動的にアセンブルすることが可能です。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎特徴
|
||||
|
||||
・ヘッダファイルオンリー
|
||||
xbyak.hをインクルードするだけですぐ利用することができます。
|
||||
C++の枠組み内で閉じているため、外部アセンブラは不要です。
|
||||
32bit/64bit両対応です。
|
||||
対応ニーモニック:特権命令除くx86, MMX/MMX2/SSE/SSE2/SSE3/SSSE3/SSE4/FPU(一部)/AVX/AVX2/FMA/VEX-encoded GPR
|
||||
|
||||
・Windows Xp(32bit, 64bit), Windows 7/Linux(32bit, 64bit)/Intel Mac対応
|
||||
Windows Xp, Windows 7上ではVC2008, VC2010, VC2012
|
||||
Linux (kernel 3.8)上ではgcc 4.7.3, clang 3.3
|
||||
Intel Mac
|
||||
などで動作確認をしています。
|
||||
|
||||
※ and, orなどの代わりにand_, or_を使用してください。
|
||||
and, orなどを使いたい場合は-fno-operator-namesをgcc/clangに指定してください。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎準備
|
||||
xbyak.h
|
||||
xbyak_bin2hex.h
|
||||
これらを同一のパスに入れてインクルードパスに追加してください。
|
||||
|
||||
Linuxではmake installで/usr/local/include/xbyakにコピーされます。
|
||||
-----------------------------------------------------------------------------
|
||||
◎下位互換性の破れ
|
||||
* push byte, immまたはpush word, immが下位8bit, 16bitにキャストした値を使うように変更。
|
||||
* (Windows) `<winsock2.h>`をincludeしなくなったので必要なら明示的にincludeしてください。
|
||||
* XBYAK_USE_MMAP_ALLOCATORがデフォルトで有効になりました。従来の方式にする場合はXBYAK_DONT_USE_MMAP_ALLOCATORを定義してください。
|
||||
* Xbyak::Errorの型をenumからclassに変更
|
||||
** 従来のenumの値をとるにはintにキャストしてください。
|
||||
* (古い)Reg32eクラスを(新しい)Reg32eとRegExpに分ける。
|
||||
** (新しい)Reg32eはReg32かReg64
|
||||
** (新しい)RegExpは'Reg32e + (Reg32e|Xmm|Ymm) * scale + disp'の型
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎新機能
|
||||
|
||||
例外なしモード追加
|
||||
XBYAK_NO_EXCEPTIONを定義してコンパイルするとgcc/clangで-fno-exceptionsオプションでコンパイルできます。
|
||||
エラーは例外の代わりに`Xbyak::GetError()`で通達されます。
|
||||
この値が0でなければ何か問題が発生しています。
|
||||
この値は自動的に変更されないので`Xbyak::ClearError()`でリセットしてください。
|
||||
`CodeGenerator::reset()`は`ClearError()`を呼びます。
|
||||
|
||||
MmapAllocator追加
|
||||
これはUnix系OSでのみの仕様です。XBYAK_USE_MMAP_ALLOCATORを使うと利用できます。
|
||||
デフォルトのAllocatorはメモリ確保時にposix_memalignを使います。
|
||||
この領域に対するmprotectはmap countを減らします。
|
||||
map countの最大値は/proc/sys/vm/max_map_countに書かれています。
|
||||
デフォルトでは3万個ほどのXbyak::CodeGeneratorインスタンスを生成するとエラーになります。
|
||||
test/mprotect_test.cppで確認できます。
|
||||
これを避けるためにはmmapを使うMmapAllocatorを使ってください。
|
||||
|
||||
|
||||
AutoGrowモード追加
|
||||
これはメモリ伸長を動的に行うモードです。
|
||||
今まではXbyak::CodeGenerator()に渡したメモリサイズを超えると例外が発生して
|
||||
いましたが、このモードでは内部でメモリを再確保して伸長します。
|
||||
ただし、getCode()を呼び出す前にジャンプ命令のアドレス解決をするためにready()
|
||||
関数を呼ぶ必要があります。
|
||||
|
||||
次のように使います。
|
||||
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code()
|
||||
: Xbyak::CodeGenerator(<default memory size>, Xbyak::AutoGrow)
|
||||
{
|
||||
...
|
||||
}
|
||||
};
|
||||
Code c;
|
||||
c.ready(); // この呼び出しを忘れてはいけない
|
||||
|
||||
注意1. ready()を呼んで確定するまではgetCurr()で得たポインタは無効化されている
|
||||
可能性があります。getSize()でoffsetを保持しておきready()のあとにgetCode()を
|
||||
呼び出してからgetCode() + offsetで新しいポインタを取得してください。
|
||||
|
||||
注意2. AutoGrowモードでは64bitモードの相対アドレッシング[rip]は非サポートです。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎文法
|
||||
|
||||
Xbyak::CodeGeneratorクラスを継承し、そのクラスメソッド内でx86, x64アセンブラを
|
||||
記述します。そのメソッドを呼び出した後、getCode()メソッドを呼び出し、その戻
|
||||
り値を自分が使いたい関数ポインタに変換して利用します。アセンブルエラーは例外
|
||||
により通知されます(cf. main.cpp)。
|
||||
|
||||
・基本的にnasmの命令で括弧をつければよいです。
|
||||
|
||||
mov eax, ebx --> mov(eax, ebx);
|
||||
inc ecx inc(ecx);
|
||||
ret --> ret();
|
||||
|
||||
・アドレッシング
|
||||
|
||||
(ptr|dword|word|byte) [base + index * (1|2|4|8) + displacement]
|
||||
[rip + 32bit disp] ; x64 only
|
||||
という形で指定します。サイズを指定する必要がない限りptrを使えばよいです。
|
||||
|
||||
セレクター(セグメントレジスタ)をサポートしました。
|
||||
(注意)セグメントレジスタはOperandを継承していません。
|
||||
|
||||
mov eax, [fs:eax] --> putSeg(fs); mov(eax, ptr [eax]);
|
||||
mov ax, cs --> mov(ax, cs);
|
||||
|
||||
mov eax, [ebx+ecx] --> mov (eax, ptr[ebx+ecx]);
|
||||
test byte [esp], 4 --> test (byte [esp], 4);
|
||||
|
||||
(注意) dword, word, byteはメンバ変数です。従ってたとえばunsigned intの
|
||||
つもりでdwordをtypedefしないでください。
|
||||
|
||||
・AVX
|
||||
|
||||
FMAについては簡略表記を導入するか検討中です(アイデア募集中)。
|
||||
|
||||
vaddps(xmm1, xmm2, xmm3); // xmm1 <- xmm2 + xmm3
|
||||
vaddps(xmm2, xmm3, ptr [rax]); // メモリアクセスはptrで
|
||||
|
||||
vfmadd231pd(xmm1, xmm2, xmm3); // xmm1 <- (xmm2 * xmm3) + xmm1
|
||||
|
||||
*注意*
|
||||
デスティネーションの省略形はサポートされなくなりました。
|
||||
|
||||
vaddps(xmm2, xmm3); // xmm2 <- xmm2 + xmm3
|
||||
|
||||
XBYAK_ENABLE_OMITTED_OPERANDを定義すると使えますが、将来はそれも非サポートになるでしょう。
|
||||
|
||||
・AVX-512
|
||||
|
||||
vaddpd zmm2, zmm5, zmm30 --> vaddpd(zmm2, zmm5, zmm30);
|
||||
vaddpd xmm30, xmm20, [rax] --> vaddpd(xmm30, xmm20, ptr [rax]);
|
||||
vaddps xmm30, xmm20, [rax] --> vaddps(xmm30, xmm20, ptr [rax]);
|
||||
vaddpd zmm2{k5}, zmm4, zmm2 --> vaddpd(zmm2 | k5, zmm4, zmm2);
|
||||
vaddpd zmm2{k5}{z}, zmm4, zmm2 --> vaddpd(zmm2 | k5 | T_z, zmm4, zmm2);
|
||||
vaddpd zmm2{k5}{z}, zmm4, zmm2,{rd-sae} --> vaddpd(zmm2 | k5 | T_z, zmm4, zmm2 | T_rd_sae);
|
||||
vaddpd(zmm2 | k5 | T_z | T_rd_sae, zmm4, zmm2); // the position of `|` is arbitrary.
|
||||
vcmppd k4{k3}, zmm1, zmm2, {sae}, 5 --> vcmppd(k4 | k3, zmm1, zmm2 | T_sae, 5);
|
||||
|
||||
vaddpd xmm1, xmm2, [rax+256]{1to2} --> vaddpd(xmm1, xmm2, ptr_b [rax+256]);
|
||||
vaddpd ymm1, ymm2, [rax+256]{1to4} --> vaddpd(ymm1, ymm2, ptr_b [rax+256]);
|
||||
vaddpd zmm1, zmm2, [rax+256]{1to8} --> vaddpd(zmm1, zmm2, ptr_b [rax+256]);
|
||||
vaddps zmm1, zmm2, [rax+rcx*8+8]{1to16} --> vaddps(zmm1, zmm2, ptr_b [rax+rcx*8+8]);
|
||||
vmovsd [rax]{k1}, xmm4 --> vmovsd(ptr [rax] | k1, xmm4);
|
||||
|
||||
vcvtpd2dq xmm16, oword [eax+33] --> vcvtpd2dq(xmm16, xword [eax+33]); // use xword for m128 instead of oword
|
||||
vcvtpd2dq(xmm16, ptr [eax+33]); // default xword
|
||||
vcvtpd2dq xmm21, [eax+32]{1to2} --> vcvtpd2dq(xmm21, ptr_b [eax+32]);
|
||||
vcvtpd2dq xmm0, yword [eax+33] --> vcvtpd2dq(xmm0, yword [eax+33]); // use yword for m256
|
||||
vcvtpd2dq xmm19, [eax+32]{1to4} --> vcvtpd2dq(xmm19, yword_b [eax+32]); // use yword_b to broadcast
|
||||
|
||||
vfpclassps k5{k3}, zword [rax+64], 5 --> vfpclassps(k5|k3, zword [rax+64], 5); // specify m512
|
||||
vfpclasspd k5{k3}, [rax+64]{1to2}, 5 --> vfpclasspd(k5|k3, xword_b [rax+64], 5); // broadcast 64-bit to 128-bit
|
||||
vfpclassps k5{k3}, [rax+64]{1to4}, 5 --> vfpclassps(k5|k3, xword_b [rax+64], 5); // broadcast 64-bit to 256-bit
|
||||
|
||||
vpdpbusd(xm0, xm1, xm2); // default encoding is EVEX
|
||||
vpdpbusd(xm0, xm1, xm2, EvexEncoding); // same as the above
|
||||
vpdpbusd(xm0, xm1, xm2, VexEncoding); // VEX encoding
|
||||
|
||||
注意
|
||||
* k1, ..., k7 は新しいopmaskレジスタです。
|
||||
* z, sae, rn-sae, rd-sae, ru-sae, rz-saeの代わりにT_z, T_sae, T_rn_sae, T_rd_sae, T_ru_sae, T_rz_saeを使ってください。
|
||||
* `k4 | k3`と`k3 | k4`は意味が異なります。
|
||||
* {1toX}の代わりにptr_bを使ってください。Xは自動的に決まります。
|
||||
* 一部の命令はメモリサイズを指定するためにxword/yword/zword(_b)を使ってください。
|
||||
|
||||
・ラベル
|
||||
|
||||
L(文字列);
|
||||
で定義します。ジャンプするときはその文字列を指定します。後方参照も可能ですが、
|
||||
相対アドレスが8ビットに収まらない場合はT_NEARをつけないと実行時に例外が発生
|
||||
します。
|
||||
mov(eax, "L2");の様にラベルが表すアドレスをmovの即値として使えます。
|
||||
|
||||
・hasUndefinedLabel()を呼び出して真ならジャンプ先が存在しないことを示します。
|
||||
コードを見直してください。
|
||||
|
||||
L("L1");
|
||||
jmp ("L1");
|
||||
|
||||
jmp ("L2");
|
||||
...
|
||||
少しの命令の場合。
|
||||
...
|
||||
L("L2");
|
||||
|
||||
jmp ("L3", T_NEAR);
|
||||
...
|
||||
沢山の命令がある場合
|
||||
...
|
||||
L("L3");
|
||||
|
||||
<応用編>
|
||||
|
||||
1. MASMライクな@@, @f, @bをサポート
|
||||
|
||||
L("@@"); // <A>
|
||||
jmp("@b"); // jmp to <A>
|
||||
jmp("@f"); // jmp to <B>
|
||||
L("@@"); // <B>
|
||||
jmp("@b"); // jmp to <B>
|
||||
mov(eax, "@b");
|
||||
jmp(eax); // jmp to <B>
|
||||
|
||||
2. ラベルの局所化
|
||||
|
||||
ピリオドで始まるラベルをinLocalLabel(), outLocalLabel()で挟むことで局所化できます。
|
||||
inLocalLabel(), outLocalLabel()は入れ子にすることができます。
|
||||
|
||||
void func1()
|
||||
{
|
||||
inLocalLabel();
|
||||
L(".lp"); // <A> ; ローカルラベル
|
||||
...
|
||||
jmp(".lp"); // jmpt to <A>
|
||||
L("aaa"); // グローバルラベル
|
||||
outLocalLabel();
|
||||
}
|
||||
|
||||
void func2()
|
||||
{
|
||||
inLocalLabel();
|
||||
L(".lp"); // <B> ; ローカルラベル
|
||||
func1();
|
||||
jmp(".lp"); // jmp to <B>
|
||||
outLocalLabel();
|
||||
}
|
||||
|
||||
上記サンプルではinLocalLabel(), outLocalLabel()が無いと、
|
||||
".lp"ラベルの二重定義エラーになります。
|
||||
|
||||
3. 新しいLabelクラスによるジャンプ命令
|
||||
|
||||
ジャンプ先を文字列による指定だけでなくラベルクラスを使えるようになりました。
|
||||
|
||||
Label label1, label2;
|
||||
L(label1);
|
||||
...
|
||||
jmp(label1);
|
||||
...
|
||||
jmp(label2);
|
||||
...
|
||||
L(label2);
|
||||
|
||||
更にラベルの割り当てを行うassignL(dstLabel, srcLabel)という命令も追加されました。
|
||||
|
||||
Label label2;
|
||||
Label label1 = L(); // Label label1; L(label1);と同じ意味
|
||||
...
|
||||
jmp(label2);
|
||||
...
|
||||
assignL(label2, label1);
|
||||
|
||||
上記jmp命令はlabel1にジャンプします。
|
||||
|
||||
制限
|
||||
* srcLabelはL()により飛び先が確定していないといけません。
|
||||
* dstLabelはL()により飛び先が確定していてはいけません。
|
||||
|
||||
ラベルは`getAddress()`によりそのアドレスを取得できます。
|
||||
未定義のときは0が返ります。
|
||||
```
|
||||
// not AutoGrow mode
|
||||
Label label;
|
||||
assert(label.getAddress(), 0);
|
||||
L(label);
|
||||
assert(label.getAddress(), getCurr());
|
||||
```
|
||||
|
||||
・Xbyak::CodeGenerator()コンストラクタインタフェース
|
||||
|
||||
@param maxSize [in] コード生成最大サイズ(デフォルト4096byte)
|
||||
@param userPtr [in] ユーザ指定メモリ
|
||||
|
||||
CodeGenerator(size_t maxSize = DEFAULT_MAX_CODE_SIZE, void *userPtr = 0);
|
||||
|
||||
デフォルトコードサイズは4096(=DEFAULT_MAX_CODE_SIZE)バイトです。
|
||||
それより大きなコードを生成する場合はCodeGenerator()のコンストラクタに指定してください。
|
||||
|
||||
class Quantize : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
Quantize()
|
||||
: CodeGenerator(8192)
|
||||
{
|
||||
}
|
||||
...
|
||||
};
|
||||
|
||||
またユーザ指定メモリをコード生成最大サイズと共に指定すると、CodeGeneratorは
|
||||
指定されたメモリ上にバイト列を生成します。
|
||||
|
||||
補助関数として指定されたアドレスの実行属性を変更するCodeArray::protect()と
|
||||
与えられたポインタからアライメントされたポインタを取得するCodeArray::getAlignedAddress()
|
||||
も用意しました。詳細はsample/test0.cppのuse memory allocated by userを参考に
|
||||
してください。
|
||||
|
||||
/**
|
||||
change exec permission of memory
|
||||
@param addr [in] buffer address
|
||||
@param size [in] buffer size
|
||||
@param canExec [in] true(enable to exec), false(disable to exec)
|
||||
@return true(success), false(failure)
|
||||
*/
|
||||
bool CodeArray::protect(const void *addr, size_t size, bool canExec);
|
||||
|
||||
/**
|
||||
get aligned memory pointer
|
||||
*/
|
||||
uint8 *CodeArray::getAlignedAddress(uint8 *addr, size_t alignedSize = ALIGN_SIZE);
|
||||
|
||||
・read/execモード
|
||||
デフォルトのCodeGeneratorはコンストラクト時にJIT用の領域をread/write/execモードに設定して利用します。
|
||||
コード生成時はread/writeでコード実行時にはread/execにしたい場合、次のようにしてください。
|
||||
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code()
|
||||
: Xbyak::CodeGenerator(4096, Xbyak::DontUseProtect) // JIT領域をread/writeのままコード生成
|
||||
{
|
||||
mov(eax, 123);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
Code c;
|
||||
c.setProtectModeRE(); // read/execモードに変更
|
||||
// JIT領域を実行
|
||||
|
||||
AutoGrowの場合はreadyの代わりにreadyRE()を読んでください。
|
||||
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code()
|
||||
: Xbyak::CodeGenerator(4096, Xbyak::AutoGrow) // JIT領域をread/writeのままコード生成
|
||||
{
|
||||
mov(eax, 123);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
Code c;
|
||||
c.readyRE(); // read/exeモードに変更
|
||||
// JIT領域を実行
|
||||
|
||||
setProtectModeRW()を呼ぶと領域が元のread/execモードに戻ります。
|
||||
|
||||
|
||||
その他詳細は各種サンプルを参照してください。
|
||||
-----------------------------------------------------------------------------
|
||||
◎マクロ
|
||||
|
||||
32bit環境上でコンパイルするとXBYAK32が、64bit環境上でコンパイルするとXBYAK64が
|
||||
定義されます。さらに64bit環境上ではWindows(VC)ならXBYAK64_WIN、cygwin, gcc上では
|
||||
XBYAK64_GCCが定義されます。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎使用例
|
||||
|
||||
test0.cpp ; 簡単な例(x86, x64)
|
||||
quantize.cpp ; 割り算のJITアセンブルによる量子化の高速化(x86)
|
||||
calc.cpp ; 与えられた多項式をアセンブルして実行(x86, x64)
|
||||
boost(http://www.boost.org/)が必要
|
||||
bf.cpp ; JIT Brainfuck(x86, x64)
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎ライセンス
|
||||
|
||||
修正された新しいBSDライセンスに従います。
|
||||
http://opensource.org/licenses/BSD-3-Clause
|
||||
|
||||
sample/{echo,hello}.bfは http://www.kmonos.net/alang/etc/brainfuck.php から
|
||||
いただきました。
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎履歴
|
||||
|
||||
2021/05/09 ver 5.992 endbr32とendbr64のサポート
|
||||
2020/11/16 ver 5.991 g++-5のC++14でconstexpr機能の抑制
|
||||
2020/10/19 ver 5.99 VNNI命令サポート(Thanks to akharito)
|
||||
2020/10/17 ver 5.98 [scale * reg]のサポート
|
||||
2020/09/08 ver 5.97 uint32などをuint32_tに置換
|
||||
2020/08/28 ver 5.95 レジスタクラスのコンストラクタがconstexprに対応(C++14以降)
|
||||
2020/08/04 ver 5.941 `CodeGenerator::reset()`が`ClearError()`を呼ぶように変更
|
||||
2020/07/28 ver 5.94 #include <winsock2.h>の削除 (only windows)
|
||||
2020/07/21 ver 5.93 例外なしモード追加
|
||||
2020/06/30 ver 5.92 Intel AMX命令サポート (Thanks to nshustrov)
|
||||
2020/06/19 ver 5.913 32ビット環境でXBYAK64を定義したときのmov(r64, imm64)を修正
|
||||
2020/06/19 ver 5.912 macOSの古いXcodeでもMAP_JITを有効にする(Thanks to rsdubtso)
|
||||
2020/05/10 ver 5.911 Linux/macOSでXBYAK_USE_MMAP_ALLOCATORがデフォルト有効になる
|
||||
2020/04/20 ver 5.91 マスクレジスタk0を受け入れる(マスクをしない)
|
||||
2020/04/09 ver 5.90 kmov{b,w,d,q}がサポートされないレジスタを受けると例外を投げる
|
||||
2020/02/26 ver 5.891 zm0のtype修正
|
||||
2020/01/03 ver 5.89 vfpclasspdの処理エラー修正
|
||||
2019/12/20 ver 5.88 Windowsでのコンパイルエラー修正
|
||||
2019/12/19 ver 5.87 未定義ラベルへのjmp命令のデフォルト挙動をT_NEARにするsetDefaultJmpNEAR()を追加
|
||||
2019/12/13 ver 5.86 [変更] -fno-operator-namesが指定されたときは5.84以前の挙動に戻す
|
||||
2019/12/07 ver 5.85 mmapにMAP_JITフラグを追加(macOS mojave以上)
|
||||
2019/11/29 ver 5.84 [変更] XBYAK_USE_OP_NAMESが定義されていない限りXBYAK_NO_OP_NAMESが定義されるように変更
|
||||
2019/10/12 ver 5.83 exit(1)の除去
|
||||
2019/09/23 ver 5.82 monitorx, mwaitx, clzero対応 (thanks to MagurosanTeam)
|
||||
2019/09/14 ver 5.81 いくつかの一般命令をサポート
|
||||
2019/08/01 ver 5.802 AVX512_BF16判定修正 (thanks to vpirogov)
|
||||
2019/05/27 support vp2intersectd, vp2intersectq (not tested)
|
||||
2019/05/26 ver 5.80 support vcvtne2ps2bf16, vcvtneps2bf16, vdpbf16ps
|
||||
2019/04/27 ver 5.79 vcmppd/vcmppsのptr_b対応忘れ(thanks to jkopinsky)
|
||||
2019/04/15 ver 5.78 Reg::changeBit()のリファクタリング(thanks to MerryMage)
|
||||
2019/03/06 ver 5.77 LLCキャッシュを共有数CPU数の修整(by densamoilov)
|
||||
2019/01/17 ver 5.76 Cpu::getNumCores()追加(by shelleygoel)
|
||||
2018/10/31 ver 5.751 互換性のためにXbyak::CastToの復元
|
||||
2018/10/29 ver 5.75 LabelManagerのデストラクタでLabelから参照を切り離す
|
||||
2018/10/21 ver 5.74 RegRip +/intの形をサポート Xbyak::CastToを削除
|
||||
2018/10/15 util::StackFrameでmovの代わりにpush/popを使う
|
||||
2018/09/19 ver 5.73 vpslld, vpslldq, vpsllwなどの(reg, mem, imm8)に対するevexエンコーディング修整
|
||||
2018/09/19 ver 5.72 fix the encoding of vinsertps for disp8N(Thanks to petercaday)
|
||||
2018/08/27 ver 5.71 新しいlabelインスタンスを返すL()を追加
|
||||
2018/08/27 ver 5.70 read/exec設定のためのsetProtectMode()とDontUseProtectの追加
|
||||
2018/08/24 ver 5.68 indexが16以上のVSIBエンコーディングのバグ修正(thanks to petercaday)
|
||||
2018/08/14 ver 5.67 Addressクラス内のmutableを削除 ; fix setCacheHierarchy for cloud vm
|
||||
2018/07/26 ver 5.661 mingw64対応
|
||||
2018/07/24 ver 5.66 protect()のmodeにCodeArray::PROTECT_REを追加
|
||||
2018/06/26 ver 5.65 fix push(qword [mem])
|
||||
2018/03/07 ver 5.64 Cpu()の中でzero divisionが出ることがあるのを修正
|
||||
2018/02/14 ver 5.63 Cpu::setCacheHierarchy()の修正とclang<3.9のためのEvexModifierZero修正(thanks to mgouicem)
|
||||
2018/02/13 ver 5.62 Cpu::setCacheHierarchy() by mgouicem and rsdubtso
|
||||
2018/02/07 ver 5.61 vmov*がmem{k}{z}形式対応(忘れてた)
|
||||
2018/01/24 ver 5.601 xword, ywordなどをXbyak::util名前空間に追加
|
||||
2018/01/05 ver 5.60 Ice lake系命令対応(319433-030.pdf)
|
||||
2017/08/22 ver 5.53 mpxエンコーディングバグ修正, bnd()プレフィクス追加
|
||||
2017/08/18 ver 5.52 align修正(thanks to MerryMage)
|
||||
2017/08/17 ver 5.51 multi-byte nop追加 align()はそれを使用する(thanks to inolen)
|
||||
2017/08/08 ver 5.50 mpx追加(thanks to magurosan)
|
||||
2017/08/08 ver 5.45 sha追加(thanks to magurosan)
|
||||
2017/08/08 ver 5.44 prefetchw追加(thanks to rsdubtso)
|
||||
2017/07/12 ver 5.432 PVS-studioの警告を減らす
|
||||
2017/07/09 ver 5.431 hasRex()修正 (影響なし) (thanks to drillsar)
|
||||
2017/05/14 ver 5.43 CodeGenerator::resetSize()修正(thanks to gibbed)
|
||||
2017/05/13 ver 5.42 movs{b,w,d,q}追加
|
||||
2017/01/26 ver 5.41 prefetcwt1追加とscale == 0対応(thanks to rsdubtso)
|
||||
2016/12/14 ver 5.40 Labelが示すアドレスを取得するLabel::getAddress()追加
|
||||
2016/12/07 ver 5.34 disp8N時の負のオフセット処理の修正(thanks to rsdubtso)
|
||||
2016/12/06 ver 5.33 disp8N時のvpbroadcast{b,w,d,q}, vpinsr{b,w}, vpextr{b,w}のバグ修正
|
||||
2016/12/01 ver 5.32 clang for Visual Studioサポートのために__xgetbv()を_xgetbv()に変更(thanks to freiro)
|
||||
2016/11/27 ver 5.31 AVX512_4VNNIをAVX512_4VNNIWに変更
|
||||
2016/11/27 ver 5.30 AVX512_4VNNI, AVX512_4FMAPS命令の追加(thanks to rsdubtso)
|
||||
2016/11/26 ver 5.20 AVX512_4VNNIとAVX512_4FMAPSの判定追加(thanks to rsdubtso)
|
||||
2016/11/20 ver 5.11 何故か消えていたvptest for ymm追加(thanks to gregory38)
|
||||
2016/11/20 ver 5.10 [rip+&var]の形のアドレッシング追加
|
||||
2016/09/29 ver 5.03 ERR_INVALID_OPMASK_WITH_MEMORYの判定ミス修正(thanks to PVS-Studio)
|
||||
2016/08/15 ver 5.02 xbyak_bin2hex.hをincludeしない
|
||||
2016/08/15 ver 5.011 gcc 5.4のバージョン取得ミスの修正
|
||||
2016/08/03 ver 5.01 AVXの省略表記非サポート
|
||||
2016/07/24 ver 5.00 avx-512フルサポート
|
||||
2016/06/13 avx-512 opmask命令サポート
|
||||
2016/05/05 ver 4.91 AVX-512命令の検出サポート
|
||||
2016/03/14 ver 4.901 ready()関数にコメント加筆(thanks to skmp)
|
||||
2016/02/04 ver 4.90 条件分岐命令にjcc(const void *addr);のタイプを追加
|
||||
2016/01/30 ver 4.89 vpblendvbがymmレジスタをサポートしていなかった(thanks to John Funnell)
|
||||
2016/01/24 ver 4.88 lea, cmovの16bitレジスタ対応(thanks to whyisthisfieldhere)
|
||||
2015/08/16 ver 4.87 セグメントセレクタに対応
|
||||
2015/08/16 ver 4.86 [rip + label]アドレッシングで即値を使うと壊れる(thanks to whyisthisfieldhere)
|
||||
2015/08/10 ver 4.85 Address::operator==()が間違っている(thanks to inolen)
|
||||
2015/07/22 ver 4.84 call()がvariadic template対応
|
||||
2015/05/24 ver 4.83 mobveサポート(thanks to benvanik)
|
||||
2015/05/24 ver 4.82 F16Cが使えるかどうかの判定追加
|
||||
2015/04/25 ver 4.81 setSizeが例外を投げる条件を修正(thanks to whyisthisfieldhere)
|
||||
2015/04/22 ver 4.80 rip相対でLabelのサポート(thanks to whyisthisfieldhere)
|
||||
2015/01/28 ver 4.71 adcx, adox, cmpxchg, rdseed, stacのサポート
|
||||
2014/10/14 ver 4.70 MmapAllocatorのサポート
|
||||
2014/06/13 ver 4.62 VC2014で警告抑制
|
||||
2014/05/30 ver 4.61 bt, bts, btr, btcのサポート
|
||||
2014/05/28 ver 4.60 vcvtph2ps, vcvtps2phのサポート
|
||||
2014/04/11 ver 4.52 rdrandの判定追加
|
||||
2014/03/25 ver 4.51 参照されなくなったラベルの状態を削除する
|
||||
2014/03/16 ver 4.50 新しいラベルクラスのサポート
|
||||
2014/03/05 ver 4.40 VirtualBox上でBMI/enhanced repのサポート判定を間違うことがあるのを修正
|
||||
2013/12/03 ver 4.30 Reg::cvt8(), cvt16(), cvt32()のサポート
|
||||
2013/10/16 ver 4.21 ラベルでstd::stringを受け付ける。
|
||||
2013/07/30 ver 4.20 [break backward compatibility] 従来のReg32eクラスをアドレッシング用のRegExpとReg32, Reg64を表すReg32eに分離
|
||||
2013/07/04 ver 4.10 [break backward compatibility] Xbyak::Errorの型をenumからclassに変更
|
||||
2013/06/21 ver 4.02 LABELの指すアドレスを書き込むputL(LABEL)関数の追加。
|
||||
2013/06/21 ver 4.01 vpsllw, vpslld, vpsllq, vpsraw, vpsrad, vpsrlw, vpsrld, vpsrlq support (ymm, ymm, xmm)
|
||||
support vpbroadcastb, vpbroadcastw, vpbroadcastd, vpbroadcastq(thanks to Gabest)
|
||||
2013/05/30 ver 4.00 AVX2, VEX-encoded GPR-instructionをサポート
|
||||
2013/03/27 ver 3.80 mov(reg, "label");をサポート
|
||||
2013/03/13 ver 3.76 cqo, jcxz, jecxz, jrcxz追加
|
||||
2013/01/15 ver 3.75 生成されたコードを修正するためにsetSize()を追加
|
||||
2013/01/12 ver 3.74 CodeGenerator::reset()とAllocator::useProtect()を追加
|
||||
2013/01/06 ver 3.73 可能ならunordered_mapを使う
|
||||
2012/12/04 ver 3.72 eaxなどをCodeGeneratorのメンバ変数に戻す. Xbyak::util::eaxはstatic const変数
|
||||
2012/11/17 ver 3.71 and_(), or_(), xor_(), not_()をXBYAK_NO_OP_NAMESが定義されていないときでも使えるようにした
|
||||
2012/11/17 CodeGeneratorのeax, ecx, ptrなどのメンバ変数をstaticにし、const参照をXbyak::utilにも定義
|
||||
2012/11/09 ver 3.70 and()をand_()にするためのマクロXBYAK_NO_OP_NAMESを追加(thanks to Mattias)
|
||||
2012/11/01 ver 3.62 add fwait/fnwait/finit/fninit
|
||||
2012/11/01 ver 3.61 add fldcw/fstcw
|
||||
2012/05/03 ver 3.60 Allocatorクラスのインタフェースを変更
|
||||
2012/03/23 ver 3.51 userPtrモードがバグったのを修正
|
||||
2012/03/19 ver 3.50 AutoGrowモードサポート
|
||||
2011/11/09 ver 3.05 rip相対の64bitサイズ以外の扱いのバグ修正 / movsxdサポート
|
||||
2011/08/15 ver 3.04 add(dword [ebp-8], 0xda);などにおけるimm8の扱いのバグ修正(thanks to lolcat)
|
||||
2011/06/16 ver 3.03 Macのgcc上での__GNUC_PREREQがミスってたのを修正(thanks to t_teruya)
|
||||
2011/04/28 ver 3.02 Macのgcc上ではxgetbvをdisable
|
||||
2011/03/24 ver 3.01 fix typo of OSXSAVE
|
||||
2011/03/23 ver 3.00 vcmpeqpsなどを追加
|
||||
2011/02/16 ver 2.994 beta add vmovq for 32-bit mode(I forgot it)
|
||||
2011/02/16 ver 2.993 beta remove cvtReg to avoid thread unsafe
|
||||
2011/02/10 ver 2.992 beta support one argument syntax for fadd like nasm
|
||||
2011/02/07 ver 2.991 beta fix pextrw reg, xmm, imm(Thanks to Gabest)
|
||||
2011/02/04 ver 2.99 beta support AVX
|
||||
2010/12/08 ver 2.31 fix ptr [rip + 32bit offset], support rtdscp
|
||||
2010/10/19 ver 2.30 support pclmulqdq, aesdec, aesdeclast, aesenc, aesenclast, aesimc, aeskeygenassist
|
||||
2010/07/07 ver 2.29 fix call(<label>)
|
||||
2010/06/17 ver 2.28 move some member functions to public
|
||||
2010/06/01 ver 2.27 support encoding of mov(reg64, imm) like yasm(not nasm)
|
||||
2010/05/24 ver 2.26 fix sub(rsp, 1000)
|
||||
2010/04/26 ver 2.25 add jc/jnc(I forgot to implement them...)
|
||||
2010/04/16 ver 2.24 change the prototype of rewrite() method
|
||||
2010/04/15 ver 2.23 fix align() and xbyak_util.h for Mac
|
||||
2010/02/16 ver 2.22 fix inLocalLabel()/outLocalLabel()
|
||||
2009/12/09 ver 2.21 support cygwin(gcc 4.3.2)
|
||||
2009/11/28 ver 2.20 FPUの一部命令サポート
|
||||
2009/06/25 ver 2.11 64bitモードでの mov(qword[rax], imm); 修正(thanks to Martinさん)
|
||||
2009/03/10 ver 2.10 jmp/call reg64の冗長なREG.W削除
|
||||
2009/02/24 ver 2.09 movq reg64, mmx/xmm; movq mmx/xmm, reg64追加
|
||||
2009/02/13 ver 2.08 movd(xmm7, dword[eax])が0x66を落とすバグ修正(thanks to Gabestさん)
|
||||
2008/12/30 ver 2.07 call()の相対アドレスが8bit以下のときのバグ修正(thanks to katoさん)
|
||||
2008/09/18 ver 2.06 @@, @f, @bとラベルの局所化機能追加(thanks to nobu-qさん)
|
||||
2008/09/18 ver 2.05 ptr [rip + 32bit offset]サポート(thanks to 団子厨(Dango-Chu)さん)
|
||||
2008/06/03 ver 2.04 align()のポカミス修正。mov(ptr[eax],1);などをエラーに
|
||||
2008/06/02 ver 2.03 ユーザ定義メモリインタフェースサポート
|
||||
2008/05/26 ver 2.02 protect()(on Linux)で不正な設定になることがあるのを修正(thanks to sinichiro_hさん)
|
||||
2008/04/30 ver 2.01 cmpxchg16b, cdqe追加
|
||||
2008/04/29 ver 2.00 x86/x64-64版公開
|
||||
2008/04/25 ver 1.90 x64版β公開
|
||||
2008/04/18 ver 1.12 コード整理
|
||||
2008/04/14 ver 1.11 コード整理
|
||||
2008/03/12 ver 1.10 bsf/bsr追加(忘れていた)
|
||||
2008/02/14 ver 1.09 sub eax, 1234が16bitモードで出力されていたのを修正(thanks to Robertさん)
|
||||
2007/11/05 ver 1.08 lock, xadd, xchg追加
|
||||
2007/11/02 ver 1.07 SSSE3/SSE4対応(thanks to 団子厨(Dango-Chu)さん)
|
||||
2007/09/25 ver 1.06 call((int)関数ポインタ); jmp((int)関数ポインタ);のサポート
|
||||
2007/08/04 ver 1.05 細かい修正
|
||||
2007/02/04 後方へのジャンプでT_NEARをつけないときに8bit相対アドレスに入らない
|
||||
場合に例外が発生しないバグの修正
|
||||
2007/01/21 [disp]の形のアドレス生成のバグ修正
|
||||
mov (eax|ax|al, [disp]); mov([disp], eax|ax|al);の短い表現選択
|
||||
2007/01/17 webページ作成
|
||||
2007/01/04 公開開始
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
◎著作権者
|
||||
|
||||
光成滋生(MITSUNARI Shigeo, [email protected])
|
||||
@@ -1,123 +0,0 @@
|
||||
XBYAK_INC=../xbyak/xbyak.h
|
||||
|
||||
BOOST_EXIST=$(shell echo "\#include <boost/spirit/core.hpp>" | (gcc -E - 2>/dev/null) | grep "boost/spirit/core.hpp" >/dev/null && echo "1")
|
||||
UNAME_M=$(shell uname -m)
|
||||
|
||||
ONLY_64BIT=0
|
||||
ifeq ($(shell uname -s),Darwin)
|
||||
ONLY_64BIT=1
|
||||
OS=mac
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
BIT=64
|
||||
endif
|
||||
ifeq ($(UNAME_M),i386)
|
||||
BIT=32
|
||||
endif
|
||||
ifeq ($(shell sw_vers -productVersion | cut -c1-4 | sed 's/\.//'),105)
|
||||
ifeq ($(shell sysctl -n hw.cpu64bit_capable),1)
|
||||
BIT=64
|
||||
endif
|
||||
endif
|
||||
else
|
||||
BIT=32
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
BIT=64
|
||||
endif
|
||||
ifeq ($(UNAME_M),amd64)
|
||||
BIT=64
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(BIT),64)
|
||||
TARGET += test64 bf64 memfunc64 test_util64 jmp_table64
|
||||
ifeq ($(BOOST_EXIST),1)
|
||||
TARGET += calc64 #calc2_64
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(OS),mac)
|
||||
TARGET += static_buf64
|
||||
endif
|
||||
|
||||
|
||||
ifneq ($(ONLY_64BIT),1)
|
||||
TARGET += test quantize bf toyvm test_util memfunc static_buf jmp_table
|
||||
ifeq ($(BOOST_EXIST),1)
|
||||
TARGET += calc #calc2
|
||||
endif
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
CFLAGS_WARN=-Wall -Wextra -Wformat=2 -Wcast-qual -Wcast-align -Wwrite-strings -Wfloat-equal -Wpointer-arith #-pedantic
|
||||
|
||||
CFLAGS=-g -O2 -fomit-frame-pointer -Wall -I../ $(CFLAGS_WARN)
|
||||
|
||||
test:
|
||||
$(CXX) $(CFLAGS) test0.cpp -o $@ -m32
|
||||
|
||||
quantize:
|
||||
$(CXX) $(CFLAGS) quantize.cpp -o $@ -m32
|
||||
|
||||
calc:
|
||||
$(CXX) $(CFLAGS) calc.cpp -o $@ -m32
|
||||
calc64:
|
||||
$(CXX) $(CFLAGS) calc.cpp -o $@ -m64
|
||||
calc2:
|
||||
$(CXX) $(CFLAGS) calc2.cpp -o $@ -m32
|
||||
calc2_64:
|
||||
$(CXX) $(CFLAGS) calc2.cpp -o $@ -m64
|
||||
|
||||
bf:
|
||||
$(CXX) $(CFLAGS) bf.cpp -o $@ -m32
|
||||
bf64:
|
||||
$(CXX) $(CFLAGS) bf.cpp -o $@ -m64
|
||||
|
||||
memfunc:
|
||||
$(CXX) $(CFLAGS) memfunc.cpp -o $@ -m32
|
||||
memfunc64:
|
||||
$(CXX) $(CFLAGS) memfunc.cpp -o $@ -m64
|
||||
|
||||
toyvm:
|
||||
$(CXX) $(CFLAGS) toyvm.cpp -o $@ -m32
|
||||
|
||||
test64:
|
||||
$(CXX) $(CFLAGS) test0.cpp -o $@ -m64
|
||||
test_util:
|
||||
$(CXX) $(CFLAGS) test_util.cpp -o $@ -m32
|
||||
test_util64:
|
||||
$(CXX) $(CFLAGS) test_util.cpp -o $@ -m64
|
||||
static_buf:
|
||||
$(CXX) $(CFLAGS) static_buf.cpp -o $@ -m32
|
||||
static_buf64:
|
||||
$(CXX) $(CFLAGS) static_buf.cpp -o $@ -m64
|
||||
jmp_table:
|
||||
$(CXX) $(CFLAGS) jmp_table.cpp -o $@ -m32
|
||||
jmp_table64:
|
||||
$(CXX) $(CFLAGS) jmp_table.cpp -o $@ -m64
|
||||
profiler: profiler.cpp ../xbyak/xbyak_util.h
|
||||
$(CXX) $(CFLAGS) profiler.cpp -o $@
|
||||
profiler-vtune: profiler.cpp ../xbyak/xbyak_util.h
|
||||
$(CXX) $(CFLAGS) profiler.cpp -o $@ -DXBYAK_USE_VTUNE -I /opt/intel/vtune_amplifier/include/ -L /opt/intel/vtune_amplifier/lib64 -ljitprofiling -ldl
|
||||
|
||||
clean:
|
||||
rm -rf *.o $(TARGET) *.exe profiler profiler-vtune
|
||||
|
||||
test : test0.cpp $(XBYAK_INC)
|
||||
test64: test0.cpp $(XBYAK_INC)
|
||||
quantize : quantize.cpp $(XBYAK_INC)
|
||||
calc : calc.cpp $(XBYAK_INC)
|
||||
calc64 : calc.cpp $(XBYAK_INC)
|
||||
calc2 : calc2.cpp $(XBYAK_INC)
|
||||
calc2_64 : calc2.cpp $(XBYAK_INC)
|
||||
bf : bf.cpp $(XBYAK_INC)
|
||||
bf64 : bf.cpp $(XBYAK_INC)
|
||||
memfunc : memfunc.cpp $(XBYAK_INC)
|
||||
memfunc64 : memfunc.cpp $(XBYAK_INC)
|
||||
toyvm : toyvm.cpp $(XBYAK_INC)
|
||||
static_buf: static_buf.cpp $(XBYAK_INC)
|
||||
static_buf64: static_buf.cpp $(XBYAK_INC)
|
||||
test_util : test_util.cpp $(XBYAK_INC) ../xbyak/xbyak_util.h
|
||||
test_util2 : test_util.cpp $(XBYAK_INC) ../xbyak/xbyak_util.h
|
||||
jmp_table: jmp_table.cpp $(XBYAK_INC)
|
||||
jmp_table64: jmp_table.cpp $(XBYAK_INC)
|
||||
@@ -1,211 +0,0 @@
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include "xbyak/xbyak.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stack>
|
||||
#include <fstream>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996) // scanf
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
|
||||
class Brainfuck : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
int getContinuousChar(std::istream& is, char c)
|
||||
{
|
||||
int count = 1;
|
||||
char p;
|
||||
while (is >> p) {
|
||||
if (p != c) break;
|
||||
count++;
|
||||
}
|
||||
is.unget();
|
||||
return count;
|
||||
}
|
||||
Brainfuck(std::istream& is) : CodeGenerator(100000)
|
||||
{
|
||||
// void (*)(void* putchar, void* getchar, int *stack)
|
||||
using namespace Xbyak;
|
||||
#ifdef XBYAK32
|
||||
const Reg32& pPutchar(esi);
|
||||
const Reg32& pGetchar(edi);
|
||||
const Reg32& stack(ebp);
|
||||
const Address cur = dword [stack];
|
||||
push(ebp); // stack
|
||||
push(esi);
|
||||
push(edi);
|
||||
const int P_ = 4 * 3;
|
||||
mov(pPutchar, ptr[esp + P_ + 4]); // putchar
|
||||
mov(pGetchar, ptr[esp + P_ + 8]); // getchar
|
||||
mov(stack, ptr[esp + P_ + 12]); // stack
|
||||
#elif defined(XBYAK64_WIN)
|
||||
const Reg64& pPutchar(rsi);
|
||||
const Reg64& pGetchar(rdi);
|
||||
const Reg64& stack(rbp); // stack
|
||||
const Address cur = dword [stack];
|
||||
push(rsi);
|
||||
push(rdi);
|
||||
push(rbp);
|
||||
mov(pPutchar, rcx); // putchar
|
||||
mov(pGetchar, rdx); // getchar
|
||||
mov(stack, r8); // stack
|
||||
#else
|
||||
const Reg64& pPutchar(rbx);
|
||||
const Reg64& pGetchar(rbp);
|
||||
const Reg64& stack(r12); // stack
|
||||
const Address cur = dword [stack];
|
||||
push(rbx);
|
||||
push(rbp);
|
||||
push(r12);
|
||||
mov(pPutchar, rdi); // putchar
|
||||
mov(pGetchar, rsi); // getchar
|
||||
mov(stack, rdx); // stack
|
||||
#endif
|
||||
std::stack<Label> labelF, labelB;
|
||||
char c;
|
||||
while (is >> c) {
|
||||
switch (c) {
|
||||
case '+':
|
||||
case '-':
|
||||
{
|
||||
int count = getContinuousChar(is, c);
|
||||
if (count == 1) {
|
||||
c == '+' ? inc(cur) : dec(cur);
|
||||
} else {
|
||||
add(cur, (c == '+' ? count : -count));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '>':
|
||||
case '<':
|
||||
{
|
||||
int count = getContinuousChar(is, c);
|
||||
add(stack, 4 * (c == '>' ? count : -count));
|
||||
}
|
||||
break;
|
||||
case '.':
|
||||
#ifdef XBYAK32
|
||||
push(cur);
|
||||
call(pPutchar);
|
||||
pop(eax);
|
||||
#elif defined(XBYAK64_WIN)
|
||||
mov(ecx, cur);
|
||||
sub(rsp, 32);
|
||||
call(pPutchar);
|
||||
add(rsp, 32);
|
||||
#else
|
||||
mov(edi, cur);
|
||||
call(pPutchar);
|
||||
#endif
|
||||
break;
|
||||
case ',':
|
||||
#if defined(XBYAK32) || defined(XBYAK64_GCC)
|
||||
call(pGetchar);
|
||||
#elif defined(XBYAK64_WIN)
|
||||
sub(rsp, 32);
|
||||
call(pGetchar);
|
||||
add(rsp, 32);
|
||||
#endif
|
||||
mov(cur, eax);
|
||||
break;
|
||||
case '[':
|
||||
{
|
||||
Label B = L();
|
||||
labelB.push(B);
|
||||
mov(eax, cur);
|
||||
test(eax, eax);
|
||||
Label F;
|
||||
jz(F, T_NEAR);
|
||||
labelF.push(F);
|
||||
}
|
||||
break;
|
||||
case ']':
|
||||
{
|
||||
Label B = labelB.top(); labelB.pop();
|
||||
jmp(B);
|
||||
Label F = labelF.top(); labelF.pop();
|
||||
L(F);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef XBYAK32
|
||||
pop(edi);
|
||||
pop(esi);
|
||||
pop(ebp);
|
||||
#elif defined(XBYAK64_WIN)
|
||||
pop(rbp);
|
||||
pop(rdi);
|
||||
pop(rsi);
|
||||
#else
|
||||
pop(r12);
|
||||
pop(rbp);
|
||||
pop(rbx);
|
||||
#endif
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
void dump(const uint8_t *code, size_t size)
|
||||
{
|
||||
puts("#include <stdio.h>\nstatic int stack[128 * 1024];");
|
||||
#ifdef _MSC_VER
|
||||
printf("static __declspec(align(4096)) ");
|
||||
#else
|
||||
printf("static __attribute__((aligned(4096)))");
|
||||
#endif
|
||||
puts("const unsigned char code[] = {");
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
printf("0x%02x,", code[i]); if ((i % 16) == 15) putchar('\n');
|
||||
}
|
||||
puts("\n};");
|
||||
#ifdef _MSC_VER
|
||||
puts("#include <windows.h>");
|
||||
#else
|
||||
puts("#include <unistd.h>");
|
||||
puts("#include <sys/mman.h>");
|
||||
#endif
|
||||
puts("int main()\n{");
|
||||
#ifdef _MSC_VER
|
||||
puts("\tDWORD oldProtect;");
|
||||
puts("\tVirtualProtect((void*)code, sizeof(code), PAGE_EXECUTE_READWRITE, &oldProtect);");
|
||||
#else
|
||||
puts("\tlong pageSize = sysconf(_SC_PAGESIZE) - 1;");
|
||||
puts("\tmprotect((void*)code, (sizeof(code) + pageSize) & ~pageSize, PROT_READ | PROT_EXEC);");
|
||||
#endif
|
||||
puts(
|
||||
"\t((void (*)(void*, void*, int *))code)((void*)putchar, (void*)getchar, stack);\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef XBYAK32
|
||||
fprintf(stderr, "32bit mode\n");
|
||||
#else
|
||||
fprintf(stderr, "64bit mode\n");
|
||||
#endif
|
||||
if (argc == 1) {
|
||||
fprintf(stderr, "bf filename.bf [0|1]\n");
|
||||
return 1;
|
||||
}
|
||||
std::ifstream ifs(argv[1]);
|
||||
int mode = argc == 3 ? atoi(argv[2]) : 0;
|
||||
try {
|
||||
Brainfuck bf(ifs);
|
||||
if (mode == 0) {
|
||||
static int stack[128 * 1024];
|
||||
bf.getCode<void (*)(const void*, const void*, int *)>()(reinterpret_cast<const void*>(putchar), reinterpret_cast<const void*>(getchar), stack);
|
||||
} else {
|
||||
dump(bf.getCode(), bf.getSize());
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
printf("ERR:%s\n", e.what());
|
||||
} catch (...) {
|
||||
printf("unknown error\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{654BD79B-59D3-4B10-BBAA-158BAB272828}</ProjectGuid>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Midl>
|
||||
<TypeLibraryName>.\Release/bf.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeaderOutputFile>.\Release/bf.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Midl>
|
||||
<TypeLibraryName>.\Debug/bf.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderOutputFile>.\Debug/bf.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>.\Debug/bf.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
<TypeLibraryName>.\Release/bf.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeaderOutputFile>.\Release/bf.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
<TypeLibraryName>.\Debug/bf.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderOutputFile>.\Debug/bf.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>.\Debug/bf.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bf.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
@author herumi
|
||||
|
||||
tiny calculator
|
||||
This program generates a function to calc the value of
|
||||
polynomial given by user in run-time.
|
||||
use boost::spirit::classic
|
||||
see calc2.cpp for new version of boost::spirit
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include "xbyak/xbyak.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4127) // for boost(constant condition)
|
||||
#pragma warning(disable : 4512) // for boost
|
||||
#endif
|
||||
#include <boost/spirit/include/classic_file_iterator.hpp>
|
||||
#include <boost/spirit/include/classic_core.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
enum Error {
|
||||
UNDEFINED_VARIABLE = 1
|
||||
};
|
||||
|
||||
/*
|
||||
JIT assemble of given polynomial for VC or gcc
|
||||
*/
|
||||
class FuncGen : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
typedef std::map<std::string, int> Map;
|
||||
private:
|
||||
enum {
|
||||
MAX_CONST_NUM = 32
|
||||
};
|
||||
double constTbl_[MAX_CONST_NUM];
|
||||
size_t constTblPos_;
|
||||
int regIdx_;
|
||||
Map varMap_; // map var name to index
|
||||
#ifdef XBYAK32
|
||||
const Xbyak::Reg32& valTbl_;
|
||||
const Xbyak::Reg32& tbl_;
|
||||
#else
|
||||
const Xbyak::Reg64& valTbl_;
|
||||
const Xbyak::Reg64& tbl_;
|
||||
#endif
|
||||
public:
|
||||
/*
|
||||
@param y [out] the value of f(var)
|
||||
@param var [in] table of input variables
|
||||
func(double *y, const double var[]);
|
||||
@note func does not return double to avoid difference of compiler
|
||||
*/
|
||||
FuncGen(const std::vector<std::string>& varTbl)
|
||||
: constTblPos_(0)
|
||||
, regIdx_(-1)
|
||||
#ifdef XBYAK32
|
||||
, valTbl_(eax)
|
||||
, tbl_(edx)
|
||||
#elif defined(XBYAK64_WIN)
|
||||
, valTbl_(rcx)
|
||||
, tbl_(rdx)
|
||||
#else
|
||||
, valTbl_(rdi)
|
||||
, tbl_(rsi)
|
||||
#endif
|
||||
{
|
||||
#ifdef XBYAK32
|
||||
mov(valTbl_, ptr[esp+8]); // eax == varTbl
|
||||
mov(tbl_, (size_t)constTbl_);
|
||||
#else
|
||||
#ifdef XBYAK64_WIN
|
||||
movaps(ptr [rsp + 8], xm6); // save xm6, xm7
|
||||
movaps(ptr [rsp + 8 + 16], xm7);
|
||||
#endif
|
||||
mov(tbl_, (size_t)constTbl_);
|
||||
#endif
|
||||
for (int i = 0, n = static_cast<int>(varTbl.size()); i < n; i++) {
|
||||
varMap_[varTbl[i]] = i;
|
||||
}
|
||||
}
|
||||
// use edx
|
||||
void genPush(double n)
|
||||
{
|
||||
if (constTblPos_ >= MAX_CONST_NUM) throw;
|
||||
constTbl_[constTblPos_] = n;
|
||||
if (regIdx_ == 7) throw;
|
||||
movsd(Xbyak::Xmm(++regIdx_), ptr[tbl_ + (int)(constTblPos_ * sizeof(double))]);
|
||||
constTblPos_++;
|
||||
}
|
||||
// use eax
|
||||
void genVal(const char *begin, const char *end)
|
||||
{
|
||||
std::string var(begin, end);
|
||||
if (varMap_.find(var) == varMap_.end()) throw UNDEFINED_VARIABLE;
|
||||
if (regIdx_ == 7) throw;
|
||||
movsd(Xbyak::Xmm(++regIdx_), ptr[valTbl_ + varMap_[var] * sizeof(double)]);
|
||||
}
|
||||
void genAdd(const char*, const char*)
|
||||
{
|
||||
addsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void genSub(const char*, const char*)
|
||||
{
|
||||
subsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void genMul(const char*, const char*)
|
||||
{
|
||||
mulsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void genDiv(const char*, const char*)
|
||||
{
|
||||
divsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void complete()
|
||||
{
|
||||
#ifdef XBYAK32
|
||||
mov(eax, ptr [esp + 4]); // eax = valTbl
|
||||
movsd(ptr [eax], xm0);
|
||||
#else
|
||||
#ifdef XBYAK64_WIN
|
||||
movaps(xm6, ptr [rsp + 8]);
|
||||
movaps(xm7, ptr [rsp + 8 + 16]);
|
||||
#endif
|
||||
#endif
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
struct Grammar : public boost::spirit::classic::grammar<Grammar> {
|
||||
FuncGen& f_;
|
||||
Grammar(FuncGen& f) : f_(f) { }
|
||||
template<typename ScannerT>
|
||||
struct definition {
|
||||
boost::spirit::classic::rule<ScannerT> poly0, poly1, poly2, var;
|
||||
|
||||
definition(const Grammar& self)
|
||||
{
|
||||
using namespace boost;
|
||||
using namespace boost::spirit::classic;
|
||||
|
||||
poly0 = poly1 >> *(('+' >> poly1)[bind(&FuncGen::genAdd, ref(self.f_), _1, _2)]
|
||||
| ('-' >> poly1)[bind(&FuncGen::genSub, ref(self.f_), _1, _2)]);
|
||||
poly1 = poly2 >> *(('*' >> poly2)[bind(&FuncGen::genMul, ref(self.f_), _1, _2)]
|
||||
| ('/' >> poly2)[bind(&FuncGen::genDiv, ref(self.f_), _1, _2)]);
|
||||
var = (+alpha_p)[bind(&FuncGen::genVal, ref(self.f_), _1, _2)];
|
||||
poly2 = real_p[bind(&FuncGen::genPush, ref(self.f_), _1)]
|
||||
| var
|
||||
| '(' >> poly0 >> ')';
|
||||
}
|
||||
const boost::spirit::classic::rule<ScannerT>& start() const { return poly0; }
|
||||
};
|
||||
};
|
||||
|
||||
void put(const std::vector<double>& x)
|
||||
{
|
||||
for (size_t i = 0, n = x.size(); i < n; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%f", x[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc <= 2) {
|
||||
fprintf(stderr, "calc \"var1 var2 ...\" \"function of var\"\n");
|
||||
fprintf(stderr, "eg. calc x \"x*x\"\n");
|
||||
fprintf(stderr, "eg. calc \"x y z\" \"x*x + y - z\"\n");
|
||||
return 1;
|
||||
}
|
||||
const char *poly = argv[2];
|
||||
try {
|
||||
std::vector<std::string> varTbl;
|
||||
|
||||
// get varTbl from argv[1]
|
||||
{
|
||||
std::istringstream is(argv[1]);
|
||||
int i = 0;
|
||||
printf("varTbl = { ");
|
||||
while (is) {
|
||||
std::string var;
|
||||
is >> var;
|
||||
if (var.empty()) break;
|
||||
printf("%s:%d, ", var.c_str(), i);
|
||||
varTbl.push_back(var);
|
||||
i++;
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
FuncGen funcGen(varTbl);
|
||||
Grammar calc(funcGen);
|
||||
boost::spirit::classic::parse_info<> r = parse(poly, calc, boost::spirit::classic::space_p);
|
||||
if (!r.full) {
|
||||
printf("err poly=%s\n", poly);
|
||||
return 1;
|
||||
}
|
||||
funcGen.complete();
|
||||
std::vector<double> valTbl;
|
||||
valTbl.resize(varTbl.size());
|
||||
#ifdef XBYAK32
|
||||
puts("32bit mode");
|
||||
void (*func)(double *ret, const double *valTbl) = funcGen.getCode<void (*)(double *, const double*)>();
|
||||
#else
|
||||
puts("64bit mode");
|
||||
double (*func)(const double *valTbl) = funcGen.getCode<double (*)(const double*)>();
|
||||
#endif
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (size_t j = 0, n = valTbl.size(); j < n; j++) {
|
||||
valTbl[j] = rand() % 7;
|
||||
}
|
||||
double y;
|
||||
#ifdef XBYAK32
|
||||
func(&y, &valTbl[0]);
|
||||
#else
|
||||
y = func(&valTbl[0]);
|
||||
#endif
|
||||
printf("f("); put(valTbl); printf(")=%f\n", y);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
printf("ERR:%s\n", e.what());
|
||||
} catch (Error err) {
|
||||
printf("ERR:%d\n", err);
|
||||
} catch (...) {
|
||||
printf("unknown error\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{5FDDFAA6-B947-491D-A17E-BBD863846579}</ProjectGuid>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Midl>
|
||||
<TypeLibraryName>.\Release/calc.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeaderOutputFile>.\Release/calc.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Midl>
|
||||
<TypeLibraryName>.\Debug/calc.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderOutputFile>.\Debug/calc.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>.\Debug/calc.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
<TypeLibraryName>.\Release/calc.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeaderOutputFile>.\Release/calc.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
<TypeLibraryName>.\Debug/calc.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderOutputFile>.\Debug/calc.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>.\Debug/calc.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="calc.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,302 +0,0 @@
|
||||
/*
|
||||
@author herumi
|
||||
|
||||
tiny calculator 2
|
||||
This program generates a function to calc the value of
|
||||
polynomial given by user in run-time.
|
||||
use boost::spirit::qi
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable : 4127) // for boost(constant condition)
|
||||
#pragma warning(disable : 4512) // for boost
|
||||
#pragma warning(disable : 4819)
|
||||
#endif
|
||||
#include <boost/config/warning_disable.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix_core.hpp>
|
||||
#include <boost/spirit/include/phoenix_container.hpp>
|
||||
#include <boost/spirit/include/phoenix_bind.hpp>
|
||||
#include <boost/timer.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include "xbyak/xbyak.h"
|
||||
|
||||
enum Operand {
|
||||
OpAdd,
|
||||
OpSub,
|
||||
OpMul,
|
||||
OpDiv,
|
||||
OpNeg,
|
||||
OpImm,
|
||||
OpVarX
|
||||
};
|
||||
|
||||
struct Code {
|
||||
Operand op_;
|
||||
double val_;
|
||||
Code(Operand op)
|
||||
: op_(op)
|
||||
, val_(0)
|
||||
{
|
||||
}
|
||||
Code(double val)
|
||||
: op_(OpImm)
|
||||
, val_(val)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<Code> CodeSet;
|
||||
|
||||
struct Vm {
|
||||
CodeSet code_;
|
||||
double operator()(double x) const
|
||||
{
|
||||
const size_t maxStack = 16;
|
||||
double stack[maxStack];
|
||||
double *p = stack;
|
||||
CodeSet::const_iterator pc = code_.begin();
|
||||
|
||||
while (pc != code_.end()) {
|
||||
switch (pc->op_) {
|
||||
case OpVarX:
|
||||
*p++ = x;
|
||||
break;
|
||||
case OpImm:
|
||||
*p++ = pc->val_;
|
||||
break;
|
||||
case OpNeg:
|
||||
p[-1] = -p[-1];
|
||||
break;
|
||||
case OpAdd:
|
||||
--p;
|
||||
p[-1] += p[0];
|
||||
break;
|
||||
case OpSub:
|
||||
--p;
|
||||
p[-1] -= p[0];
|
||||
break;
|
||||
case OpMul:
|
||||
--p;
|
||||
p[-1] *= p[0];
|
||||
break;
|
||||
case OpDiv:
|
||||
--p;
|
||||
p[-1] /= p[0];
|
||||
break;
|
||||
}
|
||||
++pc;
|
||||
assert(p < stack + maxStack);
|
||||
}
|
||||
return p[-1];
|
||||
}
|
||||
};
|
||||
|
||||
class Jit : public Xbyak::CodeGenerator {
|
||||
private:
|
||||
enum {
|
||||
MAX_CONST_NUM = 32
|
||||
};
|
||||
MIE_ALIGN(16) double constTbl_[MAX_CONST_NUM];
|
||||
Xbyak::uint64_t negConst_;
|
||||
size_t constTblPos_;
|
||||
#ifdef XBYAK32
|
||||
const Xbyak::Reg32& varTbl_;
|
||||
const Xbyak::Reg32& tbl_;
|
||||
#else
|
||||
const Xbyak::Reg64& tbl_;
|
||||
#endif
|
||||
int regIdx_;
|
||||
public:
|
||||
/*
|
||||
double jit(double x);
|
||||
@note 32bit: x : [esp+4], return fp0
|
||||
64bit: x [rcx](win), xmm0(gcc), return xmm0
|
||||
*/
|
||||
Jit()
|
||||
: negConst_(Xbyak::uint64_t(1) << 63)
|
||||
, constTblPos_(0)
|
||||
#ifdef XBYAK32
|
||||
, varTbl_(eax)
|
||||
, tbl_(edx)
|
||||
#elif defined(XBYAK64_WIN)
|
||||
, tbl_(rcx)
|
||||
#else
|
||||
, tbl_(rdi)
|
||||
#endif
|
||||
, regIdx_(-1)
|
||||
{
|
||||
#ifdef XBYAK32
|
||||
lea(varTbl_, ptr [esp+4]);
|
||||
#else
|
||||
#ifdef XBYAK64_WIN
|
||||
movaps(ptr [rsp + 8], xm6); // save xm6, xm7
|
||||
movaps(ptr [rsp + 8 + 16], xm7);
|
||||
#endif
|
||||
movaps(xm7, xm0); // save xm0
|
||||
#endif
|
||||
mov(tbl_, (size_t)constTbl_);
|
||||
}
|
||||
void genPush(double n)
|
||||
{
|
||||
if (constTblPos_ >= MAX_CONST_NUM) throw;
|
||||
constTbl_[constTblPos_] = n;
|
||||
if (regIdx_ == 7) throw;
|
||||
movsd(Xbyak::Xmm(++regIdx_), ptr[tbl_ + constTblPos_ * sizeof(double)]);
|
||||
constTblPos_++;
|
||||
}
|
||||
void genVarX()
|
||||
{
|
||||
#ifdef XBYAK32
|
||||
if (regIdx_ == 7) throw;
|
||||
movsd(Xbyak::Xmm(++regIdx_), ptr[varTbl_]);
|
||||
#else
|
||||
if (regIdx_ == 6) throw;
|
||||
movsd(Xbyak::Xmm(++regIdx_), xm7);
|
||||
#endif
|
||||
}
|
||||
void genAdd()
|
||||
{
|
||||
addsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void genSub()
|
||||
{
|
||||
subsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void genMul()
|
||||
{
|
||||
mulsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void genDiv()
|
||||
{
|
||||
divsd(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
|
||||
}
|
||||
void genNeg()
|
||||
{
|
||||
xorpd(Xbyak::Xmm(regIdx_), ptr [tbl_ + MAX_CONST_NUM * sizeof(double)]);
|
||||
}
|
||||
void complete()
|
||||
{
|
||||
#ifdef XBYAK32
|
||||
sub(esp, 8);
|
||||
movsd(ptr [esp], xm0);
|
||||
fld(qword [esp]);
|
||||
add(esp, 8);
|
||||
#else
|
||||
#ifdef XBYAK64_WIN
|
||||
movaps(xm6, ptr [rsp + 8]);
|
||||
movaps(xm7, ptr [rsp + 8 + 16]);
|
||||
#endif
|
||||
#endif
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct Parser : boost::spirit::qi::grammar<Iterator, boost::spirit::ascii::space_type> {
|
||||
boost::spirit::qi::rule<Iterator, boost::spirit::ascii::space_type> expression, term, factor;
|
||||
CodeSet& code_;
|
||||
Parser(CodeSet& code)
|
||||
: Parser::base_type(expression)
|
||||
, code_(code)
|
||||
{
|
||||
namespace qi = boost::spirit::qi;
|
||||
using namespace qi::labels;
|
||||
|
||||
using boost::phoenix::ref;
|
||||
using boost::phoenix::push_back;
|
||||
|
||||
expression = term >> *(('+' > term[push_back(ref(code_), OpAdd)])
|
||||
| ('-' > term[push_back(ref(code_), OpSub)]));
|
||||
|
||||
term = factor >> *(('*' > factor[push_back(ref(code_), OpMul)])
|
||||
| ('/' > factor[push_back(ref(code_), OpDiv)]));
|
||||
|
||||
factor = qi::double_[push_back(ref(code_), _1)]
|
||||
| qi::lit('x')[push_back(ref(code_), OpVarX)]
|
||||
| ('(' > expression > ')')
|
||||
| ('-' > factor[push_back(ref(code_), OpNeg)])
|
||||
| ('+' > factor);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct ParserJit : boost::spirit::qi::grammar<Iterator, boost::spirit::ascii::space_type> {
|
||||
boost::spirit::qi::rule<Iterator, boost::spirit::ascii::space_type> expression, term, factor;
|
||||
Jit code_;
|
||||
ParserJit()
|
||||
: ParserJit::base_type(expression)
|
||||
{
|
||||
namespace qi = boost::spirit::qi;
|
||||
using namespace qi::labels;
|
||||
|
||||
using boost::phoenix::ref;
|
||||
using boost::phoenix::push_back;
|
||||
using boost::phoenix::bind;
|
||||
|
||||
expression = term >> *(('+' > term[bind(&Jit::genAdd, ref(code_))])
|
||||
| ('-' > term[bind(&Jit::genSub, ref(code_))]));
|
||||
|
||||
term = factor >> *(('*' > factor[bind(&Jit::genMul, ref(code_))])
|
||||
| ('/' > factor[bind(&Jit::genDiv, ref(code_))]));
|
||||
|
||||
factor = qi::double_[bind(&Jit::genPush, ref(code_), _1)]
|
||||
| qi::lit('x')[bind(&Jit::genVarX, ref(code_))]
|
||||
| ('(' > expression > ')')
|
||||
| ('-' > factor[bind(&Jit::genNeg, ref(code_))])
|
||||
| ('+' > factor);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Func>
|
||||
void Test(const char *msg, const Func& f)
|
||||
{
|
||||
printf("%s:", msg);
|
||||
boost::timer t;
|
||||
double sum = 0;
|
||||
for (double x = 0; x < 1000; x += 0.0001) {
|
||||
sum += f(x);
|
||||
}
|
||||
printf("sum=%f, %fsec\n", sum, t.elapsed());
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "input formula\n");
|
||||
return 1;
|
||||
}
|
||||
const std::string str(argv[1]);
|
||||
|
||||
try {
|
||||
Vm vm;
|
||||
Parser<std::string::const_iterator> parser(vm.code_);
|
||||
ParserJit<std::string::const_iterator> parserJit;
|
||||
|
||||
const std::string::const_iterator end = str.end();
|
||||
|
||||
std::string::const_iterator i = str.begin();
|
||||
if (!phrase_parse(i, end, parser, boost::spirit::ascii::space) || i != end) {
|
||||
puts("err 1");
|
||||
return 1;
|
||||
}
|
||||
printf("ret=%f\n", vm(2.3));
|
||||
|
||||
i = str.begin();
|
||||
if (!phrase_parse(i, end, parserJit, boost::spirit::ascii::space) || i != end) {
|
||||
puts("err 2");
|
||||
return 1;
|
||||
}
|
||||
parserJit.code_.complete();
|
||||
double (*jit)(double) = parserJit.code_.getCode<double (*)(double)>();
|
||||
|
||||
Test("VM ", vm);
|
||||
Test("JIT", jit);
|
||||
} catch (...) {
|
||||
fprintf(stderr, "err\n");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
>>++++++++[->++++++++<]>>>>+++++++++[->++++++++++<]>[<<,[->+<<+<<+>>>]<<<[
|
||||
->>>+<<<]>>>>>[->+>>+<<<]>[<<[->+>>+<<<]>>>[-<<<+>>>]<<[[-]<->]>-]>>[-<<<+
|
||||
>>>]<<<<<<<[-<+<<+>>>]<[>>[-<+<<+>>>]<<<[->>>+<<<]>>[[-]>-<]<-]<<[->>>+<<<
|
||||
]>>>>><[[-]>++++++++++++++++++++++++++++++++>[[-]<------------------------
|
||||
-------->]<<]>>[-]<.>>]
|
||||
@@ -1,19 +0,0 @@
|
||||
++++++[->++++>>+>+>-<<<<<]>
|
||||
[<++++>>+++>++++>>+++>+++++>+++++>>>>>>++>>++<<<<<<<<<<<<<<-]
|
||||
<++++>+++>-->+++>->>--->++>>>+++++[->++>++<<]<<<<<<<<<<
|
||||
|
||||
[->
|
||||
-[>>>>>>>]>[<+++>.>.>>>>..>>>+<]<<<<<
|
||||
-[>>>>]>[<+++++>.>.>..>>>+<]>>>>
|
||||
|
||||
+<-[<<<]<[
|
||||
[-<<+>>]>>>+>+<<<<<<[->>+>+>-<<<<]<
|
||||
]>>
|
||||
|
||||
[[-]<]>[
|
||||
>>>[>.<<.<<<]<[.<<<<]>
|
||||
]
|
||||
|
||||
>.<<<<<<<<<<<
|
||||
]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
|
||||
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
|
||||
++++++++[<++++>-]<+.[-]++++++++++.
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
sample of move(reg, LABEL);, L(LABEL), putL(LABEL);
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
const int expectTbl[] = {
|
||||
5, 9, 12
|
||||
};
|
||||
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
explicit Code(int mode, size_t size, void *p)
|
||||
: Xbyak::CodeGenerator(size, p)
|
||||
{
|
||||
inLocalLabel();
|
||||
#ifdef XBYAK64
|
||||
const Xbyak::Reg64& a = rax;
|
||||
const Xbyak::Reg64& c = rcx;
|
||||
#ifdef XBYAK64_WIN
|
||||
mov(rax, rcx);
|
||||
#else
|
||||
mov(rax, rdi);
|
||||
#endif
|
||||
#else
|
||||
const Xbyak::Reg32& a = eax;
|
||||
const Xbyak::Reg32& c = ecx;
|
||||
mov(a, ptr [esp + 4]);
|
||||
#endif
|
||||
|
||||
switch (mode) {
|
||||
case 0:
|
||||
mov(c, ".jmp_table");
|
||||
lea(c, ptr [c + a * 8]);
|
||||
jmp(c);
|
||||
align(8);
|
||||
L(".jmp_table");
|
||||
mov(a, expectTbl[0]);
|
||||
ret();
|
||||
align(8);
|
||||
mov(a, expectTbl[1]);
|
||||
ret();
|
||||
align(8);
|
||||
mov(a, expectTbl[2]);
|
||||
ret();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/*
|
||||
the label for putL is defined when called
|
||||
*/
|
||||
mov(c, ".jmp_table");
|
||||
jmp(ptr [c + a * (int)sizeof(size_t)]);
|
||||
L(".label1");
|
||||
mov(a, expectTbl[0]);
|
||||
jmp(".end");
|
||||
L(".label2");
|
||||
mov(a, expectTbl[1]);
|
||||
jmp(".end");
|
||||
L(".label3");
|
||||
mov(a, expectTbl[2]);
|
||||
jmp(".end");
|
||||
L(".end");
|
||||
ret();
|
||||
ud2();
|
||||
|
||||
align(8);
|
||||
L(".jmp_table");
|
||||
putL(".label1");
|
||||
putL(".label2");
|
||||
putL(".label3");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/*
|
||||
the label for putL is not defined when called
|
||||
*/
|
||||
jmp(".in");
|
||||
ud2();
|
||||
align(8);
|
||||
L(".jmp_table");
|
||||
putL(".label1");
|
||||
putL(".label2");
|
||||
putL(".label3");
|
||||
L(".in");
|
||||
mov(c, ".jmp_table");
|
||||
jmp(ptr [c + a * (int)sizeof(size_t)]);
|
||||
L(".label1");
|
||||
mov(a, expectTbl[0]);
|
||||
jmp(".end");
|
||||
L(".label2");
|
||||
mov(a, expectTbl[1]);
|
||||
jmp(".end");
|
||||
L(".label3");
|
||||
mov(a, expectTbl[2]);
|
||||
jmp(".end");
|
||||
L(".end");
|
||||
ret();
|
||||
break;
|
||||
}
|
||||
outLocalLabel();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
try
|
||||
{
|
||||
for (int mode = 0; mode < 3; mode++) {
|
||||
printf("mode=%d\n", mode);
|
||||
for (int grow = 0; grow < 2; grow++) {
|
||||
printf("auto grow=%s\n", grow ? "on" : "off");
|
||||
Code c(mode, grow ? 30 : 4096, grow ? Xbyak::AutoGrow : 0);
|
||||
int (*f)(int) = c.getCode<int (*)(int)>();
|
||||
c.ready();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const int a = expectTbl[i];
|
||||
const int b = f(i);
|
||||
if (a != b) {
|
||||
printf("ERR i=%d, a=%d, b=%d\n", i, a, b);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
puts("ok");
|
||||
} catch (std::exception& e) {
|
||||
printf("ERR %s\n", e.what());
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
struct A {
|
||||
int x_;
|
||||
int y_;
|
||||
A() : x_(3), y_(5) {}
|
||||
int func(int a, int b, int c, int d, int e) const { return x_ + y_ + a + b + c + d + e; }
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4510 4512 4610)
|
||||
#endif
|
||||
|
||||
struct Code : public Xbyak::CodeGenerator {
|
||||
Code()
|
||||
{
|
||||
using namespace Xbyak;
|
||||
|
||||
int RET_ADJ = 0;
|
||||
#ifdef XBYAK32
|
||||
#ifdef _WIN32
|
||||
const int PARA_ADJ = 0;
|
||||
RET_ADJ = 5 * 4;
|
||||
#else
|
||||
const int PARA_ADJ = 4;
|
||||
mov(ecx, ptr [esp + 4]);
|
||||
#endif
|
||||
#endif
|
||||
const struct {
|
||||
#ifdef XBYAK32
|
||||
const Reg32& self;
|
||||
#else
|
||||
const Reg64& self;
|
||||
#endif
|
||||
const Operand& a;
|
||||
const Operand& b;
|
||||
const Operand& c;
|
||||
const Operand& d;
|
||||
const Operand& e;
|
||||
} para = {
|
||||
#if defined(XBYAK64_WIN)
|
||||
rcx,
|
||||
edx,
|
||||
r8d,
|
||||
r9d,
|
||||
ptr [rsp + 8 * 5],
|
||||
ptr [rsp + 8 * 6],
|
||||
#elif defined(XBYAK64_GCC)
|
||||
rdi,
|
||||
esi,
|
||||
edx,
|
||||
ecx,
|
||||
r8d,
|
||||
r9d,
|
||||
#else
|
||||
ecx,
|
||||
ptr [esp + 4 + PARA_ADJ],
|
||||
ptr [esp + 8 + PARA_ADJ],
|
||||
ptr [esp + 12 + PARA_ADJ],
|
||||
ptr [esp + 16 + PARA_ADJ],
|
||||
ptr [esp + 20 + PARA_ADJ],
|
||||
#endif
|
||||
};
|
||||
mov(eax, ptr [para.self]);
|
||||
add(eax, ptr [para.self + 4]);
|
||||
add(eax, para.a);
|
||||
add(eax, para.b);
|
||||
add(eax, para.c);
|
||||
add(eax, para.d);
|
||||
add(eax, para.e);
|
||||
ret(RET_ADJ);
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef XBYAK64
|
||||
printf("64bit");
|
||||
#else
|
||||
printf("32bit");
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
puts(" win");
|
||||
#else
|
||||
puts(" linux");
|
||||
#endif
|
||||
try {
|
||||
Code code;
|
||||
int (A::*p)(int, int, int, int, int) const = 0;
|
||||
const void *addr = code.getCode<void*>();
|
||||
memcpy(&p, &addr, sizeof(void*));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
A a;
|
||||
int t1, t2, t3, t4, t5, x, y;
|
||||
a.x_ = rand(); a.y_ = rand();
|
||||
t1 = rand(); t2 = rand(); t3 = rand();
|
||||
t4 = rand(); t5 = rand();
|
||||
x = a.func(t1, t2, t3, t4, t5);
|
||||
y = (a.*p)(t1, t2, t3, t4, t5);
|
||||
printf("%c %d, %d\n", x == y ? 'o' : 'x', x, y);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
printf("err=%s\n", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
How to profile JIT-code with perf or VTune
|
||||
sudo perf record ./profiler 1
|
||||
amplxe-cl -collect hotspots -result-dir r001hs -quiet ./profiler-vtune 2
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
const int N = 3000000;
|
||||
struct Code : public Xbyak::CodeGenerator {
|
||||
Code()
|
||||
{
|
||||
mov(eax, N);
|
||||
Xbyak::Label lp = L();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
sub(eax, 1);
|
||||
}
|
||||
jg(lp);
|
||||
mov(eax, 1);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
struct Code2 : public Xbyak::CodeGenerator {
|
||||
Code2()
|
||||
{
|
||||
mov(eax, N);
|
||||
Xbyak::Label lp = L();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
xorps(xm0, xm0);
|
||||
}
|
||||
sub(eax, 1);
|
||||
jg(lp);
|
||||
mov(eax, 1);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
double s1(int n)
|
||||
{
|
||||
double r = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
r += 1.0 / (i + 1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
double s2(int n)
|
||||
{
|
||||
double r = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
r += 1.0 / (i * i + 1) + 2.0 / (i + 3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int mode = argc == 1 ? 0 : atoi(argv[1]);
|
||||
Code c;
|
||||
Code2 c2;
|
||||
int (*f)() = (int (*)())c.getCode();
|
||||
int (*g)() = (int (*)())c2.getCode();
|
||||
|
||||
printf("f:%p, %d\n", (const void*)f, (int)c.getSize());
|
||||
printf("g:%p, %d\n", (const void*)g, (int)c2.getSize());
|
||||
Xbyak::util::Profiler prof;
|
||||
printf("mode=%d\n", mode);
|
||||
prof.init(mode);
|
||||
prof.set("f", (const void*)f, c.getSize());
|
||||
prof.set("g", (const void*)g, c2.getSize());
|
||||
|
||||
double sum = 0;
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
sum += s1(i);
|
||||
sum += s2(i);
|
||||
}
|
||||
printf("sum=%f\n", sum);
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
sum += f();
|
||||
}
|
||||
printf("f=%f\n", sum);
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
sum += g();
|
||||
}
|
||||
printf("g=%f\n", sum);
|
||||
puts("end");
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
struct Code1 : Xbyak::CodeGenerator {
|
||||
Code1()
|
||||
: Xbyak::CodeGenerator(4096, Xbyak::DontSetProtectRWE)
|
||||
{
|
||||
mov(eax, 123);
|
||||
ret();
|
||||
}
|
||||
void update()
|
||||
{
|
||||
db(0);
|
||||
}
|
||||
};
|
||||
|
||||
void test1(bool updateCode)
|
||||
{
|
||||
Code1 c;
|
||||
c.setProtectModeRE();
|
||||
if (updateCode) c.update(); // segmentation fault
|
||||
int (*f)() = c.getCode<int (*)()>();
|
||||
printf("f=%d\n", f());
|
||||
|
||||
c.setProtectModeRW();
|
||||
c.update();
|
||||
puts("ok");
|
||||
}
|
||||
|
||||
struct Code2 : Xbyak::CodeGenerator {
|
||||
Code2()
|
||||
: Xbyak::CodeGenerator(4096, Xbyak::AutoGrow)
|
||||
{
|
||||
mov(eax, 123);
|
||||
ret();
|
||||
}
|
||||
void update()
|
||||
{
|
||||
db(0);
|
||||
}
|
||||
};
|
||||
|
||||
void test2(bool updateCode)
|
||||
{
|
||||
Code2 c;
|
||||
c.readyRE();
|
||||
if (updateCode) c.update(); // segmentation fault
|
||||
int (*f)() = c.getCode<int (*)()>();
|
||||
printf("f=%d\n", f());
|
||||
|
||||
c.setProtectModeRW();
|
||||
c.update();
|
||||
puts("ok");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "%s <testNum> [update]\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
bool update = argc == 3;
|
||||
int n = atoi(argv[1]);
|
||||
printf("n=%d update=%d\n", n, update);
|
||||
switch (n) {
|
||||
case 1: test1(update); break;
|
||||
case 2: test2(update); break;
|
||||
default: fprintf(stderr, "no test %d\n", n); break;
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
@author herumi
|
||||
|
||||
JPEG quantize sample
|
||||
This program generates a quantization routine by using fast division algorithm in run-time.
|
||||
|
||||
time(sec)
|
||||
quality 1(high) 10 50 100(low)
|
||||
VC2005 8.0 8.0 8.0 8.0
|
||||
Xbyak 1.6 0.8 0.5 0.5
|
||||
|
||||
|
||||
; generated code at q = 1
|
||||
push esi
|
||||
push edi
|
||||
mov edi,dword ptr [esp+0Ch]
|
||||
mov esi,dword ptr [esp+10h]
|
||||
mov eax,dword ptr [esi]
|
||||
shr eax,4
|
||||
mov dword ptr [edi],eax
|
||||
mov eax,dword ptr [esi+4]
|
||||
mov edx,0BA2E8BA3h
|
||||
mul eax,edx
|
||||
shr edx,3
|
||||
...
|
||||
|
||||
; generated code at q = 100
|
||||
push esi
|
||||
push edi
|
||||
mov edi,dword ptr [esp+0Ch]
|
||||
mov esi,dword ptr [esp+10h]
|
||||
mov eax,dword ptr [esi]
|
||||
mov dword ptr [edi],eax
|
||||
mov eax,dword ptr [esi+4]
|
||||
mov dword ptr [edi+4],eax
|
||||
mov eax,dword ptr [esi+8]
|
||||
mov dword ptr [edi+8],eax
|
||||
mov eax,dword ptr [esi+0Ch]
|
||||
...
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include "xbyak/xbyak.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996) // scanf
|
||||
#endif
|
||||
|
||||
const int N = 64;
|
||||
|
||||
class Quantize : public Xbyak::CodeGenerator {
|
||||
static int ilog2(int x)
|
||||
{
|
||||
int shift = 0;
|
||||
while ((1 << shift) <= x) shift++;
|
||||
return shift - 1;
|
||||
}
|
||||
public:
|
||||
/*
|
||||
input : esi
|
||||
output : eax = [esi+offset] / dividend
|
||||
destroy : edx
|
||||
*/
|
||||
void udiv(uint32_t dividend, int offset)
|
||||
{
|
||||
mov(eax, ptr[esi + offset]);
|
||||
|
||||
/* dividend = odd x 2^exponent */
|
||||
int exponent = 0, odd = dividend;
|
||||
while ((odd & 1) == 0) {
|
||||
odd >>= 1; exponent++;
|
||||
}
|
||||
|
||||
if (odd == 1) { // trivial case
|
||||
if (exponent) {
|
||||
shr(eax, exponent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t mLow, mHigh;
|
||||
int len = ilog2(odd) + 1;
|
||||
{
|
||||
uint64_t roundUp = uint64_t(1) << (32 + len);
|
||||
uint64_t k = roundUp / (0xFFFFFFFFL - (0xFFFFFFFFL % odd));
|
||||
mLow = roundUp / odd;
|
||||
mHigh = (roundUp + k) / odd;
|
||||
}
|
||||
|
||||
while (((mLow >> 1) < (mHigh >> 1)) && (len > 0)) {
|
||||
mLow >>= 1; mHigh >>= 1; len--;
|
||||
}
|
||||
|
||||
uint64_t m; int a;
|
||||
if ((mHigh >> 32) == 0) {
|
||||
m = mHigh; a = 0;
|
||||
} else {
|
||||
len = ilog2(odd);
|
||||
uint64_t roundDown = uint64_t(1) << (32 + len);
|
||||
mLow = roundDown / odd;
|
||||
int r = (int)(roundDown % odd);
|
||||
m = (r <= (odd >> 1)) ? mLow : mLow + 1;
|
||||
a = 1;
|
||||
}
|
||||
while ((m & 1) == 0) {
|
||||
m >>= 1; len--;
|
||||
}
|
||||
len += exponent;
|
||||
|
||||
mov(edx, int(m));
|
||||
mul(edx);
|
||||
if (a) {
|
||||
add(eax, int(m));
|
||||
adc(edx, 0);
|
||||
}
|
||||
if (len) {
|
||||
shr(edx, len);
|
||||
}
|
||||
mov(eax, edx);
|
||||
}
|
||||
/*
|
||||
quantize(uint32_t dest[64], const uint32_t src[64]);
|
||||
*/
|
||||
Quantize(const uint32_t qTbl[64])
|
||||
{
|
||||
push(esi);
|
||||
push(edi);
|
||||
const int P_ = 4 * 2;
|
||||
mov(edi, ptr [esp+P_+4]); // dest
|
||||
mov(esi, ptr [esp+P_+8]); // src
|
||||
for (int i = 0; i < N; i++) {
|
||||
udiv(qTbl[i], i * 4);
|
||||
mov(ptr[edi+i*4], eax);
|
||||
}
|
||||
pop(edi);
|
||||
pop(esi);
|
||||
ret();
|
||||
}
|
||||
};
|
||||
|
||||
void quantize(uint32_t dest[64], const uint32_t src[64], const uint32_t qTbl[64])
|
||||
{
|
||||
for (int i = 0; i < N; i++) {
|
||||
dest[i] = src[i] / qTbl[i];
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef XBYAK64
|
||||
int main()
|
||||
{
|
||||
puts("not implemented for 64bit");
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int q;
|
||||
if (argc > 1) {
|
||||
q = atoi(argv[1]);
|
||||
} else {
|
||||
printf("input quantize=");
|
||||
if (scanf("%d", &q) != 1) {
|
||||
fprintf(stderr, "bad number\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
printf("q=%d\n", q);
|
||||
uint32_t qTbl[] = {
|
||||
16, 11, 10, 16, 24, 40, 51, 61,
|
||||
12, 12, 14, 19, 26, 58, 60, 55,
|
||||
14, 13, 16, 24, 40, 57, 69, 56,
|
||||
14, 17, 22, 29, 51, 87, 80, 62,
|
||||
18, 22, 37, 56, 68, 109, 103, 77,
|
||||
24, 35, 55, 64, 81, 104, 113, 92,
|
||||
49, 64, 78, 87, 103, 121, 120, 101,
|
||||
72, 92, 95, 98, 112, 100, 103, 99
|
||||
};
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
qTbl[i] /= q;
|
||||
if (qTbl[i] == 0) qTbl[i] = 1;
|
||||
}
|
||||
|
||||
try {
|
||||
uint32_t src[N];
|
||||
uint32_t dest[N];
|
||||
uint32_t dest2[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
src[i] = rand() % 2048;
|
||||
}
|
||||
|
||||
Quantize jit(qTbl);
|
||||
//printf("jit size=%d, ptr=%p\n", jit.getSize(), jit.getCode());
|
||||
void (*quantize2)(uint32_t*, const uint32_t*, const uint32_t *) = jit.getCode<void (*)(uint32_t*, const uint32_t*, const uint32_t *)>();
|
||||
|
||||
quantize(dest, src, qTbl);
|
||||
quantize2(dest2, src, qTbl);
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (dest[i] != dest2[i]) {
|
||||
printf("err[%d] %d %d\n", i, dest[i], dest2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const int count = 10000000;
|
||||
int begin;
|
||||
|
||||
begin = clock();
|
||||
for (int i = 0; i < count; i++) {
|
||||
quantize(dest, src, qTbl);
|
||||
}
|
||||
printf("time=%.1fsec\n", (clock() - begin) / double(CLOCKS_PER_SEC));
|
||||
|
||||
begin = clock();
|
||||
for (int i = 0; i < count; i++) {
|
||||
quantize2(dest, src, qTbl);
|
||||
}
|
||||
printf("time=%.1fsec\n", (clock() - begin) / double(CLOCKS_PER_SEC));
|
||||
} catch (std::exception& e) {
|
||||
printf("ERR:%s\n", e.what());
|
||||
} catch (...) {
|
||||
printf("unknown error\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,228 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{D06753BF-E1F3-4578-9B18-08673327F77C}</ProjectGuid>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Midl>
|
||||
<TypeLibraryName>.\Debug/quantize.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderOutputFile>.\Debug/quantize.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>.\Debug/quantize.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Midl>
|
||||
<TypeLibraryName>.\Release/quantize.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeaderOutputFile>.\Release/quantize.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
<TypeLibraryName>.\Debug/quantize.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderOutputFile>.\Debug/quantize.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>.\Debug/quantize.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
<TypeLibraryName>.\Release/quantize.tlb</TypeLibraryName>
|
||||
<HeaderFileName />
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeaderOutputFile>.\Release/quantize.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0411</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Bscmake>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="quantize.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,29 +0,0 @@
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
#ifdef XBYAK32
|
||||
#error "this sample is for only 64-bit mode"
|
||||
#endif
|
||||
|
||||
struct Code : public Xbyak::CodeGenerator {
|
||||
Code()
|
||||
{
|
||||
// see xbyak/sample/sf_test.cpp for how to use other parameter
|
||||
Xbyak::util::StackFrame sf(this, 3);
|
||||
mov(rax, sf.p[0]);
|
||||
add(rax, sf.p[1]);
|
||||
add(rax, sf.p[2]);
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Code c;
|
||||
int (*f)(int, int, int) = c.getCode<int(*) (int, int, int)>();
|
||||
int ret = f(3, 5, 2);
|
||||
if (ret == 3 + 5 + 2) {
|
||||
puts("ok");
|
||||
} else {
|
||||
puts("ng");
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
sample to use static memory
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#define XBYAK_NO_OP_NAMES
|
||||
#include "xbyak/xbyak.h"
|
||||
|
||||
MIE_ALIGN(4096) char buf[4096];
|
||||
|
||||
struct Code : Xbyak::CodeGenerator {
|
||||
Code()
|
||||
: Xbyak::CodeGenerator(sizeof(buf), buf)
|
||||
{
|
||||
puts("generate");
|
||||
printf("ptr=%p, %p\n", getCode(), buf);
|
||||
#ifdef XBYAK32
|
||||
mov(eax, ptr [esp + 4]);
|
||||
add(eax, ptr [esp + 8]);
|
||||
#elif defined(XBYAK64_WIN)
|
||||
lea(rax, ptr [rcx + rdx]);
|
||||
#else
|
||||
lea(rax, ptr [rdi + rsi]);
|
||||
#endif
|
||||
ret();
|
||||
Xbyak::CodeArray::protect(buf, sizeof(buf), Xbyak::CodeArray::PROTECT_RE);
|
||||
}
|
||||
~Code()
|
||||
{
|
||||
Xbyak::CodeArray::protect(buf, sizeof(buf), Xbyak::CodeArray::PROTECT_RW);
|
||||
}
|
||||
} s_code;
|
||||
|
||||
inline int add(int a, int b)
|
||||
{
|
||||
return reinterpret_cast<int (*)(int, int)>(buf)(a, b);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
sum += add(i, 5);
|
||||
}
|
||||
printf("sum=%d\n", sum);
|
||||
}
|
||||
Loaded 100 of 505 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user