326 Commits
Author SHA1 Message Date
Flyinghead 5eadfb388e dreampotato: change timeout values. disconnect on receive error
initial RTT timeout is 500 ms then 100 ms
2026-01-16 13:12:03 +01:00
Flyinghead 83f6d818b5 maplelink: disable savestates. Change some options default. Bug fix
Disable load/save state if at least one MapleLink with storage enabled.
Change UsePhysicalMemory default to false.
Change Per Game VMU A1 default to true.
Fix DreamLink infinite loop on exit.
dreampotato, dreamconn: don't use stringstream for output
dreampotato: libretro support (no VMU storage yet)
2026-01-16 13:08:54 +01:00
Flyinghead bbae3de9a4 switch: build fix 2026-01-15 17:23:08 +01:00
Flyinghead c2f66018d8 lr: build fix 2026-01-15 16:17:00 +01:00
Flyinghead 82ca2db61a !win32 build fix 2026-01-15 16:01:26 +01:00
Flyinghead b4d83c0d12 DreamLink refactoring
Introduce MapleLink base interface and DreamPotato implementation.
2026-01-15 15:45:14 +01:00
Stefan Schlosser 1aba1b467e deps: prevent installing DreamPicoPort artifacts
Currently the install-target of the DreamPicoPort-API dependency is
executed when installing flycast. This results in the include file
DreamPicoPortApi.hpp and the static library libdream_pico_port_api.a to
be installed alongside flycast.

Add EXCLUDE_FROM_ALL to add_subdirectory to prevent this.
2026-01-14 12:17:21 +01:00
Flyinghead 5be5eeb905 naomi: more rom crc32
[ci skip]
2026-01-14 12:17:02 +01:00
Flyinghead 2f842238a5 ini: don't save transient values and empty sections 2026-01-14 12:16:23 +01:00
flyinghead 3de74672ce Crowdin Translations: Japanese, Swedish 2026-01-13 11:41:42 +01:00
Flyinghead b76b1c7275 Merge remote-tracking branch 'origin/master' into dev
[ci skip]
2026-01-13 11:39:33 +01:00
Flyinghead 392a429e8b boxart: replace invalid utf-8 chars instead of failing when saving
Open (and truncate) the file only after json serialization.
2026-01-07 14:28:32 +01:00
Flyinghead 0491f2e6a5 reios: check boot file destination ptr before loading
Abort load if ptr is null (size > 0xff0000)
Fixes MINIDUMP-7W9 MINIDUMP-84B MINIDUMP-87V
2026-01-07 09:50:35 +01:00
Flyinghead 33d9a58cf9 emu: don't crash if unexpected state in Emulator::start()
Simply log and ignore. Happens on android under some undetermined
conditions.
Fixes MINIDUMP-2Q MINIDUMP-3SG
2026-01-07 08:52:36 +01:00
Flyinghead 401332d520 sdl: don't run haptic sine effect if rumblePower is 0 2026-01-06 16:23:47 +01:00
Flyinghead 8511d1ad3c dreamconn: only build on win32. remove unused code 2026-01-06 16:21:35 +01:00
Flyinghead 7ce50e5229 mmu: stricter test before enabling on-demand MMU
Visual Concepts games and many naomi and systemsp games create some
bogus mappings at boot. Ignore those

Issue #2184
2026-01-05 11:48:03 +01:00
Flyinghead 99fc194dd0 ini: transient values always hide regular ones regardless of order 2026-01-05 11:42:41 +01:00
Craig d794b53512 webOS: disable OPENMP as not available until webOS 3.9.2 (#2188)
[ci skip]
2026-01-04 21:10:03 +01:00
vkedwardli bd8f7c8a75 Update imgui to v1.92.5 (#2180)
Update ImGui from v1.92.1 to v1.92.5
2026-01-04 10:56:36 +01:00
Craig 594d82b223 add webOS to CMakeLists (#2167)
[ci skip]
2026-01-04 10:55:23 +01:00
Flyinghead fd72eebbe3 mmu: enable full MMU when non-SQ mapping is created
Fixes super mario 64 audio
Issue #2184
[ci skip]
2026-01-04 10:53:12 +01:00
Flyinghead fea8699884 modem: enforce correct tx speed for dissymmetric connections 2026-01-04 10:48:01 +01:00
Flyinghead d00b5d4c17 modem: 56K support, V8 bis protocol 2026-01-03 17:52:43 +01:00
Flyinghead 74cbee908f arm64 rec: proper fix for MINIDUMP-7W0
ModeImmediateHelper can generate code, resulting in the Mov op being
generated twice.
Fixes MINIDUMP-7W0
2026-01-02 11:51:23 +01:00
flyinghead 1652143f5f New translations flycast.pot (Hungarian)
New translations for {languageName}
2025-12-31 18:25:10 +01:00
Flyinghead 4886dcb1c2 enable rtt2vram for Jikkyo Powerful Pro Yakyu
Issue #2182
2025-12-29 20:20:15 +01:00
flyinghead 75bca2a166 Crowdin Translations: Hungarian and Japanese 2025-12-28 17:51:30 +01:00
Flyinghead 13df86c520 modem: V42bis compression support
[ci skip]
2025-12-28 17:51:01 +01:00
Rikki Gibson 16fc172ffc DreamConn: fix compat issue involving slot 2 VMU support (#2172)
* Do not create a shared_ptr from destructor
* Fall back when MDC_DeviceRequest message does not have an expected response
* Assume the device type for physical DreamConn controllers
* DreamConn controllers do not support physical VMU memory
* Do not maple_ReconnectDevices for DreamConn controller
2025-12-27 19:40:13 +01:00
Flyinghead dfb2ae8987 android: touch mouse may be null so check before delivering events
Fixes MINIDUMP-6S6
[ci skip]
2025-12-27 19:36:51 +01:00
Flyinghead 4529a31372 don't save state when game filename is empty
Happens on android apparently due to issues with automatic savestate and
app pausing.
2025-12-27 19:35:30 +01:00
Flyinghead 5a29ac489f sdl: fix DreamPicoPort crash
io_context_thread can be null in the destructor.
Fixes MINIDUMP-83F
2025-12-27 19:31:32 +01:00
github-actions[bot] c4c3c2ae93 Fetch translations & Recreate libretro_core_options_intl.h 2025-12-26 22:21:56 +00:00
flyinghead 8a228e4e16 Crowdin Translations: Swedish 2025-12-24 17:25:38 +01:00
Flyinghead 0a3c68f7cc i18n: delete bogus zh_CH.po. Load correct one
[ci skip]
2025-12-24 17:25:01 +01:00
flyinghead 4905dc21c8 Crowdin Translations: Japanese 2025-12-23 15:30:29 +01:00
Flyinghead 98dfb4c32a modem: implement V14 sync-async mode and V42 protocol
Use modem sync mode when ASYN is reset.
Implement V14 sync-async mode for V42 detection. Also used by Power
Smash and Pro Yakyu Team de Asobou Net!
Switch to V42 protocol for most games.
Set correct modem speed and timing as set by the game.
Get rid of modem speed/automode cheats.
[ci skip]
2025-12-23 15:29:42 +01:00
Flyinghead df2bdfc9bf ui: support for Asian fonts on linux
Fix width of "Filter" field in the game list.
crowdin.yml: fix typo for zh_CN resource files.
2025-12-23 12:00:53 +01:00
flyinghead e86c1c992f Crowdin Translations: Japanese
courtesy of @kihato
2025-12-22 21:23:57 +01:00
Flyinghead 5b6cc33a75 i18n: add Japanese translation
[ci skip]
2025-12-22 21:22:27 +01:00
flyinghead e37518524f Crowdin Translations: Hungarian and Portuguese, Brazilian 2025-12-21 15:04:05 +01:00
Flyinghead 994f72f954 ci: upload symbols to sentry on push only
[ci skip]
2025-12-21 15:03:09 +01:00
Flyinghead 5978b3a058 ui: merge Asian glyphs into large font too
Issue #2169
2025-12-21 15:00:28 +01:00
flyinghead bb12d5c72d Crowdin Translations (#2162) Hungarian
courtesy of oldman63
2025-12-18 15:56:18 +01:00
Flyinghead b127c96cf3 i18n: Add hu.po to resources
[ci skip]
2025-12-18 15:54:19 +01:00
scribam 0d3beb1c6d fix misleading indentation warning 2025-12-17 14:00:11 +01:00
Flyinghead 9bd3179745 i18n: set all stringstreams to classic locale
Fixes dumped texture names being formatted with the thousands separator
of the current locale.
[ci skip]
2025-12-17 13:59:25 +01:00
Flyinghead 5833fb3a47 ui: use imgui touch support and get rid of delay touch hack 2025-12-16 17:20:45 +01:00
Flyinghead 49ec396e24 i18n: delete empty message files. add zh_HK.po 2025-12-16 17:20:45 +01:00
Stefan Schlosser 5607b8eebd libretro: freebsd build fix 2025-12-16 17:18:52 +01:00
scribam 35888406d3 ci: libretro android build fixes (#2156)
Followup to #2151
[ci skip]
2025-12-16 17:18:32 +01:00
scribam 6b3bbd6f85 ci: update actions (#2155)
* ci: update actions
* ci: use freebsd 15 and openbsd 7.8
[ci skip]
2025-12-16 17:18:07 +01:00
James Smith ef689fd35d Minor bug fixes for DreamPicoPort 2025-12-15 18:05:32 +01:00
scribam 6a78f27bef ci: libretro android build fixes (#2151)
[ci skip]
2025-12-15 18:05:11 +01:00
scribam 97de155fd8 ci: enable libretro on bsd systems (#2150)
[ci skip]
2025-12-15 18:01:47 +01:00
scribam f183f30262 dragonflybsd: build fixes (#2149)
[ci skip]
2025-12-15 18:01:08 +01:00
flyinghead 065c91f9ad Crowdin Translations (#2148)
[ci skip]
2025-12-15 18:00:22 +01:00
Edward Li f65907a256 Remove custom_texture.terminate() during TexCache.Clear() 2025-12-15 12:27:57 +01:00
scribam 161d057d4c haiku: build fixes 2025-12-14 14:47:24 +01:00
scribam a7ee2e9a66 libretro: netbsd/openbsd build fixes 2025-12-14 14:46:52 +01:00
flyinghead e08ec86962 Crowdin Translations (#2143)
[ci skip]
2025-12-14 14:46:29 +01:00
Flyinghead 7653b1c811 sh4: don't crash when FIPR is executed with PR=1
[ci skip]
2025-12-14 14:45:53 +01:00
Flyinghead 1475812a94 i18n: localize input devices names, buttons and axes 2025-12-14 14:35:09 +01:00
flyinghead c8a724ad63 Crowdin Translations (#2142)
French, Swedish
2025-12-13 17:25:16 +01:00
Flyinghead 2cfce747c5 ui: better egl init test. display message box on graphics init error
Fail if eglGetDisplay() is null.
Throw exception on initRenderApi/switchRenderApi failure.
Catch in SDL and Android and show error message.
Localize Android java code.

Fixes MINIDUMP-7SD MINIDUMP-2XH MINIDUMP-5KM MINIDUMP-2FF MINIDUMP-4ZP
MINIDUMP-6T1 MINIDUMP-5D3 MINIDUMP-7ME
[ci skip]
2025-12-13 17:23:55 +01:00
github-actions[bot] 37a9077304 Fetch translations & Recreate libretro_core_options_intl.h 2025-12-12 22:21:51 +00:00
Flyinghead f8bdf6c35e crowdin.yml: fix pt_BR typo
[ci skip]
2025-12-12 21:51:46 +01:00
Flyinghead f4c2d001bd crowdin.yml: add android resource
[ci skip]
2025-12-12 20:04:07 +01:00
Flyinghead f3ddec5f1e android: add localizable strings resource
[ci skip]
2025-12-12 19:53:04 +01:00
scribam 13c7ddc6c9 deps: remove core/deps/libusb-cmake 2025-12-12 19:39:50 +01:00
nexus382 c24d7fb0fe Send game id to vmu pro/vm2 (#2126)
[ci skip]
2025-12-12 19:39:28 +01:00
Flyinghead 3ebc79a5ce Merge remote-tracking branch 'origin/master' into dev [ci skip] 2025-12-12 19:32:09 +01:00
Flyinghead 27a913fc02 arm recs: ensure block linking size doesn't depend on mmu mode
Due to d0ec876936 the mmu enabled path may
be taken when the emu is pausing, so make sure block linking uses the
same number of ops in both branches.
Fixes MINIDUMP-82C MINIDUMP-7T2 MINIDUMP-7W0
2025-12-12 16:49:50 +01:00
Flyinghead 97442c0411 terminate game id at first null character
Fixes MINIDUMP-7YW MINIDUMP-800
2025-12-12 16:13:44 +01:00
Flyinghead 5b12f9ca68 boxart: catch-all handler for scraper thread
Fixes MINIDUMP-7YW
2025-12-12 16:07:22 +01:00
Flyinghead 6a298289a9 ice: guard against null icePipe
Fixes MINIDUMP-7ZF
2025-12-12 16:03:44 +01:00
Flyinghead 59ef8579d2 ui: disable imgui debug tools
Fixes MINIDUMP-7RF
2025-12-12 16:01:52 +01:00
Flyinghead 6989a0aa2c test: fneg on odd register in PR=1 mode
Issue #2139
2025-12-11 16:26:37 +01:00
Flyinghead bd24812d0c jvs: return value from RawDma is incorrect for MIE firmware upload
It didn't seem to bother anyone however
2025-12-11 16:25:03 +01:00
Flyinghead 15889616dd add zh_CH and zh_TW messages. delete outdated docs
Issue #1830
2025-12-11 15:15:43 +01:00
Flyinghead e60efed8b5 pvr: limit framebuffer height to 240 when interlacing is disabled
Fixes Confidential Mission PAL Echelon intro
Issue #2062
2025-12-11 15:11:24 +01:00
Flyinghead 11fcadf37e sh4: fneg doesn't depend on FPSCR.PR at all
When PR=1, fneg can also be used on odd registers.
Issue #2139
2025-12-11 15:03:16 +01:00
Flyinghead 88b8a0b51e crowdin.yml: add zh_HK mapping [ci skip] 2025-12-10 17:15:24 +01:00
flyinghead 8e9a0f9c1f Crowdin Translations (#2136) pt_BR
New translations flycast.pot (Portuguese, Brazilian)
2025-12-10 11:42:58 +01:00
Flyinghead dd50fcf81c i18n: include pt_BR.po [ci skip] 2025-12-10 11:41:30 +01:00
Flyinghead 7b8656b944 imgread: more checks when loading GDI and CUE files (fail fast)
Add missing CMakeLists.txt change for new cue and gdi tests
2025-12-10 11:40:41 +01:00
Flyinghead 00ae6c42a2 fix some include paths 2025-12-09 19:41:48 +01:00
Flyinghead 8c4c463c38 add cue and gdi parsing tests. add non-C locale tests
delete old en resource file
libretro doesn't need cfg/ini.cpp/h anymore
2025-12-09 17:31:31 +01:00
Flyinghead 14f759eeb7 ice: fix crash when loading a state without being connected 2025-12-09 14:36:53 +01:00
flyinghead 329b6a1e34 Crowdin Translations (#2134)
New translations: sv, pt, pt_BR
2025-12-09 09:23:08 +01:00
Flyinghead 9c28800384 i18n: std::from/to_chars missing on BSD and macOS
Use stringstream instead

[ci skip]
2025-12-09 09:21:54 +01:00
Flyinghead 602982fb56 add pt_BR crowdin mapping [ci skip] 2025-12-08 23:38:12 +01:00
flyinghead cb830e0bd3 Update Crowdin configuration file 2025-12-08 23:35:08 +01:00
Flyinghead ec3d85a96c remove [ci skip] in crowdin commit messages 2025-12-08 23:26:58 +01:00
Flyinghead d3bc72b0d0 ci: ignore push on l10n_* branches 2025-12-08 22:28:30 +01:00
Flyinghead 4170e783b9 fix GDI and CUE parsing error in non-English locale
IniFile: use locale-independant float conversions functions
Use std::ostringstream and std::istringstream as appropriate.
Use C locale for GDI and CUE parsing.
2025-12-08 22:26:33 +01:00
flyinghead 083ccb984a Crowdin Translations (#2133)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
* New translations flycast.pot (Swedish)
New translations for {languageName} [ci skip]
2025-12-08 21:19:31 +01:00
Flyinghead 0220207545 strprintf utility. localize exception messages. add sv.po (Swedish) 2025-12-08 16:22:26 +01:00
flyinghead aee4cd34fc Crowdin Translations (#2132)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
* New translations flycast.pot (Swedish)
New translations for {languageName} [ci skip]
* New translations flycast.pot (English, United Kingdom)
New translations for {languageName} [ci skip]
2025-12-08 16:15:17 +01:00
flyinghead a99cce27bb Crowdin Translations (#2131)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-08 14:11:01 +01:00
Flyinghead b2941a8593 ui: more l10n. add plural and context support. no l10n for debug stuff 2025-12-08 11:50:51 +01:00
flyinghead 3ad072f9aa New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-08 09:37:39 +01:00
flyinghead 14374084d5 New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-07 23:18:42 +01:00
Flyinghead 5c7a241897 uwp build fix 2025-12-07 21:39:13 +01:00
Flyinghead 7fcc5fa2f7 ui: update .pot and .po 2025-12-07 20:37:35 +01:00
Flyinghead cf2fe027f7 ci: bsd,switch,uwp build fixes. Better workflow names 2025-12-07 20:33:57 +01:00
Flyinghead b13685f0bb ui: switch to tinygettext for i18n 2025-12-07 19:57:03 +01:00
Flyinghead cf7b6bdcb0 ui: more localization 2025-12-06 17:11:48 +01:00
github-actions[bot] 230d971d4d Fetch translations & Recreate libretro_core_options_intl.h 2025-12-05 22:22:07 +00:00
Flyinghead 894c61ed64 libretro build fix 2025-12-04 21:41:36 +01:00
Flyinghead 82cadfde22 win: use GetLocaleInfoEx and localtime. android: build fix 2025-12-04 21:29:20 +01:00
Flyinghead 1586cbaea9 android: use system getifaddrs if available (api 24+) 2025-12-04 18:21:17 +01:00
Flyinghead 2113e4e249 localization support WIP. rewrite ini file reader/writer and more.
Support for localized dates, collation and messages.
Rewrite ini file reader/writer and command line parser.
Standardize all config stuff in the config namespace.
2025-12-04 18:17:13 +01:00
Flyinghead 5a150f4121 Merge remote-tracking branch 'origin/dev' 2025-12-03 19:23:43 +01:00
vkedwardli f178e5d76e Custom texture preloading (#2116)
* Creating a `CustomTextureSource` interface for subclassing
* `DumpReplacedTextures` option
* `PreloadCustomTextures` option
2025-12-02 17:02:19 +01:00
James 9222fa8442 Fix: DreamPicoPort locks up and causes flycast to hang (#2124)
* Use DreamPicoPort-API 1.0.2 which automatically clears USB stalls
* Getting condition in order to detect controller seems to be problematic
* Use the libusb implementation of DreamPicoPort-API on Windows because WinRT is buggy
2025-12-01 16:50:02 +01:00
Flyinghead 77359cefb2 ui: add char filter for dns names and IP addresses text input 2025-12-01 16:49:13 +01:00
Flyinghead a6dc38a35b switch: use sync keyboard applet for keyboard input 2025-11-30 18:23:15 +01:00
Flyinghead eab952fcbe ui: UWP keyboard fix. Fix DPad nav & scrolling with child windows
Detect UWP on-screen keyboard hiding and validate imgui text edit (code
by @danprice142).
Don't use list boxes for paths in Settings > General since child windows
cause issues with DPad/cursor keys navigation and thumbstick scrolling.
2025-11-29 18:06:21 +01:00
Flyinghead 1666eb0875 android: ignore touchpad axes (dualshock 4)
The touchpad also reports motion events on the same axis numbers, but
the range source is different so ignore them.
Override known axes in mapping when the gamepad is connected.
Issue #2109
2025-11-22 11:34:36 +01:00
Flyinghead 5bce763f66 Merge remote-tracking branch 'origin/dev' 2025-11-21 12:49:47 +01:00
Flyinghead 0f4fca415b ui: set VMUPath and SavePath options a single path instead of a list
Fix naming of arcade flash save files when using a custom path.
Get rid of legacy net:nvmem option.
Issue #2111
2025-11-20 16:43:27 +01:00
Flyinghead f2fe918379 android: Virtual Gamepad and Touch Mouse duplicated after app pause
These devices were recreated every time the app is unpaused.
2025-11-20 09:43:53 +01:00
Flyinghead a9be4d111c Use nowide::remove instead of unlink or std::remove 2025-11-20 09:37:04 +01:00
danprice142 ce07a7fbf5 Add user-defined paths for boxart, mappings & cheats (#2108)
* Add user-configurable paths for boxart, mappings, and cheats

Introduces new configuration options for Boxart path, Mappings path, and Cheat path, allowing users to specify custom folders for box art, controller mappings, and cheat files.
2025-11-19 15:30:30 +01:00
Flyinghead e44c03fa0e ui: reset button state when closing mapping popup and on map/unmap
Fixes button state staying on until app restart.
2025-11-19 15:29:15 +01:00
Flyinghead 1dac3695fd imgui: ImguiStyleVar must be deleted before the end of the window 2025-11-18 18:58:44 +01:00
Flyinghead 997f6179dd android: normalize axis value using min and range. Use event.actionIndex
Normalize analog axis values using InputDevice.MotionRange.min and
InputDevice.MotionRange.range.
Specify Event.actionIndex when getting axis values like SDL does.
Issue #2109
2025-11-18 18:57:02 +01:00
Flyinghead bed524f0d0 imgui: ImguiStyleVar must be deleted before the end of the window 2025-11-17 19:18:55 +01:00
Flyinghead 410b37b041 naomi: add dc microphone to king of route 66. Add cd-mic switch binding
King of route 66 uses a maple microphone for the CB-MIC feature.
Also define the mic switch button binding (button 6).
Don't add VMUs to naomi2 games since no game uses them afaik
2025-11-17 19:13:45 +01:00
Flyinghead ead4b51d6f fix savestate regression due to custom path
Issue #2111
2025-11-16 11:45:23 +01:00
Flyinghead 043bb5ee99 Merge remote-tracking branch 'origin/master' into dev 2025-11-16 11:41:40 +01:00
Flyinghead 4747e61908 vgamepad: enable touch mouse for mouse/rotary encoders arcade games
Issue #2110
2025-11-15 15:49:26 +01:00
github-actions[bot] 548cf6fe2e Fetch translations & Recreate libretro_core_options_intl.h 2025-11-14 22:21:15 +00:00
Edward Li 339d288dfb macOS: update Syphon library 2025-11-14 17:13:35 +01:00
Flyinghead 8f7e4bf2d3 aica: fix ALFO and PLFO random waveform
Fixes skating sound in Jet Set Radio
Issue #2107
2025-11-14 17:12:58 +01:00
Flyinghead 922ca3e54b cue: read pregap length from cue file and adjust track start
Use the INDEX 01 mm:ss:ff tag to determine the pregap size and add it to
the track start FAD.
Remove previous +02:00 hack for audio tracks.
Fixes Sonic Team jingle cut off in ChuChu Rocket redump cue.
Issue #2103
2025-11-14 11:12:44 +01:00
Flyinghead 4b09de2fc1 vulkan: recreate swap chain when the vulkan context becomes invalid
Fixes freeze when switching to fullscreen on gentoo.
Issue #2082
2025-11-14 10:57:14 +01:00
Flyinghead 98db7a6cd5 android: vgamepad LT and RT not set to minimum when released
LT and RT were left to 50% depressed when released.
Regression due to 7b0c106fc2
Issue #2104
2025-11-13 10:06:16 +01:00
Flyinghead 3405b5b2a6 aica: use member functions for ChannelEx instead of static ones
More enums
No functional changes
2025-11-12 20:50:31 +01:00
Flyinghead 4320685ff8 uwp: simplify activation and exit URIs handling
Use http::urlDecode to decode URIs.
Move exit URI handling to dc_exit() so that all exit paths are covered.
2025-11-12 13:40:07 +01:00
Flyinghead cd3fe7393c http: add urlDecode and tests for urlEncode and urlDecode 2025-11-11 20:43:13 +01:00
Flyinghead 2da0d392fd move oslib/directory.h function defs into a .cpp file 2025-11-11 19:24:12 +01:00
Flyinghead 8d1bf3382c android: add customisable bios and texture pack folders 2025-11-11 19:15:59 +01:00
Bobby Smith b1489d665e [Libretro] Update AV info when switching a PAL game to 60Hz 2025-11-10 14:16:02 +01:00
Flyinghead fc56bd283e fix double-nested game folder when dumping textures
Change TextureDumpPath to a single path instead of a list
Issue #305
2025-11-10 14:11:14 +01:00
Flyinghead 807e4a20ed aica: yet another LPF improvement
Use more bits for coefficients and formula closer to original hardware.
2025-11-10 13:52:21 +01:00
Flyinghead 943311752e cheats: increase max code length and input text height 2025-11-10 11:50:40 +01:00
Flyinghead e7ad12b031 rend: convert palette textures for area 1 on cpu
Only one palette is supported in the shader, so convert area 1 palette
textures (2-volume mode) on the cpu and handle them normally in the
shader.
Issue #1528
2025-11-09 15:44:48 +01:00
danprice142 b2bc6251c7 Add configurable paths for BIOS, VMU, saves, textures & dumps (#2097)
* Add configurable paths for BIOS, VMU, saves, textures & dumps

Introduces new options for user-defined folders for BIOS, VMU, savestates, game saves, texture packs, and texture dumps. Updates path resolution logic in oslib.cpp to prioritize user-specified paths before falling back to defaults.

* Add UWP support for directory and file access

Introduces UWP-specific implementations for directory handling and file access in core/oslib, including custom opendir, readdir, and closedir functions, this is so we can see subfolders properly on UWP

* Custom Paths section hidden on Android and iOS
2025-11-08 15:06:47 +01:00
Flyinghead 6f38dc3210 msvc build fix 2025-10-28 12:23:22 +01:00
danprice142 4bb32466bd Add UWP protocol activation support for ROM loading from UWP Frontends (#2095)
Adds UWP protocol activation support for ROM loading from UWP Frontends/Browsers, allows launching ROMs via SDL's `SDL_WinRTGetProtocolActivationURI()` and redirection using the 'launchOnExit' URI parameter from protocol activation in SDL_WinRTApp, Closing a Rom/Game from the quick menu or closing the app will make use of `launchOnExit` and redirect to the specified app

Implements launching a URI via Windows.System.Launcher after the game is stopped from the Exit button in the quick menu, enabling return to sender/redirection on UWP

Update SDL submodule to include SDL_WinRTGetProtocolActivationURI()
2025-10-28 11:37:16 +01:00
Rikki Gibson e26d356265 DreamConn: allow using a VMU in slot 2 (#2094) 2025-10-28 11:34:31 +01:00
Flyinghead 265c5c6c3d aica: fix EG rate and LSA > LEA behaviour from real hw tests
EG rate formula specified in the doc is correct but FRS+OCT is ignored
if negative.
If LSA > LEA, playback continues after LEA until reaching LSA. Then CA
is reset and playback stops (including looping sounds). Get rid of
Legacy of Kain hack.
2025-10-27 17:26:42 +01:00
Flyinghead 2b1eb55daa maple: flush VMU files when unloading a game
destroy maple devices when unloading a game to flush and close VMU
files.
2025-10-24 15:31:22 +02:00
Flyinghead bd136b1856 ice: vonot support
ice::term() ice on emu termination
2025-10-24 15:29:12 +02:00
Flyinghead bd8adf1da1 delete outdated dreampi dcnet binary 2025-10-22 15:35:51 +02:00
Flyinghead 11625176c2 ggpo: catch serialization exception when saving state 2025-10-22 15:33:10 +02:00
Flyinghead c64441a02f pvr: avoid pure blue video border color. change to black
Hides blue vertical bars in dinosaur king.
Issue #1246
2025-10-22 15:29:35 +02:00
Flyinghead 8e9eee246d Merge remote-tracking branch 'origin/master' into dev 2025-10-21 15:26:52 +02:00
Flyinghead d0ec876936 arm32/64 dynarec: fix crash when disabling mmu
With block linking, CpuRunning isn't tested immediately so add a special
case in DynaRBI::Relink()
Issue #893
Fixes MINIDUMP-56 MINIDUMP-6V MINIDUMP-6X MINIDUMP-CA
Fixes MINIDUMP-H8 MINIDUMP-K6 MINIDUMP-QN MINIDUMP-YZ
Fixes MINIDUMP-YM MINIDUMP-148
2025-10-21 15:24:31 +02:00
Flyinghead b19cbcbf29 android: clean up openmp stuff 2025-10-21 15:18:48 +02:00
John Lansing bf2bd7efed Move duplicate Exception class to SerializeBase 2025-10-21 12:48:39 +02:00
John Lansing d15d5be44a libretro: detect and communicate buffer overflow on save state serialization 2025-10-21 12:48:39 +02:00
Flyinghead 70c8d1d22b vk: handle acquireNextImageKHR error when resizing window
Ignoring the error leads to a deadlock since the imageAcquired semaphore
can't be signaled.
Fixes freeze when resizing window.
Issue #1620
2025-10-21 12:47:46 +02:00
JamesandRikki Gibson 3917c0181b DreamPicoPort API Update (#2079)
Co-authored-by: Rikki Gibson <[email protected]>
2025-10-20 10:48:23 +02:00
Flyinghead 6f58d204b6 arm, arm64 dynarecs: check FPU enabled after PC and block checks
Fixes hang/crash in Sega Rally 2 when exiting the Records menu.
Issue #1877
2025-10-20 10:46:30 +02:00
Flyinghead d450c84a14 rend: disable texture upscaling on mobile. hide option on desktop 2025-10-20 10:31:35 +02:00
Flyinghead 3b7036d14a android: fix init crash on 32-bit platforms 2025-10-19 15:45:40 +02:00
Flyinghead 5d628f8167 naomi: default eeprom for clubkprz. default nvmem for smarinef
Use clubk2k3 eeprom for clubk2kp and clubk2kpa.
Issue #2086
2025-10-18 18:09:29 +02:00
github-actions[bot] d1ed878446 Fetch translations & Recreate libretro_core_options_intl.h 2025-10-17 22:20:56 +00:00
Flyinghead 28ea3dc69c arcade: add CRC for all working roms. Fix mushik2k bios
All roms can now be loaded by CRC value.
Issue #1952
Fix mushik2k to use the multi-region hacked bios set to Korean region
2025-10-16 18:11:16 +02:00
Flyinghead 031a645ffc move HideCdromDrives check into ui/game_scanner 2025-10-16 18:07:09 +02:00
Flyinghead 0a526d7c94 gdb: fix race condition when stepping. Improve vCont handling
Work around race condition when stepping (CpuRunning still true in
Sh4Executor::Step() on emu thread since Step() hasn't be called yet in
main thread).
Don't advertise vCont commands that aren't supported.
Range stepping end is exclusive.
2025-10-16 17:56:39 +02:00
Flyinghead 3d594054eb libretro build fix 2025-10-15 20:30:23 +02:00
Flyinghead 3b651463dc New [config]HideCdromDrives setting to ignore physical CD/DVD drives 2025-10-15 20:00:30 +02:00
Flyinghead 4e6b5b270b vk: remove image barrier at end of oit pipeline
It seems to cause issues on snapdragon 8 elite (SM8750-AB). These GPUs
never cease to amaze me....
Partial revert of b4b4b3ab1e
Issue #2083
2025-10-15 19:58:41 +02:00
Flyinghead fd8f2e24b8 android: switch to sdk 35, min 21, ndk 29
Drop support for Android 4.* (kitkat, jelly bean)
Minimum SDK 21 (Android 5.0, lollipop)
Target SDK 35 (Android 15)
Build with NDK 29 for 16KB-page by default. Issue #1864
2025-10-14 16:47:50 +02:00
Flyinghead 82b4c55f09 fix gdb server networking
Issue #2081
2025-10-14 14:43:47 +02:00
Flyinghead 8176646c5a android: don't set section name for dynarec code cache
Use #define to declare platform-specific dynarec code buffers
Don't set section name on android to avoid error "W+E load segments are
not allowed"
2025-10-13 19:19:48 +02:00
Flyinghead 3f0dd626c1 android: update oboe to 1.10.0 2025-10-13 19:11:12 +02:00
Flyinghead 8edd8fc447 Merge remote-tracking branch 'origin/dev' 2025-10-13 16:58:06 +02:00
Flyinghead 0c7dca9cb7 sdl: move mapping parser into its own class. Add tests 2025-10-13 16:20:34 +02:00
Flyinghead 086e8bc20c android: exclude libz.so from apk 2025-10-13 16:06:47 +02:00
Flyinghead 11880bca30 atomiswave: fix lightgun games button labels
Issue #2080
2025-10-13 10:25:30 +02:00
scribam 37187467e7 cmake: lower minimum requirement 2025-10-12 11:55:35 +02:00
Flyinghead 7b0c106fc2 input: axes handling improvements
Range for all axes, including triggers, is now [-32768,32767]
Detect trigger direction when mapping and store in mapping file.
sdl: parse controller bindings string to correctly map axes.
Issue #2014
2025-10-12 11:54:16 +02:00
github-actions[bot] fd654614ae Fetch translations & Recreate libretro_core_options_intl.h 2025-10-10 22:20:45 +00:00
Flyinghead 4ee24ca612 vk: test automation fix 2025-10-10 09:01:31 +02:00
scribam 4b3704953f ci: use freebsd 14.3 2025-10-09 19:24:16 +02:00
Flyinghead 6ea9a34e8a macOS CI: update vulkan sdk version 2025-10-08 17:09:05 +02:00
Flyinghead c018d46d00 atomiswave: reset maple devices register during reset
Reset aw_maple_devs to 0 during reset.
Fixes analog controls not working after playing a mouse/trackball or
lightgun game.
Issue #2072
2025-10-06 19:39:31 +02:00
Flyinghead 400da2bdfe vk: add yet another image barrier for libretro (nvidia 30xx issue)
Also add image barrier in endFrame for libretro
Issue #1631
2025-10-06 15:42:52 +02:00
Flyinghead 3b5536dd43 new system sp games: btlracer, monomedal 2025-10-06 15:39:20 +02:00
Flyinghead 34e2f426ac build fix 2025-10-05 15:53:24 +02:00
Flyinghead a246964a99 fix some warnings 2025-10-05 15:35:52 +02:00
Flyinghead b4b4b3ab1e vk: add image barrier to work around nvidia 30xx issue
Add image barrier in VulkanContext::PresentFrame() for nvidia gpus.
Tentative fix for square glitches on nvidia 30xx series.
Issue #1631
oit: Add dependency from external to subpass 2 since vulkan's implicit
one is too relaxed.
2025-10-05 15:34:18 +02:00
github-actions[bot] af5f67c15d Fetch translations & Recreate libretro_core_options_intl.h 2025-10-03 22:21:01 +00:00
Rikki GibsonandJames 909ea496ac DreamPotato connectivity (#1989)
Co-authored-by: James <[email protected]>
2025-10-03 14:34:25 +02:00
Flyinghead 3347dc1a46 custom textures: reset available flag when initializing
Fixes crash when starting a new game without custom texture after one
that has some.
Fixes MINIDUMP-6C9
2025-10-03 14:28:31 +02:00
Flyinghead dd5e148e2b input: don't handle button input when port is set to none
Fixes physical keyboard usage on android, where the keyboard is also
handled as a gamepad and must be disabled.
2025-10-03 14:12:23 +02:00
Flyinghead 1f818ca750 naomi: fix zombrvn and zombrvne default eeproms
zombrvn: Disable free play, do analog calibration
zombrvne: Select 2-player cab
Issue #1980
2025-10-03 11:10:44 +02:00
Flyinghead ab6b95ff4d naomi: spawn needs the region number in its eeprom
The current region must be set in the eeprom, otherwise the eeprom is
reset to default and network settings are ignored.
Issue #696
2025-09-27 17:28:20 +02:00
github-actions[bot] 760c62a3da Fetch translations & Recreate libretro_core_options_intl.h 2025-09-26 22:20:52 +00:00
Flyinghead b2d8ad633d boxart: use flycast-content github repo for arcade games screenshots 2025-09-26 15:30:01 +02:00
Flyinghead f9b47b6f91 boxart: use correct file extension for images 2025-09-23 15:55:36 +02:00
Flyinghead 372ef91211 opengl: per-pixel support for GLES
Issue #2034
2025-09-23 15:53:14 +02:00
scribam 3375f6c476 deps: update sdl to version 2.32.10 2025-09-22 09:57:07 +02:00
scribam c5396a041e cmake: add windows setupapi lib 2025-09-22 09:56:56 +02:00
github-actions[bot] 851dd22664 Fetch translations & Recreate libretro_core_options_intl.h 2025-09-19 22:21:19 +00:00
github-actions[bot] ac32a80352 Fetch translations & Recreate libretro_core_options_intl.h 2025-09-12 22:20:41 +00:00
Flyinghead eff11d6a07 Merge remote-tracking branch 'origin/master' into dev 2025-09-12 18:15:23 +02:00
scribam b6c135b0c6 macOS: update libomp to version 21.1.1 2025-09-12 13:44:50 +02:00
scribam 0fe7073496 macOS: remove workaround with local/pinned/cmake from openmp formula 2025-09-12 13:44:50 +02:00
Edward Li d5c5839a92 macOS libomp: Use local/pinned/cmake in homebrew formula, update libomp to 21.1 2025-09-09 14:38:25 +02:00
Clownacy 7ac45a7e46 Remove -DCMAKE_BUILD_TYPE=Release from .gitlab-ci.yml.
There is no longer a need to set this explicitly, as libretro's CI templates have been corrected to not leave it undefined.

See this commit: https://git.libretro.com/libretro-infrastructure/ci-templates/-/commit/0241726e188a7c077dcff6cd7c1739039a79a7fb
2025-09-08 10:39:53 +02:00
github-actions[bot] 0243f81c26 Fetch translations & Recreate libretro_core_options_intl.h 2025-08-29 22:20:49 +00:00
Flyinghead 2dfa647445 naomi: cab linking support for sgtetris 2025-08-29 09:12:55 +02:00
Flyinghead 9c5408a6d3 macOS build fix
Work around brew change: https://github.com/Homebrew/brew/pull/20414
2025-08-20 14:55:37 +02:00
Eric Warmenhoven d3dcb13ef9 Update .gitlab-ci.yml 2025-08-18 14:40:14 +02:00
Flyinghead 2cb2ac23e0 force real bios for wsb2k2
Issue #1757
2025-08-18 14:39:39 +02:00
Flyinghead e38b0ac01f naomi: delete some unnecessary eeproms
Issue #2023
Fix some warnings
2025-08-14 14:30:20 +02:00
Flyinghead 09b27ddd5c maple: use PantherDC id for full controller. ui: buttons and axes state
Use actual Panther DC identifier for full controller. Fixes KISS: Psycho
Circus.
Issue #2026
Show current state of mapped gamepad buttons and axes in controller
mapping.
2025-08-13 18:26:26 +02:00
Flyinghead 33833cfd1e vonot: don't disable VM when acting as master
Issue #2037
2025-08-12 17:30:22 +02:00
Edward Li 3c5c1e0b66 Indentation should use <space> in imgui_impl_vulkan.cpp 2025-08-12 15:40:14 +02:00
Edward Li 3f446a5835 Use target_compile_definitions to disable imgui shell functions for Switch 2025-08-12 15:40:14 +02:00
Edward Li df3c676a04 chmod 644 2025-08-12 15:40:14 +02:00
Edward Li 9ff3ab618c Fix OpenGL3 and Vulkan backend 2025-08-12 15:40:14 +02:00
Edward Li ba33b8c3f7 IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS for Switch 2025-08-12 15:40:14 +02:00
Edward Li eab13e85bc minor fixes 2025-08-12 15:40:14 +02:00
Edward Li a1f1c9825a Fix EllipsisCharCount
Fonts: Rasterizing ellipsis character from dot as one glyph + avoid preloading if it not needed. https://github.com/ocornut/imgui/commit/4ff1631b316fe849da9a649cf30a79aad752d8bb
2025-08-12 15:40:14 +02:00
Edward Li 43a9c1384a Replace ImFont->FontSize with ImFont->LegacySize 2025-08-12 15:40:14 +02:00
Edward Li b4ab21a768 Reapply ImGuiWindowFlags_DragScrolling 2025-08-12 15:40:14 +02:00
Edward Li 30365d13ef replace with upstream v1.92.1 files 2025-08-12 15:40:14 +02:00
scribam f7eae0ff9e deps: update glslang (#1920) 2025-08-11 17:23:33 +02:00
sonninnos e26f170558 Libretro updates (#2033)
Add -Wno-misleading-indentation
Libretro av_info fps timing correction
Stop forcing audio muting on libretro fast-forward
Allow less strict chd extensions
2025-08-11 09:38:59 +02:00
github-actions[bot] 40e400ab08 Fetch translations & Recreate libretro_core_options_intl.h 2025-08-01 22:21:35 +00:00
github-actions[bot] 5743502cdd Fetch translations & Recreate libretro_core_options_intl.h 2025-07-25 22:21:43 +00:00
Flyinghead 8f33b63153 linux build: fix AppImageTool location 2025-07-22 15:37:54 +02:00
Flyinghead 6fca05f00e sdl: set default axis direction for all axes
Fixes regression with known gamepads.
Only use initial state for unknown controllers.
Issue #2014
2025-07-22 15:24:05 +02:00
Flyinghead 8c07a2e1d6 sdl: detect half-axes using initial state. Fix axis range and direction
Assume that full axes are roughly centered at start up and determine
which axis are half ones, and the axis direction.
Fixes driving wheel pedals range to the expected [0, 32767].
Issue #2014
2025-07-18 11:38:15 +02:00
desyn ffbe266413 bump minimal rcheevos 2025-07-15 15:52:07 +02:00
Flyinghead 5d3b7fae6b libretro: boot dreamcast bios when loading core without content
Get rid of "Boot to BIOS" core setting.
Issue #2007
2025-07-15 15:49:32 +02:00
Flyinghead 738b95d2be miniupnpc: include cstddef before miniupnpc headers
Issue #2010
2025-07-15 15:30:15 +02:00
github-actions[bot] 48c58dbd18 Fetch translations & Recreate libretro_core_options_intl.h 2025-07-11 22:21:51 +00:00
James 54b06a5c34 Recover DreamLink hardware after load state (#2003) 2025-07-10 20:32:00 +02:00
Flyinghead d3a46a1e33 windows: fix crash caused by regression
introduced by 6ea61b9
Issue #2001
2025-07-08 12:36:52 +02:00
Rikki Gibson 4fe2f4760f DreamConn: do not lock mutex multiple times 2025-07-07 11:28:27 +02:00
Flyinghead c0809d771d dcnet: support for Pro Yakyuu Team de Asobou Net!
Delete fakeTCP power smash cheat (not needed)
Add pro yakyuu team cheats
dcnet: Use same port as power smash for pro yakyuu team
2025-07-07 11:26:08 +02:00
Flyinghead 54ed92e2b0 fix ggpo regression
introduced by 4baf529ff7
2025-07-06 22:18:58 +02:00
Flyinghead 6ea61b9084 android: support 4KB and 16KB page sizes on 64-bit platforms 2025-07-06 22:16:06 +02:00
Flyinghead f3e0f09ca9 upgrade oboe and libadrenotools to latest 2025-07-06 18:51:51 +02:00
scribam 301b1a0f00 input: set InputMapping::INVALID_CODE as constexpr 2025-07-06 18:09:23 +02:00
scribam b787427446 android: update gradle and agp 2025-07-06 18:08:54 +02:00
Flyinghead 5df5c70994 dcnet: Power Smash support 2025-07-03 17:18:46 +02:00
Shockblast 75defb590b fix: added missing addrspace inclusion in settings.cpp for GDB_SERVER 2025-07-01 19:29:16 +02:00
scribam fbeaf8ba16 ci: fix switch libretro artifact location 2025-06-30 21:34:41 +02:00
github-actions[bot] 3f79b6baed Fetch translations & Recreate libretro_core_options_intl.h 2025-06-27 22:21:54 +00:00
Flyinghead 34f095a91e mingw build fix 2025-06-24 17:19:12 +02:00
Flyinghead 09247b4503 picotcp: use random local UDP port if already in use
Bind to random local UDP port if requested one is in use.
Log to NETWORK instead of MODEM.
Fix get_last_error name conflict with asio.
2025-06-24 16:54:57 +02:00
scribam 109e084e18 deps: update glslang (#1920) 2025-06-24 16:54:29 +02:00
Isaac Marovitz af043c55cd CMake Refactors (#1979)
Signed-off-by: Isaac Marovitz <[email protected]>
2025-06-24 16:33:12 +02:00
Flyinghead 04999d8969 sh4: fix wince api trace calls 2025-06-21 10:25:52 +02:00
Flyinghead 26b8684e78 serial: don't depend on SerialPTY option on windows
On windows, open serial console window when config::SerialConsole is
enabled. Ignore config::SerialPTY.
2025-06-21 10:23:22 +02:00
Flyinghead eefcb0e05d picotcp: improve ppp dump time resolution 2025-06-21 10:17:09 +02:00
github-actions[bot] 449d256995 Fetch translations & Recreate libretro_core_options_intl.h 2025-06-20 22:21:29 +00:00
Flyinghead 0897f0ed8a ui: split settings into their own files 2025-06-19 23:13:47 +02:00
Flyinghead 9bdb90e605 vk: add missing pp_Texture vertex param (naomi 2) 2025-06-19 17:48:20 +02:00
Flyinghead 5b75975237 naomi: fix zombie revenge analog stick for P2
Issue #1980
2025-06-19 17:29:25 +02:00
Flyinghead c73336a86f boxart: don't save absolute paths for images
Issue #1935
2025-06-17 17:13:27 +02:00
Flyinghead 27318443a7 more atomiswave code into its own files 2025-06-16 22:21:10 +02:00
Flyinghead df93c069f2 sdl: add sdl_gamepad.cpp 2025-06-16 18:50:37 +02:00
Flyinghead 7358f29556 naomi: refactor midiffb into its own file 2025-06-16 18:13:36 +02:00
Flyinghead 8801b2ae16 haptic: add sine effect. implement spring for midi FFB
Add sine effect to haptic api and stop using maple rumble for naomi FFB.
Emulate spring in midi FFB. Thanks to njz3 for his help on decoding the
protocol.
Don't use exponential scale for rumble when using haptic sine effect.

serialize Sh4OCache::writeBack/ThroughBufferCycles
2025-06-16 17:50:36 +02:00
github-actions[bot] b10db5a078 Fetch translations & Recreate libretro_core_options_intl.h 2025-06-13 22:21:19 +00:00
xander-will 71341a27e4 Integer Scaling + Screen-Level Linear Interpolation Options (#1976) 2025-06-13 11:59:15 +02:00
Flyinghead 7e7f645f37 sdl: use SDL_HAPTIC_STEERING_AXIS instead of SDL_HAPTIC_CARTESIAN
tentative fix for Issue #1962
2025-06-13 11:58:19 +02:00
oltolm 146e90dae6 Fix MSYS2 build (#1975)
* fix GCC build
* revert c53a1c42c6
it was fixed in https://sourceforge.net/p/mingw-w64/mingw-w64/ci/251fc7e1c3bd3509e291c1862ee745fce8550360
2025-06-13 09:55:41 +02:00
Flyinghead bb5bd71437 Merge remote-tracking branch 'origin/master' into dev 2025-06-07 16:08:03 +02:00
Flyinghead e8217ca0c5 fix input mapping regressions
Emulator keyboard bindings no longer working when UI is open (Exit).
All-ports mapping not being loaded correctly.
All-ports mapping not working on ports B,C and D.
Restore automatic binding of opposite key/axis.
caused by 64b9e51af0
2025-06-07 16:05:34 +02:00
github-actions[bot] 8f033723a1 Fetch translations & Recreate libretro_core_options_intl.h 2025-06-06 22:21:35 +00:00
Flyinghead b097aa3331 dropping iOS support
well done Gabe!
2025-06-06 09:08:14 +02:00
nexus382andJames Smith 64b9e51af0 button mapping multi binds (#1938)
Add button combination mappping support
- Implement tracking of pressed buttons in GamepadDevice
- Add ability to detect button combinations- Fix mapping system to preserve individual button mappings
- Ensure buttons can work both individually and as part of combinations
- Ensured trigger buttons can be the first button in a combination
- Maintained detection state when binding multiple buttons
- Added special handling to recognize both original and normalized trigger codes

---------

Co-authored-by: James Smith <[email protected]>
2025-06-03 16:28:51 +02:00
nexus382andJames Smith 9b91430513 Flycast UI Themes (#1929)
Added a Theme selection system.
---------
Co-authored-by: James Smith <[email protected]>
2025-06-01 19:03:42 +02:00
Flyinghead d9ec0cb3ab map dinoking P2 rock button to P1 button 4. Show on vgamepad
Remap player 2 Rock button to player 1 button 4 and display on vgamepad
so it's accessible on mobile devices.
Issue #1969
2025-06-01 10:57:59 +02:00
Flyinghead 39d68ec7fa sdl: don't reset haptic gain to 0 on exit
Issue #1963
2025-06-01 10:52:29 +02:00
github-actions[bot] a1a782df38 Fetch translations & Recreate libretro_core_options_intl.h 2025-05-30 22:21:39 +00:00
scribam acc106e988 cmake: disable link option --execute-only on openbsd 2025-05-30 18:45:55 +02:00
Flyinghead 11de0eda13 dx9: half-pixel shift left, not right
Fixes shifting of doom64 screen melt effect
Issue #1939
2025-05-30 18:45:18 +02:00
scribam a7148ad7ae cmake: use c++ extensions for alloca on netbsd/arm64 2025-05-26 19:27:55 +02:00
Flyinghead 27f19a5b54 rend: fix dithering kernel
Use the actual PVR2 dithering kernel (standard 4x4 Bayer matrix) used on
real hardware.
Fixes the screen melt effect of Doom64 (per-pixel only).
Issue #1939
2025-05-26 19:27:02 +02:00
scribam b973453317 ci: build uwp for ARM64 and Win32 2025-05-24 19:24:24 +02:00
github-actions[bot] ffaada9e9d Fetch translations & Recreate libretro_core_options_intl.h 2025-05-23 22:21:23 +00:00
Flyinghead 1953eb0eea outtrigger (US) widescreen cheat. Propeller arena modem string patch 2025-05-22 20:23:23 +02:00
scribam c3275d3c6f ci: update bsd workflows 2025-05-21 23:22:47 +02:00
Flyinghead 5e769d5cd2 Merge remote-tracking branch 'origin/master' into dev 2025-05-21 23:21:49 +02:00
Flyinghead f26e10e44a modem string patch for Mobile Suit Gundam: Federation vs.Zeon
Set minimum tx and rx speed to 0.
2025-05-21 08:28:29 +02:00
Flyinghead 3d60fc8c60 Revert "core/deps/picotcp/modules/pico_tcp.c: More checks for correct header sizes"
This reverts commit ffc32d2d86.
PR #1937
2025-05-21 08:22:35 +02:00
Flyinghead 7adeb53aee arm64 rec: fix typo in FTRC implementation
Issue #1905
2025-05-20 08:52:09 +02:00
James eacadf24a6 DreamPicoPort Mapping Updates (for maraca support) (#1940)
Add rightx and righty for DreamPicoPort (for maraca support)
Add secondary d-pad to dreampicoport mapping
Allow DreamLink device to dictate button/axis name; set custom button names in DreamPicoPort to known codes
2025-05-19 10:37:03 +02:00
Flyinghead 8e14c7f394 Merge remote-tracking branch 'origin/master' into dev 2025-05-18 20:28:41 +02:00
Flyinghead a842edb09e naomi: add mushi2k3 rom 2025-05-18 12:40:49 +02:00
github-actions[bot] e2247e4872 Fetch translations & Recreate libretro_core_options_intl.h 2025-05-16 22:21:04 +00:00
Flyinghead eb662da389 flash: add bba settings if missing. cheats for outtrigger and bomberman
Add bba config information to flash if missing. Fixes outtrigger.
cheat: Enable automode in modem connection string for Bomberman Online
and Outtrigger (US)
cheat: disable dricas auth for outtrigger (JP)
2025-05-15 16:46:54 +02:00
Flyinghead 2b79f93d6b ui: dcnet is no longer experimental 2025-05-14 19:50:14 +02:00
npt-1707 ffc32d2d86 core/deps/picotcp/modules/pico_tcp.c: More checks for correct header sizes 2025-05-10 20:56:59 +02:00
Flyinghead 892cdb7cea x86 dynarec: fix crash
Regression due to e394840344
Fixes MINIDUMP-6RC
2025-05-10 20:56:21 +02:00
Flyinghead 90ed30f70c appimage: fix some missing shared libraries
Issue #1930
2025-05-08 21:43:40 +02:00
scribam 34f8ab292f deps: update sdl to version 2.32.6 2025-05-04 20:57:18 +02:00
Flyinghead e1ab9af89d oit: incorrect color used when src=OTHER_COLOR if dest is 2nd accum
Using src=OTHER_COLOR with destination secondary accumulator should use
the secondary accumulator color, not the final one.
Fixes dark gun in Doom 64 with Ultra graphics (bump mapping).
Issue #1771
2025-05-02 17:46:29 +02:00
Flyinghead 3330225de4 vk: (mali) add subpass dependency from subpass 2 to external
Add subpass dependency from the last subpass to external/top of pipe.
Fix glitches in upper left corner when using OIT on Mali GPUs.
Issue #1014
Issue #1234
Issue #1356
Issue #1497
Issue #1852
2025-05-02 15:37:29 +02:00
Flyinghead 58ce9a4159 vk: (mali) add memory barrier in GetLastFrame
Add memory barrier between drawing and transfer.
Fix savestate thumbnail image corruption.
Issue #1922
2025-05-02 15:30:45 +02:00
Flyinghead 1b5c8678a4 dreampi: select best access point when connecting 2025-04-30 23:02:41 +02:00
Flyinghead c20ec680a8 Merge remote-tracking branch 'origin/master' into dev 2025-04-30 22:58:14 +02:00
nexus382 f62582a4bd Screen position bug fix with multiple monitors (#1916) 2025-04-28 18:58:33 +02:00
James Smith 1273c06d4b Load mapping for sdl_gamepad through loadMapping() and getDefaultMapping() instead of custom implementation 2025-04-16 21:05:40 +02:00
Flyinghead 332be9c8aa linux AppImage build needs libfuse 2025-04-12 09:52:54 +02:00
Flyinghead 77776be20b upgrade xBRZ to 1.8 2025-04-12 09:34:39 +02:00
Flyinghead f7676faa68 ci: build AppImage with ubuntu 22 2025-04-12 09:33:26 +02:00
Flyinghead 10a5ed6cec network: ICE improvements
F355: flush tx buffer every 34 bytes
Flush tx/rx buffers when loading a state
Use moving average for stats
2025-04-08 21:40:26 +02:00
Flyinghead b9ba3f1e63 libretro and switch build fixes 2025-04-07 19:43:12 +02:00
JamesandNexus382 34fda43d9c DreamPicoPort fixes/enhancements (#1892)
* added getFunctionDefinitions
* Set DreamPort device default deadzone to 0% while preserving user customization
* Fixed default dead zone 0% for DreamPicoPort; set UsePhysicalVmuMemory default to true
* Use DisabledScope for UsePhysicalVmuMemory; moved DreamLinkGamepad::getDefaultMapping() to protected scope to match parent
* Set unique ID for DreamPicoPort devices so they enumerate the same way by flycast on every load
* Implement DreamLinkGamepad::resetMappingToDefault() and set C, D, and Z buttons for DreamPicoPort
---------
Co-authored-by: Nexus382 <[email protected]>
2025-04-07 19:23:14 +02:00
Flyinghead b602cc9c57 network: add ice/websocket match code system for battle cable 2025-04-07 19:22:02 +02:00
Flyinghead 63534c5d56 add websocket++ and libjuice as submodules 2025-04-07 18:06:44 +02:00
Flyinghead 0267de4997 upgrade xbyak to 7.23.1. Convert to git submodule 2025-04-07 17:58:21 +02:00
505 changed files with 98760 additions and 47732 deletions

No files matched your search

+12 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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' }}
+3 -3
View File
@@ -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
+4 -4
View File
@@ -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 }}
+14 -8
View File
@@ -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
+17 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+4
View File
@@ -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 ![xbox logo](https://flyinghead.github.io/flycast-builds/xbox.png)
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**.
+3
View File
@@ -0,0 +1,3 @@
target_sources(${PROJECT_NAME} PRIVATE
achievements.cpp
achievements.h)
+23 -22
View File
@@ -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);
}
+3 -3
View File
@@ -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>
+2 -2
View File
@@ -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>
+9
View File
@@ -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)
+17
View File
@@ -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()
+1 -1
View File
@@ -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") {
+6 -2
View File
@@ -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;
};
+8 -2
View File
@@ -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;
+1 -1
View File
@@ -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
{
+3 -3
View File
@@ -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;
}
+2 -1
View File
@@ -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
View File
@@ -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
+12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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>
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+1309 -516
View File
File diff suppressed because it is too large. Load diff
Regular → Executable
+4078 -1949
View File
File diff suppressed because it is too large. Load diff
Regular → Executable
+2826 -1106
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+16 -1
View File
@@ -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
+16
View File
@@ -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
View File
@@ -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)
File diff suppressed because it is too large. Load diff
Regular → Executable
View File
File mode changed.
Regular → Executable
+160 -74
View File
@@ -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
View File
@@ -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)) {
Submodule core/deps/libjuice added at 77daa8befd.
+1 -1
View File
@@ -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)
+18 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+7 -5
View File
@@ -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
View File
@@ -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);
}
}
}
}
+1
Submodule core/deps/xbyak added at 0d67fd1530.
-11
View File
@@ -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
View File
@@ -1 +0,0 @@
/build* # cmake
-42
View File
@@ -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
)
-47
View File
@@ -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.
-----------------------------------------------------------------------------
ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満た
す場合に限り、再頒布および使用が許可されます。
ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項
を含めること。
バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作
権表示、本条件一覧、および下記免責条項を含めること。
書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進
に、著作権者の名前またはコントリビューターの名前を使用してはならない。
本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供さ
れており、明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性
に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。
著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを
問わず、かつ責任の根拠が契約であるか厳格責任であるか(過失その他の)不法行為で
あるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、
本ソフトウェアの使用によって発生した(代替品または代用サービスの調達、使用の
喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない)直接
損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、
一切責任を負わないものとします。
-24
View File
@@ -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
View File
@@ -1 +0,0 @@
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
-26
View File
@@ -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)
-170
View File
@@ -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;
}
-17
View File
@@ -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;
}
-750
View File
@@ -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
-23
View File
@@ -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;
}
}
-17
View File
@@ -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%
-617
View File
@@ -1,617 +0,0 @@
[![Build Status](https://github.com/herumi/xbyak/actions/workflows/main.yml/badge.svg)](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)
-560
View File
@@ -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])
-123
View File
@@ -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)
-211
View File
@@ -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");
}
}
-228
View File
@@ -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>
-229
View File
@@ -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;
}
-228
View File
@@ -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>
-302
View File
@@ -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");
}
}
-5
View File
@@ -1,5 +0,0 @@
>>++++++++[->++++++++<]>>>>+++++++++[->++++++++++<]>[<<,[->+<<+<<+>>>]<<<[
->>>+<<<]>>>>>[->+>>+<<<]>[<<[->+>>+<<<]>>>[-<<<+>>>]<<[[-]<->]>-]>>[-<<<+
>>>]<<<<<<<[-<+<<+>>>]<[>>[-<+<<+>>>]<<<[->>>+<<<]>>[[-]>-<]<-]<<[->>>+<<<
]>>>>><[[-]>++++++++++++++++++++++++++++++++>[[-]<------------------------
-------->]<<]>>[-]<.>>]
-19
View File
@@ -1,19 +0,0 @@
++++++[->++++>>+>+>-<<<<<]>
[<++++>>+++>++++>>+++>+++++>+++++>>>>>>++>>++<<<<<<<<<<<<<<-]
<++++>+++>-->+++>->>--->++>>>+++++[->++>++<<]<<<<<<<<<<
[->
-[>>>>>>>]>[<+++>.>.>>>>..>>>+<]<<<<<
-[>>>>]>[<+++++>.>.>..>>>+<]>>>>
+<-[<<<]<[
[-<<+>>]>>>+>+<<<<<<[->>+>+>-<<<<]<
]>>
[[-]<]>[
>>>[>.<<.<<<]<[.<<<<]>
]
>.<<<<<<<<<<<
]
-3
View File
@@ -1,3 +0,0 @@
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.
-128
View File
@@ -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());
}
-111
View File
@@ -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;
}
}
-90
View File
@@ -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");
}
-70
View File
@@ -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;
}
}
-226
View File
@@ -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
-228
View File
@@ -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>
-29
View File
@@ -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");
}
}
-45
View File
@@ -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