Compare commits

...

462 Commits

Author SHA1 Message Date
Jesse Talavera 0fcf1f6e3a
Add support for using the solar sensor without requiring a Boktai ROM (#2221)
* Add a `GBAHeader` struct

* Add extra `GBAAddon` entries for the Boktai carts

- Each game in the trilogy has a different effect on Lunar Knights (the only commercial DS game to support the solar sensor)

* Copy the logo data from the NDS ROM's header to the Boktai stub's header
2025-03-09 18:20:27 +01:00
Nadia Holmquist Pedersen 63b468927e ci: enable building of appimages on aarch64
Looks like whatever was causing linuxdeploy to crash got fixed, so we can build them now.
2025-02-21 08:58:48 +01:00
Nadia Holmquist Pedersen e8265df4bd vcpkg 2025.01.13, update nixpkgs 2025-02-10 22:53:51 +01:00
Nadia Holmquist Pedersen 15c3faa26e Use GitHub's new arm64 Linux runners for the Ubuntu CI builds 2025-01-17 04:15:13 +01:00
Jakly a9cce557d2
fix framelimiter bugs (#2256) 2025-01-14 18:21:03 +01:00
Nadia Holmquist Pedersen 0c5dd28b1c just case the string length to int to make std::min happy in all cases 2024-12-26 09:17:46 +01:00
Nadia Holmquist Pedersen c41951d49c
Fix almost every warning (#2195)
Fix almost every warning as of Clang 19

* <codecvt> is deprecated, we can use QString's UTF-16 conversion
  instead
* remove sem_timedwait implementation as we don't need it anymore
* remove a useless shift that has its result discarded
* change usages of deprecated sprintf to snprintf
2024-12-25 16:54:10 +01:00
izder456 be26878b4c FIX: this should be namespace std:: to preserve compatibilty with non-glibc when building without gdb stub 2024-12-25 16:48:46 +01:00
Jakly 66d1091330
improve audio handling at non-60 fps targets (#2246) 2024-12-25 16:34:30 +01:00
Campbell Suter 72c86ade31
Fix gdbstub not activating until the console is reset (#2245)
The check for initialising the gdbstub depending on whether the JIT was
enabled or not was the wrong way around: previously, it would only
enable the gdbstub if the JIT was enabled.

The stub started working again if you reset the console, as
NDS::SetGdbArgs didn't have any such check and it was called by
EmuInstance::updateConsole.
2024-12-24 00:29:21 +01:00
Nadia Holmquist Pedersen 7d718ada39 cmake: set default CMAKE_OSX_DEPLOYMENT_TARGET before project()
project() appears to set it to an empty string (the value of nonexistent
$ENV{MACOSX_DEPLOYMENT_TARGET}?), causing our attempt to set its default
to fail. CMake bug?
2024-12-05 15:40:29 +01:00
Jakly 817b409ec8
ah. (#2225) 2024-11-30 02:54:54 +01:00
Rayyan Ansari cba838dd52
TitleManager: fix handling of title string
Truncate the title at the first occurrence of \0, as title strings
should be null-terminated.

Fixes #2219 (Weird characters on DSi Title Manager on melonDS 1.0RC)
2024-11-27 13:15:18 +00:00
Nadia Holmquist Pedersen 730b488fe3 vcpkg 2024.11.16 & update nixpkgs 2024-11-23 14:41:25 +01:00
Nadia Holmquist Pedersen 1d6c9023ff get rid of the incorrect CLOCK_MONOTONIC redefinition 2024-11-23 12:43:06 +01:00
Nadia Holmquist Pedersen 0db536c063 Set _WIN32_WINNT to Windows 8 when JIT is enabled (fixes #2209) 2024-11-23 12:40:02 +01:00
RSDuck 6a15dbfa12 unmappinged everything 2024-11-22 03:34:18 +01:00
Nadia Holmquist Pedersen e3fa6f4224 Fix version number in the generated Windows resource 2024-11-21 01:10:13 +01:00
Arisotura 98ceadd44c really?! 2024-11-21 00:37:22 +01:00
Arisotura dc10df0796 FUCK THAT SHIT 2024-11-21 00:27:30 +01:00
Arisotura 97a00e3137 BAHAHAHAHAHAHSKSHFOS-#%~/%% 2024-11-21 00:21:47 +01:00
RSDuck d0d010b09d don't use std::map and std::function in scheduler 2024-11-20 02:55:40 +01:00
Arisotura f6f993cb41 Merge remote-tracking branch 'origin/master' 2024-11-19 01:07:39 +01:00
Arisotura 86c6740b24 fuck that shit 2024-11-19 01:07:27 +01:00
Nadia Holmquist Pedersen 1b3f4664d8 fix order of ScreenLayoutType 2024-11-19 00:57:58 +01:00
Arisotura 13096f9fdc add warnings to the LAN dialogs 2024-11-19 00:50:42 +01:00
Nadia Holmquist Pedersen c4f7c1bff7 fix compiling without JIT 2024-11-19 00:34:16 +01:00
Arisotura 317b91533b avoid spawning message boxes from the emuthread 2024-11-19 00:33:39 +01:00
Arisotura 259eb4b408 dsfsdhgf 2024-11-19 00:11:46 +01:00
Arisotura d68091ee9c fix another oversight 2024-11-18 23:13:48 +01:00
Arisotura b491e99954 actually save path settings in instance-local config 2024-11-18 23:10:21 +01:00
Arisotura bca0457bea fuck, why did these files get committed 2024-11-18 22:56:34 +01:00
Arisotura f1c96281a9 update toml11. fixes bug with FPS settings corrupting config file 2024-11-18 22:53:55 +01:00
Arisotura bdc8f635de change splash logo to 384x384 png 2024-11-18 22:06:50 +01:00
RSDuck 9ad3d42252 hopefully fix macos 2024-11-18 21:31:56 +01:00
RSDuck f0503a6a28 fix 4kb page check
oops
2024-11-18 21:21:02 +01:00
Kemal Afzal 99ce959913
Multiinstance jit (#2201)
* works on Linux x64
still needs to be fixed for everything else

* use lots of PROT_NONE memory to reliably reserve virtual address space

* multi instance fastmem on Linux

* Windows

* blarg

* disable fastmem if the page size is not 4kb

* fix fast mem dialog option

* make aarch64 work as well

* fastmem 16kb pages support
2024-11-18 20:43:05 +01:00
RSDuck cb7af652f5 aarch64 lto broken GPU2D workaround 2024-11-18 20:08:49 +01:00
RSDuck c01b2bf7a0 prevent out of bounds access for microphone data 2024-11-18 18:58:39 +01:00
Arisotura 0ea0af3abf make it possible to change gdb stub settings without destroying/recreating a NDS 2024-11-17 20:00:52 +01:00
Arisotura 99aa5676db actually remove NDS/GBA ROM args from NDSArgs, since we won't be using them 2024-11-17 19:38:36 +01:00
Arisotura 5e3d2d07c3 fix Key1 code to source the DS-mode key data from the ARM9i BIOS, so it works even if no DS BIOSes are provided
(had to rework the loading code to make it work -- if carts are passed to the DSi constructor, they get initialized before the DSi stuff is initialized, and can't read the DSi BIOSes)
2024-11-17 19:04:13 +01:00
Arisotura f0a023b572 Merge remote-tracking branch 'origin/master' 2024-11-17 18:18:00 +01:00
Arisotura 5f8255bc90 allow DSi mode to run with internal DS BIOS 2024-11-17 18:17:43 +01:00
fincs 584508230f
Assortment of fixes related to libnds v2/calico (#2197)
* Support 8-bit writes to REG_IPCSYNC

* Support CP15 Trace Process ID register

* NWifi: expose correct manfid information in CIS0/CIS1 area

* NWifi: basic support for WMI_SET_PROBED_SSID

# Conflicts:
#	src/DSi_NWifi.cpp

* DSi_NAND: fix incorrect CTR IV calculation code
2024-11-17 15:57:00 +01:00
Arisotura 023dc0c446 avoid reopening the microphone if it was already opened 2024-11-17 15:47:55 +01:00
Arisotura 871a167d8b also fix crashes when inserting/ejecting a NDS cart while nothing is loaded 2024-11-17 15:43:22 +01:00
Arisotura 0a4287c6ad fix crashes when inserting/ejecting GBA carts/addons with nothing loaded 2024-11-17 15:23:25 +01:00
Nadia Holmquist Pedersen 5e8beb3ab7 fix a typo 2024-11-13 15:23:59 +01:00
Nadia Holmquist Pedersen 7c1d2a64f4 Set WIN32_LEAN_AND_MEAN, gets rid of the winsock2 warnings and probably
speeds up compilation a tiny bit

oh and NOMINMAX too for good measure while we're at it
2024-11-11 14:18:05 +01:00
Nadia Holmquist Pedersen b2f6fab6f4 cmake: use interface include directories properly
and fix an indent I guess
2024-11-11 12:06:12 +01:00
RSDuck 4528441c74 for OGL renderer W buffer rendering avoid undefined vertex z
see https://github.com/melonDS-emu/melonDS/issues/2017
2024-11-09 14:19:02 +01:00
Nadia Holmquist Pedersen 8e3f6cc519 add missing qtbase-private-devel for Fedora 2024-11-09 08:32:34 +01:00
Rayyan Ansari 7041b52ebc
Remove extra backtick in Windows build instructions 2024-11-08 21:41:27 +00:00
Rayyan Ansari adf143a38d
Fix link to contributors in About dialog
Add openExternalLinks property to the label to allow the hyperlink to
open in the user's web browser.
2024-11-08 21:32:45 +00:00
RSDuck 8d4f419546 correct assert in gdb stub 2024-11-08 18:57:44 +01:00
Nadia Holmquist Pedersen 5959009ebd
Use Qt 6 by default on all platforms and update build instructions (#2187) 2024-11-05 17:03:07 +01:00
Nadia Holmquist Pedersen d8f1d106f0 flake: remove workaround no longer needed with Darwin SDK changes
also add the Qt tools to the dev shell since they're needed for Qt
Designer and such
2024-11-05 07:58:31 +01:00
Nadia Holmquist Pedersen a5389286e8 Make macOS OpenGL deprecation warnings shut up 2024-11-02 13:52:56 +01:00
Arisotura 7740634e6a reimplement MP audio mode 2 (active instance only) 2024-11-01 02:19:29 +01:00
Arisotura 58ee191cc8 make mic input less shitty (and less dangerous) 2024-11-01 01:31:45 +01:00
Arisotura 78aae252d5 fix bug where opening a new instance would pause other instances 2024-11-01 00:41:55 +01:00
Arisotura 09e4400f3c fix hang when closing an instance that is engaged into local multiplayer 2024-11-01 00:40:09 +01:00
Arisotura e3e561da3f lock framebuffer stuff to prevent conflicts when reiniting the core or changing the renderer 2024-10-31 22:51:18 +01:00
Arisotura 9c8f229fed misc shit 2024-10-31 22:37:46 +01:00
Arisotura f3bd58f75e do touchscreen input more properly 2024-10-31 22:10:25 +01:00
Arisotura 1b8daa0465 fix up cheat toggle 2024-10-31 20:33:57 +01:00
Arisotura 6c6cefad6c add splashscreen 2024-10-31 19:26:41 +01:00
Arisotura 540ebe7256 disable 'Enable cheats' until something is loaded, to make it consistent with 'Manage cheats' 2024-10-31 13:57:53 +01:00
Arisotura 6dc396741f make sure it doesn't crash if you click the window while nothing is loaded 2024-10-30 23:39:17 +01:00
Jakly 4ba8f330c4
fix framerate target not adjusting with vcount writes (#2181) 2024-10-30 19:40:33 +01:00
Jesse Talavera 3877a8e46b
Allow `CartGameSolarSensor::LightLevel` to be set explicitly (#2179)
* Allow `CartGameSolarSensor::LightLevel` to be set explicitly

* Add `CartGameSolarSensor::GetLightLevel`

* Update GBACart.cpp

---------

Co-authored-by: Kemal Afzal <RSDuck@users.noreply.github.com>
2024-10-30 01:10:32 +01:00
Arisotura 7a4255b732 fix LDM bugs 2024-10-29 14:18:57 +01:00
Nadia Holmquist Pedersen dfd6338992 it shouldn't be ifdef... How did that even compile here? 2024-10-28 01:28:57 +01:00
RSDuck 58ab33210a handle address wrap around in texture cache
fixes out of bounds access in Mario 64
also slightly optimise paletted texture conversion
2024-10-27 23:32:05 +01:00
GalaxyShard b60f42b281
Fix gdb break on start & gdb ports not closing after restarting/crashing (#2167) 2024-10-27 22:06:59 +01:00
Nadia Holmquist Pedersen 98d969ab15 only apply windows11 theme workaround to Qt6. Qt5 doesn't have it anywya. 2024-10-27 21:23:15 +01:00
Nadia Holmquist Pedersen b03bceb5c1 flake: shell should also use qt6's stdenv 2024-10-27 21:22:49 +01:00
Arisotura 12b207d915 remember which windows are opened 2024-10-27 18:49:17 +01:00
Arisotura e42829ea81 pause emu during file select prompts 2024-10-27 17:21:41 +01:00
Arisotura 238c552599 limit to 4 windows, and disable 'new window' menu item when that amount is reached 2024-10-27 16:26:29 +01:00
Arisotura d79d45a117 properly sync up menus between windows of a same instance 2024-10-27 16:21:09 +01:00
Arisotura 94955aee81 fix another OpenGL bug (when closing secondary window) 2024-10-27 14:51:11 +01:00
Arisotura f2dce621ce proof all dialogs against use-after-free when closing main window while a dialog is open 2024-10-27 14:24:59 +01:00
Arisotura a61754bb58 fix possible crash when closing window while video settings dialog is open 2024-10-27 13:53:12 +01:00
Arisotura 6d345cc1ea correctly propagate video settings changes to all windows 2024-10-27 13:43:26 +01:00
Arisotura e576538268 synchronize pause/unpause across all instances 2024-10-27 11:21:30 +01:00
Arisotura e6f0d77aa0 fix freeze when starting new emu instance while using OpenGL 2024-10-27 10:17:59 +01:00
Arisotura 24ca1a5fdb lay base for keeping config in sync across multiple instances 2024-10-27 10:02:57 +01:00
Gess1t 2bf0eb7ead
Handle failure of OpenGL context creation (#2172) 2024-10-27 09:20:51 +01:00
Arisotura 2d561a60c8 fix Qt5 compatibility (sdffdf) 2024-10-27 09:16:46 +01:00
Arisotura 4ae4397547 properly update display type across all windows
(remind me to also propagate changes across instances)
2024-10-27 02:42:27 +02:00
Arisotura b2ae4c7dc5 lay base for a window with no menubar 2024-10-27 02:07:33 +02:00
Arisotura 881a740cab start actually implementing multi-window feature, still rough around the edges
fix crash when closing main window if sub windows are involved

fix OpenGL context handling, still need to fix when changing display type
2024-10-27 01:14:29 +02:00
Citrodata f375099613
Update EmuThread.h (#2171)
* Update EmuThread.h

Add missing include QVariant. This fixes and error when building on Arch.

* Update AboutDialog.cpp
2024-10-26 23:03:36 +02:00
Arisotura 1d284f6f1e as promised, reroute dropEvent() through EmuThread 2024-10-25 16:16:23 +02:00
Arisotura fc3c7440d1 fix that crash 2024-10-25 12:33:04 +02:00
Arisotura 979f1ed615 same shit with Import Savefile 2024-10-25 10:52:47 +02:00
Nadia Holmquist Pedersen 8b6628b070 Work around Qt windows11 theme menu bar padding 2024-10-25 01:15:59 +02:00
Nadia Holmquist Pedersen 287f6642fc
Add an About dialog with build info (#2138)
add About dialog
2024-10-24 22:05:30 +02:00
Nadia Holmquist Pedersen a97463b0ac nix: update inputs 2024-10-24 21:02:41 +02:00
Nadia Holmquist Pedersen 9ebc96d121 vcpkg 2024.10.21 2024-10-24 19:55:41 +02:00
Arisotura 75e6856af4 route savestate stuff through EmuThread 2024-10-24 19:27:45 +02:00
Arisotura 3fc065d72d fix ROM preloading to also go through EmuThread 2024-10-24 17:48:34 +02:00
Arisotura 1787235e09 fix more shit
now it doesn't shit itself on startup if the BIOS paths are wrong
2024-10-24 17:20:14 +02:00
Arisotura 13b4cea171 move screen layout/etc options to new View menu 2024-10-24 16:08:04 +02:00
Arisotura 1666049531 fix shit 2024-10-24 14:00:11 +02:00
Arisotura 6d3ea6a485 fix bug with the GBA addon menu (and make it a proper list so we don't have to hardcode the length all over) 2024-10-24 13:49:43 +02:00
Arisotura 079341f102 take this a bit further 2024-10-24 11:44:21 +02:00
Arisotura 82f38f0b7a start moving ROM/firmware loading to the emuthread to avoid cursed bugs 2024-10-24 00:27:05 +02:00
Arisotura 1428bfb2cf fix one of the cursed bugs 2024-10-23 20:33:31 +02:00
Arisotura d68b58f37e fix DSi wifi hang 2024-10-23 14:55:02 +02:00
Arisotura b993ec10cd remove "Test" menu item 2024-10-23 11:57:37 +02:00
kaitou e5501e555f
fix: set default mode to 24 hours (#2166) 2024-10-22 20:13:55 +02:00
Nadia Holmquist Pedersen fbf753257b cmake: Add easy sanitizers option
Set -DSANITIZE to a comma-separated list of options to pass to
-fsanitize=, like -DSANITIZE=address,undefined
2024-10-19 18:30:30 +02:00
Nadia Holmquist Pedersen 216b8e045d fix audio interpolation setting range 2024-10-07 11:28:58 +02:00
RSDuck 30441fed24 do not restore fullscreen state from saved geometry 2024-10-06 19:00:54 +02:00
RSDuck aa443c8846 fix #2083 and minor cleanup 2024-10-06 17:33:03 +02:00
RSDuck f13c70d028 fix blow noise input
(microphone input takes signed values)
2024-10-01 19:52:10 +02:00
Jakly e9446fa9dc
implement 3 configurable and toggleable framerate targets (#2159)
This pr allows for configuring the framerate target and adds support for two other framerate targets, a "fastforward" and "slowmo" target which can be enabled via either a toggle or holding a button.
this allows for supporting a more accurate framerate target and allows for users to slow down the speed of gameplay if they so desire
2024-09-29 09:30:13 +02:00
RSDuck 2eb6d44c2c prevent use after free through focusOutEvent when window is closed 2024-09-24 20:08:07 +02:00
CasualPokePlayer 2179ca2a41
Set the correct save type for Puzzler World USA (#2156)
Fixes #1804
2024-09-18 20:58:55 +02:00
RSDuck 7ac2eb2d71 attempt at fixing Windows build 2024-09-15 07:38:28 +02:00
RSDuck a3d696121e rework gdb packet parsing
it should be a bit more robust now
2024-09-15 07:30:53 +02:00
Nadia Holmquist Pedersen 50d32f3c96 flake: clean up dependencies a bit
* qt6.* instead of kdePackages.*
* use an extra-cmake-modules that depends on Qt6 rather than 5, and
  exclude it on macOS
2024-09-13 05:43:39 +02:00
RSDuck 74f479ce6d gdb stub config setup fixes 2024-09-11 14:41:22 +02:00
Jakly f719438a6e
Improve calculation of light colors (#1967)
* maintain precision until all lights are calculated

fixes lugia on the soul silver title screen

* small optimization

* small note

* small cleanup/notes

shouldn't need to check that every time, since the variable shouldn't be able to overflow

* hw doesn't cap difflevel at 255

Should it cap at all?
Can vtx colors overflow...?

* diffuse level appears to be shifted right by 9

fixes some minor inaccuracies

* improve specular lighting a little

* small improvement to diffuse lighting

fixes a few off by ones
- finding by azusa

* small tweaks

* handle overflows of diffuse lighting properly

-credits to azusa once more

* attempt at improving specular lighting calcs

still far from correct, but its a start.
fixes: https://github.com/melonDS-emu/melonDS/issues/1545

* meh

* improve specular lighting further

* add notes

* theory: add half vec instead of subt 1

* implement azusa's specular lighting algorithm

* fix minor edge case with spec lighting

* give proper credit in comments

* fix some bugs/misc tweaks

* more quirky overflow/underflow handling

* fix a spec lighting edgecase

remove some redundant parentheses

* fix an edge case with light vector calcs

* spec recip uses a different calc for light dir?

also remove a check that shouldn't be mathematically possible to trigger

* nvm that thing i thought couldn't trigger was required

also move reciprocal calc into the light vector calc function since i might as well now ig

* replace a bunch of stuff with much *much* simpler algorithms

* misc cleanup

PARENTHESES WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

* leave a note abt shininess table's default value being incorrect
2024-09-10 16:13:51 +02:00
Nadia Holmquist Pedersen 268c4f14c1 vcpkg: support building on Linux 2024-09-06 22:50:12 +02:00
Nadia Holmquist Pedersen d18524d5ac Nix: Add dev shell for building using vcpkg 2024-09-06 19:44:48 +02:00
Nadia Holmquist Pedersen 277b151ada update vcpkg and nixpkgs 2024-09-06 13:39:42 +02:00
Alex 4f6498c99c
Fix "Ejected GBA cart" not adding a newline (#2140) 2024-08-21 13:39:34 +02:00
Jesse Talavera 824eb370e4
Fix the build when the JIT is disabled (#2139) 2024-08-19 15:21:34 +02:00
Arisotura 0e6235a7c4 LAN: remember player name and max players setting 2024-08-15 13:34:27 +02:00
Arisotura 5b986d3111 wifi: add hack to facilitate multiplayer connections (extend post-beacon interval when connection is being initiated) 2024-08-14 16:47:08 +02:00
Nadia Holmquist Pedersen 2fff4c0b5a vcpkg: build qtbase with harfbuzz enabled, fixes windows widget drawing 2024-08-14 06:42:06 +02:00
Nadia Holmquist Pedersen deb1ba2bb2 README: Update build instructions to include enet and qtsvg
(input and battery dialogs don't render correctly without qtsvg, we
should have been installing it)
2024-08-13 08:40:10 +02:00
Nadia Holmquist Pedersen f54b6311c1 ubuntu... 2024-08-13 08:05:36 +02:00
Nadia Holmquist Pedersen cb6f60c383 ci: Linux runners should install qtsvg for proper bundling 2024-08-13 07:59:38 +02:00
Nadia Holmquist Pedersen c6cab9ed41 mac-libs.rb: Make bundling a bit smarter
* Resolve symlinks to avoid including the same thing twice (like
  version-numered dylib symlinks)
* Look in all Qt prefix paths for plugins - the package may not
  necessarily have the same path
* reduce install_name_tool invocations to make it a bit faster
* change dylib IDs to remove original source path
2024-08-13 07:53:09 +02:00
Nadia Holmquist Pedersen e290c42360 flake: add wayland to library path like nixpkgs does 2024-08-11 04:54:05 +02:00
Nadia Holmquist Pedersen 5598065985 Netplay.cpp should not include main.h, also fix a format warning 2024-08-11 03:14:14 +02:00
Arisotura 8d31875902
Backport LAN (#2131)
backport the old LAN feature to the modern melonDS codebase.
2024-08-10 23:20:50 +02:00
Nadia Holmquist Pedersen ec71b15505
Add a Nix flake (#2097)
Adds a Nix flake, allowing easy building and running of melonDS using the Nix package manager, as well as potentially very stable and reproducible CI in the future.
2024-08-08 05:36:06 +02:00
Nadia Holmquist Pedersen 53c58bd777 fix potential issue with glib shim min/max defines 2024-08-07 17:49:29 +02:00
Nadia Holmquist Pedersen 4359bccfcb fix the slirp shit some more 2024-08-07 17:45:56 +02:00
Nadia Holmquist Pedersen b47563e888 Apply FixInterfaceIncludes to the slirp package again to work around
package inconsistencies
2024-08-07 17:28:25 +02:00
Nadia Holmquist Pedersen a174901412 fix building with system libslirp 2024-08-07 17:24:07 +02:00
Nadia Holmquist Pedersen 8423dae6ff Add "Open melonDS directory" menu option 2024-08-07 15:53:58 +02:00
RSDuck dd386d12a9 use templates to only execute GDB stub related code if enabled 2024-08-05 03:23:49 +02:00
pants64DS 76c2723f5c
Fix console output on Windows (#2122)
Co-authored-by: pants64DS <pants64DS@users.noreply.github.com>
2024-08-02 02:21:37 +02:00
CasualPokePlayer 757a608b6e
Fix a few compiler warnings (#2063) 2024-08-01 23:26:46 +02:00
Jakly 54e87c915f
fix a couple polygons (#1920)
hw prioritizes the earlier vertex instead of leftmost
2024-08-01 22:55:00 +02:00
Jakly 12be06beb6
fix gdb stub causing the emulator to hang on undefined instructions (#2054)
* dont hang on undefined instruction

* Add spaces

---------

Co-authored-by: Kemal Afzal <RSDuck@users.noreply.github.com>
2024-08-01 22:48:52 +02:00
Jakly 161bd9d3d2
Default zero dot display register to the 24 bit integer limit (#1968)
* 0 dot disp defaults to the 24 bit integer limit

* useless correction

it goes through the reset function to set the variable on boot anyway but why not have the initialized state be correct too
2024-08-01 22:46:05 +02:00
Jakly d0a7239f15
fix some bugs with compressed texture look up (#2051) 2024-08-01 22:44:04 +02:00
Jakly 29c67f2140
implement 16 bit r/w IF and DISPCAPCNT (#2061) 2024-08-01 22:41:21 +02:00
Jesse Talavera 327ce45124
Refactor network implementations to be more reusable and less buggy (#2107)
encapsulate network interfaces
2024-08-01 22:02:45 +02:00
Jesse Talavera c6bf5d5181
Allow `AREngine` to be used independently of `ARCodeFile` (#2108)
* Make `EmuInstance::cheatFile` use a `unique_ptr`

- Fixes a memory leak, as the cheat file wasn't cleaned up in the destructor

* Split `AREngine` and `ARCodeFile` apart

- Suitable for frontends that have their own way of storing cheats
- Store the cheats in `AREngine` in a `std::vector`
- Apparently cheats are _supposed_ to be executed each frame; I didn't understand this until recently
2024-08-01 22:01:00 +02:00
Nadia Holmquist Pedersen f3f6a6a194 Fix building on i686 Linux
I doubt anyone actually cares about support for it but hey may as well?
2024-08-01 21:57:32 +02:00
pants64DS 37c0320cbe
Fix an inaccuracy with the No$gba debug registers 04fffa14 and 04fffa18 (#2119)
Writing to either of the two registers would print one newline too many

Co-authored-by: pants64DS <pants64DS@users.noreply.github.com>
2024-07-31 01:13:06 +02:00
RSDuck b778fbaad1 attempt at correct utf8 decoding for toml config file path 2024-07-30 17:48:41 +02:00
Nadia Holmquist Pedersen 01c2d65f07 Explicitly include QModernWindowsStylePlugin in static builds
I hope this'll make it look right.
2024-07-30 03:10:01 +02:00
Nadia Holmquist Pedersen d62c61ba08 vcpkg 2024.07.12 2024-07-30 00:43:15 +02:00
RSDuck 12563fb636 use platform file functions for savestates and NAND importing file check
should fix character encoding issues on Windows
2024-07-29 00:15:19 +02:00
Nadia Holmquist Pedersen c0ada2f445 Fix invalid .gitattributes entries 2024-07-28 03:23:07 +02:00
Nadia Holmquist Pedersen ececf337f2
Build Windows CI builds with vcpkg and Qt 6 (#2112)
If using Qt 6 becomes a problem in terms of compatibility, toggle it off in the CMakePresets.json.
2024-07-27 17:22:35 +02:00
Nadia Holmquist Pedersen d31eabb6fa Don't disable firmware settings controls when override external firmware
settings is disabled
2024-07-27 16:56:29 +02:00
Jesse Talavera 837a582087
Expose some fields of `DSi_BPTWL` needed to support direct boot (#2103)
expose boot flag
2024-07-21 17:23:19 +02:00
Jesse Talavera 821f5f5f29
Allow frontends to use different headers for OpenGL declarations (#2102) 2024-07-21 17:02:31 +02:00
BueniaDev 9b828c2cde
Implement Rumble Pak support. (#2101) 2024-07-21 17:01:30 +02:00
Nadia Holmquist Pedersen 5eadd67df6 EmuInstance: Use GetLocalFilePath to get the wfcsettings path
Fixes the file being created in the current working directory.
2024-07-17 15:44:47 +02:00
Nadia Holmquist Pedersen 4b17de7e8c Fix annoying Qt touch event deprecations for real this time 2024-07-15 14:37:37 +02:00
Nadia Holmquist Pedersen 71f4d7b222 deviceType doesn't exist in Qt5 apparently 2024-07-15 14:04:40 +02:00
Nadia Holmquist Pedersen 6d2ad2a3f0 Ignore touch events originating from trackpads
Fixes multi-finger gestures causing spurious touch screen presses
Also fix some deprecations when we're at it anyway
2024-07-15 13:57:21 +02:00
Arisotura 2031f491f0 fix some Qt6 bugs (buttonClicked -> idClicked) 2024-07-15 13:00:40 +02:00
Arisotura d92c9fcd90 do not immediately create a core when creating an EmuInstance. fixes some nasty potential bugs. watch out for bugs this may introduce though... 2024-07-15 12:57:18 +02:00
Jesse Talavera 94ba7c1594
Split networking code into its own target (#2091) 2024-07-14 17:03:21 +02:00
CasualPokePlayer a812a43bda
Fix various issues with firmware generation (#2058) 2024-07-11 15:55:06 +02:00
Jesse Talavera 841e3eb060
Use `CMAKE_CURRENT_SOURCE_DIR` to set some CMake-related paths (#2089)
- This prevents dependent projects that pull in melonDS via `FetchContent` from breaking
2024-07-11 14:02:40 +02:00
OverdueWeevil2 a82b5758bc
Update EmuInstance.cpp (#2090) 2024-07-11 13:48:27 +02:00
Nadia Holmquist Pedersen b55eb431bd ensure that we actually link the OpenGL library on macOS 2024-07-07 13:45:45 +02:00
Nadia Holmquist Pedersen 1302cbdd64 frontend: Restore linear interpolation for audio output resampling
Also, do it smarter this time. This seems to have gone missing with the
doublemelon merge.
2024-07-07 08:19:49 +02:00
Antonio Niño Díaz 25b5ac91bd
Fix horizontal mosaic on sprites (#2084)
The code itself is correct, but the variable being read is the Y mosaic
coordinate, not the X coordinate.
2024-07-04 00:24:29 +02:00
RSDuck 613569a2ab append newline to nocash logging
makes behaviour consistent with no$gba
2024-07-02 19:09:32 +02:00
Nadia Holmquist Pedersen 7898b46435 Add Discord invite link to README.md 2024-07-02 10:06:05 +02:00
Nadia Holmquist Pedersen 4e3592f2d4 Fix loading GBA carts
UpdateConsole forgot to reinsert it when messing with the NDS.
2024-07-02 08:18:44 +02:00
Nadia Holmquist Pedersen f2611680ca EmuInstance: check consoleType from NDS rather than the instance's when
updating console, fixes crash on console type switch

EmuInstance::consoleType is already updated, so the check for whether we
should recreate the NDS to switch console type never succeeds.
2024-07-02 06:23:21 +02:00
Nadia Holmquist Pedersen db20771ef3 slirp: Add -fvisibility=hidden to glib stub
On Linux, our stubbed glib functions were conflicting with the ones from
real glib, which gets used by Qt when we're running on a GTK-based
desktop. Avoid a crash by not exposing them.
2024-06-24 05:25:00 +02:00
Nadia Holmquist Pedersen cbb0f4b872 Tell the compiler that we want wrapping signed arithmetic
The core relies on this and without -fwrapv there will be rendering
issues on some targets. Thanks CasualPokePlayer for noticing this.
2024-06-18 15:51:08 +02:00
Arisotura e234385c20 OpenGL: add support for changing BG0HOFS midframe. fixes #2072 2024-06-18 13:11:42 +02:00
Arisotura 626d2379bc * fix default for audio volume
* add default bools for battery settings
2024-06-17 14:51:32 +02:00
Nadia Holmquist Pedersen eb2bd73c7d ensure teakra is always static linked even in dynamically linked builds 2024-06-15 21:31:23 +02:00
Arisotura ae44ba2475 blarg 2024-06-15 21:01:27 +02:00
Arisotura 649462ff5c fix more bugs and crap 2024-06-15 20:57:26 +02:00
Arisotura 890dc4f228 fix other bugs with these regexes 2024-06-15 19:23:18 +02:00
Arisotura d449888405 fix up the regexes in Config.cpp 2024-06-15 19:10:28 +02:00
Arisotura ccc5c955e9 fix one potential crash bug 2024-06-15 17:42:26 +02:00
Arisotura 8fc403cdad update copyright headers 2024-06-15 17:01:19 +02:00
Arisotura 25a7b1ca1d
merge doublemelon (#2067)
non-exhaustive (but exhausting) list of changes:

* base laid for multiple window support, but will likely require more work to work correctly
* encapsulation of frontend state for proper multi-instance support
* (JIT still needs a fix for the NDS::Current workaround but we can get there later)
* new, more flexible configuration system
2024-06-15 13:52:47 +02:00
CasualPokePlayer 8e9b88d01d
Actually generate DownscaleFramebuffer (#2059) 2024-06-08 20:48:42 +02:00
Nadia Holmquist Pedersen a72b79a55a that needs to be public actually 2024-05-15 19:43:34 +02:00
Nadia Holmquist Pedersen cfc49eb286 Revert slirp dynamic/static check, and make it not use dllimport/export
when building statically
2024-05-15 19:37:14 +02:00
Nadia Holmquist Pedersen d21bc64cb3 MinGW build portability fixes
AKA you can build melonDS for Windows on Fedora now
2024-05-15 18:57:49 +02:00
Nadia Holmquist Pedersen a2406e3c0e
Vendored libslirp (#2045)
Add vendored libslirp into the repo with a shim to remove its dependency on glib.
2024-05-15 18:00:55 +02:00
Nadia Holmquist Pedersen 747f50de98 Refactor how CCache is set up
* Use RULE_LAUNCH_COMPILE property as you're apparently supposed to
* Detect if compiler is already ccache to prevent build failure
2024-05-15 10:55:10 +02:00
Nadia Holmquist Pedersen 5ab8161a21 Prevent turning on compute renderer on macOS 2024-05-14 11:39:06 +02:00
RSDuck 442661747e add assert to make sure all shaders were compiled 2024-05-14 04:09:20 +02:00
RSDuck 5df83c97c7 bump up compute shader renderer tile amount factor to 16
also why did I think DS could render 4096 polygons? Use that extra bit for larger work offset
2024-05-13 19:15:48 +02:00
RSDuck 347f4a79fd Fix division by zero 2024-05-13 17:23:05 +02:00
RSDuck 043244a56d
Compute shader renderer (#2041)
* nothing works yet

* don't double buffer 3D framebuffers for the GL Renderer
looks like leftovers from when 3D+2D composition was done in the frontend

* oops

* it works!

* implement display capture for compute renderer
it's actually just all stolen from the regular OpenGL renderer

* fix bad indirect call

* handle cleanup properly

* add hires rendering to the compute shader renderer

* fix UB
also misc changes to use more unsigned multiplication
also fix framebuffer resize

* correct edge filling behaviour when AA is disabled

* fix full color textures

* fix edge marking (polygon id is 6-bit not 5)
also make the code a bit nicer

* take all edge cases into account for XMin/XMax calculation

* use hires coordinate again

* stop using fixed size buffers based on scale factor in shaders
this makes shader compile times tolerable on Wintel
- beginning of the shader cache
- increase size of tile idx in workdesc to 20 bits

* apparently & is not defined on bvec4
why does this even compile on Intel and Nvidia?

* put the texture cache into it's own file

* add compute shader renderer properly to the GUI
also add option to toggle using high resolution vertex coordinates

* unbind sampler object in compute shader renderer

* fix GetRangedBitMask for 64 bit aligned 64 bits
pretty embarassing

* convert NonStupidBitfield.h back to LF only new lines

* actually adapt to latest changes

* fix stupid merge

* actually make compute shader renderer work with newest changes

* show progress on shader compilation

* remove merge leftover
2024-05-13 17:17:39 +02:00
Nadia Holmquist Pedersen c85a2103bb Allow adding a suffix to the displayed melonDS version 2024-05-11 22:40:45 +02:00
Nadia Holmquist Pedersen 10798c3464 fix README build badges finally 2024-05-05 08:40:37 +02:00
Nadia Holmquist Pedersen ee2c6cc7c2 actually add the cmake script too 2024-05-05 08:14:00 +02:00
Nadia Holmquist Pedersen 474bf6e784 Set default optimization flags less intrusively 2024-05-05 08:10:21 +02:00
Nadia Holmquist Pedersen 35cea5e1d7 Fix zstd ROM loading issues
* fix use-after-free of inContent
* don't try to free the DStream twice
2024-05-04 18:16:24 +02:00
Jakly 6112aa120a
Pu region sizing/bounds fix (#2024)
* fix the pu region's end point overflowing

According to gericom it cannot overflow at all

* set a minimum and a better maximum for the pu region size

* fix pu logging

* PU regions with a size of 31 always take up the entire address space

also tweak some logging a little more

* start is actually force aligned by size, oops

* small tweaks

* hopefully more clear code

* math is for nerds
2024-05-02 17:44:59 +02:00
Nadia Holmquist Pedersen ba8d547dfa Windows: Work around CMake not finding libarchive's include directory because MSYS2 CMake doesn't like UNIX paths. 2024-04-18 12:25:41 +02:00
RealAstolfo 84474105e2
ssize_t is not defined in stddef.h (#1999)
i had to add sys/types.h and patch my gentoo ebuilds for a successful compilation.
2024-04-18 13:40:38 +02:00
Rayyan Ansari 5a852cb00d
ROMManager: optimise ROMIcon function
Precompute all 16 5-bit RGB palette colours into 8-bit RGBA to avoid
repeated and superfluous calculation within the nested loop at the
point of index lookup.

A speedup was observed, from ~7ms, to a consistent 1ms
(i.e. now practically instantaneous) through timing with
std::chrono::high_resolution_clock.

Also improve comprehensibility, by using meaningful names, where
appropriate, for loop counter variables.
2024-04-16 23:39:22 +01:00
Arisotura 95adc87f6d wifi: try ignoring MP frames if not engaging in MP comm 2024-04-13 12:17:16 +02:00
Arisotura 8feeee6103 Input: only check joystick input if a joystick actually exists 2024-04-12 20:02:16 +02:00
Arisotura d99c571f94 FATStorage: make sure to always properly unmount the volume (fixes evil bug) 2024-04-12 19:43:02 +02:00
Arisotura 111dc7a563 wifi improvements:
* implement channels
* rework power-down support, fixing bugs
* fix bug when W_BeaconInterval is zero
* fix potential missing IRQs when writing to W_IE
2024-04-12 17:28:51 +02:00
Arisotura 0b87dd5fa6 fix touchscreen bug on Wayland 2024-04-09 12:54:31 +02:00
Arisotura 968bd26d85 fix generation of instance-unique MAC address when using an external firmware 2024-04-09 11:38:38 +02:00
Nadia Holmquist Pedersen 6e26559cd2
ci: fix macOS build
GitHub Actions' macOS runners have Python from homebrew installed and it's used by default instead of the Python that ships with macOS. Apparently Homebrew decided you shouldn't be able to install stuff with `pip3` anymore so our build broke since `setuptools` is no longer included by default and `glib` from vcpkg needs it to build.

Additionally,, the whole liblzma mess ended up breaking our builds too because libarchive (and its dependency libxml2) depends on it and the download is no longer available. The build option changes here should be reverted once this is sorted out because this is probably partially breaking archive support.

PS: Fuck you Jia Tan.
2024-04-03 14:49:27 +02:00
Jesse Talavera 31a7f53282
Fix a crash when using DSi mode in debug builds on macOS (#1976)
Store the BIOS images in `NDSArgs`/`DSiArgs` through pointers, not directly

- This will make it easier to keep such objects on the stack
2024-03-13 14:55:20 +01:00
RSDuck ea1755bed0 call Start again NDS object after Reset
fixes issue where game doesn't properly start after changing settings
2024-03-12 09:23:20 +01:00
RSDuck 5fdd285c9a fix aarch64 build 2024-03-12 08:41:42 +01:00
RSDuck 18d1df606f fix #1959
Use QT again for opening file so that we don't depend on locale
2024-03-12 08:35:56 +01:00
Nadia Holmquist Pedersen b117bb8f58 that should be 0x8000 2024-03-08 16:59:31 +01:00
Nadia Holmquist Pedersen faf3c0f2e0 Add Gaussian (SNES) audio interpolation
Probably not a good choice for most DS games unless you really want a
very soft sound, but it could be fun if you wanted to run lolSnes in
melonDS :p
2024-03-08 16:36:00 +01:00
Nadia Holmquist Pedersen e227902cec Util_Audio: use basic linear interpolation
Should remove the artifacts caused by the previous nearest
resampling. May be worth replacing with something better in the
future, but this is an improvement for now.
2024-03-03 16:58:59 +01:00
Nadia Holmquist Pedersen 67ca4997e2 Release all keyboard keys on focus loss (fixes #1987) 2024-02-25 14:25:50 +01:00
Nadia Holmquist Pedersen 21e2a876ec build teakra's test_generator.cpp only if building its unit tests is
enabled

speeds up builds a bit
2024-02-24 01:47:04 +01:00
λP.(P izzy) 9430502b16
fix malloc on OpenBSD targets (#1979) 2024-02-20 03:33:39 +01:00
Jaklyy a8429af131
dont make a save file on launching a game (#1974)
avoids the issue of saves being created for roms that dont use save files.
2024-02-13 20:17:29 +01:00
Nadia Holmquist Pedersen 3415e23105 delete-artifact keeps failing PR CI even when you tell it not to fail on error so I guess we're just not using it. 2024-02-13 20:15:03 +01:00
Luca D'Amico 646ed3cb32
Add Haiku (BeOS-like OS) support (#1858) 2024-02-07 23:15:30 +01:00
Jaklyy 5ffa642980
Check for write permissions for some key files (#1972)
* check if an nds save file can be opened for writing

also add the ability to open a file in append mode

* fix multi-instance saves

also move the check for file writability into a separate function (probably uneeded?)

* implement check for gba roms

* move rom load error messages into the functions

also finish gba slot (oops)

* improve error string

* check write perms before saving path settings

* fix memory leak

* check for writability of firmware/nand/sds

* add secondary checks for nand/firmware

* add check for config file being writable

* Return the file write error as a QString to avoid the invalid char*
causing a garbled error message.

Qt wants it as QString either way.
2024-02-07 23:04:36 +01:00
Nadia Holmquist Pedersen 71e1ba8c40
Linux CI updates (#1965)
* Switch to using Qt 6
* Use Ubuntu 22.04 for newer dependency versions
* Combine AppImage and regular x86_64 builds so it doesn't have to build twice
* Misc cleanup
2024-02-07 22:29:13 +01:00
Nadia Holmquist Pedersen 17a1bfa673
macOS CI updates (#1973)
* Use macOS 14 M1-based runners for macOS CI
* Hopefully make the universal build erroring not fail the build (does sometimes with delete-artifact)
* Update vcpkg version
2024-02-07 22:27:04 +01:00
Nadia Holmquist Pedersen a7575ec7b3 Allow the user to choose the UI theme
Mainly useful for those who want dark mode on Windows.
2024-02-07 20:20:54 +01:00
Jesse Talavera d48e5f2da0
Fix DSiWare detection (#1969)
- According to GBATek, all DSiWare games have a high title ID of 0x00030004
- Some homebrew apps set the Unitcode bits to DSi mode to enable support of DSi features
2024-02-01 14:36:35 +01:00
Nadia Holmquist Pedersen 7dd4152d67 Add MaxFPS setting 2024-01-26 13:06:32 +01:00
Nadia Holmquist Pedersen 890035c688 readme: fix macOS build badge 2024-01-25 10:21:19 +01:00
Steveice10 4b576d066e
Add support for using a portable directory without special build flags. (#1956) 2024-01-24 10:27:25 +01:00
Nadia Holmquist Pedersen 77274735d6 the readme for delete-artifact doesn't have this anymore so maybe it'll
stop failing if I remove it
2024-01-24 09:52:22 +01:00
Arisotura 7897bd387b also add writes while we're at it, we know Gericom's gonna abuse them someday :P 2024-01-17 18:54:48 +01:00
Arisotura f4c8202b1a add missing 8/16-bit reads to ROMCnt and ROM SPICnt 2024-01-17 18:50:08 +01:00
Jesse Talavera 740305cc25
Don't reset the VRAM cache if saving a state (#1944)
- This fixes a flickering bug in melonDS DS
2024-01-08 15:20:48 +01:00
Jesse Talavera 8143f54956
Protect savestates while the threaded software renderer is running (#1864)
* First crack at ensuring the render thread doesn't touch GPU state while it's being serialized

* Get rid of the semaphore wait

* Add some extra fields into GPU3D's serialization

* Oops, TempVertexBuffer is already serialized

* Move vertex serialization into its own method

* Lock the GPU3D state when rendering on the render thread or serializing it

* Revert "Lock the GPU3D state when rendering on the render thread or serializing it"

This reverts commit 2f49a551c1.

* Add comments that describe the synchronization within GPU3D_Soft

- I need to understand it before I can solve my actual problem
- Now I do

* Revert "Revert "Lock the GPU3D state when rendering on the render thread or serializing it""

This reverts commit 1977566a6d.

* Let's try locking the GPU3D state throughout NDS::RunFrame

- Just to see what happens

* Slim down the lock's scope

* Narrow the lock's scope some more

* Remove the lock entirely

* Try protecting the GPU3D state with just a mutex

- I'll clean this up once I know it works

* Remove a duplicate method definition

* Add a missing `noexcept` specifier

* Remove an unused function

* Cut some non-hardware state from `GPU3D`'s savestate

* Assume that the next frame after loading a savestate won't be identical

* Actually, it _is_ worth it

* Don't serialize the clip matrix

- It's recalculated anyway

* Serialize `RenderPolygonRAM` as an array of indexes

* Clean up some comments

- I liked the dialogue style, but oh well

* Try restarting the render thread instead of using the lock

- Let's see what happens

* Put the lock back

* Fix some polygon and vertex indexes being saved incorrectly

- Taking the difference between two pointers results in the number of elements, not the number of bytes

* Remove `SoftRenderer::StateBusy` since it turns out we don't need it

- The real synchronization was the friends we made along the way
2024-01-07 23:39:43 +01:00
Eric Warmenhoven f68f55d002
Reset the JIT when loading savestate (#1937)
The effect of this change is simply to call JitEnableWrite(), which is
necessary on apple silicon
2024-01-03 14:42:08 +01:00
Jesse Talavera d1cbc41115
Slight fixups with `FATStorage` (#1934)
* Reload the SD card for `CartSD` and all subclasses

* Make `ROMManager::LoadDLDISDCard` delegate to `GetDLDISDCardArgs`

* Add a method overload for `CartSD::SetSDCard`

* Initialize new SD card images with the correct size

* Sync the old card to the host (if applicable) when move-assigning a new one

* Only sync the old card to the host if it's not read-only

* Remove static state in `FATStorage`

- Replace `FF_ReadStorage` and `FF_WriteStorage` with lambda functions
- Keep open and use the single `File` handle throughout the `FATStorage`'s life
2024-01-03 13:32:17 +01:00
Nadia Holmquist Pedersen 8bfc6df8de TitleManagerDialog: Fix wrong color format 2023-12-31 13:52:58 +01:00
Nadia Holmquist Pedersen 63141c086a guard the default vcpkg directory against multiple CMake instances using it 2023-12-28 19:51:46 +01:00
Nadia Holmquist Pedersen 1cd8c16bbb Disable default-features for host qtbase to speed up cross build times 2023-12-28 15:54:29 +01:00
Nadia Holmquist Pedersen da26453911
CI stuff (#1935)
Add new macOS CI using vcpkg
2023-12-28 15:03:44 +01:00
Jesse Talavera a4b2b0c40d
Resolve or silence some warnings (#1905)
* Resolve some warnings

- Their frequent appearance in the build logs is driving me nuts

* Silence warnings about `offsetof`

* Don't apply `-Wno-invalid-offset` to C, only to C++
2023-12-28 14:54:31 +01:00
Arisotura 6d0de509c4 Merge branch 'master' of github.com:melonDS-emu/melonDS 2023-12-28 14:40:41 +01:00
Arisotura 345b7439e4 integrate OSD into ScreenPanel and make it nicer 2023-12-28 14:40:37 +01:00
Nadia Holmquist Pedersen 5439742578 Add basic CMake presets file
I'll probably use this for CI, but regardless it's nice to have to make
it easier for users to build melonDS.
2023-12-28 09:25:05 +01:00
Arisotura fa835ecf68 blarg 2023-12-27 21:29:25 +01:00
Arisotura 5a08118c87 sfjsh 2023-12-27 21:28:03 +01:00
Jesse Talavera cbd65a131e
Add `alignas` specifiers to some arrays based on how they're accessed (#1933)
* Align some two-element `u32` arrays as `u64`s

- To pacify "unaligned read/write" warnings from UBSan

* Align some more arrays based on how they're accessed
2023-12-26 22:09:39 +01:00
Arisotura ab8938a695 fix OSD scaling on hiDPI screens 2023-12-26 19:32:38 +01:00
Arisotura f905b6fb93 separate EmuThread to its own file 2023-12-26 19:24:14 +01:00
Arisotura fd1e4379b9 Merge branch 'master' of github.com:melonDS-emu/melonDS 2023-12-26 19:04:31 +01:00
Arisotura 7f437d48db start cleaning up: move OpenGL stuff out of EmuThread 2023-12-26 19:04:01 +01:00
Jesse Talavera d55a384c88
Apply some quick hotfixes (#1931) 2023-12-26 16:34:04 +01:00
Nadia Holmquist Pedersen 740489f7a4 Don't call Reset on nullptr 2023-12-26 08:49:02 +01:00
Nadia Holmquist Pedersen ac3153d86b "Incorrectly" link libslirp to stop its broken build system fucking up
our linker flags
2023-12-26 08:14:30 +01:00
Nadia Holmquist Pedersen 65780e6ba2 Update vcpkg 2023-12-26 08:14:20 +01:00
Nadia Holmquist Pedersen 27ac8dbc14
Integrate support for building with dependencies from vcpkg (#1880)
* Integrate support for building with dependencies from vcpkg

Configure the build using -DUSE_VCPKG=ON to use vcpkg. By default
recommended triplets targeting the OS versions official builds support
are used. You can opt out of this with -DUSE_RECOMMENDED_TRIPLETS=OFF.

* Add the vcpkg manifest

* Fetch vcpkg with FetchContent if we don't have it

* macOS cross compiling fixes

- can't use the x86_64 one as host triplet on arm64 because building Qt
  fails for whatever reason. Because of course it does :D
- vcpkg doesn't always like periods in triplet names so removed those

* x86_64 macOS should also use its recommended target when building arm64 builds
2023-12-26 06:51:49 +01:00
Nadia Holmquist Pedersen 4d3af0d915 Make that FindWayland warning shut up
Hopefully "stable" distros use a not-ancient-enough CMake version that
this should be okay.
2023-12-26 05:12:26 +01:00
Nadia Holmquist Pedersen f16de402cf Screen.cpp: include missing headers 2023-12-26 04:26:38 +01:00
Arisotura 5c90cb939d oops 2023-12-25 22:52:44 +01:00
Arisotura 80c6dd524b add convenience method to Window class for OSD messages 2023-12-25 16:34:29 +01:00
Arisotura 6a1232b9a9 move MainWindow and Screen stuff to separate files; WIP 2023-12-24 15:11:30 +01:00
Samuel Magnan f580d20a7b
Support GBA .sav file with appended .rtc (#1927)
* Support GBA .sav file with appended .rtc

* Change comment
2023-12-24 01:09:43 +01:00
Arisotura de4ae9dd92 fix possible crash 2023-12-23 23:59:59 +01:00
RSDuck 989b93c92a do not access NDS object emulation is paused 2023-12-22 23:30:59 +01:00
Nadia Holmquist Pedersen 521fc249a3 Don't try to call transferLayout on non-GL ScreenPanel 2023-12-22 02:17:26 +01:00
Nadia Holmquist Pedersen 752b37ed82 Attempt to get rid of leftover QSharedMemory instance after crash 2023-12-22 01:35:45 +01:00
RSDuck 084747abc5 Reset DS object directly after creation 2023-12-21 22:15:12 +01:00
RSDuck ed650f2b46 call Reset on 3D renderer object 2023-12-21 21:43:57 +01:00
Nadia Holmquist Pedersen fd3c349735 Check Q_OS_UNIX define instead 2023-12-20 21:38:55 +01:00
Daniel Simon 6c6318b63b Fix generic icon when using Wayland 2023-12-20 21:38:55 +01:00
Jesse Talavera 01f8ad009e
Wrap the `EnableJIT` initialization in an `#ifdef` (#1922) 2023-12-20 14:25:49 +01:00
Jesse Talavera 4b4239de62
Set `NDS::EnableJIT` in the constructor (#1921) 2023-12-19 15:15:35 +01:00
Jesse Talavera 1bec2a9293
Fix an incorrect use of `std::move` (#1919)
- When I adapted `GBACart::ParseROM` to use `unique_ptr` instead of a plain pointer, I forgot to remove the code that copied the SRAM data
- That code was made unnecessary because of the move
2023-12-16 00:05:43 +01:00
Jesse Talavera eedb0ba478
Add a call to `std::move` that I missed (#1917) 2023-12-15 20:52:35 +01:00
Jesse Talavera e1821d0023
Simplify the SRAM's representation in `NDSCartArgs` (#1914)
* Simplify the SRAM's representation in  `NDSCartArgs`

- I overthought this one.
- I could've just checked `args && args->SRAM`, but then some other poor bastard might make this mistake.
- Don't mix `pair`, `optional`, and `unique_ptr` all at once, kids.

* Fix a `nullptr` read
2023-12-15 14:56:10 +01:00
Jesse Talavera 24c402af51
Fix detection of native NDS ARM BIOS images (#1910)
* Fix detection of native NDS ARM BIOS images

- Instead of checking for built-in BIOS images, now the altered methods check for native ones
- The CRC32 must match exactly; patched BIOS images will result in `false`

* Encapsulate `NDS::ARM9BIOS` and `ARM7BIOS`

- Also compute the checksum only when setting the BIOS
2023-12-15 14:54:41 +01:00
Jesse Talavera c867a7f1c0
Make the initial 3D renderer configurable via `NDSArgs` (#1913)
* Allow 3D renderers to be created without passing `GPU` to the constructor

* Make the initial 3D renderer configurable via `NDSArgs`

* Fix a compiler error
2023-12-15 14:53:31 +01:00
Adrian Siekierka 6f47c9ed4c
Support emulating R4 Revolution/M3DS Simply cartridges. (#1854)
* Support emulating R4 Revolution/M3DS Simply cartridges.

* NDSCartR4: Write state information to savestate file.

* NDSCart: Use strncmp instead of strcmp for R4 detection.

* NDSCartR4: stylistic improvements

* NDSCartR4: rudimentary Ace3DS support

* NDSCartR4: fix boot when firmware enabled

* NDSCartR4: Fix for namespace changes

---------

Co-authored-by: RSDuck <RSDuck@users.noreply.github.com>
2023-12-15 08:19:53 +01:00
Jesse Talavera 9bfc9c08ff
Sprinkle `const` around where appropriate (#1909)
* Sprinkle `const` around where appropriate

- This will make it easier to use `NDS` objects in `const` contexts (e.g. `const` parameters or methods)

* Remove the `const` qualifier on `DSi_DSP::DSPRead16`

- MMIO reads can be non-pure, so this may not be `const` in the future
2023-12-12 11:07:22 +01:00
Jaklyy 2cba2e783a
fix default emu settings tab (#1912) 2023-12-12 00:04:04 +01:00
Nadia Holmquist Pedersen 81219a9f5d Fix some conflicts with windows.h in some configurations
Fixes build in the MSYS2 Clang/ClangARM64 environments.
2023-12-11 10:59:05 +01:00
RSDuck 082310d5d5 hopefully reset all GPU3D attributes properly 2023-12-08 23:42:08 +01:00
Nadia Holmquist Pedersen 6949100446 Fix GBA<->DS comm not working when using FreeBIOS
Thanks CasualPokePlayer for pointing this out
2023-12-08 18:34:38 +01:00
Nadia Holmquist Pedersen 5ef35a4ccf Don't try to poke at the header on deinit if MPQueue is nullptr 2023-12-08 17:39:56 +01:00
Nadia Holmquist Pedersen 890a66c0eb I'm sick of this interfering with debugging 2023-12-08 17:27:06 +01:00
Jesse Talavera-Greenberg 5dac65f52e Fix some minor instances of undefined behavior 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg bbecab6cb0 Correctly use the refactored `JitEnableWrite` 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 72d4eba477 Rearrange some `#include`s 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 7cef13031f Add some headers that were transitively included by the JIT 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 399a6af91c Move some constants to `MemConstants.h` 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 53e5aa6298 Exclude JIT-related declarations more aggressively 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 733769303c Add `override` to the ARM64 JIT's destructor 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 582a421447 Don't set the JIT args if the JIT is off 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 644d190e98 Add stubs for `NDS::IsJITEnabled` and `SetJITArgs` for when the JIT is excluded 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg f4377e4f0f Remove a loose qualifier 2023-12-08 17:19:00 +01:00
Jesse Talavera-Greenberg 2a35af5bb9 Soften restrictions around `ARMJIT_Memory::RemapNWRAM`
- If in NDS mode, return instead of `assert`ing
- Use `static_cast` and `ConsoleType` instead of `dynamic_cast`
2023-12-08 17:19:00 +01:00
CasualPokePlayer 1b7b5106e2 FreeBIOS: Ensure upper 16 bits are cleared in the initial crc16 value.
Fixes Castlevania: Dawn of Sorrow's checksumming which uses crc16 swi and has garbage in the upper 16 bits of r0.
The official BIOS would seem to implicitly clear these upper 16 bits.
2023-12-06 16:51:22 +01:00
Jesse Talavera 090627b3c1
Remove the last `ConfigEntry` state (#1902)
* Get rid of `ConfigEntry::ExternalBIOSEnable`

- Now the BIOS files themselves are checked
- The frontend's `Config::ExternalBIOSEnable` is not affected

* Add `JITArgs`

* Pass the JIT status to the `ARM` constructors

* Encapsulate `NDS::EnableJIT`

* Pass `JITArgs` to `ARMJIT`'s constructor

* Remove the `JIT_*` `ConfigEntry`s in favor of members

- Allow all the JIT args to be set with `NDS::SetJITArgs`
- Encapsulate the JIT-related parameters in `ARMJIT` so they can reset the block cache if changed
- Update the active (or newly-created) console in the frontend with adjusted JIT args

* Make audio bit depth and interpolation configurable in `NDSArgs`

- Define enums for both
- Give those settings default values in `NDSArgs`
- Remove `ConfigEntry::AudioBitDepth`
- Initialize these settings in the relevant SPU constructors

* Move the last DSi-specific logic in `Reset` to its own subclass

* Remove `ConfigEntry::DSi_FullBIOSBoot`

- Add members to `DSi` instead for getting and setting this
- Update the frontend to accommodate these changes

* Oops, missed a spot

* Remove `ConfigEntry::Firm_MAC` and `Platform::GetConfigArray`

- Also move the MAC parsing code to `ROMManager`

* Remove the last `ConfigEntry` state

- Make GDB support configurable via members

* Add some `#ifdef`s that I'd almost forgotten
2023-12-05 16:47:16 +01:00
Jesse Talavera ae91d89f7c
Use a `constexpr`-friendly cosine implementation (#1903) 2023-12-05 12:41:28 +01:00
Jesse Talavera 2e8cca9ca1
Initialize the framebuffers within the constructor (#1901) 2023-12-04 17:57:51 +01:00
Jesse Talavera bb42c8b639
Refactor how save data (including SD cards) is initialized (#1898)
* Remove `FATStorage::Open` and `FATStorage::Close`

- That's what the constructor and destructor are for, respectively

* Add `FATStorage::IsReadOnly`

* Slight cleanup of `FATStorage`

- Make it move-constructible and move-assignable
- Represent the absence of a sync directory with `std::optional`, not an empty string
- Add `FATStorageArgs` for later use

* Refactor `CartHomebrew` to accept an optional `FATStorageArgs`

- `CartHomebrew` uses it to load an SD card image
- Not passing a `FATStorage` directly because we won't know if we need to load the card until we parse the ROM
- Store the `FATStorage` inside a `std::optional` instead of a pointer
- `CartHomebrew::Reset` no longer reloads the SD card; the frontend needs to set it with the `SetSDCard` method

* Close `NANDImage::CurFile` when move-assigning

- Whoops

* Add `Args.h`

- To construct a `NDS` or `DSi` with arguments
- Mostly intended for system files

* Fix incorrect `final` placement

* Refactor how `DSi`'s NAND and SD card are set

- Provide them via a `DSiArgs` argument in the constructor
- Give `DSi_MMCStorage` ownership of the `NANDImage` or `FATStorage` as needed, and expose getters/setters
- Replace `DSi_SDHost::Ports` with a `array<unique_ptr, 2>` to reduce the risk of leaks
- Store `DSi_MMCStorage`'s disk images in a `std::variant`
- The SD card and NAND image are no longer reset in `Reset()`; the frontend will need to do that itself

* Add getters/setters on `DSi` itself for its storage media

* Remove newly-unused `Platform::ConfigEntry`s

* Use `DSi::SetNAND` in the frontend

* Add `EmuThread::NeedToRecreateConsole`

* Document `NDSArgs` and give its fields default values

* Refactor how system files are loaded upon construction

- Pass `NDSArgs&&` into `NDS`'s constructor
- Use `std::array` for the emulator's BIOS images and the built-in FreeBIOS, to simplify copying and comparison
- Initialize the BIOS, firmware, and SD cards from `NDSArgs` or `DSiArgs`
- Add a new default constructor for `NDS` (not `DSi`) that initializes the DS with default system files
- Embed `FirmwareMem::Firmware` directly instead of in a `unique_ptr`
- `SPIHost` now takes a `Firmware&&` that it forwards to `FirmwareMem`
- Add `Firmware` getters/setters plus `const` variants for `NDS`, `Firmware`, and `FirmwareMem`
- Simplify installation of firmware

* Initialize the DSi BIOS in the constructor

- Change `DSi::ARM9iBIOS` and `ARM7iBIOS` to `std::array`

* Update the frontend to reflect the core's changes

* Remove `DSi_SDHost::CloseHandles`

* Pass `nullopt` instead of the empty string when folder sync is off

* Deduplicate ROM extraction logic

- `LoadGBAROM` and `LoadROM` now delegate to `LoadROMData`
- Also use `unique_ptr` instead of `new[]`

* Oops, missed some `get()`'s

* Move `NDS::IsLoadedARM9BIOSBuiltIn` to the header

- So it's likelier to be inlined
- Same for the ARM7 version

* Remove `NDS::SetConsoleType`

* Add `NDS::SetFirmware`

* Move `GBACart::SetupSave` to be `protected`

- It was only ever used inside the class

* Rename `GBACart::LoadSave` to `SetSaveMemory`

- Same for the cart slot

* Declare `GBACartSlot` as a friend of `GBACart::CartCommon`

* Revise `GBACartSlot`'s getters and setters

- Rename `InsertROM` and `LoadROM` to `SetCart`
- Add a `GetCart` method

* Clean up getters and setters for NDS and GBA carts

* Clean up how carts are inserted into the slots

- Remove setters that operate directly on pointers, to simplify error-handling (use ParseROM instead)
- Add overloads for all carts that accept a `const u8*` (to copy the ROM data) and a `unique_ptr<u8[]>` (to move the ROM data)
- Store all ROM and RAM data in `unique_ptr`
- Default-initialize all fields
- Simplify constructors and destructors, inheriting where applicable

* Refactor GBA save data insertion

- Make `SetupSave` private and non-virtual and move its logic to be in `SetSaveMemory`
- Add overloads for setting save data in the constructor
- Update the SRAM completely in `SetSaveMemory`

* Clean up `NDSCart::CartCommon::SetSaveMemory`

- Move its declaration next to the other `SaveMemory` methods
- Move its (empty) implementation to the header

* Add some comments

* Add Utils.cpp and Utils.h

* Rename some functions in Utils for clarity

* Add `GBACart::ParseROM` and `NDSCart::ParseROM` overloads that accept `unique_ptr<u8[]>`

- The `u8*` overloads delegate to these new overloads
- Also move `SetupSave` for both kinds of carts to be private non-virtual methods

* Finalize the `NDSCart` refactor

- Add `NDSCartArgs` to pass to `ParseROM`
- Add SRAM arguments for all retail carts
- Initialize SRAM inside the constructor
- Delegate to other constructors where possible

* Replace `ROMManager::NDSSave` and `GBASave` with `unique_ptr`

* Make both cart slots return the previously-inserted cart in `EjectCart`

- Primarily intended for reusing carts when resetting the console

* Make `NDS::EjectCart` return the old cart

* Initialize both cart slots with the provided ROM (if any)

* Make `NDS::EjectGBACart` return the ejected cart

* Clean up some comments in Args.h

* Rename `ROMManager::LoadBIOS` to `BootToMenu`

- Clarifies the intent

* Add `ROMManager::LoadDLDISDCard`

* Add a doc comment

* Refactor how the `NDS` is created or updated

- Rewrite `CreateConsole` to read from `Config` and load system files, but accept carts as arguments
- Fail without creating an `NDS` if any required system file doesn't load
- Add `UpdateConsole`, which delegates to `CreateConsole` if switching modes or starting the app
- Use `std::variant` to indicate whether a cart should be removed, inserted, or reused
- Load all system files (plus SD cards) in `UpdateConsole`
- Eject the cart and reinsert it into the new console if applicable

* Respect some more `Config` settings in the `Load*` functions

* Remove `InstallNAND` in favor of `LoadNAND`

* Fix some potential bugs in `LoadROMData`

* Oops, forgot to delete the definition of `InstallNAND`

* Add functions to get `FATStorageArgs`

- Not the cards themselves, but to get the arguments you _would_ use to load the cards

* Refactor `ROMManager::LoadROM`

- Load the ROM and save data before trying to initialize the console

* Clean up `ROMManager::Reset` and `BootToMenu`

- Let `EmuThread::UpdateConsole` do the heavy lifting

* Clean up `LoadGBAROM`

* Remove some unused functions

* Set the default DSi BIOS to be broken in `DSiArgs`

* Respect `Config::DSiFullBIOSBoot` when loading DSi BIOS files

* Remove some more unused functions

* Remove redundant `virtual` specifiers

* Refactor `NDSCart::CartCommon::Type()` to return a member instead of a constant

- One less virtual dispatch
- The cart type is read in `NDSCartSlot::DoSavestate`, which is a path downstream (due to rewinding)

* Remove a hash that I computed for debugging purposes

* Make `ByteSwap` `constexpr`

* Remove an unused `#include`

* Remove unnecessary functions from the NDS carts

- Mostly overrides that added nothing

* Default-initialize all NDSCart fields

* Make `GBACart::Type()` not rely on virtual dispatch

- `GBACartSlot::DoSavestate` calls it, and savestates can be a hot path downstream

* Don't forget to reset the base class in `CartGameSolarSensor::Reset()`

* Remove redundant `virtual` specifiers

* Default-initialize some fields in `GBACart`

* Fix ROMs not loading from archives in the frontend

- Whoops

* Change how the `Firmware` member is declared

* Forgot an include in Utils.cpp

* Rename `FirmwareMem::Firmware` to `FirmwareData` to fix a build error on Linux

- One of these days I'll convince you people to let me use `camelCaseMemberNames`

* Add `override` to places in `DSi_MMCStorage` that warrant it

* Fix firmware saving on the frontend

- Remove `GetConfigString` and `ConfigEntry::WifiSettingsPath` while I'm at it

* Add a non-const `GetNAND()`
2023-12-04 17:57:22 +01:00
Jesse Talavera da8d413ad9
Slight cleanup to SPU (#1900)
* Move `SPUChannel` and `SPUCaptureUnit` to be stored inside `array`s instead of allocated separately

* Default-initialize most of `SPU`'s fields

* Generate the interpolation tables at compile-time with `constexpr`

- Now it's faster and thread-safe

* Slight cleanup in SPU

- Iniitialize most fields in the class declaration

* Mark `SPU` as `explicit`
2023-12-04 17:56:01 +01:00
Jesse Talavera-Greenberg 7caddf9615
Clean up the 3D renderer for enhanced flexibility (#1895)
* Give `GPU2D::Unit` a virtual destructor

- Undefined behavior avoided!

* Add an `array2d` alias

* Move various parts of `GPU2D::SoftRenderer` to `constexpr`

- `SoftRenderer::MosaicTable` is now initialized at compile-time
- Most of the `SoftRenderer::Color*` functions are now `constexpr`
- The aforementioned functions are used with a constant value in at least one place, so they'll be at least partially computed at compile-time

* Generalize `GLRenderer::PrepareCaptureFrame`

- Declare it in the base `Renderer3D` class, but make it empty

* Remove unneeded `virtual` specifiers

* Store `Framebuffer`'s memory in `unique_ptr`s

- Reduce the risk of leaks this way

* Clean up how `GLCompositor` is initialized

- Return it as an `std::optional` instead of a `std::unique_ptr`
- Make `GLCompositor` movable
- Replace `GLCompositor`'s plain arrays with `std::array` to simplify moving

* Pass `GPU` to `GLCompositor`'s important functions instead of passing it to the constructor

* Move `GLCompositor` to be a field within `GLRenderer`

- Some methods were moved up and made `virtual`

* Fix some linker errors

* Set the renderer in the frontend

* Remove unneeded `virtual` specifiers

* Remove `RenderSettings` in favor of just exposing the relevant member variables

* Update the frontend to accommodate the core changes

* Add `constexpr` and `const` to places in the interpolator

* Qualify references to `size_t`

* Construct the `optional` directly instead of using `make_optional`

- It makes the Linux build choke
- I think it's because `GLCompositor`'s constructor is `private`
2023-11-29 15:23:11 +01:00
Jesse Talavera-Greenberg e973236203
Refactor `NDS` and `DSi` to be objects (#1893)
* First crack at refactoring NDS and DSi into objects

- Remove all global/`static` variables in `NDS` and related classes
- Rely more on virtual dispatch when we need to pick methods at runtime
- Pass `NDS&` or `DSi&` to its constituent components where necessary
- Introduce some headers or move some definitions to break `#include` cycles

* Refactor the frontend to accommodate the core's changes

* Move up `SchedList`'s declaration

- Move it to before the components are initialized so the `map`s inside are initialized
- Fields in C++ are initialized in the order they're declared

* Fix a crash when allocating memory

* Fix JIT-free builds

* Fix GDB-free builds

* Fix Linux builds

- Explicitly qualify some member types in NDS, since they share the same name as their classes

* Remove an unnecessary template argument

- This was causing the build to fail on macOS

* Fix ARM and Android builds

* Rename `Constants.h` to `MemConstants.h`

* Add `NDS::IsRunning()`

* Use an `#include` guard instead of `#pragma once`
2023-11-28 23:16:41 +01:00
Adrian Siekierka c84cb17462
DSi_SD: implement command 17, 24 (#1877) 2023-11-26 20:07:31 +01:00
Jaklyy ad7b1a8c61
only fill edges when translucent if blending is enabled (#1882) 2023-11-25 18:40:07 +01:00
Jesse Talavera-Greenberg 346dd4006e
Move all core types into namespaces (#1886)
* Reorganize namespaces

- Most types are now moved into the `melonDS` namespace
- Only good chance to do this for a while, since a big refactor is next

* Fix the build
2023-11-25 18:32:09 +01:00
Jesse Talavera-Greenberg 651b0f680c
Use Platform::File calls in NDS::debug (#1888) 2023-11-24 19:17:22 +01:00
Rayyan Ansari 679c37ddce
Add support for saving animated icons
Add support for exporting animated DSi icons as GIF using the
header-only gif.h library.
2023-11-23 18:50:23 +00:00
Jesse Talavera-Greenberg 544fefa27f
Refactor the JIT to be object-oriented (#1879)
* Move TinyVector to a new file

- So it's less sensitive to #include ordering

* Forgot to include assert.h

* Refactor ARMJIT_Memory into an object

* Oops, forgot a declaration

* Refactor ARMJIT to be contained in an object

* Remove an unused function declaration

* Add a missing #include

* Remove a now-unused global

* Use ARMJIT_Memory's own memory access functions

* Fix some omissions in the ARM JIT

* Move libandroid to be a member of ARMJIT_Memory instead of a global

* Default-initialize most fields in ARMJIT_Compiler.h

* Define NOOP_IF_NO_JIT

* Finish refactoring the JIT to be object-oriented
2023-11-18 16:40:54 +01:00
Nadia Holmquist Pedersen f2d7a29015 fix forgotten <array> include 2023-11-15 17:26:01 +01:00
orbea e63e29ca91 DSi_Camera: fix gcc-14 build issue
melonDS/src/DSi_Camera.cpp:190:23: error: 'clamp' is not a member of 'std'
  190 |             r1 = std::clamp(r1, 0, 255); g1 = std::clamp(g1, 0, 255); b1 = std::clamp(b1, 0, 255);
      |                       ^~~~~
2023-11-11 19:53:00 +01:00
Jesse Talavera-Greenberg 4558be0d8e
Refactor the GPU to be object-oriented (#1873)
* Refactor GPU3D to be an object

- Who has two thumbs and is the sworn enemy of global state? This guy!

* Refactor GPU itself to be an object

- Wow, it's used in a lot of places
- Also introduce a new `Melon` namespace for a few classes
- I expect other classes will be moved into `Melon` over time

* Change signature of Renderer3D::SetRenderSettings

- Make it noexcept, and its argument const

* Remove some stray whitespace
2023-11-09 21:54:51 +01:00
Jesse Talavera-Greenberg 88072a02c5
Move NDSCart-related global state into objects (#1871)
* Move NDSCart-related global state into objects

- RAII will now do the heavy lifting
- Mark some methods as const or noexcept

* Move GBACart-related global state into objects (#1870)

- RAII will now do the heavy lifting
- Mark some methods as const or noexcept
- Once the `NDS` object is finalized, most of these `assert`s can go away

* Make AREngine::RunCheat public (#1872)

- I use it directly in melonDS DS to apply single cheats without using ARCodeFile
- Before the AREngine refactor I could just redeclare the function in my code
- Now I can't
2023-11-09 18:57:16 +01:00
Jesse Talavera-Greenberg 3d3e4240a0
Make AREngine::RunCheat public (#1872)
- I use it directly in melonDS DS to apply single cheats without using ARCodeFile
- Before the AREngine refactor I could just redeclare the function in my code
- Now I can't
2023-11-08 22:21:30 +01:00
Jesse Talavera-Greenberg 8b47178add
Move GBACart-related global state into objects (#1870)
- RAII will now do the heavy lifting
- Mark some methods as const or noexcept
- Once the `NDS` object is finalized, most of these `assert`s can go away
2023-11-08 22:21:21 +01:00
Jaklyy b4ff911fa3
Fix regression caused by change to front face polygon culling (#1820)
* fix regression with facing view

Only the check for a polygon being counter-clockwise is supposed to be <=

* only use dot < 0 for 'cull front face' polygons

this is the fix.
2023-11-07 21:22:25 +01:00
Nadia Holmquist Pedersen 24a33e505e Also exclude .note.GNU-stack section on Windows arm64 2023-11-07 10:53:01 +01:00
Rayyan Ansari 8fa9705079
ArchiveUtil: use signed return type instead of unsigned
The ExtractFileFromArchive function can sometimes return -1 on error,
however the function's return type was specified as u32, which would
mean that it would instead be represented as the maximum value.
Change the function's return type to the signed s32 instead, and
correct uses.
2023-11-06 21:27:09 +00:00
Rayyan Ansari 2b3bba512e
Fix some memory leaks
Free some objects that were allocated with new but not deleted, and in
one case, do not set a pointer to nullptr before deleting, as this
results in a memory leak due to memory allocated not being freed.
2023-11-06 20:25:32 +00:00
Rayyan Ansari 0e4d082361
ROMManager: initialise filedata to nullptr
If a user manages to open a file as a ROM that is greater than 1 GiB,
it will cause a segmentation fault (a crash) in LoadROM due to a delete
being called on an uninitialised pointer, which is undefined behaviour.

Initialise filedata to nullptr to prevent this, as deleting a null
pointer is defined as a no-op.
2023-11-05 20:21:16 +00:00
Rayyan Ansari df571078cf
CameraManager: wait for camera to be loaded
In QCamera in Qt 5, the camera is required to have been loaded before
querying its settings and resolutions. Doing so without loading being
finished would result in the returned list being empty.
See https://doc.qt.io/qt-5.15/qcamera.html#supportedViewfinderSettings

Add a QEventLoop that waits for the state to change from Loading to
Loaded before supportedViewfinderSettings() is called to ensure that
valid information is returned.

(Fixes my camera being blank in preview, same issue also presumed
to occur when camera is needed in game, fix tested on a USB camera on
a Linux system and Qt 5.)
2023-11-05 15:52:13 +00:00
Arisotura 0aff9471c5 fuck every aspect of this 2023-11-05 15:38:22 +01:00
Arisotura 11c22f077d convert DSP 2023-11-05 11:58:50 +01:00
Arisotura 54ebf1b1b2 convert DSi I2C and camera 2023-11-04 19:42:36 +01:00
Arisotura 7837c169a1 convert AES 2023-11-04 17:46:52 +01:00
Arisotura 8f1b0d4a05 convert AREngine 2023-11-04 17:28:16 +01:00
Arisotura 2bd09eafeb convert Wifi and WifiAP 2023-11-04 17:00:12 +01:00
Arisotura 76976fef30 convert SPU to OOP 2023-11-04 14:20:58 +01:00
Arisotura ac38faef14 update copyright years 2023-11-04 00:21:46 +01:00
Arisotura f188c2cf1a hopefully get the compiler to shut up 2023-11-03 21:33:13 +01:00
Arisotura e4f4e94694 convert RTC to OOP 2023-11-03 21:20:09 +01:00
Arisotura 440b356674 get this started: refactor SPI in OOP 2023-11-03 20:17:00 +01:00
Arisotura 70c6750561 better, less hacky, more OOP-friendly scheduler design 2023-11-02 21:04:09 +01:00
Arisotura 5ccd3916ff better be safe than sorry 2023-11-02 15:46:35 +01:00
Arisotura 2959d089fe fix weird hang when returning to the DSi menu 2023-11-02 15:31:26 +01:00
Arisotura bff7a0d114 make linebreaks in text files not weird 2023-11-02 12:40:49 +01:00
Arisotura eb13bce6e7 RTC: add the DSi alarm expansion registers 2023-11-02 12:21:59 +01:00
Jesse Talavera-Greenberg d11ba63bb0
Fix compilation with the GDB stub disabled (#1863) 2023-11-01 16:02:15 +01:00
Arisotura 3b4fdea376 minor cleanup 2023-11-01 12:03:35 +01:00
Nadia Holmquist Pedersen b8963b0738 use a grid layout in the date/time dialog so Qt will behave 2023-10-30 19:37:39 +01:00
Arisotura e89b8a871b fdsfd 2023-10-30 18:40:26 +01:00
Arisotura 9a450f5f28
RTC revamp (#1867)
* get this started

* implement DSi RTC commands

* set up RTC clock timer. lay down basic idea of a clock.

* make the date/time registers writable

* move RTC state to its own structure, to make it easier to deal with

* more RTC work
lay base for date/time dialog

* get the bulk of the RTC functionality going

* much simpler design for RTC stuff

* aha, that is what it is

* start working on the RTC IRQ

* implement all types of RTC IRQ

* start refining sleep mode. code still kinda sucks.

* implement keypad IRQ

* refine it some more

* shut the fuck uuuuuupppppppppppppp
2023-10-30 18:37:49 +01:00
Jesse Talavera-Greenberg 21590b0709
Miscellaneous DSi NAND fixes (#1852)
* Replace some standard I/O calls with Platform equivalents

- I missed a spot when I submitted that PR a few months ago

* Include <memory> in DSi_NAND.h

- Because it uses unique_ptr

* Split DSi_NAND::ReadHardwareInfo into ReadSerialData and ReadHardwareInfoN

* Add a RegionMask enum

* Move DSi NAND patching to the frontend

* Add DSiSupportedLanguageMask

- Not currently used by the frontend, but I use it in melonDS DS

* Remove some Platform::ConfigEntry values

- The core no longer needs to know about them
- The corresponding Config values are unchanged

* Mark NANDMount's destructor as noexcept
2023-10-24 23:28:14 +02:00
Jesse Talavera-Greenberg 8c4e5af737
Slight polish to DMA (#1856)
* Slight polish to DMA

- Default-initialize members explicitly
- Mark some methods as const noexcept
- Initialize DMA::MRAMBurstTable to DMATiming::MRAMDummy
- Use the default destructor

* Move DMA_Timings definitions to a source file

- To ensure constant and unique addresses

* Include some extra DMA members in the savestate

* Simplify serializing the DMA table

- Extend the dummy table to 256 bytes (same length as the real ones)

* Revert the type change to DMA::DoSavestate

* Keep the MRAMBurstTable inside the DMA class, instead of using a pointer

- If we use a pointer to an external table, then we can't use it in savestates (else that external table gets overwritten)
2023-10-24 23:27:55 +02:00
Jesse Talavera-Greenberg bf81b87a60
Generalize a path in .gitignore (#1862)
- Covers all of CLion's default CMake build paths
2023-10-24 21:49:36 +02:00
PoroCYon 3ab752b8ca
GDB stub (#1583)
* gdbstub beginnings

* gdbstub: finish gdb impl things, next up is integration with melonDS

* holy fuck the gdbstub works

* gdb breakpoints work, but there's a mysterious crash on continue

* fix memory corruption that sometimes happened, and make resetting the console thru gdb work

* remove some gdb debug printing

* fix things in gdbstub

* separate option for enabling gdbstub

* add mode-dependent CPU registers

* C++ize the GDBstub code

* add gdbstub config in emu settings dialog

* make sure gdb is disabled when jit is enabled

* Remove unnecessary compiler flags, mark ARMJIT assembly code as no-execute-stack

This hardens the binary a little bit against common exploitation methods

* add option to wait for debugger attach on startup

* only insert GNU stack notes on linux

* disable gdbstub enable checkbox when jit is enabled

* fix non-linux incompatibilities

* enable gdbstub by default

* fix issues with gdbstub settings disable stuff

* format stuff

* update gdb test code

* Fix segfault when calling StubCallbacks->GetCPU()

C++ overrides are hard. Please I'm just a lowly C programmer.

* fix packet size not being sent correctly

Thanks to @GlowingUmbreon on Github for troubleshooting this

* fix select(2) calls (i should read docs more properly)

* fix GDB command sequencing/parsing issue (hopefully)

* [GDB] implement no-ack mode

* fix sending ack on handshake

* get lldb to work
2023-10-22 15:35:31 +02:00
RSDuck 3d58a338a1 store pc+12 when storing r15 2023-10-22 15:21:03 +02:00
Jesse Talavera-Greenberg d4e51f8060
Refactor DSi_NAND (#1844)
* Refactor diskio's contents

- Change ff_disk_read_cb/write_cb into a std::function instead of a raw pointer
- Add const specifiers as needed

* Refactor DSi_NAND to manage the file system's mounted lifetime with RAII

* Split NANDMount into NANDMount and NANDImage

- NANDImage is used for information about the NAND that doesn't require decryption or filesystem access
- NANDMount is used to actually access the file system
- Both classes manage their respective resources (the NAND file handle and the NAND's mount) with RAII
- Also split the file loading into another function that I will remove in a later PR

* Make NANDMount immovable

* Remove NAND-loading code that I had sectioned off into a function

- Incomplete copypasta
- I must have gotten distracted

* Tidy up NANDImage's initialization

- Don't unmount the disk image if the constructor fails (that's NANDMount's job now)
- Only assign CurFile if the constructor succeeds

* Add some const-correctness

* Move DSi NAND initialization to the frontend

- The NANDImage is now installed via a unique_ptr in DSi

* Remove Platform::DSi_NANDPath

- Not Config::DSiNANDPath; that can still be configured as usual
- The core no longer needs to care
2023-10-11 17:20:05 +02:00
Jesse Talavera-Greenberg b2fcff97c1
Add some structs for files that DSi_NAND reads (#1842)
* Add DSiFirmwareSystemSettings

* Replace DSiFirmwareSystemSettings::TouchCalibration fields with std::arrays

- So assignment can be done in one line

* Make DSiFirmwareSystemSettings a union

- So its bytes can be accessed

* Add a comment

* Use DSiFirmwareSystemSettings instead of raw byte offsets

* Add definitions for DSiSerialData and DSiHardwareInfoN

* Move DSiFirmwareSystemSettings's hash update logic into its own method
2023-10-02 17:54:17 +02:00
Jesse Talavera-Greenberg bb09ce7d70
Replace DSi_NAND's uses of sprintf with snprintf (#1841)
- Now clang oughta stop complaining
2023-10-01 21:58:56 +02:00
Jesse Talavera-Greenberg f8fdc77e43
Wrap CurGLCompositor cleanup in an #ifdef (#1837) 2023-09-24 18:48:37 +02:00
Jesse Talavera-Greenberg 9d9ba83731
Clean up some rendering-related resources in DeInit (#1836)
- The unique_ptr destructors will take care of the cleanup
2023-09-24 18:33:14 +02:00
Jesse Talavera-Greenberg 7d4a7969d9
Ensure that the new firmware is installed when resetting (#1834)
- It might have been changed in the settings
2023-09-21 13:54:17 +02:00
RSDuck 6ca02aab2c only recalculate extended access point checksum when firmware is a DSi one 2023-09-20 19:17:26 +02:00
Jesse Talavera-Greenberg 5bfe51e670
Refactor the core's handling of firmware and BIOS images to rely less on the file system (#1826)
* Introduce firmware-related structs

* Fix some indents

* Move the generated firmware identifier to a constant

* Document the WifiAccessPoint constructors

* Add some constants

* Remove a stray comment

* Implement Firmware::UserData

* Add Firmware::Mask

* Document Firmware::Buffer

* Add a Firmware constructor that uses a FileHandle

* Set the default username in UserData

* Update the UserData checksum

* Forgot to include Platform.h

* Remove some redundant assignments in the default Firmware constructor

* const-ify CRC16

* Replace the plain Firmware buffer with a Firmware object

- Remove some functions that were reimplemented in the Firmware constructors

* Fix some crashes due to undefined behavior

* Fix the user data initialization

- Initialize both user data objects to default
- Set both user data objects to the same touch screen calibration

* Follow the DS logic in determining which user data section is current

* Remove an unneeded include

* Remove another unneeded include

* Initialize FirmwareMask in Firmware::Firmware

* Use the DEFAULT_SSID constant

* Add SPI_Firmware::InstallFirmware and SPI_Firmware::RemoveFirmware

* Move a logging call to after the file is written

* Add a SaveManager for the firmware

* Touch up the SPI_Firmware::Firmware declaration

* Move all firmware loading and customization to the frontend

* Call Platform::WriteFirmware when it's time to write the firmware back to disk

* Fix some missing stuff

* Remove the Get* functions from SPI_Firmware in favor of GetFirmware()

* Implement SPI_Firmware::DeInit in terms of RemoveFirmware

* Add Firmware::UpdateChecksums

* Fix an incorrect length

* Update all checksums in the firmware after setting touch screen calibration data

* Use the Firmware object's Position methods

* Remove register fields from the Firmware object

* Install the firmware before seeing if direct boot is necessary

* Install the firmware before calling NDS::Reset in LoadROM

* Slight cleanup in ROMManager

* Fix the default access point name

* Shorten the various getters in Firmware

* Add qualifiers for certain uses of firmware types

- GCC can get picky if -fpermissive isn't defined

* Add an InstallFirmware overload that takes a unique_ptr

* Log when firmware is added or removed

* Don't clear the firmware in SPI_Firmware::Init

- The original code didn't, it just set the pointer to nullptr

* Fix a typo

* Write back the full firmware if it's not generated

* Move the FreeBIOS to an external file

* Load wfcsettings.bin into the correct part of the generated firmware blob

* Load BIOS files in the frontend, not in the core

* Fix logging the firmware ID

* Add some utility functions

* Mark Firmware's constructors as explicit

* Remove obsolete ConfigEntry values

* Include <locale> explicitly in ROMManager

* Fix up some includes

* Add Firmware::IsBootable()

* Add a newline to a log entry

- Whoops

* Log the number of bytes written out in SaveManager

* Mark FirmwareHeader's constructor as explicit

* Clean up GenerateDefaultFirmware and LoadFirmwareFromFile

- Now they return a pair instead of two by-ref values

* Refactor SaveManager a little bit

- Manage its buffers as unique_ptrs to mitigate leaks
- Reallocate the internal buffer if SetPath is asked to reload the file (and the new length is different)

* Remove some stray parens

* Fix some firmware-related bugs I introduced

- Firmware settings are now properly saved to disk (beforehand I misunderstood when the firmware blob was written)
- Firmware is no longer overwritten by contents of wfcsettings.bin

* Slight cleanup
2023-09-18 21:09:11 +02:00
Jesse Talavera-Greenberg db963aa002
Make the NDS teardown more robust (#1798)
* Make cleanup a little more robust to mitigate undefined behavior

- Add some null checks before cleaning up the GPU3D renderer
- Make sure that all deleted objects are null
- Move cleanup logic out of an assert call
- Note that deleting a null pointer is a no-op, so there's no need to check for null beforehand
- Use RAII for GLCompositor instead of Init/DeInit methods

* Replace a DeInit call that I missed

* Make ARMJIT_Memory less likely to generate errors

- Set FastMem7/9Start to nullptr at the end
- Only close and unmap the file if it's initialized

* Make Renderer3D manage its resources with RAII

* Don't try to deallocate frontend resources that aren't loaded

* Make ARMJIT_Memory::DeInit more robust on the Switch

* Reset MemoryFile on Windows to INVALID_HANDLE_VALUE, not nullptr

- There is a difference

* Don't explicitly store a Valid state in GLCompositor or the 3D renderers

- Instead, create them with static methods while making the actual constructors private

* Make initialization of OpenGL resources fail if OpenGL isn't loaded

* assert that OpenGL is loaded instead of returning failure
2023-09-15 15:31:05 +02:00
RSDuck 1aaf22d181 fix last commit 2023-09-02 18:56:58 +02:00
jdp_ 2a3a071216
Reduce code stink (#1818)
CRC32.cpp:
Make table initialization compile time

DSi_NAND.cpp:
Fix file close / unmount / disk close on error
~L427: Remove redundant calls, as they are immediately rendered useless by `rem` being overwritten

NDS.cpp / FreeBIOS.h:
Remove unneeded size values in header
Remove unneeded memset's as they are initialized anyway

sha1.c / sha1.h:
Fix useless warning

Wifi.cpp:
Remove unneeded includes

DSi.cpp:
Reduce ugly casts
Deduplicate code

qt_sdl/main.cpp:
silence clang switch statement warning

qt_sdl/main.h:
fix override warnings

dolphin/BitSet.h:
use msvc extensions only when appropriate, fix broken bit set count under _WIN32
2023-08-28 20:01:15 +02:00
Tuffy b4aa7fafc9
Updated README.md (#1681)
inserted missing dependency in dynamic build instructions (qt5-tools)
2023-08-27 13:49:13 +02:00
xenticore b4756c5944
Update macOS icon (#1609)
* Add SVG icon

* Update macOS icon

* Don't force change the application icon at runtime on macOS
2023-08-27 13:37:42 +02:00
StraDaMa bc71618457
remove AR code size limit (#1802)
Co-authored-by: Arisotura <thetotalworm@gmail.com>
2023-08-27 13:34:11 +02:00
Jaklyy 2bd12669b2
Edge fill rules for swapped polygons + a few minor fixes to edge cases (#1815)
* fix edge fill rules for swapped polygons

also fixes translucent polygons not being always edge filled.

* fix right edge fill rule

* fix right edge fill rule for realsies

* fix a few more glitchy polygons

specifically quads similar to: (-67,40) (64, 160) (192, 160), (8, 111)

* fix one edge case pixel

i hate this so much

* fix "flat bottom" edge fill

* fix regression + apply changes to shadow masks

fix a regression with certain line polygons not rendering; there seems to be an exception made by the ds'  gpu in order for these polygons to render properly.
also apply these changes to shadow masks because i forgot to

* forgot to remove a line

---------

Co-authored-by: Arisotura <thetotalworm@gmail.com>
2023-08-27 13:32:31 +02:00
Mireille a571fe19c3
Make sprite mosaic (more) accurate (#1687)
* Make horizontal sprite mosaic (more) accurate

* Vertical sprite mosaic should not extend the sprite's bounding area

* Vertical sprite mosaic should not extend the sprite's bounding area (2)

* OBJIndex is no longer needed
2023-08-27 13:29:23 +02:00
Jaklyy d69745b3a8
Fix Incorrect Polygon Swapping Behavior and Implement Correct Rules for Shifting Right Edges Left (#1816)
* fix polygons being swapped incorrectly

"borrowed" this from noods
needs verification that the >= and <= signs aren't actually supposed to be > and <

* proper rules for moving vertical right slopes left

* nvm most of that was actually pointless

that's on me for not checking
2023-08-27 13:29:12 +02:00
Jaklyy dc8efb62b8
Fix aa being upside down on swapped y-major slopes (#1803)
* fix aa being upside down on swapped y-major slopes

* further improvements to swapped aa

in addition to fixing swapped y-major slope aa, now fixes:
swapped x-major slope aa
swapped vertical slope aa

* use templates instead + style/comment tweaks

should force the compiler to precompile if statements like i want it to do, instead of just hoping it does so on its own
2023-08-27 13:28:44 +02:00
Jaklyy d7369857c3
Small Fix to Anti-Aliasing + Edge Marking Behavior (#1680)
* Anti-Alias All Edges

Changing a bunch of 0x3s to 0xF since I figure if they're checking the left and right edge they wanna be checking the top and bottom too now that they're gonna be aa'd. also copy that if statement over since otherwise there won't be anything to blend with.

* small optimization

its probably a tiny bit faster?
idk id need actual benchmarking tools.
doesn't break anything at least.
2023-08-27 13:28:26 +02:00
Jaklyy 758b5ee7a1
fix aa calc for 1px tall 0px wide slopes (#1795) 2023-08-27 13:27:42 +02:00
Jesse Talavera-Greenberg ee55677086
Assorted portability enhancements (#1800)
* Introduce some Platform calls for managing dynamic libraries

* Add Platform::WriteFATSectors

* Introduce some Platform calls for managing dynamic libraries

* Add Platform::WriteFATSectors

* Change includes of "../types.h" to "types.h"

- Makes it easier to directly include these headers in downstream projects

* Change an include of "../Wifi.h" to "Wifi.h"

* Allow CommonFuncs.cpp to compile on Android

* Tidy up some logging calls

- Use Platform::Log in LAN_Socket.cpp
- Soften some warnings to Debug logs (since they don't necessarily represent problems)

* Add Platform::EnterGBAMode

- Gracefully stop the emulator if trying to enter GBA mode

* Soften some logs that most players won't care about

* Soften some more logs

* Introduce Platform wrappers for file operations

* Fix pointer spacing

* Fix more style nits

* Log the errno when ftruncate fails

* Fix FileSeek offset argument

- With an s32 offset, we couldn't access files larger than 2GB

* Revise Platform::StopEmu to address feedback

- Remove Platform::EnterGBAMode in favor of adding a reason to Platform::StopEmu
- Also rename Platform::StopEmu to Platform::SignalStop
- Add an optional argument to NDS::Stop
- Use the new argument everywhere that the console stops itself

* Rename FileGetString to FileReadLine

- It conveys the meaning better

* Rename FileSeekOrigin::Set to Start

- It conveys the meaning better

* Change definition of FileGetString to FileReadLine

- Oops, almost forgot it

* Rename FlushFile to FileFlush

- To remain consistent with the other File functions

* Add a FileType usage

* Fix line break in FileSeekOrigin

* Document Platform::DeInit

* Clarify that StopReason::Unknown doesn't always mean an error

* Move and document FileType::HostFile

* Remove Platform::OpenDataFile

- Nothing currently uses it

* Refactor Platform::OpenFile and Platform::OpenLocalFile to accept a FileMode enum instead of a string

- The enum is converted to fopen flags under the hood
- The file type is used to decide whether to add the "b" flag
- Some helper functions are exposed for the benefit of consistent behavior among frontends
- Equivalent behavior is maintained

* Fix a tab that should be spaces

* Use Windows' 64-bit implementations of fseek/ftell

* Move Platform::IsBinaryFile to Platform.cpp

- It could vary by frontend

* Remove an unused FileType

* Rename an enum constant

* Document various Platform items

* Use Platform::DynamicLibrary to load libandroid

- And clean it up at the end

* Fix a typo

* Pass the correct filetype to FATStorage

- Since it can be used for DSI NAND images or for SD cards

* Remove Platform::FileType
2023-08-18 22:50:57 +02:00
Jaklyy f454eba3c3
check lower pixel when top pixel ignores fog (#1808) 2023-08-13 05:38:26 +02:00
Jaklyy 5f9e7e19f3
Improve Interpolation Accuracy (#1686)
* Fix Up Y Interp Inputs

* Change Linear Interp Formula

Fixes a handful of pixels.
Still not perfect.

* Cleanup

remove some unnecessary code and parentheses
2023-08-10 18:00:40 +02:00
RSDuck 7731f66e55 fix some UB 2023-08-01 03:00:41 +02:00
RSDuck 3efbf1b813 a bit of frontend refactoring 2023-07-29 21:27:28 +02:00
Arisotura 8fd46e5f8c wifi: attempt two at optimizing the sync mechanism. this time it should be far less prone to problems. 2023-07-28 10:14:33 +02:00
Arisotura 9c5cde8109 wifi: implement CMD retries 2023-07-27 21:54:30 +02:00
Arisotura c3943b29ec wifi:
* rework and clean up frame transfer code
* disable melonAP during local multiplayer comm
2023-07-27 21:11:30 +02:00
Arisotura b04c250e2f cancel CMD transfer if there isn't enough time left 2023-07-25 23:45:10 +02:00
Arisotura a87dc83279 wifi: mystery ack value is CMD_COUNT 2023-07-25 20:50:07 +02:00
Arisotura c7afa8d3f6 keep that one extra line in, tho
(remind me to rename these with the proper reg names)
2023-07-25 20:32:54 +02:00
Arisotura 193c7ed97b Revert "attempt at making local multiplayer faster"
This reverts commit 8772258fe7.
2023-07-25 20:32:01 +02:00
Nadia Holmquist Pedersen 75ae38ec7b codesign the final universal macOS app bundle
otherwise the code signature in it will be invalid, and macOS won't run
it witohout manually removing xattrs
2023-07-24 11:29:43 +02:00
Nadia Holmquist Pedersen e6cc4b14b0 Work around a strange bug in Qt5 that causes melonDS to crash on launch
...but only with LTO enabled
...but only on some UNIX systems
...but only with some additional build options except when it breaks
   without any as well
2023-07-16 15:49:51 +02:00
PoroCYon fbb41bd73d
DSi: add option to boot the full boot ROMs (#1581)
* DSi: add option to boot the full boot ROMs

added a config option for this so that this can be enabled or disabled

also added IO regs for DSi GPIO, but those don't do anything yet.

* reset GPIO regs on reset
2023-07-16 02:40:50 +02:00
Arisotura cf7375f9ea Merge branch 'master' of github.com:Arisotura/melonDS 2023-07-15 01:16:46 +02:00
Arisotura 8772258fe7 attempt at making local multiplayer faster 2023-07-15 01:16:31 +02:00
TGP17 24a4cacaae
Add AppImage Builds (#1670)
* Create build-appimage.yml

* Update build-appimage.yml

This Adds the new Dependencies for MelonDS
2023-07-14 22:40:35 +02:00
Arisotura 4b7c2ba8c2 fix bad seqno bug with MP replies (oops) 2023-07-14 12:03:58 +02:00
Arisotura a2033a62fd wifi: only allow setting TXSLOT_CMD bit15 if CMDCOUNT is nonzero, as per GBAtek 2023-07-14 11:39:58 +02:00
Nadia Holmquist Pedersen f432e559d4 Add a fallback to streaming decompression when loading zstd-compressed ROMs.
Because of course some compression programs aren't nice enough to tell
you the decompressed size up front in the file, so the other approach
will fail. Things just can't ever be easy and straight forward, can they?
2023-07-14 03:05:34 +02:00
Nadia Holmquist Pedersen ca5e8792c8 Don't try to open the mic device every time if SDL says it has none
Fixes the UI hanging up on Windows 11 when there are no mics, but the mic
input is set to external device as it is by default.
2023-07-14 02:32:09 +02:00
Jesse Talavera-Greenberg 0947e941b8
Modest cleanups for DSi_NAND (#1714)
* Add a definition for TMD files

* Wrap TitleMetadata in a namespace

* Add a comment

* Remove TitleMetadataCertificate

- melonDS ignores it anyway

* Refactor the use of title metadata

- Move bitwise operations on the title ID into helper methods
- Use TitleMetadata objects instead of pointers to raw data

* Slight cleanup in DSi_NAND

- Replace some constants with sizeof
- Use an NDSHeader object instead of a raw array of bytes

* Add a DSi_NAND::ImportFile overload that loads a file from memory

* Split most of ImportTitle into InitTitleFileStructure

- It will be reused in the next commit

* Add ability to import title from memory

* Fix another potential issue

* Fix broken DSiWare installation

- The bytes of the title ID/category were being swapped in most places, but not all

* Add some logging calls

* Declare array sizes in DSi_TMD in decimal, not hex

* Add a space after the #endif

- To adhere to the style guide

* Assert the size of TitleMetadataContent

* Change the type of SignatureName

* Don't mark the TMD structs as packed

* Remove extraneous comments

* Cut down some newlines
2023-07-08 22:17:30 +02:00
Jesse Talavera-Greenberg d1ff103259
Make linking librt conditional on it containing shm_open
Fixes building on UNIX platforms with no librt.

Co-authored-by: Nadia Holmquist Pedersen <nadia@nhp.sh>
2023-07-08 01:36:22 +02:00
Jesse Talavera-Greenberg 3c6359837d
Forgot an #include (#1712) 2023-06-30 14:28:53 +02:00
Jesse Talavera-Greenberg b659bce3c1
Split the cart parsing and loading steps (#1707)
* Split ROMList into a .cpp file

- Its definition in ROMList.h was causing multiple-definition linker errors
- Introduce ROMListSize, since you can't take a sizeof() of an extern declaration
- Mark ROMList and ROMListSize as const

* Update ReadROMParams to accommodate ROMList changes

* Split parsing and loading of NDS ROMs

- Introduce an NDSCartData class for parsing a ROM file
- Introduce InsertROM for loading a NDS cart
- Refactor LoadROM to use NDSCartData and InsertROM under the hood

* Reset cart state and initialize save memory in the NDSCartData constructor

- Otherwise there's no way to know about SRAM-specific attributes before inserting the game

* Add a comment to NDSCartData

* First crack at splitting parsing and loading for GBACart

* Add some logging calls for encrypting the secure area

* Log the XXH64 hash of the inserted NDS ROM

* Log the XXH64 hash of the secure area after decryption

* Add some logging to Key1_LoadKeyBuf

* Re-encrypt the secure area when inserting the cart, not when parsing it

- This way, constructing a NDSCart doesn't imply a read from the filesystem (as is done in Key1_KeyBuf)

* Load Key1_KeyBuf from memory, not from the file system

- Now that the cart's secure area isn't re-encrypted until insertion, we can expect that the BIOS will be ready at this point

* Add some helper query methods to NDSHeader

* Query the DSi region directly from the header instead of checking the ROM again

* Introduce a CartType enum

- So CartCommon::Type doesn't have to return magic numbers

* Reset the cart in NDSCart::InsertROM instead of the NDSCartData constructor

- That way the constructor doesn't rely on the config or on file I/O when loading homebrew
- This keeps the use of global state closer to one place

* Add non-const getters for the carts

* Add InsertROM overloads that accept unique_ptrs

* Fix a comment

* Rename member functions on NDSCartData and GBACartData to adhere to the convention

* Rename members on NDSCartData and GBACartData to adhere to the convention

* Fix build errors on some platforms

* Add NDSHeader::IsDSiWare

* Add a ROMListEntry parameter to the cart constructors

- To allow for looking up details of SRAM or expected ROM size

* Add some new getters to CartCommon

* Use the Header/Banner members instead of globals

* Make CartCommon abstract

- It's not supposed to be instantiated anyway

* Change the signature of CartCommon::Checksum

- It's neither overridden nor mutating

* Add some clarifying comments to NDSHeader

* Delete CartCommon::ROM in its destructor

- ParseROM copies its input and gives that copy to the cart object, so it's okay

* Add some getters to CartCommon

* Refactor NDSCart

- Get rid of NDSCartData
- Get rid of cart-specific global state within NDSCart (so registers are untouched)
- Refactor uses of removed global variables to use the Cart pointer instead
- Refactor ROMInfoDialog's icon functions to accept const arguments

* Return the cart pointer

- So *that's* why it was crashing. Whoops
- Why is this even allowed?

* Refactor GBACart

- Delete CartGame::ROM in the destructor
- Get rid of GBACartData
- Remove some global state

* Mark NDSCart::CartCommon::Type as const

* Slightly refactor GBACart::CartCommon

- Mark Type as const
- Use enum constants
- Make CartCommon itself abstract

* Mark CRC32's data parameter as const

* Mark GBACart::CartCommon::Checksum as const

* Use assert.h instead of cassert

- As demanded by the style guide

* Fix some includes to adhere to the style guide

* Get the ARM9 entry address directly from the header object

* Use more Header fields directly

* Rename some parameters to match the style guide

* Remove some unused includes

* Slightly change NDS_Header::IsHomebrew for clarity
2023-06-30 13:28:52 +02:00
Nadia Holmquist Pedersen 7b948e6ec9 Assign Qt standard Quit keyboard shortcut 2023-06-29 19:12:07 +02:00
falsidge 52d6265b58
Add microphone combobox using SDL (#1709) 2023-06-27 21:31:41 +02:00
Nadia Holmquist Pedersen 4b32fb802c actually rename that setting 2023-06-24 16:34:23 +02:00
Nadia Holmquist Pedersen 74b166cba3 Add a description to the wifi settings dialog
Explains that the settings only affect online play, not local
multiplayer
2023-06-24 16:14:13 +02:00
Jesse Talavera-Greenberg 391ad8c95e
Implement in-memory savestates (#1693)
* Refactor Savestate::Var{8,16,32,64}

- They now delegate to VarArray
- They're declared in the class header so they're likely to be inlined

* First crack at refactoring Savestate to work in-memory

- Well, third, but who's counting?

* Implement Savestate::Finish

* Remove the VersionMajor and VersionMinor fields

- Instead, pull their values directly from the savestate buffer

* Mark a new constructor as explicit

* Rename Reset to Rewind

* Fix a linebreak

* Implement Savestate::Rewind

* Add ROMManager::ClearBackupState

* Refactor ROMManager to use the refactored Savestate

* Capitalize "Least"

- It was driving me nuts

* Add a log call

* Increase default Savestate buffer length to 32MB

* Use C-style file I/O instead of C++-style

- Dumping bytes to a file with C++'s standard library is a MONSTROUS PAIN IN THE ASS

* Quote the savestate's file path for clarity

* Write the savestate's length into the header

* Add some extra logging calls

* Fix section-loading

* Remove the deprecated Savestate constructor

* Convert a char* to a u32 with memcpy, not a cast

* Fix section-handling in loads

* Include <cstring> in Savestate.h

- This was causing a build error on Linux
2023-06-12 23:56:09 +02:00
RSDuck ca7fb4f55e disable DSi mode cartridge protection when DSi header is borked 2023-05-11 13:24:01 +02:00
RSDuck 758db2b986 disable screen filtering when drawing with QT
it can bring everything to a crawl and OpenGL display can be used as an alternative
2023-05-06 19:41:28 +02:00
Nadia Holmquist Pedersen 5f991d07a3 Fix tab ordering everywhere 2023-05-01 06:24:53 +02:00
Nadia Holmquist Pedersen c6778bc6d5 Fix the layout of the video settings dialog a bit 2023-05-01 05:36:44 +02:00
RSDuck 787227b9ae add missing GPL header 2023-05-01 03:07:56 +02:00
RSDuck e4586e53ad fix UB 2023-05-01 02:34:59 +02:00
RSDuck 81c284b7cf refactor frontend audio functionality 2023-05-01 02:30:26 +02:00
Nadia Holmquist Pedersen 38b0d21c22
Support loading Zstandard-compressed ROMs (#1667)
This is different from the archive support in that the compressed ROMs
are standalone files, rather than archives, making it possible to use
them exactly as if they were regular ROMs, while saving a bunch of space
on disk. This is supported both for DS and GBA ROMs, though given GBA
ROMs' generally small size it's mostly useful for the former.
2023-04-28 19:19:58 +02:00
RSDuck 4b170b94d5 pointless micro optimisations 2023-04-28 17:05:34 +02:00
Nadia Holmquist Pedersen 3ada5b9bc8 Add the missing extensions we support to the macOS app info
macOS seems to require that we declare the extensions we want to
consider those files to be droppable on the window. Additionally this is
of course needed to show melonDS as a selectable option in the "Open
with" menu and such.

Setting LSHandlerRank to Alternate appears to stop it from suggesting
melonDS as a primary handler for a file type, so we set this for GBA
ROMs and archives as the user would most likely want them always to be
associated with a more relevant app.
2023-04-15 21:02:35 +02:00
RSDuck e8967a937c fix JIT backjump detection
pretty embarrassing to loop variable mixup
2023-04-13 01:49:32 +02:00
RSDuck a864f845e0 fix JIT profiling with VTune 2023-04-13 01:49:32 +02:00
Ed_IT 613280d3b4
DSi power button and volume switch support (#1630)
* Add proper BPTWL interrupts

* Added DSi power button and volume switch hotkeys

* Added hardware reset workaround

* Adjusted syntax to follow guidelines

* Added DSi output volume synchronization

* Fix trivial member function error
2023-04-04 12:31:58 +02:00
DarkOK 350292fb3c
Allow toggling Wayland support (#1654) 2023-04-03 16:02:50 +02:00
RSDuck 41b95b9c8f I'm stupid 2023-03-28 05:29:08 +02:00
RSDuck 5999fddccb comment out JIT debug code 2023-03-28 04:23:36 +02:00
Jesse Talavera-Greenberg b078ca802f
Expose SRAM pointers for frontends that manage save data their own way (#1643)
* Add a clarifying comment

- In case it saves some poor bastard hours of fruitless work

* Expose GBA and NDS save memory

- Add GetSaveMemory and GetSaveMemoryLength functions
- Where unsupported, they return null and zero
2023-03-27 22:36:26 +02:00
Sergio 808292e424
Fixed unhandled error status for LastSep(std::string) (#1645) 2023-03-25 13:15:47 +01:00
Jesse Talavera-Greenberg 79dfb8dc8f
Introduce `Platform::Log` (#1640)
* Add Platform::Log and Platform::LogLevel

* Replace most printf calls with Platform::Log calls

* Move a brace down

* Move some log entries to one Log call

- Some implementations of Log may assume a full line

* Log the MAC address as LogLevel::Info
2023-03-23 18:04:38 +01:00
Gloria 19280cff2d
BIOS/NAND UI changes (#1587)
* Remember the last folder for BIOS files
Add .mmc to the list of NAND filename extensions

* Add .sd file extension for SD images

* Fix UI code according to the review

---------

Co-authored-by: yeah-its-serena <32610623+yeah-its-serena@users.noreply.github.com>
2023-03-20 16:56:14 +01:00
RSDuck 4ba7a2c5e6 tiny refactoring around the input config dialog 2023-03-02 14:41:46 +01:00
RSDuck ca221381e9 gotta blow harder 2023-03-02 08:36:03 +01:00
Nadia Holmquist Pedersen 382155e6fe Add hotkey to toggle screen emphasis
Toggles which screen is emphasized when the screen sizing is set to
emphasize top or bottom. (Closes issue #1565)
2023-02-17 04:33:26 +01:00
UltraHDR 00edeb3c3c Set LSApplicationCategoryType to games
https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype
Adds it to the games folder in the macOS Launchpad
2023-01-20 12:17:41 +01:00
patata 35f4e504f1
Increase savestate major version (#1567) 2023-01-19 15:11:42 +01:00
patata 105af5cf1a
Update CI badges in readme (#1602)
- See https://github.com/badges/shields/issues/8671
- Change Azure Pipelines macOS badges to the current GitHub Actions
2023-01-19 15:10:27 +01:00
Janfel 3e02d3ff76
Rebase: Make archive detection more robust and add it to the CLI (#1560)
* Rebase/recreate my changes and add MIME support

This commit recreates the changes proposed in #1394 on top of the
current master (b069a2acf1).
This also adds support for determining filetypes using the MIME database
provided by `QMimeDatabase`.

* Move member syntax warning to a more appropriate place

* Deduplicate member syntax warning

* Change warning from "vertical bars" to "|"

* Conform brace placement to coding style

* Fix QFileDialog filter when ArchiveExtensions is empty

* Final cleanup and fixes

- Changes the NDS and GBA ROM MIME-Type constants to QStrings.
- Removes a leftover warning message.
- Uses Type() syntax instead of Type{} syntax for temporaries.

* Explain the origin of the supported archive list

Co-authored-by: Jan Felix Langenbach <insert-penguin@protonmail.com>
2023-01-18 00:49:18 +01:00
RSDuck d83172e595 fix window aspect ratio 2023-01-17 20:00:21 +01:00
RSDuck 8ec8a6ce09 ehhh got that one wrong, now it should be right 2023-01-06 20:17:04 +01:00
RSDuck eadfeec7ab hopefully correct overflow flag for ADC and SBC
also use inline functions instead of macros to make things more neat
2023-01-06 20:11:11 +01:00
RSDuck 86d2e60df7 fix #1593 2023-01-03 18:50:03 +01:00
Rupert Carmichael bd9c614ceb
Wifi: -Wformat warning caused by missing printf argument (#1532) 2022-12-30 21:13:39 +01:00
RSDuck 43d091361e fix #1551 2022-11-25 23:47:36 +01:00
RSDuck 3111431a3f fix #1556 2022-11-14 18:29:44 +01:00
pepper-jelly 5488e0bf3d
prevent screensaver (#1485) 2022-11-11 23:18:28 +01:00
mariomadproductions 88c1371a96
Added LEGO Batman 2 - DC Super Heroes (Canada) (En,Fr) [B6FL] to ROMList (#1422) 2022-11-11 23:08:55 +01:00
Quinn Painter 2256b64fcd
Fix nocash Char Out debug register (#1500)
To match no$gba, this register should output any ASCII character written to it, not print the number as it currently does.
2022-11-11 23:03:17 +01:00
Nadia Holmquist Pedersen b069a2acf1 Clean Windows build instructions
* CMake in MSYS2 now depends on Ninja and uses it by default, use it
  instead of Make as it has much easier to read output when doing
  parallel builds and doesn't need an extra program
* Ninja uses the maximum number of cores by default, so we don't have to
  run nproc --all
* We don't need mesa for its headers anymore
2022-11-09 19:54:35 +01:00
Nadia Holmquist Pedersen 726fde4e8d Explicitly include the build/res directory so windres will find it when
using the CMake Makefile generator
2022-11-09 19:44:07 +01:00
Nadia Holmquist Pedersen 9587d35d1d For some reason this didn't get renamed 2022-11-09 19:02:53 +01:00
Pk11 31a0d7ecd2
Allow import/export of .pub/.prv/.bnr saves (#1558) 2022-11-07 16:03:00 +01:00
Nadia Holmquist Pedersen ca19ea1ce8 Windows resource/manifest fixes
* Report correct Windows architecture for the build in the manifest
* Output processed .rc/.manifest in the CMake binary dir to prevent
  potential issues with the files conflicting if cross compiling melonDS
  on the same Windows machine
* Make the original file name actually make sense
2022-11-06 02:53:51 +01:00
Nadia Holmquist Pedersen 659b7214c7 Use WGL for GL context creation on Windows ARM64 as well.
This may not work, as I'm not sure why the code originally did as it
did. But this at least lets it compile, we'll need someone with a real
Windows ARM device to test this as I only have a VM with no GPU
acceleration.
2022-11-05 22:38:23 +01:00
Nadia Holmquist Pedersen 9a5e884913 JIT: Add bits for Windows ARM64 support 2022-11-05 22:37:27 +01:00
Nadia Holmquist Pedersen c387fb1819 mainWindow may be nullptr when audioMute() is called.
Fixes potential crash on launch when multiplayer audio output is set to only the current window.
2022-11-05 16:57:28 +01:00
552 changed files with 97771 additions and 36157 deletions

18
.gitattributes vendored Normal file
View File

@ -0,0 +1,18 @@
# Vendored Dependencies
src/frontend/glad/** linguist-vendored
src/frontend/qt_sdl/gif-h/** linguist-vendored
src/frontend/qt_sdl/toml/** linguist-vendored
src/net/libslirp/** linguist-vendored
src/net/pcap/** linguist-vendored
src/sha1/** linguist-vendored
src/teakra/** linguist-vendored
src/tiny-AES-c/** linguist-vendored
src/xxhash/** linguist-vendored
# A handful of custom files embedded in the vendored dependencies
## Ad-hoc CMakeLists.txt for melonDS
src/net/libslirp/src/CMakeLists.txt -linguist-vendored
## glib stub
src/net/libslirp/src/glib/** -linguist-vendored

View File

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

94
.github/workflows/build-macos.yml vendored Normal file
View File

@ -0,0 +1,94 @@
name: macOS
on:
push:
branches:
- master
- ci/vcpkg-update
pull_request:
branches:
- master
env:
VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836
MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions
MELONDS_VERSION_SUFFIX: " RC"
jobs:
build-macos:
strategy:
matrix:
arch: [x86_64, arm64]
name: ${{ matrix.arch }}
runs-on: macos-14
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install dependencies for package building
run: |
brew install autoconf automake autoconf-archive libtool python-setuptools
- name: Set up CMake
uses: lukka/get-cmake@latest
- name: Set up vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
- name: Build
uses: lukka/run-cmake@v10
with:
configurePreset: release-mac-${{ matrix.arch }}
buildPreset: release-mac-${{ matrix.arch }}
configurePresetAdditionalArgs: "['-DMELONDS_EMBED_BUILD_INFO=ON']"
- name: Compress app bundle
shell: bash
run: |
cd build/release-mac-${{ matrix.arch }}
zip -r -y ../../macOS-${{ matrix.arch }}.zip melonDS.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: macOS-${{ matrix.arch }}
path: macOS-${{ matrix.arch }}.zip
retention-days: 1
universal-binary:
name: Universal binary
needs: [build-macos]
runs-on: macos-13
continue-on-error: true
steps:
- name: Download x86_64
uses: actions/download-artifact@v4
with:
name: macOS-x86_64
path: x86_64
- name: Download arm64
uses: actions/download-artifact@v4
with:
name: macOS-arm64
path: arm64
- name: Combine app bundles
shell: bash
run: |
unzip x86_64/*.zip -d x86_64
unzip arm64/*.zip -d arm64
lipo {x86_64,arm64}/melonDS.app/Contents/MacOS/melonDS -create -output melonDS
cp -a arm64/melonDS.app melonDS.app
cp melonDS melonDS.app/Contents/MacOS/melonDS
codesign -s - --deep melonDS.app
zip -r -y macOS-universal.zip melonDS.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: macOS-universal
path: macOS-universal.zip
# - name: Clean up architecture-specific artifacts
# uses: geekyeggo/delete-artifact@v4
# with:
# failOnError: false
# name: |
# macOS-x86_64
# macOS-arm64

View File

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

View File

@ -1,37 +1,63 @@
name: CMake Build (Ubuntu x86-64)
name: Ubuntu
on:
push:
branches:
- master
- ci/*
pull_request:
branches:
- master
env:
MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions
MELONDS_VERSION_SUFFIX: " RC"
jobs:
build:
continue-on-error: true
strategy:
matrix:
arch:
- runner: ubuntu-22.04
name: x86_64
- runner: ubuntu-22.04-arm
name: aarch64
runs-on: ubuntu-20.04
name: ${{ matrix.arch.name }}
runs-on: ${{ matrix.arch.runner }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
name: Check out sources
- name: Install dependencies
run: |
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
sudo apt update
sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev --allow-downgrades
- name: Create build environment
run: mkdir ${{runner.workspace}}/build
sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \
qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2
- name: Configure
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE
- name: Make
working-directory: ${{runner.workspace}}/build
run: cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON
- name: Build
run: |
make -j$(nproc --all)
mkdir dist
cp melonDS dist
- uses: actions/upload-artifact@v1
cmake --build build
DESTDIR=AppDir cmake --install build
- uses: actions/upload-artifact@v4
with:
name: melonDS-ubuntu-x86_64
path: ${{runner.workspace}}/build/dist
name: melonDS-ubuntu-${{ matrix.arch.name }}
path: AppDir/usr/bin/melonDS
- name: Fetch AppImage tools
run: |
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.arch.name }}.AppImage
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch.name }}.AppImage
chmod a+x linuxdeploy-*.AppImage
- name: Build the AppImage
env:
QMAKE: /usr/lib/qt6/bin/qmake
run: |
./linuxdeploy-${{ matrix.arch.name }}.AppImage --appdir AppDir --plugin qt --output appimage
- uses: actions/upload-artifact@v4
with:
name: melonDS-appimage-${{ matrix.arch.name }}
path: melonDS*.AppImage

View File

@ -1,47 +1,45 @@
name: CMake Build (Windows x86-64)
name: Windows
on:
push:
branches:
- master
- ci/*
pull_request:
branches:
- master
env:
BUILD_TYPE: Release
VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836
MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions
MELONDS_VERSION_SUFFIX: " RC"
jobs:
build:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v1
- uses: msys2/setup-msys2@v2
- name: Check out sources
uses: actions/checkout@v3
- name: Set up MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
- name: Install dependencies
run: pacman -Sq --noconfirm git make pkgconf mingw-w64-x86_64-{cmake,mesa,SDL2,qt5-static,libslirp,libarchive,libepoxy,toolchain}
- name: Create build environment
working-directory: ${{runner.workspace}}
run: mkdir build
msystem: ucrt64
update: true
pacboy: gcc:p cmake:p ninja:p make:p
- name: Set up vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
- name: Configure
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=C:/tools/msys64/mingw64/qt5-static
- name: Make
working-directory: ${{runner.workspace}}/build
run: make -j$(nproc --all)
- uses: actions/upload-artifact@v1
run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON
- name: Build
run: cmake --build --preset=release-mingw-x86_64
- uses: actions/upload-artifact@v4
with:
name: melonDS-windows-x86_64
path: ${{runner.workspace}}\build\melonDS.exe
path: .\build\release-mingw-x86_64\melonDS.exe

9
.gitignore vendored
View File

@ -1,4 +1,4 @@
build
build*/
bin
obj
*.depend
@ -7,7 +7,7 @@ obj
melon_grc.c
melon_grc.h
melon.rc
cmake-build
cmake-build*
cmake-build-debug
compile_commands.json
.idea
@ -16,3 +16,8 @@ compile_commands.json
*.exe
.DS_Store
.vs
.vscode
CMakeFiles
CMakeCache.txt

81
BUILD.md Normal file
View File

@ -0,0 +1,81 @@
# Building melonDS
* [Linux](#linux)
* [Windows](#windows)
* [macOS](#macos)
## Linux
1. Install dependencies:
* Ubuntu:
* All versions: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libarchive-dev libenet-dev libzstd-dev`
* 24.04: `sudo apt install qt6-{base,base-private,multimedia,svg}-dev`
* 22.04: `sudo apt install qtbase6-dev qtbase6-private-dev qtmultimedia6-dev libqt6svg6-dev`
* Older versions: `sudo apt install qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev`
Also add `-DUSE_QT6=OFF` to the first CMake command below.
* Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtbase-private,qtmultimedia,qtsvg}-devel wayland-devel`
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt6-{base,multimedia,svg} libarchive enet zstd`
2. Download the melonDS repository and prepare:
```bash
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
3. Compile:
```bash
cmake -B build
cmake --build build -j$(nproc --all)
```
## Windows
1. Install [MSYS2](https://www.msys2.org/)
2. Open the MSYS2 terminal from the Start menu:
* For x64 systems (most common), use **MSYS2 UCRT64**
* For ARM64 systems, use **MSYS2 CLANGARM64**
3. Update the packages using `pacman -Syu` and reopen the same terminal if it asks you to
4. Install git and clone the repository
```bash
pacman -S git
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
5. Install dependencies:
Replace `<prefix>` below with `mingw-w64-ucrt-x86_64` on x64 systems, or `mingw-w64-clang-aarch64` on ARM64 systems.
```bash
pacman -S <prefix>-{toolchain,cmake,SDL2,libarchive,enet,zstd}
```
6. Install Qt and configure the build directory
* Dynamic builds (with DLLs)
1. Install Qt: `pacman -S <prefix>-{qt6-base,qt6-svg,qt6-multimedia,qt6-svg,qt6-tools}`
2. Set up the build directory with `cmake -B build`
* Static builds (without DLLs, standalone executable)
1. Install Qt: `pacman -S <prefix>-qt5-static`
(Note: As of writing, the `qt6-static` package does not work.)
2. Set up the build directory with `cmake -B build -DBUILD_STATIC=ON -DUSE_QT6=OFF -DCMAKE_PREFIX_PATH=$MSYSTEM_PREFIX/qt5-static`
7. Compile: `cmake --build build`
If everything went well, melonDS should now be in the `build` folder. For dynamic builds, you may need to run melonDS from the MSYS2 terminal in order for it to find the required DLLs.
## macOS
1. Install the [Homebrew Package Manager](https://brew.sh)
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd`
3. Download the melonDS repository and prepare:
```zsh
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
4. Compile:
```zsh
cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)"
cmake --build build -j$(sysctl -n hw.logicalcpu)
```
If everything went well, melonDS.app should now be in the `build` directory.
### Self-contained app bundle
If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run `
../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
## Nix (macOS/Linux)
melonDS provides a Nix flake with support for both macOS and Linux. The [Nix package manager](https://nixos.org) needs to be installed to use it.
* To run melonDS, just type `nix run github:melonDS-emu/melonDS`.
* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory.

View File

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

105
CMakePresets.json Normal file
View File

@ -0,0 +1,105 @@
{
"version": 6,
"configurePresets": [
{
"name": "release",
"displayName": "Release",
"description": "Default release build configuration.",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/release"
},
{
"inherits": "release",
"name": "release-vcpkg",
"displayName": "Release (vcpkg)",
"description": "Release build with packages from vcpkg.",
"cacheVariables": {
"USE_VCPKG": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "release-mingw-x86_64",
"inherits": "release-vcpkg",
"displayName": "Windows MinGW release (x86_64)",
"binaryDir": "${sourceDir}/build/release-mingw-x86_64",
"generator": "Ninja",
"cacheVariables": {
"BUILD_STATIC": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "release-mac-x86_64",
"inherits": "release-vcpkg",
"displayName": "macOS release (x86_64)",
"binaryDir": "${sourceDir}/build/release-mac-x86_64",
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "x86_64" }
},
{
"name": "release-mac-arm64",
"inherits": "release-vcpkg",
"displayName": "macOS release (arm64)",
"binaryDir": "${sourceDir}/build/release-mac-arm64",
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" }
}
],
"buildPresets": [
{
"name": "release",
"configurePreset": "release"
},
{
"name": "release-vcpkg",
"configurePreset": "release-vcpkg"
},
{
"name": "release-mingw-x86_64",
"configurePreset": "release-mingw-x86_64"
},
{
"name": "release-mac-x86_64",
"configurePreset": "release-mac-x86_64"
},
{
"name": "release-mac-arm64",
"configurePreset": "release-mac-arm64"
}
],
"workflowPresets": [
{
"name": "release",
"displayName": "Release",
"steps": [
{ "type": "configure", "name": "release" },
{ "type": "build", "name": "release" }
]
},
{
"name": "release-vcpkg",
"displayName": "Release (vcpkg)",
"steps": [
{ "type": "configure", "name": "release-vcpkg" },
{ "type": "build", "name": "release-vcpkg" }
]
},
{
"name": "release-mac-x86_64",
"steps": [
{ "type": "configure", "name": "release-mac-x86_64" },
{ "type": "build", "name": "release-mac-x86_64" }
]
},
{
"name": "release-mac-arm64",
"steps": [
{ "type": "configure", "name": "release-mac-arm64" },
{ "type": "build", "name": "release-mac-arm64" }
]
}
]
}

View File

@ -5,12 +5,11 @@
<a href="http://melonds.kuribo64.net/downloads.php" alt="Release: 0.9.5"><img src="https://img.shields.io/badge/release-0.9.5-%235c913b.svg"></a>
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/License-GPL%20v3-%23ff554d.svg"></a>
<a href="https://kiwiirc.com/client/irc.badnik.net/?nick=IRC-Source_?#melonds" alt="IRC channel: #melonds"><img src="https://img.shields.io/badge/IRC%20chat-%23melonds-%23dd2e44.svg"></a>
<a href="https://discord.gg/pAMAtExcqV" alt="Discord"><img src="https://img.shields.io/badge/Discord-Kuribo64-7289da?logo=discord&logoColor=white"></a>
<br>
<a href="https://github.com/melonDS-emu/melonDS/actions?query=workflow%3A%22CMake+Build+%28Windows+x86-64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/melonDS-emu/melonDS/CMake%20Build%20(Windows%20x86-64)?label=Windows%20x86-64&logo=GitHub"></img></a>
<a href="https://github.com/melonDS-emu/melonDS/actions?query=workflow%3A%22CMake+Build+%28Ubuntu+x86-64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/melonDS-emu/melonDS/CMake%20Build%20(Ubuntu%20x86-64)?label=Linux%20x86-64&logo=GitHub"></img></a>
<a href="https://github.com/melonDS-emu/melonDS/actions?query=workflow%3A%22CMake+Build+%28Ubuntu+aarch64%29%22+event%3Apush"><img src="https://img.shields.io/github/workflow/status/melonDS-emu/melonDS/CMake%20Build%20(Ubuntu%20aarch64)?label=Linux%20ARM64&logo=GitHub"></img></a>
<a href="https://dev.azure.com/melonDS/melonDS/_build?definitionId=1&repositoryFilter=1&branchFilter=2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2%2C2"><img src="https://img.shields.io/azure-devops/build/melonDS/7c9c08a1-669f-42a4-bef4-a6c74eadf723/1/master?label=macOS%20x86-64&logo=Azure%20Pipelines"></img></a>
<a href="https://dev.azure.com/melonDS/melonDS/_build?definitionId=2&_a=summary&repositoryFilter=1&branchFilter=2%2C2%2C2%2C2%2C2"><img src="https://img.shields.io/azure-devops/build/melonDS/7c9c08a1-669f-42a4-bef4-a6c74eadf723/2/master?label=macOS%20ARM64&logo=Azure%20Pipelines"></img></a>
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml/badge.svg" /></a>
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml/badge.svg" /></a>
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml/badge.svg" /></a>
</p>
DS emulator, sorta
@ -33,75 +32,7 @@ DS BIOS dumps from a DSi or 3DS can be used with no compatibility issues. DSi BI
As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though!
## How to build
### Linux
1. Install dependencies:
* Ubuntu 22.04: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libslirp-dev libarchive-dev`
* Older Ubuntu: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp-dev libarchive-dev`
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia libslirp libarchive`
3. Download the melonDS repository and prepare:
```bash
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
3. Compile:
```bash
cmake -B build
cmake --build build -j$(nproc --all)
```
### Windows
1. Install [MSYS2](https://www.msys2.org/)
2. Open the **MSYS2 MinGW 64-bit** terminal
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to
4. Install git to clone the repository
```bash
pacman -S git
```
5. Download the melonDS repository and prepare:
```bash
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
#### Dynamic builds (with DLLs)
5. Install dependencies: `pacman -S make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-base,qt5-svg,qt5-multimedia,libslirp,libarchive}`
6. Compile:
```bash
cmake -B build -G "MSYS Makefiles"
cmake --build build -j$(nproc --all)
cd build
../tools/msys-dist.sh
```
If everything went well, melonDS and the libraries it needs should now be in the `dist` folder.
#### Static builds (without DLLs, standalone executable)
5. Install dependencies: `pacman -S make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-static,libslirp,libarchive}`
6. Compile:
```bash
cmake -B build -G 'MSYS Makefiles' -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static
cmake --build build -j$(nproc --all)
```
If everything went well, melonDS should now be in the `build` folder.
### macOS
1. Install the [Homebrew Package Manager](https://brew.sh)
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libslirp libarchive`
3. Download the melonDS repository and prepare:
```zsh
git clone https://github.com/melonDS-emu/melonDS
cd melonDS
```
4. Compile:
```zsh
cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DUSE_QT6=ON
cmake --build build -j$(sysctl -n hw.logicalcpu)
```
If everything went well, melonDS.app should now be in the `build` directory.
#### Self-contained app bundle
If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run `
../tools/mac-bundle.rb melonDS.app` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
See [BUILD.md](./BUILD.md) for build instructions.
## TODO LIST

114
cmake/ConfigureVcpkg.cmake Normal file
View File

@ -0,0 +1,114 @@
include(FetchContent)
set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg")
set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository")
if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
if (APPLE) # this doesn't work on non-macOS
file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE)
endif()
FetchContent_Declare(vcpkg
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
GIT_TAG 2025.01.13
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
FetchContent_MakeAvailable(vcpkg)
endif()
set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets")
option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
# Duplicated here because it needs to be set before project()
option(USE_QT6 "Use Qt 6 instead of Qt 5" ON)
# Since the Linux build pulls in glib anyway, we can just use upstream libslirp
if (UNIX AND NOT APPLE)
option(USE_SYSTEM_LIBSLIRP "Use system libslirp instead of the bundled version" ON)
endif()
if (NOT USE_QT6)
list(APPEND VCPKG_MANIFEST_FEATURES qt5)
set(VCPKG_MANIFEST_NO_DEFAULT_FEATURES ON)
endif()
if (CMAKE_OSX_ARCHITECTURES MATCHES ";")
message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.")
endif()
if (USE_RECOMMENDED_TRIPLETS)
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE _HOST_PROCESSOR
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(_CAN_TARGET_AS_HOST OFF)
if (APPLE)
if (NOT CMAKE_OSX_ARCHITECTURES)
if (_HOST_PROCESSOR STREQUAL arm64)
set(CMAKE_OSX_ARCHITECTURES arm64)
else()
set(CMAKE_OSX_ARCHITECTURES x86_64)
endif()
endif()
if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
set(_WANTED_TRIPLET arm64-osx-11-release)
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
else()
set(_WANTED_TRIPLET x64-osx-1015-release)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
endif()
elseif(WIN32)
# TODO Windows arm64 if possible
set(_CAN_TARGET_AS_HOST ON)
set(_WANTED_TRIPLET x64-mingw-static-release)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
# Can't really detect cross compiling here.
set(_CAN_TARGET_AS_HOST ON)
if (_HOST_PROCESSOR STREQUAL x86_64)
set(_WANTED_TRIPLET x64-linux-release)
elseif(_HOST_PROCESSOR STREQUAL "aarch64")
set(_WANTED_TRIPLET arm64-linux-release)
endif()
endif()
# Don't override it if the user set something else
if (NOT VCPKG_TARGET_TRIPLET)
set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}")
else()
set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}")
endif()
if (APPLE)
if (_HOST_PROCESSOR MATCHES arm64)
if (_WANTED_TRIPLET MATCHES "^arm64-osx-")
set(_CAN_TARGET_AS_HOST ON)
elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release")
# Use the default triplet for when building for arm64
# because we're probably making a universal build
set(VCPKG_HOST_TRIPLET arm64-osx-11-release)
endif()
else()
if (_WANTED_TRIPLET MATCHES "^x64-osx-")
set(_CAN_TARGET_AS_HOST ON)
elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release")
set(VCPKG_HOST_TRIPLET x64-osx-1015-release)
endif()
endif()
endif()
# If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries.
# In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant.
if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET)
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON)
else()
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF)
endif()
if (VCPKG_TARGET_AS_HOST)
set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg")
endif()
endif()
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")

View File

@ -0,0 +1,9 @@
if (CMAKE_C_COMPILER_ID STREQUAL GNU)
set(CMAKE_C_FLAGS_DEBUG_INIT "-g -Og")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og")
endif()
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}")
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}")

48
cmake/FindENet.cmake Normal file
View File

@ -0,0 +1,48 @@
# - Try to find enet
# Once done this will define
#
# ENET_FOUND - system has enet
# ENET_INCLUDE_DIRS - the enet include directory
# ENET_LIBRARIES - the libraries needed to use enet
#
# $ENETDIR is an environment variable used for finding enet.
#
# Borrowed from The Mana World
# http://themanaworld.org/
#
# Several changes and additions by Fabian 'x3n' Landau
# Lots of simplifications by Adrian Friedli
# > www.orxonox.net <
FIND_PATH(ENET_INCLUDE_DIRS enet/enet.h
PATHS
$ENV{ENETDIR}
/usr/local
/usr
PATH_SUFFIXES include
)
FIND_LIBRARY(ENET_LIBRARY
NAMES enet
PATHS
$ENV{ENETDIR}
/usr/local
/usr
PATH_SUFFIXES lib
)
# handle the QUIETLY and REQUIRED arguments and set ENET_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ENet DEFAULT_MSG ENET_LIBRARY ENET_INCLUDE_DIRS)
IF (ENET_FOUND)
IF(WIN32)
SET(WINDOWS_ENET_DEPENDENCIES "ws2_32;winmm")
SET(ENET_LIBRARIES ${ENET_LIBRARY} ${WINDOWS_ENET_DEPENDENCIES})
ELSE(WIN32)
SET(ENET_LIBRARIES ${ENET_LIBRARY})
ENDIF(WIN32)
ENDIF (ENET_FOUND)
MARK_AS_ADVANCED(ENET_LIBRARY ENET_LIBRARIES ENET_INCLUDE_DIRS)

View File

@ -1,5 +1,10 @@
find_path(VTUNE_PATH "")
include_directories("${VTUNE_PATH}/include")
link_directories("${VTUNE_PATH}/lib64")
set(VTUNE_INCLUDE_DIR "${VTUNE_PATH}/include")
if (WIN32)
set(VTUNE_LIBRARY "${VTUNE_PATH}/lib64/jitprofiling.lib")
else()
set(VTUNE_LIBRARY "${VTUNE_PATH}/lib64/jitprofiling.a")
endif()

View File

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

8
cmake/Sanitizers.cmake Normal file
View File

@ -0,0 +1,8 @@
set(SANITIZE "" CACHE STRING "Sanitizers to enable.")
string(REGEX MATCHALL "[^,]+" ENABLED_SANITIZERS "${SANITIZE}")
foreach(SANITIZER ${ENABLED_SANITIZERS})
add_compile_options("-fsanitize=${SANITIZER}")
add_link_options("-fsanitize=${SANITIZER}")
endforeach()

19
cmake/SetupCCache.cmake Normal file
View File

@ -0,0 +1,19 @@
include(FindPackageMessage)
find_program(CCACHE "ccache")
cmake_dependent_option(USE_CCACHE "Use CCache to speed up repeated builds." ON CCACHE OFF)
if (NOT CCACHE OR NOT USE_CCACHE)
return()
endif()
# Fedora, and probably also Red Hat-based distros in general, use CCache by default if it's installed on the system.
# We'll try to detect this here, and exit if that's the case.
# Trying to launch ccache with ccache as we'd otherwise do seems to cause build issues.
if (CMAKE_C_COMPILER MATCHES "ccache" OR CMAKE_CXX_COMPILER MATCHES "ccache")
return()
endif()
find_package_message(CCache "Using CCache to speed up compilation" "${USE_CCACHE}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE}")

View File

@ -0,0 +1,12 @@
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_CMAKE_SYSTEM_VERSION 11.0)
set(VCPKG_OSX_ARCHITECTURES arm64)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0)
set(VCPKG_C_FLAGS -mmacosx-version-min=11.0)
set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0)

View File

@ -0,0 +1,7 @@
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_ENV_PASSTHROUGH PATH)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_CMAKE_SYSTEM_NAME MinGW)

View File

@ -0,0 +1,12 @@
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_CMAKE_SYSTEM_VERSION 10.15)
set(VCPKG_OSX_ARCHITECTURES x86_64)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)
set(VCPKG_C_FLAGS -mmacosx-version-min=10.15)
set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15)

61
flake.lock Normal file
View File

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1739020877,
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

109
flake.nix Normal file
View File

@ -0,0 +1,109 @@
{
description = "Nintendo DS emulator";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
inherit (pkgs.lib) cmakeBool optionals makeLibraryPath;
inherit (pkgs.stdenv) isLinux isDarwin;
revision = with self; if sourceInfo?dirtyRev
then sourceInfo.dirtyRev
else sourceInfo.rev;
shortRevision = with self; if sourceInfo?dirtyShortRev
then sourceInfo.dirtyShortRev
else sourceInfo.shortRev;
melonDS = pkgs.stdenv.mkDerivation {
pname = "melonDS";
version = "1.0-${shortRevision}";
src = ./.;
nativeBuildInputs = with pkgs; [
cmake
ninja
pkg-config
qt6.wrapQtAppsHook
];
buildInputs = (with pkgs; [
qt6.qtbase
qt6.qtmultimedia
SDL2
zstd
libarchive
libGL
libslirp
enet
]) ++ optionals (!isDarwin) (with pkgs; [
kdePackages.extra-cmake-modules
qt6.qtwayland
wayland
]);
cmakeFlags = [
(cmakeBool "USE_QT6" true)
(cmakeBool "USE_SYSTEM_LIBSLIRP" true)
(cmakeBool "MELONDS_EMBED_BUILD_INFO" true)
];
env.MELONDS_GIT_HASH = revision;
env.MELONDS_GIT_BRANCH = "(unknown)";
env.MELONDS_BUILD_PROVIDER = "Nix";
qtWrapperArgs = optionals isLinux [
"--prefix LD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap pkgs.wayland ]}"
] ++ optionals isDarwin [
"--prefix DYLD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap ]}"
];
passthru = {
exePath = if isDarwin then
"/Applications/melonDS.app/Contents/MacOS/melonDS"
else "/bin/melonDS";
};
};
in {
packages.default = melonDS;
apps.default = flake-utils.lib.mkApp {
drv = self.packages.${system}.default;
};
devShells = {
default = pkgs.mkShell {
inputsFrom = [ self.packages.${system}.default ];
packages = with pkgs; [
qt6.qttools
];
};
# Shell for building static melonDS release builds with vcpkg
# Use mkShellNoCC to ensure Nix's gcc/clang and stdlib isn't used
vcpkg = pkgs.mkShellNoCC {
packages = with pkgs; [
autoconf
autoconf-archive
automake
cmake
cups.dev # Needed by qtbase despite not enabling print support
git
iconv.dev
libtool
ninja
pkg-config
python3
];
# Undo the SDK setup done by nixpkgs so we can use AppleClang
shellHook = ''
unset DEVELOPER_DIR SDKROOT MACOSX_DEPLOYMENT_TARGET
'';
};
};
}
);
}

View File

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

Binary file not shown.

Binary file not shown.

BIN
res/icon/melon_192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

View File

@ -16,6 +16,8 @@
<string>${melonDS_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${melonDS_VERSION}</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>NSHumanReadableCopyright</key>
<string>Licensed under GPLv3</string>
<key>NSPrincipalClass</key>
@ -29,14 +31,71 @@
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Nintendo DS ROM</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>nds</string>
<string>srl</string>
<string>dsi</string>
<string>ids</string>
<string>nds.zst</string>
<string>srl.zst</string>
<string>dsi.zst</string>
<string>ids.zst</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>Game Boy Advance ROM</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>gba</string>
<string>agb</string>
<string>gba.zst</string>
<string>agb.zst</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>Archive containing ROM</string>
<key>CFBundleTypeExtensions</key>
<array>
<key>zip</key>
<key>7z</key>
<key>rar</key>
<key>tar</key>
<key>tar.gz</key>
<key>tgz</key>
<key>tar.xz</key>
<key>txz</key>
<key>tar.bz2</key>
<key>tbz2</key>
<key>tar.lz4</key>
<key>tlz4</key>
<key>tar.zst</key>
<key>tzst</key>
<key>tar.Z</key>
<key>taz</key>
<key>tar.lz</key>
<key>tar.lzma</key>
<key>tlz</key>
<key>tar.lrz</key>
<key>tlrz</key>
<key>tar.lzo</key>
<key>tzo</key>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>
</dict>
</plist>

View File

@ -2,5 +2,6 @@
<RCC version="1.0">
<qresource>
<file alias="melon-icon">icon/melon_256x256.png</file>
<file alias="melon-logo">melon384.png</file>
</qresource>
</RCC>

View File

@ -2,12 +2,12 @@
#define VFT_APP 0x00000001L
//this will set your .exe icon
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "res/melon.ico"
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "${CMAKE_SOURCE_DIR}/res/melon.ico"
//include version information in .exe, modify these values to match your needs
1 VERSIONINFO
FILEVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
FILEVERSION ${MELON_RC_VERSION}
PRODUCTVERSION ${MELON_RC_VERSION}
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
@ -18,9 +18,9 @@ FILETYPE VFT_APP
VALUE "FileVersion", "${melonDS_VERSION}"
VALUE "FileDescription", "melonDS emulator"
VALUE "InternalName", "SDnolem"
VALUE "LegalCopyright", "2016-2022 melonDS team"
VALUE "LegalCopyright", "2016-2023 melonDS team"
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "zafkflzdasd.exe"
VALUE "OriginalFilename", "melonDS.exe"
VALUE "ProductName", "melonDS"
VALUE "ProductVersion", "${melonDS_VERSION}"
}
@ -31,4 +31,4 @@ FILETYPE VFT_APP
}
}
1 24 "res/xp.manifest"
1 24 "xp.manifest"

80
res/melon.svg Normal file
View File

@ -0,0 +1,80 @@
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
<path fill="#5c913b" fill-rule="evenodd" d="M357.62 154.38c56.821-56.821 85.232-85.232 118.463-94.604a132.32 132.32 0 0 1 71.834 0c33.231 9.372 61.642 37.783 118.463 94.604l203.24 203.24c56.821 56.821 85.232 85.232 94.604 118.463a132.32 132.32 0 0 1 0 71.834c-9.372 33.231-37.783 61.642-94.604 118.463L666.38 869.62c-56.821 56.821-85.232 85.232-118.463 94.604a132.319 132.319 0 0 1-71.834 0c-33.231-9.372-61.642-37.783-118.463-94.604L154.38 666.38c-56.821-56.821-85.232-85.232-94.604-118.463a132.318 132.318 0 0 1 0-71.834c9.372-33.231 37.783-61.642 94.604-118.463Z"/>
<filter id="a" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="18.378"/>
<feOffset dy="7.351" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".4"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#ffe8b6" fill-rule="evenodd" filter="url(#a)" d="M383.23 209.478c47.35-47.35 71.026-71.026 98.72-78.836a110.266 110.266 0 0 1 59.86 0c27.694 7.81 51.369 31.486 98.72 78.836L814.66 383.61c47.351 47.351 71.027 71.026 78.837 98.72a110.265 110.265 0 0 1 0 59.86c-7.81 27.694-31.486 51.37-78.837 98.72L640.53 815.042c-47.351 47.35-71.027 71.026-98.72 78.836a110.265 110.265 0 0 1-59.86 0c-27.694-7.81-51.37-31.485-98.72-78.836L209.098 640.91c-47.35-47.35-71.026-71.026-78.836-98.72a110.266 110.266 0 0 1 0-59.86c7.81-27.694 31.485-51.369 78.836-98.72Z"/>
<filter id="b" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="18.378"/>
<feOffset dy="7.351" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".4"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#dd2e44" fill-rule="evenodd" filter="url(#b)" d="M404.822 239.527c39.46-39.46 59.189-59.189 82.266-65.697a91.889 91.889 0 0 1 49.885 0c23.077 6.508 42.807 26.238 82.266 65.697l165.295 165.295c39.459 39.46 59.188 59.189 65.697 82.266a91.888 91.888 0 0 1 0 49.884c-6.509 23.078-26.238 42.808-65.697 82.266L619.239 784.534c-39.46 39.459-59.189 59.188-82.266 65.697a91.888 91.888 0 0 1-49.885 0c-23.077-6.509-42.807-26.238-82.266-65.697L239.527 619.239c-39.46-39.46-59.189-59.189-65.697-82.267a91.888 91.888 0 0 1 0-49.884c6.508-23.077 26.238-42.807 65.697-82.266Z"/>
<filter id="c" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".104"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#ff554d" fill-rule="evenodd" filter="url(#c)" d="M405.568 606.767s-14.487 11.68-31.187 15.594c-9.553 2.238-24.422 1.568-31.188-5.198l-103.96-103.96c-6.766-6.766-7.436-21.635-5.198-31.187 3.914-16.7 15.594-31.188 15.594-31.188l197.523-197.523s14.488-11.68 31.188-15.594c9.553-2.239 24.422-1.568 31.188 5.198l103.96 103.96c6.765 6.765 7.436 21.634 5.197 31.187-3.913 16.7-15.594 31.188-15.594 31.188Z"/>
<filter id="d" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".104"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#ff554d" fill-rule="evenodd" filter="url(#d)" d="M574.579 779.453s-14.488 11.68-31.188 15.594c-9.553 2.238-24.422 1.568-31.188-5.198l-103.96-103.96c-6.765-6.766-7.436-21.635-5.197-31.187 3.914-16.7 15.594-31.188 15.594-31.188L616.163 425.99s14.488-11.68 31.187-15.594c9.553-2.239 24.422-1.568 31.188 5.198l103.96 103.96c6.766 6.765 7.436 21.634 5.198 31.187-3.914 16.7-15.594 31.188-15.594 31.188Z"/>
<filter id="e" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".35"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#31352e" fill-rule="evenodd" filter="url(#e)" d="M414.022 532.978s-8.315 6.704-17.9 8.95c-5.483 1.285-14.016.9-17.9-2.983l-59.667-59.667c-3.883-3.884-4.268-12.417-2.983-17.9 2.246-9.585 8.95-17.9 8.95-17.9L437.89 330.11s8.315-6.704 17.9-8.95c5.482-1.285 14.016-.9 17.9 2.984l59.666 59.666c3.884 3.884 4.269 12.418 2.984 17.9-2.246 9.585-8.95 17.9-8.95 17.9Z"/>
<filter id="f" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".35"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#31352e" fill-rule="evenodd" filter="url(#f)" d="M677.203 544.885c7.177 7.176 18.813 7.176 25.99 0l19.954-19.955c7.177-7.176 7.177-18.813 0-25.99l-19.954-19.954c-7.177-7.176-18.813-7.176-25.99 0l-19.954 19.955c-7.177 7.176-7.177 18.813 0 25.99Z"/>
<filter id="g" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="3.676"/>
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
<feFlood flood-color="#000" flood-opacity=".35"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path fill="#31352e" fill-rule="evenodd" filter="url(#g)" d="M489.752 728.66c7.177 7.177 18.813 7.177 25.99 0l19.954-19.954c7.177-7.177 7.177-18.813 0-25.99l-19.954-19.954c-7.177-7.176-18.813-7.176-25.99 0l-19.954 19.955c-7.177 7.176-7.177 18.812 0 25.99Z"/>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
res/melon384.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
res/melon512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

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

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -21,31 +21,43 @@
#include "ARCodeFile.h"
#include "Platform.h"
namespace melonDS
{
using namespace Platform;
// TODO: import codes from other sources (usrcheat.dat, ...)
// TODO: more user-friendly error reporting
ARCodeFile::ARCodeFile(std::string filename)
ARCodeFile::ARCodeFile(const std::string& filename)
{
Filename = filename;
Error = false;
Categories.clear();
if (!Load())
Error = true;
}
ARCodeFile::~ARCodeFile()
std::vector<ARCode> ARCodeFile::GetCodes() const noexcept
{
Categories.clear();
if (Error)
return {};
std::vector<ARCode> codes;
for (const ARCodeCat& cat : Categories)
{
for (const ARCode& code : cat.Codes)
{
codes.push_back(code);
}
}
return codes;
}
bool ARCodeFile::Load()
{
FILE* f = Platform::OpenFile(Filename, "r");
FileHandle* f = OpenFile(Filename, FileMode::ReadText);
if (!f) return true;
Categories.clear();
@ -57,9 +69,9 @@ bool ARCodeFile::Load()
ARCode curcode;
char linebuf[1024];
while (!feof(f))
while (!IsEndOfFile(f))
{
if (fgets(linebuf, 1024, f) == nullptr)
if (!FileReadLine(linebuf, 1024, f))
break;
linebuf[1023] = '\0';
@ -79,8 +91,8 @@ bool ARCodeFile::Load()
if (ret < 1)
{
printf("AR: malformed CAT line: %s\n", start);
fclose(f);
Log(LogLevel::Error, "AR: malformed CAT line: %s\n", start);
CloseFile(f);
return false;
}
@ -102,15 +114,15 @@ bool ARCodeFile::Load()
if (ret < 2)
{
printf("AR: malformed CODE line: %s\n", start);
fclose(f);
Log(LogLevel::Error, "AR: malformed CODE line: %s\n", start);
CloseFile(f);
return false;
}
if (!isincat)
{
printf("AR: encountered CODE line with no category started\n");
fclose(f);
Log(LogLevel::Error, "AR: encountered CODE line with no category started\n");
CloseFile(f);
return false;
}
@ -119,7 +131,7 @@ bool ARCodeFile::Load()
curcode.Name = codename;
curcode.Enabled = enable!=0;
curcode.CodeLen = 0;
curcode.Code.clear();
}
else
{
@ -128,65 +140,58 @@ bool ARCodeFile::Load()
if (ret < 2)
{
printf("AR: malformed data line: %s\n", start);
fclose(f);
Log(LogLevel::Error, "AR: malformed data line: %s\n", start);
CloseFile(f);
return false;
}
if (!isincode)
{
printf("AR: encountered data line with no code started\n");
fclose(f);
Log(LogLevel::Error, "AR: encountered data line with no code started\n");
CloseFile(f);
return false;
}
if (curcode.CodeLen >= 2*64)
{
printf("AR: code too long!\n");
fclose(f);
return false;
}
u32 idx = curcode.CodeLen;
curcode.Code[idx+0] = c0;
curcode.Code[idx+1] = c1;
curcode.CodeLen += 2;
curcode.Code.push_back(c0);
curcode.Code.push_back(c1);
}
}
if (isincode) curcat.Codes.push_back(curcode);
if (isincat) Categories.push_back(curcat);
fclose(f);
CloseFile(f);
return true;
}
bool ARCodeFile::Save()
{
FILE* f = Platform::OpenFile(Filename, "w");
FileHandle* f = Platform::OpenFile(Filename, FileMode::WriteText);
if (!f) return false;
for (ARCodeCatList::iterator it = Categories.begin(); it != Categories.end(); it++)
{
ARCodeCat& cat = *it;
if (it != Categories.begin()) fprintf(f, "\r\n");
fprintf(f, "CAT %s\r\n\r\n", cat.Name.c_str());
if (it != Categories.begin()) FileWriteFormatted(f, "\n");
FileWriteFormatted(f, "CAT %s\n\n", cat.Name.c_str());
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
{
ARCode& code = *jt;
fprintf(f, "CODE %d %s\r\n", code.Enabled, code.Name.c_str());
FileWriteFormatted(f, "CODE %d %s\n", code.Enabled, code.Name.c_str());
for (u32 i = 0; i < code.CodeLen; i+=2)
for (size_t i = 0; i < code.Code.size(); i+=2)
{
fprintf(f, "%08X %08X\r\n", code.Code[i], code.Code[i+1]);
FileWriteFormatted(f, "%08X %08X\n", code.Code[i], code.Code[i + 1]);
}
fprintf(f, "\r\n");
FileWriteFormatted(f, "\n");
}
}
fclose(f);
CloseFile(f);
return true;
}
}

View File

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

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -21,83 +21,34 @@
#include "NDS.h"
#include "DSi.h"
#include "AREngine.h"
#include "Platform.h"
namespace AREngine
namespace melonDS
{
// AR code file - frontend is responsible for managing this
ARCodeFile* CodeFile;
using Platform::Log;
using Platform::LogLevel;
u8 (*BusRead8)(u32 addr);
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite8)(u32 addr, u8 val);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
bool Init()
{
CodeFile = nullptr;
return true;
}
void DeInit()
AREngine::AREngine(melonDS::NDS& nds) : NDS(nds)
{
}
void Reset()
{
if (NDS::ConsoleType == 1)
{
BusRead8 = DSi::ARM7Read8;
BusRead16 = DSi::ARM7Read16;
BusRead32 = DSi::ARM7Read32;
BusWrite8 = DSi::ARM7Write8;
BusWrite16 = DSi::ARM7Write16;
BusWrite32 = DSi::ARM7Write32;
}
else
{
BusRead8 = NDS::ARM7Read8;
BusRead16 = NDS::ARM7Read16;
BusRead32 = NDS::ARM7Read32;
BusWrite8 = NDS::ARM7Write8;
BusWrite16 = NDS::ARM7Write16;
BusWrite32 = NDS::ARM7Write32;
}
}
ARCodeFile* GetCodeFile()
{
return CodeFile;
}
void SetCodeFile(ARCodeFile* file)
{
CodeFile = file;
}
#define case16(x) \
case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \
case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
void RunCheat(ARCode& arcode)
void AREngine::RunCheat(const ARCode& arcode)
{
u32* code = &arcode.Code[0];
const u32* code = &arcode.Code[0];
u32 offset = 0;
u32 datareg = 0;
u32 cond = 1;
u32 condstack = 0;
u32* loopstart = code;
const u32* loopstart = code;
u32 loopcount = 0;
u32 loopcond = 1;
u32 loopcondstack = 0;
@ -107,7 +58,7 @@ void RunCheat(ARCode& arcode)
for (;;)
{
if (code >= &arcode.Code[arcode.CodeLen])
if (code >= &arcode.Code[arcode.Code.size()])
break;
u32 a = *code++;
@ -132,15 +83,15 @@ void RunCheat(ARCode& arcode)
switch (op)
{
case16(0x00): // 32-bit write
BusWrite32((a & 0x0FFFFFFF) + offset, b);
NDS.ARM7Write32((a & 0x0FFFFFFF) + offset, b);
break;
case16(0x10): // 16-bit write
BusWrite16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
NDS.ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
break;
case16(0x20): // 8-bit write
BusWrite8((a & 0x0FFFFFFF) + offset, b & 0xFF);
NDS.ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF);
break;
case16(0x30): // IF b > u32[a]
@ -150,7 +101,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = BusRead32(addr);
u32 chk = NDS.ARM7Read32(addr);
cond = (b > chk) ? 1:0;
}
@ -163,7 +114,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = BusRead32(addr);
u32 chk = NDS.ARM7Read32(addr);
cond = (b < chk) ? 1:0;
}
@ -176,7 +127,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = BusRead32(addr);
u32 chk = NDS.ARM7Read32(addr);
cond = (b == chk) ? 1:0;
}
@ -189,7 +140,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u32 chk = BusRead32(addr);
u32 chk = NDS.ARM7Read32(addr);
cond = (b != chk) ? 1:0;
}
@ -202,7 +153,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = BusRead16(addr);
u16 val = NDS.ARM7Read16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -217,7 +168,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = BusRead16(addr);
u16 val = NDS.ARM7Read16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -232,7 +183,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = BusRead16(addr);
u16 val = NDS.ARM7Read16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -247,7 +198,7 @@ void RunCheat(ARCode& arcode)
u32 addr = a & 0x0FFFFFFF;
if (!addr) addr = offset;
u16 val = BusRead16(addr);
u16 val = NDS.ARM7Read16(addr);
u16 chk = ~(b >> 16);
chk &= val;
@ -256,7 +207,7 @@ void RunCheat(ARCode& arcode)
break;
case16(0xB0): // offset = u32[a + offset]
offset = BusRead32((a & 0x0FFFFFFF) + offset);
offset = NDS.ARM7Read32((a & 0x0FFFFFFF) + offset);
break;
case 0xC0: // FOR 0..b
@ -271,7 +222,7 @@ void RunCheat(ARCode& arcode)
// in practice could be used for a self-modifying AR code
// could be implemented with some hackery, but, does anything even
// use it??
printf("AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
Log(LogLevel::Error, "AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
return;
case 0xC5: // count++ / IF (count & b.l) == b.h
@ -293,7 +244,7 @@ void RunCheat(ARCode& arcode)
break;
case 0xC6: // u32[b] = offset
BusWrite32(b, offset);
NDS.ARM7Write32(b, offset);
break;
case 0xD0: // ENDIF
@ -342,30 +293,30 @@ void RunCheat(ARCode& arcode)
break;
case 0xD6: // u32[b+offset] = datareg / offset += 4
BusWrite32(b + offset, datareg);
NDS.ARM7Write32(b + offset, datareg);
offset += 4;
break;
case 0xD7: // u16[b+offset] = datareg / offset += 2
BusWrite16(b + offset, datareg & 0xFFFF);
NDS.ARM7Write16(b + offset, datareg & 0xFFFF);
offset += 2;
break;
case 0xD8: // u8[b+offset] = datareg / offset += 1
BusWrite8(b + offset, datareg & 0xFF);
NDS.ARM7Write8(b + offset, datareg & 0xFF);
offset += 1;
break;
case 0xD9: // datareg = u32[b+offset]
datareg = BusRead32(b + offset);
datareg = NDS.ARM7Read32(b + offset);
break;
case 0xDA: // datareg = u16[b+offset]
datareg = BusRead16(b + offset);
datareg = NDS.ARM7Read16(b + offset);
break;
case 0xDB: // datareg = u8[b+offset]
datareg = BusRead8(b + offset);
datareg = NDS.ARM7Read8(b + offset);
break;
case 0xDC: // offset += b
@ -380,8 +331,8 @@ void RunCheat(ARCode& arcode)
u32 bytesleft = b;
while (bytesleft >= 8)
{
BusWrite32(dstaddr, *code++); dstaddr += 4;
BusWrite32(dstaddr, *code++); dstaddr += 4;
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
bytesleft -= 8;
}
if (bytesleft > 0)
@ -390,13 +341,13 @@ void RunCheat(ARCode& arcode)
code += 2;
if (bytesleft >= 4)
{
BusWrite32(dstaddr, *(u32*)leftover); dstaddr += 4;
NDS.ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
leftover += 4;
bytesleft -= 4;
}
while (bytesleft > 0)
{
BusWrite8(dstaddr, *leftover++); dstaddr++;
NDS.ARM7Write8(dstaddr, *leftover++); dstaddr++;
bytesleft--;
}
}
@ -412,14 +363,14 @@ void RunCheat(ARCode& arcode)
u32 bytesleft = b;
while (bytesleft >= 4)
{
BusWrite32(dstaddr, BusRead32(srcaddr));
NDS.ARM7Write32(dstaddr, NDS.ARM7Read32(srcaddr));
srcaddr += 4;
dstaddr += 4;
bytesleft -= 4;
}
while (bytesleft > 0)
{
BusWrite8(dstaddr, BusRead8(srcaddr));
NDS.ARM7Write8(dstaddr, NDS.ARM7Read8(srcaddr));
srcaddr++;
dstaddr++;
bytesleft--;
@ -428,28 +379,20 @@ void RunCheat(ARCode& arcode)
break;
default:
printf("!! bad AR opcode %08X %08X\n", a, b);
Log(LogLevel::Warn, "!! bad AR opcode %08X %08X\n", a, b);
return;
}
}
}
void RunCheats()
void AREngine::RunCheats()
{
if (!CodeFile) return;
if (Cheats.empty()) return;
for (ARCodeCatList::iterator i = CodeFile->Categories.begin(); i != CodeFile->Categories.end(); i++)
for (const ARCode& code : Cheats)
{
ARCodeCat& cat = *i;
for (ARCodeList::iterator j = cat.Codes.begin(); j != cat.Codes.end(); j++)
{
ARCode& code = *j;
if (code.Enabled)
RunCheat(code);
}
if (code.Enabled)
RunCheat(code);
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -19,20 +19,25 @@
#ifndef ARENGINE_H
#define ARENGINE_H
#include <vector>
#include "ARCodeFile.h"
namespace AREngine
namespace melonDS
{
class NDS;
class AREngine
{
public:
AREngine(melonDS::NDS& nds);
bool Init();
void DeInit();
void Reset();
std::vector<ARCode> Cheats {};
private:
friend class ARM;
void RunCheats();
void RunCheat(const ARCode& arcode);
ARCodeFile* GetCodeFile();
void SetCodeFile(ARCodeFile* file);
void RunCheats();
melonDS::NDS& NDS;
};
}
#endif // ARENGINE_H

File diff suppressed because it is too large Load Diff

325
src/ARM.h
View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -20,10 +20,18 @@
#define ARM_H
#include <algorithm>
#include <optional>
#include "types.h"
#include "NDS.h"
#include "MemRegion.h"
#include "MemConstants.h"
#ifdef GDBSTUB_ENABLED
#include "debug/GdbStub.h"
#endif
namespace melonDS
{
inline u32 ROR(u32 x, u32 n)
{
return (x >> (n&0x1F)) | (x << ((32-n)&0x1F));
@ -35,15 +43,33 @@ enum
RWFlags_ForceUser = (1<<21),
};
const u32 ITCMPhysicalSize = 0x8000;
const u32 DTCMPhysicalSize = 0x4000;
enum class CPUExecuteMode : u32
{
Interpreter,
InterpreterGDB,
#ifdef JIT_ENABLED
JIT
#endif
};
struct GDBArgs;
class ARMJIT;
class GPU;
class ARMJIT_Memory;
class NDS;
class Savestate;
class ARM
#ifdef GDBSTUB_ENABLED
: public Gdb::StubCallbacks
#endif
{
public:
ARM(u32 num);
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
virtual ~ARM(); // destroy shit
void SetGdbArgs(std::optional<GDBArgs> gdb);
virtual void Reset();
virtual void DoSavestate(Savestate* file);
@ -59,12 +85,9 @@ public:
Halted = halt;
}
virtual void Execute() = 0;
#ifdef JIT_ENABLED
virtual void ExecuteJIT() = 0;
#endif
void NocashPrint(u32 addr) noexcept;
bool CheckCondition(u32 code)
bool CheckCondition(u32 code) const
{
if (code == 0xE) return true;
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
@ -93,6 +116,18 @@ public:
if (v) CPSR |= 0x10000000;
}
inline bool ModeIs(u32 mode) const
{
u32 cm = CPSR & 0x1f;
mode &= 0x1f;
if (mode == cm) return true;
if (mode == 0x17) return cm >= 0x14 && cm <= 0x17; // abt
if (mode == 0x1b) return cm >= 0x18 && cm <= 0x1b; // und
return false;
}
void UpdateMode(u32 oldmode, u32 newmode, bool phony = false);
void TriggerIRQ();
@ -114,6 +149,7 @@ public:
virtual void AddCycles_CDI() = 0;
virtual void AddCycles_CD() = 0;
void CheckGdbIncoming();
u32 Num;
@ -147,75 +183,101 @@ public:
u32 ExceptionBase;
NDS::MemRegion CodeMem;
MemRegion CodeMem;
#ifdef JIT_ENABLED
u32 FastBlockLookupStart, FastBlockLookupSize;
u64* FastBlockLookup;
#endif
static u32 ConditionTable[16];
static const u32 ConditionTable[16];
#ifdef GDBSTUB_ENABLED
Gdb::GdbStub GdbStub;
#endif
melonDS::NDS& NDS;
protected:
virtual u8 BusRead8(u32 addr) = 0;
virtual u16 BusRead16(u32 addr) = 0;
virtual u32 BusRead32(u32 addr) = 0;
virtual void BusWrite8(u32 addr, u8 val) = 0;
virtual void BusWrite16(u32 addr, u16 val) = 0;
virtual void BusWrite32(u32 addr, u32 val) = 0;
#ifdef GDBSTUB_ENABLED
bool IsSingleStep;
bool BreakReq;
bool BreakOnStartup;
u16 Port;
public:
int GetCPU() const override { return Num ? 7 : 9; }
u32 ReadReg(Gdb::Register reg) override;
void WriteReg(Gdb::Register reg, u32 v) override;
u32 ReadMem(u32 addr, int size) override;
void WriteMem(u32 addr, int size, u32 v) override;
void ResetGdb() override;
int RemoteCmd(const u8* cmd, size_t len) override;
protected:
u8 (*BusRead8)(u32 addr);
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite8)(u32 addr, u8 val);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
#endif
void GdbCheckA();
void GdbCheckB();
void GdbCheckC();
};
class ARMv5 : public ARM
{
public:
ARMv5();
ARMv5(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
~ARMv5();
void Reset();
void Reset() override;
void DoSavestate(Savestate* file);
void DoSavestate(Savestate* file) override;
void UpdateRegionTimings(u32 addrstart, u32 addrend);
void FillPipeline();
void FillPipeline() override;
void JumpTo(u32 addr, bool restorecpsr = false);
void JumpTo(u32 addr, bool restorecpsr = false) override;
void PrefetchAbort();
void DataAbort();
template <CPUExecuteMode mode>
void Execute();
#ifdef JIT_ENABLED
void ExecuteJIT();
#endif
// all code accesses are forced nonseq 32bit
u32 CodeRead32(u32 addr, bool branch);
void DataRead8(u32 addr, u32* val);
void DataRead16(u32 addr, u32* val);
void DataRead32(u32 addr, u32* val);
void DataRead32S(u32 addr, u32* val);
void DataWrite8(u32 addr, u8 val);
void DataWrite16(u32 addr, u16 val);
void DataWrite32(u32 addr, u32 val);
void DataWrite32S(u32 addr, u32 val);
void DataRead8(u32 addr, u32* val) override;
void DataRead16(u32 addr, u32* val) override;
void DataRead32(u32 addr, u32* val) override;
void DataRead32S(u32 addr, u32* val) override;
void DataWrite8(u32 addr, u8 val) override;
void DataWrite16(u32 addr, u16 val) override;
void DataWrite32(u32 addr, u32 val) override;
void DataWrite32S(u32 addr, u32 val) override;
void AddCycles_C()
void AddCycles_C() override
{
// code only. always nonseq 32-bit for ARM9.
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
Cycles += numC;
}
void AddCycles_CI(s32 numI)
void AddCycles_CI(s32 numI) override
{
// code+internal
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
Cycles += numC + numI;
}
void AddCycles_CDI()
void AddCycles_CDI() override
{
// LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
// TODO: ITCM data fetches shouldn't be parallelized, they say
@ -228,7 +290,7 @@ public:
// Cycles += numC + numD;
}
void AddCycles_CD()
void AddCycles_CD() override
{
// TODO: ITCM data fetches shouldn't be parallelized, they say
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
@ -240,7 +302,7 @@ public:
// Cycles += numC + numD;
}
void GetCodeMemRegion(u32 addr, NDS::MemRegion* region);
void GetCodeMemRegion(u32 addr, MemRegion* region);
void CP15Reset();
void CP15DoSavestate(Savestate* file);
@ -258,11 +320,12 @@ public:
void ICacheInvalidateAll();
void CP15Write(u32 id, u32 val);
u32 CP15Read(u32 id);
u32 CP15Read(u32 id) const;
u32 CP15Control;
u32 RNGSeed;
u32 TraceProcessID;
u32 DTCMSetting, ITCMSetting;
@ -301,24 +364,33 @@ public:
u8* CurICacheLine;
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
bool (*GetMemRegion)(u32 addr, bool write, MemRegion* region);
#ifdef GDBSTUB_ENABLED
u32 ReadMem(u32 addr, int size) override;
void WriteMem(u32 addr, int size, u32 v) override;
#endif
protected:
u8 BusRead8(u32 addr) override;
u16 BusRead16(u32 addr) override;
u32 BusRead32(u32 addr) override;
void BusWrite8(u32 addr, u8 val) override;
void BusWrite16(u32 addr, u16 val) override;
void BusWrite32(u32 addr, u32 val) override;
};
class ARMv4 : public ARM
{
public:
ARMv4();
ARMv4(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
void Reset();
void FillPipeline() override;
void FillPipeline();
void JumpTo(u32 addr, bool restorecpsr = false);
void JumpTo(u32 addr, bool restorecpsr = false) override;
template <CPUExecuteMode mode>
void Execute();
#ifdef JIT_ENABLED
void ExecuteJIT();
#endif
u16 CodeRead16(u32 addr)
{
@ -330,134 +402,25 @@ public:
return BusRead32(addr);
}
void DataRead8(u32 addr, u32* val)
{
*val = BusRead8(addr);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataRead16(u32 addr, u32* val)
{
addr &= ~1;
*val = BusRead16(addr);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataRead32(u32 addr, u32* val)
{
addr &= ~3;
*val = BusRead32(addr);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
}
void DataRead32S(u32 addr, u32* val)
{
addr &= ~3;
*val = BusRead32(addr);
DataCycles += NDS::ARM7MemTimings[addr >> 15][3];
}
void DataWrite8(u32 addr, u8 val)
{
BusWrite8(addr, val);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataWrite16(u32 addr, u16 val)
{
addr &= ~1;
BusWrite16(addr, val);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
}
void DataWrite32(u32 addr, u32 val)
{
addr &= ~3;
BusWrite32(addr, val);
DataRegion = addr;
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
}
void DataWrite32S(u32 addr, u32 val)
{
addr &= ~3;
BusWrite32(addr, val);
DataCycles += NDS::ARM7MemTimings[addr >> 15][3];
}
void AddCycles_C()
{
// code only. this code fetch is sequential.
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
}
void AddCycles_CI(s32 num)
{
// code+internal. results in a nonseq code fetch.
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
}
void AddCycles_CDI()
{
// LDR/LDM cycles.
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
s32 numD = DataCycles;
if ((DataRegion >> 24) == 0x02) // mainRAM
{
if (CodeRegion == 0x02)
Cycles += numC + numD;
else
{
numC++;
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
}
else if (CodeRegion == 0x02)
{
numD++;
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
else
{
Cycles += numC + numD + 1;
}
}
void AddCycles_CD()
{
// TODO: max gain should be 5c when writing to mainRAM
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
s32 numD = DataCycles;
if ((DataRegion >> 24) == 0x02)
{
if (CodeRegion == 0x02)
Cycles += numC + numD;
else
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
else if (CodeRegion == 0x02)
{
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
}
else
{
Cycles += numC + numD;
}
}
void DataRead8(u32 addr, u32* val) override;
void DataRead16(u32 addr, u32* val) override;
void DataRead32(u32 addr, u32* val) override;
void DataRead32S(u32 addr, u32* val) override;
void DataWrite8(u32 addr, u8 val) override;
void DataWrite16(u32 addr, u16 val) override;
void DataWrite32(u32 addr, u32 val) override;
void DataWrite32S(u32 addr, u32 val) override;
void AddCycles_C() override;
void AddCycles_CI(s32 num) override;
void AddCycles_CDI() override;
void AddCycles_CD() override;
protected:
u8 BusRead8(u32 addr) override;
u16 BusRead16(u32 addr) override;
u32 BusRead32(u32 addr) override;
void BusWrite8(u32 addr, u8 val) override;
void BusWrite16(u32 addr, u16 val) override;
void BusWrite32(u32 addr, u32 val) override;
};
namespace ARMInterpreter
@ -467,13 +430,5 @@ void A_UNK(ARM* cpu);
void T_UNK(ARM* cpu);
}
namespace NDS
{
extern ARMv5* ARM9;
extern ARMv4* ARM7;
}
#endif // ARM_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -22,15 +22,24 @@
#include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_Branch.h"
#include "ARMInterpreter_LoadStore.h"
#include "Platform.h"
#ifdef GDBSTUB_ENABLED
#include "debug/GdbStub.h"
#endif
namespace ARMInterpreter
namespace melonDS::ARMInterpreter
{
using Platform::Log;
using Platform::LogLevel;
void A_UNK(ARM* cpu)
{
printf("undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
Log(LogLevel::Warn, "undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
#ifdef GDBSTUB_ENABLED
cpu->GdbStub.Enter(cpu->GdbStub.IsConnected(), Gdb::TgtStatus::FaultInsn, cpu->R[15]-8);
#endif
//for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
//NDS::Halt();
u32 oldcpsr = cpu->CPSR;
@ -45,7 +54,10 @@ void A_UNK(ARM* cpu)
void T_UNK(ARM* cpu)
{
printf("undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
Log(LogLevel::Warn, "undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
#ifdef GDBSTUB_ENABLED
cpu->GdbStub.Enter(cpu->GdbStub.IsConnected(), Gdb::TgtStatus::FaultInsn, cpu->R[15]-4);
#endif
//NDS::Halt();
u32 oldcpsr = cpu->CPSR;
cpu->CPSR &= ~0xBF;
@ -211,11 +223,11 @@ void A_MCR(ARM* cpu)
}
else if (cpu->Num==1 && cp==14)
{
printf("MCR p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
Log(LogLevel::Debug, "MCR p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
}
else
{
printf("bad MCR opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
Log(LogLevel::Warn, "bad MCR opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
return A_UNK(cpu); // TODO: check what kind of exception it really is
}
@ -239,11 +251,11 @@ void A_MRC(ARM* cpu)
}
else if (cpu->Num==1 && cp==14)
{
printf("MRC p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
Log(LogLevel::Debug, "MRC p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
}
else
{
printf("bad MRC opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
Log(LogLevel::Warn, "bad MRC opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
return A_UNK(cpu); // TODO: check what kind of exception it really is
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -22,6 +22,8 @@
#include "types.h"
#include "ARM.h"
namespace melonDS
{
namespace ARMInterpreter
{
@ -41,4 +43,5 @@ void A_BLX_IMM(ARM* cpu); // I'm a special one look at me
}
}
#endif // ARMINTERPRETER_H

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -18,18 +18,46 @@
#include <stdio.h>
#include "ARM.h"
#include "NDS.h"
#define CARRY_ADD(a, b) ((0xFFFFFFFF-a) < b)
#define CARRY_SUB(a, b) (a >= b)
#define OVERFLOW_ADD(a, b, res) ((!(((a) ^ (b)) & 0x80000000)) && (((a) ^ (res)) & 0x80000000))
#define OVERFLOW_SUB(a, b, res) ((((a) ^ (b)) & 0x80000000) && (((a) ^ (res)) & 0x80000000))
namespace ARMInterpreter
namespace melonDS::ARMInterpreter
{
inline bool CarryAdd(u32 a, u32 b)
{
return (0xFFFFFFFF-a) < b;
}
inline bool CarrySub(u32 a, u32 b)
{
return a >= b;
}
inline bool OverflowAdd(u32 a, u32 b)
{
u32 res = a + b;
return (!((a ^ b) & 0x80000000)) && ((a ^ res) & 0x80000000);
}
inline bool OverflowSub(u32 a, u32 b)
{
u32 res = a - b;
return ((a ^ b) & 0x80000000) && ((a ^ res) & 0x80000000);
}
inline bool OverflowAdc(u32 a, u32 b, u32 carry)
{
s64 fullResult = (s64)(s32)a + (s32)b + carry;
u32 res = a + b + carry;
return (s32)res != fullResult;
}
inline bool OverflowSbc(u32 a, u32 b, u32 carry)
{
s64 fullResult = (s64)(s32)a - (s32)b - carry;
u32 res = a - b - carry;
return (s32)res != fullResult;
}
#define LSL_IMM(x, s) \
x <<= s;
@ -364,8 +392,8 @@ A_IMPLEMENT_ALU_OP(EOR,_S)
u32 res = a - b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_SUB(a, b), \
OVERFLOW_SUB(a, b, res)); \
CarrySub(a, b), \
OverflowSub(a, b)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -397,8 +425,8 @@ A_IMPLEMENT_ALU_OP(SUB,)
u32 res = b - a; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_SUB(b, a), \
OVERFLOW_SUB(b, a, res)); \
CarrySub(b, a), \
OverflowSub(b, a)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -430,8 +458,8 @@ A_IMPLEMENT_ALU_OP(RSB,)
u32 res = a + b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_ADD(a, b), \
OVERFLOW_ADD(a, b, res)); \
CarryAdd(a, b), \
OverflowAdd(a, b)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -465,8 +493,8 @@ A_IMPLEMENT_ALU_OP(ADD,)
u32 res = res_tmp + carry; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry), \
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res)); \
CarryAdd(a, b) | CarryAdd(res_tmp, carry), \
OverflowAdc(a, b, carry)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -500,8 +528,8 @@ A_IMPLEMENT_ALU_OP(ADC,)
u32 res = res_tmp - carry; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry), \
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
CarrySub(a, b) & CarrySub(res_tmp, carry), \
OverflowSbc(a, b, carry)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -535,8 +563,8 @@ A_IMPLEMENT_ALU_OP(SBC,)
u32 res = res_tmp - carry; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_SUB(b, a) & CARRY_SUB(res_tmp, carry), \
OVERFLOW_SUB(b, a, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
CarrySub(b, a) & CarrySub(res_tmp, carry), \
OverflowSbc(b, a, carry)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
@ -575,8 +603,8 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S)
u32 res = a - b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_SUB(a, b), \
OVERFLOW_SUB(a, b, res)); \
CarrySub(a, b), \
OverflowSub(a, b)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(CMP,)
@ -587,8 +615,8 @@ A_IMPLEMENT_ALU_TEST(CMP,)
u32 res = a + b; \
cpu->SetNZCV(res & 0x80000000, \
!res, \
CARRY_ADD(a, b), \
OVERFLOW_ADD(a, b, res)); \
CarryAdd(a, b), \
OverflowAdd(a, b)); \
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(CMN,)
@ -665,7 +693,7 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu)
// but since they serve no purpose ATTOW, we can skip them
u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags
// TODO: Pass flags to NocashPrint
NDS::NocashPrint(cpu->Num, addr);
cpu->NDS.NocashPrint(cpu->Num, addr);
}
}
@ -933,7 +961,7 @@ void A_SMLAxy(ARM* cpu)
u32 res = res_mul + rn;
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
if (OVERFLOW_ADD(res_mul, rn, res))
if (OverflowAdd(res_mul, rn))
cpu->CPSR |= 0x08000000;
cpu->AddCycles_C(); // TODO: interlock??
@ -954,7 +982,7 @@ void A_SMLAWy(ARM* cpu)
u32 res = res_mul + rn;
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
if (OVERFLOW_ADD(res_mul, rn, res))
if (OverflowAdd(res_mul, rn))
cpu->CPSR |= 0x08000000;
cpu->AddCycles_C(); // TODO: interlock??
@ -1051,7 +1079,7 @@ void A_QADD(ARM* cpu)
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 res = rm + rn;
if (OVERFLOW_ADD(rm, rn, res))
if (OverflowAdd(rm, rn))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1069,7 +1097,7 @@ void A_QSUB(ARM* cpu)
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 res = rm - rn;
if (OVERFLOW_SUB(rm, rn, res))
if (OverflowSub(rm, rn))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1086,7 +1114,7 @@ void A_QDADD(ARM* cpu)
u32 rm = cpu->R[cpu->CurInstr & 0xF];
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
if (OVERFLOW_ADD(rn, rn, rn<<1))
if (OverflowAdd(rn, rn))
{
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
cpu->CPSR |= 0x08000000; // CHECKME
@ -1095,7 +1123,7 @@ void A_QDADD(ARM* cpu)
rn <<= 1;
u32 res = rm + rn;
if (OVERFLOW_ADD(rm, rn, res))
if (OverflowAdd(rm, rn))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1112,7 +1140,7 @@ void A_QDSUB(ARM* cpu)
u32 rm = cpu->R[cpu->CurInstr & 0xF];
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
if (OVERFLOW_ADD(rn, rn, rn<<1))
if (OverflowAdd(rn, rn))
{
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
cpu->CPSR |= 0x08000000; // CHECKME
@ -1121,7 +1149,7 @@ void A_QDSUB(ARM* cpu)
rn <<= 1;
u32 res = rm - rn;
if (OVERFLOW_SUB(rm, rn, res))
if (OverflowSub(rm, rn))
{
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
cpu->CPSR |= 0x08000000;
@ -1178,8 +1206,8 @@ void T_ADD_REG_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
CarryAdd(a, b),
OverflowAdd(a, b));
cpu->AddCycles_C();
}
@ -1191,8 +1219,8 @@ void T_SUB_REG_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
CarrySub(a, b),
OverflowSub(a, b));
cpu->AddCycles_C();
}
@ -1204,8 +1232,8 @@ void T_ADD_IMM_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
CarryAdd(a, b),
OverflowAdd(a, b));
cpu->AddCycles_C();
}
@ -1217,8 +1245,8 @@ void T_SUB_IMM_(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
CarrySub(a, b),
OverflowSub(a, b));
cpu->AddCycles_C();
}
@ -1238,8 +1266,8 @@ void T_CMP_IMM(ARM* cpu)
u32 res = a - b;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
CarrySub(a, b),
OverflowSub(a, b));
cpu->AddCycles_C();
}
@ -1251,8 +1279,8 @@ void T_ADD_IMM(ARM* cpu)
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
CarryAdd(a, b),
OverflowAdd(a, b));
cpu->AddCycles_C();
}
@ -1264,8 +1292,8 @@ void T_SUB_IMM(ARM* cpu)
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
CarrySub(a, b),
OverflowSub(a, b));
cpu->AddCycles_C();
}
@ -1335,8 +1363,8 @@ void T_ADC_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry),
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res));
CarryAdd(a, b) | CarryAdd(res_tmp, carry),
OverflowAdc(a, b, carry));
cpu->AddCycles_C();
}
@ -1350,8 +1378,8 @@ void T_SBC_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry),
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res));
CarrySub(a, b) & CarrySub(res_tmp, carry),
OverflowSbc(a, b, carry));
cpu->AddCycles_C();
}
@ -1383,8 +1411,8 @@ void T_NEG_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(0, b),
OVERFLOW_SUB(0, b, res));
CarrySub(0, b),
OverflowSub(0, b));
cpu->AddCycles_C();
}
@ -1395,8 +1423,8 @@ void T_CMP_REG(ARM* cpu)
u32 res = a - b;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
CarrySub(a, b),
OverflowSub(a, b));
cpu->AddCycles_C();
}
@ -1407,8 +1435,8 @@ void T_CMN_REG(ARM* cpu)
u32 res = a + b;
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
CarryAdd(a, b),
OverflowAdd(a, b));
cpu->AddCycles_C();
}
@ -1504,8 +1532,8 @@ void T_CMP_HIREG(ARM* cpu)
cpu->SetNZCV(res & 0x80000000,
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
CarrySub(a, b),
OverflowSub(a, b));
cpu->AddCycles_C();
}
@ -1534,7 +1562,7 @@ void T_MOV_HIREG(ARM* cpu)
// but since they serve no purpose ATTOW, we can skip them
u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags
// TODO: Pass flags to NocashPrint
NDS::NocashPrint(cpu->Num, addr);
cpu->NDS.NocashPrint(cpu->Num, addr);
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -19,6 +19,8 @@
#ifndef ARMINTERPRETER_ALU_H
#define ARMINTERPRETER_ALU_H
namespace melonDS
{
namespace ARMInterpreter
{
@ -134,4 +136,5 @@ void T_ADD_SP(ARM* cpu);
}
}
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -16,12 +16,13 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include "ARM.h"
#include "Platform.h"
namespace ARMInterpreter
namespace melonDS::ARMInterpreter
{
using Platform::Log;
using Platform::LogLevel;
void A_B(ARM* cpu)
@ -79,7 +80,7 @@ void T_BLX_REG(ARM* cpu)
{
if (cpu->Num==1)
{
printf("!! THUMB BLX_REG ON ARM7\n");
Log(LogLevel::Warn, "!! THUMB BLX_REG ON ARM7\n");
return;
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -19,6 +19,8 @@
#ifndef ARMINTERPRETER_BRANCH_H
#define ARMINTERPRETER_BRANCH_H
namespace melonDS
{
namespace ARMInterpreter
{
@ -36,4 +38,5 @@ void T_BL_LONG_2(ARM* cpu);
}
}
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -20,7 +20,7 @@
#include "ARM.h"
namespace ARMInterpreter
namespace melonDS::ARMInterpreter
{
@ -62,14 +62,20 @@ namespace ARMInterpreter
#define A_STR \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
storeval += 4; \
cpu->DataWrite32(offset, storeval); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
cpu->AddCycles_CD();
// TODO: user mode (bit21)
#define A_STR_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
storeval += 4; \
cpu->DataWrite32(addr, storeval); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
cpu->AddCycles_CD();
@ -424,9 +430,9 @@ void A_LDM(ARM* cpu)
}
}
u32 pc = 0;
if (cpu->CurInstr & (1<<15))
{
u32 pc;
if (preinc) base += 4;
if (first) cpu->DataRead32 (base, &pc);
else cpu->DataRead32S(base, &pc);
@ -434,13 +440,8 @@ void A_LDM(ARM* cpu)
if (cpu->Num == 1)
pc &= ~0x1;
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
if (cpu->CurInstr & (1<<21))
{
// post writeback
@ -460,6 +461,12 @@ void A_LDM(ARM* cpu)
cpu->R[baseid] = wbbase;
}
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
if (cpu->CurInstr & (1<<15))
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
cpu->AddCycles_CDI();
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -19,7 +19,7 @@
#ifndef ARMINTERPRETER_LOADSTORE_H
#define ARMINTERPRETER_LOADSTORE_H
namespace ARMInterpreter
namespace melonDS::ARMInterpreter
{
#define A_PROTO_WB_LDRSTR(x) \

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -17,7 +17,7 @@
*/
#include "ARMJIT.h"
#include "ARMJIT_Memory.h"
#include <string.h>
#include <assert.h>
#include <unordered_map>
@ -30,6 +30,7 @@
#include "ARMJIT_Internal.h"
#include "ARMJIT_Memory.h"
#include "ARMJIT_Compiler.h"
#include "ARMJIT_Global.h"
#include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_LoadStore.h"
@ -42,136 +43,52 @@
#include "SPU.h"
#include "Wifi.h"
#include "NDSCart.h"
#include "Platform.h"
#include "ARMJIT_x64/ARMJIT_Offsets.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset, "");
static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset, "");
static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset, "");
namespace ARMJIT
{
#define JIT_DEBUGPRINT(msg, ...)
//#define JIT_DEBUGPRINT(msg, ...) printf(msg, ## __VA_ARGS__)
Compiler* JITCompiler;
int MaxBlockSize;
bool LiteralOptimizations;
bool BranchOptimizations;
bool FastMemory;
std::unordered_map<u32, JitBlock*> JitBlocks9;
std::unordered_map<u32, JitBlock*> JitBlocks7;
std::unordered_map<u32, JitBlock*> RestoreCandidates;
TinyVector<u32> InvalidLiterals;
AddressRange CodeIndexITCM[ITCMPhysicalSize / 512];
AddressRange CodeIndexMainRAM[NDS::MainRAMMaxSize / 512];
AddressRange CodeIndexSWRAM[NDS::SharedWRAMSize / 512];
AddressRange CodeIndexVRAM[0x100000 / 512];
AddressRange CodeIndexARM9BIOS[sizeof(NDS::ARM9BIOS) / 512];
AddressRange CodeIndexARM7BIOS[sizeof(NDS::ARM7BIOS) / 512];
AddressRange CodeIndexARM7WRAM[NDS::ARM7WRAMSize / 512];
AddressRange CodeIndexARM7WVRAM[0x40000 / 512];
AddressRange CodeIndexBIOS9DSi[0x10000 / 512];
AddressRange CodeIndexBIOS7DSi[0x10000 / 512];
AddressRange CodeIndexNWRAM_A[DSi::NWRAMSize / 512];
AddressRange CodeIndexNWRAM_B[DSi::NWRAMSize / 512];
AddressRange CodeIndexNWRAM_C[DSi::NWRAMSize / 512];
u64 FastBlockLookupITCM[ITCMPhysicalSize / 2];
u64 FastBlockLookupMainRAM[NDS::MainRAMMaxSize / 2];
u64 FastBlockLookupSWRAM[NDS::SharedWRAMSize / 2];
u64 FastBlockLookupVRAM[0x100000 / 2];
u64 FastBlockLookupARM9BIOS[sizeof(NDS::ARM9BIOS) / 2];
u64 FastBlockLookupARM7BIOS[sizeof(NDS::ARM7BIOS) / 2];
u64 FastBlockLookupARM7WRAM[NDS::ARM7WRAMSize / 2];
u64 FastBlockLookupARM7WVRAM[0x40000 / 2];
u64 FastBlockLookupBIOS9DSi[0x10000 / 2];
u64 FastBlockLookupBIOS7DSi[0x10000 / 2];
u64 FastBlockLookupNWRAM_A[DSi::NWRAMSize / 2];
u64 FastBlockLookupNWRAM_B[DSi::NWRAMSize / 2];
u64 FastBlockLookupNWRAM_C[DSi::NWRAMSize / 2];
//#define JIT_DEBUGPRINT(msg, ...) Platform::Log(Platform::LogLevel::Debug, msg, ## __VA_ARGS__)
const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] =
{
0,
ITCMPhysicalSize,
0,
sizeof(NDS::ARM9BIOS),
NDS::MainRAMMaxSize,
NDS::SharedWRAMSize,
ARM9BIOSSize,
MainRAMMaxSize,
SharedWRAMSize,
0,
0x100000,
sizeof(NDS::ARM7BIOS),
NDS::ARM7WRAMSize,
ARM7BIOSSize,
ARM7WRAMSize,
0,
0,
0x40000,
0x10000,
0x10000,
DSi::NWRAMSize,
DSi::NWRAMSize,
DSi::NWRAMSize,
NWRAMSize,
NWRAMSize,
NWRAMSize,
};
AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
CodeIndexITCM,
NULL,
CodeIndexARM9BIOS,
CodeIndexMainRAM,
CodeIndexSWRAM,
NULL,
CodeIndexVRAM,
CodeIndexARM7BIOS,
CodeIndexARM7WRAM,
NULL,
NULL,
CodeIndexARM7WVRAM,
CodeIndexBIOS9DSi,
CodeIndexBIOS7DSi,
CodeIndexNWRAM_A,
CodeIndexNWRAM_B,
CodeIndexNWRAM_C
};
u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
FastBlockLookupITCM,
NULL,
FastBlockLookupARM9BIOS,
FastBlockLookupMainRAM,
FastBlockLookupSWRAM,
NULL,
FastBlockLookupVRAM,
FastBlockLookupARM7BIOS,
FastBlockLookupARM7WRAM,
NULL,
NULL,
FastBlockLookupARM7WVRAM,
FastBlockLookupBIOS9DSi,
FastBlockLookupBIOS7DSi,
FastBlockLookupNWRAM_A,
FastBlockLookupNWRAM_B,
FastBlockLookupNWRAM_C
};
u32 LocaliseCodeAddress(u32 num, u32 addr)
u32 ARMJIT::LocaliseCodeAddress(u32 num, u32 addr) const noexcept
{
int region = num == 0
? ARMJIT_Memory::ClassifyAddress9(addr)
: ARMJIT_Memory::ClassifyAddress7(addr);
? Memory.ClassifyAddress9(addr)
: Memory.ClassifyAddress7(addr);
if (CodeMemRegions[region])
return ARMJIT_Memory::LocaliseAddress(region, num, addr);
return Memory.LocaliseAddress(region, num, addr);
return 0;
}
@ -187,11 +104,31 @@ T SlowRead9(u32 addr, ARMv5* cpu)
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
val = *(T*)&cpu->DTCM[addr & 0x3FFF];
else if (std::is_same<T, u32>::value)
val = (ConsoleType == 0 ? NDS::ARM9Read32 : DSi::ARM9Read32)(addr);
val = NDS::Current->ARM9Read32(addr);
else if (std::is_same<T, u16>::value)
val = (ConsoleType == 0 ? NDS::ARM9Read16 : DSi::ARM9Read16)(addr);
val = NDS::Current->ARM9Read16(addr);
else
val = (ConsoleType == 0 ? NDS::ARM9Read8 : DSi::ARM9Read8)(addr);
val = NDS::Current->ARM9Read8(addr);
if (std::is_same<T, u32>::value)
return ROR(val, offset << 3);
else
return val;
}
template <typename T, int ConsoleType>
T SlowRead7(u32 addr)
{
u32 offset = addr & 0x3;
addr &= ~(sizeof(T) - 1);
T val;
if (std::is_same<T, u32>::value)
val = NDS::Current->ARM7Read32(addr);
else if (std::is_same<T, u16>::value)
val = NDS::Current->ARM7Read16(addr);
else
val = NDS::Current->ARM7Read8(addr);
if (std::is_same<T, u32>::value)
return ROR(val, offset << 3);
@ -206,7 +143,7 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
if (addr < cpu->ITCMSize)
{
CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
cpu->NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
*(T*)&cpu->ITCM[addr & 0x7FFF] = val;
}
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
@ -215,49 +152,29 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
}
else if (std::is_same<T, u32>::value)
{
(ConsoleType == 0 ? NDS::ARM9Write32 : DSi::ARM9Write32)(addr, val);
NDS::Current->ARM9Write32(addr, val);
}
else if (std::is_same<T, u16>::value)
{
(ConsoleType == 0 ? NDS::ARM9Write16 : DSi::ARM9Write16)(addr, val);
NDS::Current->ARM9Write16(addr, val);
}
else
{
(ConsoleType == 0 ? NDS::ARM9Write8 : DSi::ARM9Write8)(addr, val);
NDS::Current->ARM9Write8(addr, val);
}
}
template <typename T, int ConsoleType>
T SlowRead7(u32 addr)
{
u32 offset = addr & 0x3;
addr &= ~(sizeof(T) - 1);
T val;
if (std::is_same<T, u32>::value)
val = (ConsoleType == 0 ? NDS::ARM7Read32 : DSi::ARM7Read32)(addr);
else if (std::is_same<T, u16>::value)
val = (ConsoleType == 0 ? NDS::ARM7Read16 : DSi::ARM7Read16)(addr);
else
val = (ConsoleType == 0 ? NDS::ARM7Read8 : DSi::ARM7Read8)(addr);
if (std::is_same<T, u32>::value)
return ROR(val, offset << 3);
else
return val;
}
template <typename T, int ConsoleType>
void SlowWrite7(u32 addr, u32 val)
{
addr &= ~(sizeof(T) - 1);
if (std::is_same<T, u32>::value)
(ConsoleType == 0 ? NDS::ARM7Write32 : DSi::ARM7Write32)(addr, val);
NDS::Current->ARM7Write32(addr, val);
else if (std::is_same<T, u16>::value)
(ConsoleType == 0 ? NDS::ARM7Write16 : DSi::ARM7Write16)(addr, val);
NDS::Current->ARM7Write16(addr, val);
else
(ConsoleType == 0 ? NDS::ARM7Write8 : DSi::ARM7Write8)(addr, val);
NDS::Current->ARM7Write8(addr, val);
}
template <bool Write, int ConsoleType>
@ -313,38 +230,18 @@ void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
INSTANTIATE_SLOWMEM(0)
INSTANTIATE_SLOWMEM(1)
void Init()
{
JITCompiler = new Compiler();
ARMJIT_Memory::Init();
}
void DeInit()
ARMJIT::~ARMJIT() noexcept
{
JitEnableWrite();
ResetBlockCache();
ARMJIT_Memory::DeInit();
delete JITCompiler;
}
void Reset()
void ARMJIT::Reset() noexcept
{
MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize);
LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations);
BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations);
FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory);
if (MaxBlockSize < 1)
MaxBlockSize = 1;
if (MaxBlockSize > 32)
MaxBlockSize = 32;
JitEnableWrite();
ResetBlockCache();
ARMJIT_Memory::Reset();
Memory.Reset();
}
void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags)
@ -571,7 +468,17 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
};
#undef F
void RetireJitBlock(JitBlock* block)
ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept :
NDS(nds),
Memory(nds),
JITCompiler(nds),
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
FastMemory((jit.has_value() ? jit->FastMemory : false) && ARMJIT_Memory::IsFastMemSupported())
{}
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
{
auto it = RestoreCandidates.find(block->InstrHash);
if (it != RestoreCandidates.end())
@ -585,7 +492,44 @@ void RetireJitBlock(JitBlock* block)
}
}
void CompileBlock(ARM* cpu)
void ARMJIT::SetJITArgs(JITArgs args) noexcept
{
args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported();
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
if (MaxBlockSize != args.MaxBlockSize
|| LiteralOptimizations != args.LiteralOptimizations
|| BranchOptimizations != args.BranchOptimizations
|| FastMemory != args.FastMemory)
ResetBlockCache();
MaxBlockSize = args.MaxBlockSize;
LiteralOptimizations = args.LiteralOptimizations;
BranchOptimizations = args.BranchOptimizations;
FastMemory = args.FastMemory;
}
void ARMJIT::SetMaxBlockSize(int size) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(size), LiteralOptimizations, LiteralOptimizations, FastMemory});
}
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), enabled, BranchOptimizations, FastMemory});
}
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, enabled, FastMemory});
}
void ARMJIT::SetFastMemory(bool enabled) noexcept
{
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled});
}
void ARMJIT::CompileBlock(ARM* cpu) noexcept
{
bool thumb = cpu->CPSR & 0x20;
@ -594,7 +538,7 @@ void CompileBlock(ARM* cpu)
u32 localAddr = LocaliseCodeAddress(cpu->Num, blockAddr);
if (!localAddr)
{
printf("trying to compile non executable code? %x\n", blockAddr);
Log(LogLevel::Warn, "trying to compile non executable code? %x\n", blockAddr);
}
auto& map = cpu->Num == 0 ? JitBlocks9 : JitBlocks7;
@ -612,7 +556,7 @@ void CompileBlock(ARM* cpu)
u64* entry = &FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2];
*entry = ((u64)blockAddr | cpu->Num) << 32;
*entry |= JITCompiler->SubEntryOffset(existingBlockIt->second->EntryPoint);
*entry |= JITCompiler.SubEntryOffset(existingBlockIt->second->EntryPoint);
return;
}
@ -713,7 +657,7 @@ void CompileBlock(ARM* cpu)
nextInstr[1] = cpuv4->CodeRead32(r15);
instrs[i].CodeCycles = cpu->CodeCycles;
}
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr);
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr, LiteralOptimizations);
hasMemoryInstr |= thumb
? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA)
@ -764,7 +708,7 @@ void CompileBlock(ARM* cpu)
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, literalAddr);
if (!translatedAddr)
{
printf("literal in non executable memory?\n");
Log(LogLevel::Warn,"literal in non executable memory?\n");
}
if (InvalidLiterals.Find(translatedAddr) == -1)
{
@ -815,7 +759,7 @@ void CompileBlock(ARM* cpu)
{
for (int j = 0; j < i; j++)
{
if (instrs[i].Addr == target)
if (instrs[j].Addr == target)
{
isBackJump = true;
break;
@ -871,7 +815,7 @@ void CompileBlock(ARM* cpu)
i++;
bool canCompile = JITCompiler->CanCompile(thumb, instrs[i - 1].Info.Kind);
bool canCompile = JITCompiler.CanCompile(thumb, instrs[i - 1].Info.Kind);
bool secondaryFlagReadCond = !canCompile || (instrs[i - 1].BranchFlags & (branch_FollowCondTaken | branch_FollowCondNotTaken));
if (instrs[i - 1].Info.ReadFlags != 0 || secondaryFlagReadCond)
FloodFillSetFlags(instrs, i - 2, !secondaryFlagReadCond ? instrs[i - 1].Info.ReadFlags : 0xF);
@ -952,7 +896,7 @@ void CompileBlock(ARM* cpu)
FloodFillSetFlags(instrs, i - 1, 0xF);
JitEnableWrite();
block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr);
block->EntryPoint = JITCompiler.CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr);
JitEnableExecute();
JIT_DEBUGPRINT("block start %p\n", block->EntryPoint);
@ -972,8 +916,8 @@ void CompileBlock(ARM* cpu)
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
if (!PageContainsCode(&region[(addressRanges[j] & 0x7FFF000) / 512]))
ARMJIT_Memory::SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
if (!PageContainsCode(&region[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
AddressRange* range = &region[(addressRanges[j] & 0x7FFFFFF) / 512];
range->Code |= addressMasks[j];
@ -987,10 +931,10 @@ void CompileBlock(ARM* cpu)
u64* entry = &FastBlockLookupRegions[(localAddr >> 27)][(localAddr & 0x7FFFFFF) / 2];
*entry = ((u64)blockAddr | cpu->Num) << 32;
*entry |= JITCompiler->SubEntryOffset(block->EntryPoint);
*entry |= JITCompiler.SubEntryOffset(block->EntryPoint);
}
void InvalidateByAddr(u32 localAddr)
void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
{
JIT_DEBUGPRINT("invalidating by addr %x\n", localAddr);
@ -1025,9 +969,9 @@ void InvalidateByAddr(u32 localAddr)
range->Blocks.Remove(i);
if (range->Blocks.Length == 0
&& !PageContainsCode(&region[(localAddr & 0x7FFF000) / 512]))
&& !PageContainsCode(&region[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
{
ARMJIT_Memory::SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
}
bool literalInvalidation = false;
@ -1059,8 +1003,8 @@ void InvalidateByAddr(u32 localAddr)
if (otherRange->Blocks.Length == 0)
{
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512]))
ARMJIT_Memory::SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
otherRange->Code = 0;
}
@ -1084,7 +1028,7 @@ void InvalidateByAddr(u32 localAddr)
}
}
void CheckAndInvalidateITCM()
void ARMJIT::CheckAndInvalidateITCM() noexcept
{
for (u32 i = 0; i < ITCMPhysicalSize; i+=512)
{
@ -1102,7 +1046,7 @@ void CheckAndInvalidateITCM()
}
}
void CheckAndInvalidateWVRAM(int bank)
void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept
{
u32 start = bank == 1 ? 0x20000 : 0;
for (u32 i = start; i < start+0x20000; i+=512)
@ -1118,38 +1062,30 @@ void CheckAndInvalidateWVRAM(int bank)
}
}
template <u32 num, int region>
void CheckAndInvalidate(u32 addr)
{
u32 localAddr = ARMJIT_Memory::LocaliseAddress(region, num, addr);
if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16)))
InvalidateByAddr(localAddr);
}
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr)
JitBlockEntry ARMJIT::LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept
{
u64* entry = &entries[offset / 2];
if (*entry >> 32 == (addr | num))
return JITCompiler->AddEntryOffset((u32)*entry);
return JITCompiler.AddEntryOffset((u32)*entry);
return NULL;
}
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry)
void ARMJIT::blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept
{
u32 localAddr = LocaliseCodeAddress(num, blockAddr);
assert(JITCompiler->AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry);
assert(JITCompiler.AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry);
}
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size)
bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept
{
// amazingly ignoring the DTCM is the proper behaviour for code fetches
int region = num == 0
? ARMJIT_Memory::ClassifyAddress9(blockAddr)
: ARMJIT_Memory::ClassifyAddress7(blockAddr);
? Memory.ClassifyAddress9(blockAddr)
: Memory.ClassifyAddress7(blockAddr);
u32 memoryOffset;
if (FastBlockLookupRegions[region]
&& ARMJIT_Memory::GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size))
&& Memory.GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size))
{
//printf("setup exec region %d %d %08x %08x %x %x\n", num, region, blockAddr, start, size, memoryOffset);
entry = FastBlockLookupRegions[region] + memoryOffset / 2;
@ -1158,28 +1094,28 @@ bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32&
return false;
}
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32);
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32);
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32);
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept;
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept;
void ResetBlockCache()
void ARMJIT::ResetBlockCache() noexcept
{
printf("Resetting JIT block cache...\n");
Log(LogLevel::Debug, "Resetting JIT block cache...\n");
// could be replace through a function which only resets
// the permissions but we're too lazy
ARMJIT_Memory::Reset();
Memory.Reset();
InvalidLiterals.Clear();
for (int i = 0; i < ARMJIT_Memory::memregions_Count; i++)
@ -1217,10 +1153,10 @@ void ResetBlockCache()
JitBlocks9.clear();
JitBlocks7.clear();
JITCompiler->Reset();
JITCompiler.Reset();
}
void JitEnableWrite()
void ARMJIT::JitEnableWrite() noexcept
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))
@ -1228,7 +1164,7 @@ void JitEnableWrite()
#endif
}
void JitEnableExecute()
void ARMJIT::JitEnableExecute() noexcept
{
#if defined(__APPLE__) && defined(__aarch64__)
if (__builtin_available(macOS 11.0, *))

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -19,49 +19,188 @@
#ifndef ARMJIT_H
#define ARMJIT_H
#include <algorithm>
#include <optional>
#include <memory>
#include "types.h"
#include "MemConstants.h"
#include "Args.h"
#include "ARMJIT_Memory.h"
#include "ARM.h"
#include "ARM_InstrInfo.h"
#ifdef JIT_ENABLED
#include "JitBlock.h"
#if defined(__APPLE__) && defined(__aarch64__)
#include <pthread.h>
#endif
namespace ARMJIT
#include "ARMJIT_Compiler.h"
namespace melonDS
{
class ARM;
typedef void (*JitBlockEntry)();
class JitBlock;
class ARMJIT
{
public:
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept;
~ARMJIT() noexcept;
void InvalidateByAddr(u32) noexcept;
void CheckAndInvalidateWVRAM(int) noexcept;
void CheckAndInvalidateITCM() noexcept;
void Reset() noexcept;
void JitEnableWrite() noexcept;
void JitEnableExecute() noexcept;
void CompileBlock(ARM* cpu) noexcept;
void ResetBlockCache() noexcept;
extern int MaxBlockSize;
extern bool LiteralOptimizations;
extern bool BranchOptimizations;
extern bool FastMemory;
template <u32 num, int region>
void CheckAndInvalidate(u32 addr) noexcept
{
u32 localAddr = Memory.LocaliseAddress(region, num, addr);
if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16)))
InvalidateByAddr(localAddr);
}
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept;
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept;
u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept;
void Init();
void DeInit();
ARMJIT_Memory Memory;
private:
int MaxBlockSize {};
bool LiteralOptimizations = false;
bool BranchOptimizations = false;
bool FastMemory = false;
void Reset();
public:
melonDS::NDS& NDS;
TinyVector<u32> InvalidLiterals {};
friend class ARMJIT_Memory;
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept;
void RetireJitBlock(JitBlock* block) noexcept;
void CheckAndInvalidateITCM();
void CheckAndInvalidateWVRAM(int bank);
int GetMaxBlockSize() const noexcept { return MaxBlockSize; }
bool LiteralOptimizationsEnabled() const noexcept { return LiteralOptimizations; }
bool BranchOptimizationsEnabled() const noexcept { return BranchOptimizations; }
bool FastMemoryEnabled() const noexcept { return FastMemory; }
void InvalidateByAddr(u32 pseudoPhysical);
void SetJITArgs(JITArgs args) noexcept;
void SetMaxBlockSize(int size) noexcept;
void SetLiteralOptimizations(bool enabled) noexcept;
void SetBranchOptimizations(bool enabled) noexcept;
void SetFastMemory(bool enabled) noexcept;
template <u32 num, int region>
void CheckAndInvalidate(u32 addr);
Compiler JITCompiler;
std::unordered_map<u32, JitBlock*> JitBlocks9 {};
std::unordered_map<u32, JitBlock*> JitBlocks7 {};
void CompileBlock(ARM* cpu);
std::unordered_map<u32, JitBlock*> RestoreCandidates {};
void ResetBlockCache();
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr);
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size);
AddressRange CodeIndexITCM[ITCMPhysicalSize / 512] {};
AddressRange CodeIndexMainRAM[MainRAMMaxSize / 512] {};
AddressRange CodeIndexSWRAM[SharedWRAMSize / 512] {};
AddressRange CodeIndexVRAM[0x100000 / 512] {};
AddressRange CodeIndexARM9BIOS[ARM9BIOSSize / 512] {};
AddressRange CodeIndexARM7BIOS[ARM7BIOSSize / 512] {};
AddressRange CodeIndexARM7WRAM[ARM7WRAMSize / 512] {};
AddressRange CodeIndexARM7WVRAM[0x40000 / 512] {};
AddressRange CodeIndexBIOS9DSi[0x10000 / 512] {};
AddressRange CodeIndexBIOS7DSi[0x10000 / 512] {};
AddressRange CodeIndexNWRAM_A[NWRAMSize / 512] {};
AddressRange CodeIndexNWRAM_B[NWRAMSize / 512] {};
AddressRange CodeIndexNWRAM_C[NWRAMSize / 512] {};
void JitEnableWrite();
void JitEnableExecute();
u64 FastBlockLookupITCM[ITCMPhysicalSize / 2] {};
u64 FastBlockLookupMainRAM[MainRAMMaxSize / 2] {};
u64 FastBlockLookupSWRAM[SharedWRAMSize / 2] {};
u64 FastBlockLookupVRAM[0x100000 / 2] {};
u64 FastBlockLookupARM9BIOS[ARM9BIOSSize / 2] {};
u64 FastBlockLookupARM7BIOS[ARM7BIOSSize / 2] {};
u64 FastBlockLookupARM7WRAM[ARM7WRAMSize / 2] {};
u64 FastBlockLookupARM7WVRAM[0x40000 / 2] {};
u64 FastBlockLookupBIOS9DSi[0x10000 / 2] {};
u64 FastBlockLookupBIOS7DSi[0x10000 / 2] {};
u64 FastBlockLookupNWRAM_A[NWRAMSize / 2] {};
u64 FastBlockLookupNWRAM_B[NWRAMSize / 2] {};
u64 FastBlockLookupNWRAM_C[NWRAMSize / 2] {};
AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
CodeIndexITCM,
NULL,
CodeIndexARM9BIOS,
CodeIndexMainRAM,
CodeIndexSWRAM,
NULL,
CodeIndexVRAM,
CodeIndexARM7BIOS,
CodeIndexARM7WRAM,
NULL,
NULL,
CodeIndexARM7WVRAM,
CodeIndexBIOS9DSi,
CodeIndexBIOS7DSi,
CodeIndexNWRAM_A,
CodeIndexNWRAM_B,
CodeIndexNWRAM_C
};
u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] =
{
NULL,
FastBlockLookupITCM,
NULL,
FastBlockLookupARM9BIOS,
FastBlockLookupMainRAM,
FastBlockLookupSWRAM,
NULL,
FastBlockLookupVRAM,
FastBlockLookupARM7BIOS,
FastBlockLookupARM7WRAM,
NULL,
NULL,
FastBlockLookupARM7WVRAM,
FastBlockLookupBIOS9DSi,
FastBlockLookupBIOS7DSi,
FastBlockLookupNWRAM_A,
FastBlockLookupNWRAM_B,
FastBlockLookupNWRAM_C
};
};
}
extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry);
// Defined in assembly
extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry);
#else
namespace melonDS
{
class ARM;
// This version is a stub; the methods all do nothing,
// but there's still a Memory member.
class ARMJIT
{
public:
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs>) noexcept : Memory(nds) {}
~ARMJIT() noexcept {}
void InvalidateByAddr(u32) noexcept {}
void CheckAndInvalidateWVRAM(int) noexcept {}
void CheckAndInvalidateITCM() noexcept {}
void Reset() noexcept {}
void JitEnableWrite() noexcept {}
void JitEnableExecute() noexcept {}
void CompileBlock(ARM*) noexcept {}
void ResetBlockCache() noexcept {}
template <u32, int>
void CheckAndInvalidate(u32 addr) noexcept {}
ARMJIT_Memory Memory;
};
}
#endif // JIT_ENABLED
#endif // ARMJIT_H
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -20,7 +20,7 @@
using namespace Arm64Gen;
namespace ARMJIT
namespace melonDS
{
void Compiler::Comp_RegShiftReg(int op, bool S, Op2& op2, ARM64Reg rs)
@ -480,7 +480,7 @@ void Compiler::A_Comp_GetOp2(bool S, Op2& op2)
Comp_AddCycles_C();
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift);
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
if (S && shift && (CurInstr.SetFlags & 0x2))
{

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -17,13 +17,14 @@
*/
#include "ARMJIT_Compiler.h"
#include "../NDS.h"
using namespace Arm64Gen;
// hack
const int kCodeCacheTiming = 3;
namespace ARMJIT
namespace melonDS
{
template <typename T>
@ -82,7 +83,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
// doesn't matter if we put garbage in the MSbs there
if (addr & 0x2)
{
cpu9->CodeRead32(addr-2, true) >> 16;
cpu9->CodeRead32(addr-2, true);
cycles += cpu9->CodeCycles;
cpu9->CodeRead32(addr+2, false);
cycles += CurCPU->CodeCycles;
@ -132,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1];
cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1];
CurCPU->R[15] = compileTimePC;
}
@ -144,7 +145,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3];
cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3];
CurCPU->R[15] = compileTimePC;
}
@ -235,7 +236,7 @@ void* Compiler::Gen_JumpTo7(int kind)
LSR(W1, W0, 15);
STR(INDEX_UNSIGNED, W1, RCPU, offsetof(ARM, CodeCycles));
MOVP2R(X2, NDS::ARM7MemTimings);
MOVP2R(X2, NDS.ARM7MemTimings);
LDR(W3, X2, ArithOption(W1, true));
FixupBranch switchToThumb;
@ -388,7 +389,7 @@ void Compiler::T_Comp_BranchXchangeReg()
{
if (Num == 1)
{
printf("BLX unsupported on ARM7!!!\n");
Log(LogLevel::Warn, "BLX unsupported on ARM7!!!\n");
return;
}
MOV(W0, MapReg(CurInstr.A_Reg(3)));
@ -436,4 +437,4 @@ void Compiler::T_Comp_BL_Merged()
Comp_JumpTo(target);
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -20,15 +20,9 @@
#include "../ARMJIT_Internal.h"
#include "../ARMInterpreter.h"
#ifdef __SWITCH__
#include <switch.h>
extern char __start__;
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
#include "../ARMJIT.h"
#include "../NDS.h"
#include "../ARMJIT_Global.h"
#include <stdlib.h>
@ -36,7 +30,7 @@ using namespace Arm64Gen;
extern "C" void ARM_Ret();
namespace ARMJIT
namespace melonDS
{
/*
@ -62,11 +56,6 @@ const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
const int JitMemSize = 16 * 1024 * 1024;
#ifndef __SWITCH__
u8 JitMem[JitMemSize];
#endif
void Compiler::MovePC()
{
ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4);
@ -103,7 +92,7 @@ void Compiler::A_Comp_MSR()
if (CurInstr.Instr & (1 << 25))
{
val = W0;
MOVI2R(val, ::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
MOVI2R(val, melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
}
else
{
@ -217,7 +206,7 @@ void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
}
}
Compiler::Compiler()
Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
{
#ifdef __SWITCH__
JitRWBase = aligned_alloc(0x1000, JitMemSize);
@ -237,7 +226,7 @@ Compiler::Compiler()
break;
if (i++ > 8)
{
printf("couldn't find unmapped place for jit memory\n");
Log(LogLevel::Error, "couldn't find unmapped place for jit memory\n");
JitRXStart = NULL;
}
}
@ -256,18 +245,13 @@ Compiler::Compiler()
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
JitMemMainSize = JitMemSize;
#else
u64 pageSize = sysconf(_SC_PAGE_SIZE);
u8* pageAligned = (u8*)(((u64)JitMem & ~(pageSize - 1)) + pageSize);
u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
#ifdef __APPLE__
pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
JitEnableWrite();
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
ARMJIT_Global::Init();
SetCodeBase(pageAligned, pageAligned);
JitMemMainSize = alignedSize;
CodeMemBase = ARMJIT_Global::AllocateCodeMem();
nds.JIT.JitEnableWrite();
SetCodeBase(reinterpret_cast<u8*>(CodeMemBase), reinterpret_cast<u8*>(CodeMemBase));
JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize;
#endif
SetCodePtr(0);
@ -478,6 +462,9 @@ Compiler::~Compiler()
free(JitRWBase);
}
#endif
ARMJIT_Global::FreeCodeMem(CodeMemBase);
ARMJIT_Global::DeInit();
}
void Compiler::LoadCycles()
@ -690,13 +677,13 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
{
if (JitMemMainSize - GetCodeOffset() < 1024 * 16)
{
printf("JIT near memory full, resetting...\n");
ResetBlockCache();
Log(LogLevel::Debug, "JIT near memory full, resetting...\n");
NDS.JIT.ResetBlockCache();
}
if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8)
{
printf("JIT far memory full, resetting...\n");
ResetBlockCache();
Log(LogLevel::Debug, "JIT far memory full, resetting...\n");
NDS.JIT.ResetBlockCache();
}
JitBlockEntry res = (JitBlockEntry)GetRXPtr();
@ -709,7 +696,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
CPSRDirty = false;
if (hasMemInstr)
MOVP2R(RMemBase, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
MOVP2R(RMemBase, Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start);
for (int i = 0; i < instrsCount; i++)
{
@ -857,7 +844,7 @@ void Compiler::Reset()
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
{
s32 cycles = Num ?
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
if (forceNonConstant)
@ -871,7 +858,7 @@ void Compiler::Comp_AddCycles_CI(u32 numI)
IrregularCycles = true;
s32 cycles = (Num ?
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + numI;
if (Thumb || CurInstr.Cond() == 0xE)
@ -885,7 +872,7 @@ void Compiler::Comp_AddCycles_CI(u32 c, ARM64Reg numI, ArithOption shift)
IrregularCycles = true;
s32 cycles = (Num ?
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + c;
ADD(RCycles, RCycles, cycles);
@ -905,7 +892,7 @@ void Compiler::Comp_AddCycles_CDI()
s32 cycles;
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
@ -950,7 +937,7 @@ void Compiler::Comp_AddCycles_CD()
}
else
{
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 24) == 0x02)

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -19,8 +19,9 @@
#ifndef ARMJIT_A64_COMPILER_H
#define ARMJIT_A64_COMPILER_H
#if defined(JIT_ENABLED) && defined(__aarch64__)
#include "../ARM.h"
#include "../ARMJIT.h"
#include "../dolphin/Arm64Emitter.h"
@ -29,9 +30,9 @@
#include <unordered_map>
namespace ARMJIT
namespace melonDS
{
class ARMJIT;
const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26;
const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27;
const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28;
@ -68,7 +69,7 @@ struct Op2
bool IsSimpleReg()
{ return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; }
bool ImmFits12Bit()
{ return IsImm && (Imm & 0xFFF == Imm); }
{ return IsImm && ((Imm & 0xFFF) == Imm); }
bool IsZero()
{ return IsImm && !Imm; }
@ -97,8 +98,8 @@ class Compiler : public Arm64Gen::ARM64XEmitter
public:
typedef void (Compiler::*CompileFunc)();
Compiler();
~Compiler();
explicit Compiler(melonDS::NDS& nds);
~Compiler() override;
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
@ -113,7 +114,7 @@ public:
bool CanCompile(bool thumb, u16 kind);
bool FlagsNZNeeded()
bool FlagsNZNeeded() const
{
return CurInstr.SetFlags & 0xC;
}
@ -233,7 +234,7 @@ public:
return (u8*)entry - GetRXBase();
}
bool IsJITFault(u8* pc);
bool IsJITFault(const u8* pc);
u8* RewriteMemAccess(u8* pc);
void SwapCodeRegion()
@ -243,6 +244,7 @@ public:
OtherCodeRegion = offset;
}
melonDS::NDS& NDS;
ptrdiff_t OtherCodeRegion;
bool Exit;
@ -273,6 +275,7 @@ public:
void* JitRWStart;
void* JitRXStart;
#endif
void* CodeMemBase;
void* ReadBanked, *WriteBanked;
@ -287,3 +290,5 @@ public:
}
#endif
#endif

View File

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

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -21,13 +21,14 @@
#include "../ARMJIT.h"
#include "../ARMJIT_Memory.h"
#include "../NDS.h"
using namespace Arm64Gen;
namespace ARMJIT
namespace melonDS
{
bool Compiler::IsJITFault(u8* pc)
bool Compiler::IsJITFault(const u8* pc)
{
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
}
@ -56,15 +57,15 @@ u8* Compiler::RewriteMemAccess(u8* pc)
return pc + (ptrdiff_t)patch.PatchOffset;
}
printf("this is a JIT bug! %08x\n", __builtin_bswap32(*(u32*)pc));
Log(LogLevel::Error, "this is a JIT bug! %08x\n", __builtin_bswap32(*(u32*)pc));
abort();
}
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
{
u32 localAddr = LocaliseCodeAddress(Num, addr);
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
if (invalidLiteralIdx != -1)
{
return false;
@ -79,7 +80,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
if (size == 32)
{
CurCPU->DataRead32(addr & ~0x3, &val);
val = ::ROR(val, (addr & 0x3) << 3);
val = melonDS::ROR(val, (addr & 0x3) << 3);
}
else if (size == 16)
{
@ -111,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (size == 16)
addressMask = ~1;
if (ARMJIT::LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -146,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(W0, rnMapped);
}
bool addrIsStatic = ARMJIT::LiteralOptimizations
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -185,18 +186,18 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
MOV(rnMapped, W0);
u32 expectedTarget = Num == 0
? ARMJIT_Memory::ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
{
ptrdiff_t memopStart = GetCodeOffset();
LoadStorePatch patch;
assert((rdMapped >= W8 && rdMapped <= W15) || (rdMapped >= W19 && rdMapped <= W25) || rdMapped == W4);
patch.PatchFunc = flags & memop_Store
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped]
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped];
? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped]
: PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped];
// take a chance at fastmem
if (size > 8)
@ -225,7 +226,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
{
void* func = NULL;
if (addrIsStatic)
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
PushRegs(false, false);
@ -263,7 +264,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (flags & memop_Store)
{
MOV(W2, rdMapped);
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: QuickCallFunction(X3, SlowWrite9<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowWrite9<u32, 1>); break;
@ -275,7 +276,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
}
else
{
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: QuickCallFunction(X3, SlowRead9<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowRead9<u32, 1>); break;
@ -291,7 +292,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (flags & memop_Store)
{
MOV(W1, rdMapped);
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: QuickCallFunction(X3, SlowWrite7<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowWrite7<u32, 1>); break;
@ -303,7 +304,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
}
else
{
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: QuickCallFunction(X3, SlowRead7<u32, 0>); break;
case 33: QuickCallFunction(X3, SlowRead7<u32, 1>); break;
@ -332,7 +333,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
if (CurInstr.Info.Branches())
{
if (size < 32)
printf("LDR size < 32 branching?\n");
Log(LogLevel::Debug, "LDR size < 32 branching?\n");
Comp_JumpTo(rdMapped, Num == 0, false);
}
}
@ -452,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel()
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
u32 addr = (R15 & ~0x2) + offset;
if (!ARMJIT::LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}
@ -494,11 +495,11 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
Comp_AddCycles_CDI();
int expectedTarget = Num == 0
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
bool compileFastPath = ARMJIT::FastMemory
&& store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
{
s32 offset = decrement
@ -680,7 +681,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (Num == 0)
{
MOV(X3, RCPU);
switch ((u32)store * 2 | NDS::ConsoleType)
switch ((u32)store * 2 | NDS.ConsoleType)
{
case 0: QuickCallFunction(X4, SlowBlockTransfer9<false, 0>); break;
case 1: QuickCallFunction(X4, SlowBlockTransfer9<false, 1>); break;
@ -690,7 +691,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
}
else
{
switch ((u32)store * 2 | NDS::ConsoleType)
switch ((u32)store * 2 | NDS.ConsoleType)
{
case 0: QuickCallFunction(X4, SlowBlockTransfer7<false, 0>); break;
case 1: QuickCallFunction(X4, SlowBlockTransfer7<false, 1>); break;

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -19,6 +19,8 @@
#ifndef ARMJIT_COMPILER_H
#define ARMJIT_COMPILER_H
#ifdef JIT_ENABLED
#if defined(__x86_64__)
#include "ARMJIT_x64/ARMJIT_Compiler.h"
#elif defined(__aarch64__)
@ -27,9 +29,6 @@
#error "The current target platform doesn't have a JIT backend"
#endif
namespace ARMJIT
{
extern Compiler* JITCompiler;
}
#endif
#endif

126
src/ARMJIT_Global.cpp Normal file
View File

@ -0,0 +1,126 @@
#include "ARMJIT_Global.h"
#include "ARMJIT_Memory.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdint.h>
#include <mutex>
namespace melonDS
{
namespace ARMJIT_Global
{
std::mutex globalMutex;
#if defined(__APPLE__) && defined(__aarch64__)
#define APPLE_AARCH64
#endif
#ifndef APPLE_AARCH64
static constexpr size_t NumCodeMemSlices = 4;
static constexpr size_t CodeMemoryAlignedSize = NumCodeMemSlices * CodeMemorySliceSize;
// I haven't heard of pages larger than 16 KB
u8 CodeMemory[CodeMemoryAlignedSize + 16*1024];
u32 AvailableCodeMemSlices = (1 << NumCodeMemSlices) - 1;
u8* GetAlignedCodeMemoryStart()
{
return reinterpret_cast<u8*>((reinterpret_cast<intptr_t>(CodeMemory) + (16*1024-1)) & ~static_cast<intptr_t>(16*1024-1));
}
#endif
int RefCounter = 0;
void* AllocateCodeMem()
{
std::lock_guard guard(globalMutex);
#ifndef APPLE_AARCH64
if (AvailableCodeMemSlices)
{
int slice = __builtin_ctz(AvailableCodeMemSlices);
AvailableCodeMemSlices &= ~(1 << slice);
//printf("allocating slice %d\n", slice);
return &GetAlignedCodeMemoryStart()[slice * CodeMemorySliceSize];
}
#endif
// allocate
#ifdef _WIN32
return VirtualAlloc(nullptr, CodeMemorySliceSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#elif defined(APPLE_AARCH64)
return mmap(NULL, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
#else
//printf("mmaping...\n");
return mmap(nullptr, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
#endif
}
void FreeCodeMem(void* codeMem)
{
std::lock_guard guard(globalMutex);
#ifndef APPLE_AARCH64
for (int i = 0; i < NumCodeMemSlices; i++)
{
if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i])
{
//printf("freeing slice\n");
AvailableCodeMemSlices |= 1 << i;
return;
}
}
#endif
#ifdef _WIN32
VirtualFree(codeMem, CodeMemorySliceSize, MEM_RELEASE|MEM_DECOMMIT);
#else
munmap(codeMem, CodeMemorySliceSize);
#endif
}
void Init()
{
std::lock_guard guard(globalMutex);
RefCounter++;
if (RefCounter == 1)
{
#ifdef _WIN32
DWORD dummy;
VirtualProtect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PAGE_EXECUTE_READWRITE, &dummy);
#elif defined(APPLE_AARCH64)
// Apple aarch64 always uses dynamic allocation
#else
mprotect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
ARMJIT_Memory::RegisterFaultHandler();
}
}
void DeInit()
{
std::lock_guard guard(globalMutex);
RefCounter--;
if (RefCounter == 0)
{
ARMJIT_Memory::UnregisterFaultHandler();
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -16,21 +16,29 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef OSD_H
#define OSD_H
#ifndef ARMJIT_GLOBAL_H
#define ARMJIT_GLOBAL_H
namespace OSD
#include "types.h"
#include <stdlib.h>
namespace melonDS
{
bool Init(bool openGL);
namespace ARMJIT_Global
{
static constexpr size_t CodeMemorySliceSize = 1024*1024*32;
void Init();
void DeInit();
void AddMessage(u32 color, const char* text);
void Update();
void DrawNative(QPainter& painter);
void DrawGL(float w, float h);
void* AllocateCodeMem();
void FreeCodeMem(void* codeMem);
}
#endif // OSD_H
}
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -24,13 +24,17 @@
#include <string.h>
#include <assert.h>
#include "ARMJIT.h"
#include "ARMJIT_Memory.h"
#include "ARM_InstrInfo.h"
#include "JitBlock.h"
#include "TinyVector.h"
namespace melonDS
{
class ARM;
class ARMv5;
// here lands everything which doesn't fit into ARMJIT.h
// where it would be included by pretty much everything
namespace ARMJIT
{
enum
{
@ -69,139 +73,6 @@ struct FetchedInstr
ARMInstrInfo::Info Info;
};
/*
TinyVector
- because reinventing the wheel is the best!
- meant to be used very often, with not so many elements
max 1 << 16 elements
- doesn't allocate while no elements are inserted
- not stl confirmant of course
- probably only works with POD types
- remove operations don't preserve order, but O(1)!
*/
template <typename T>
struct __attribute__((packed)) TinyVector
{
T* Data = NULL;
u16 Capacity = 0;
u16 Length = 0;
~TinyVector()
{
delete[] Data;
}
void MakeCapacity(u32 capacity)
{
assert(capacity <= UINT16_MAX);
assert(capacity > Capacity);
T* newMem = new T[capacity];
if (Data != NULL)
memcpy(newMem, Data, sizeof(T) * Length);
T* oldData = Data;
Data = newMem;
if (oldData != NULL)
delete[] oldData;
Capacity = capacity;
}
void SetLength(u16 length)
{
if (Capacity < length)
MakeCapacity(length);
Length = length;
}
void Clear()
{
Length = 0;
}
void Add(T element)
{
assert(Length + 1 <= UINT16_MAX);
if (Length + 1 > Capacity)
MakeCapacity(((Capacity + 4) * 3) / 2);
Data[Length++] = element;
}
void Remove(int index)
{
assert(Length > 0);
assert(index >= 0 && index < Length);
Length--;
Data[index] = Data[Length];
/*for (int i = index; i < Length; i++)
Data[i] = Data[i + 1];*/
}
int Find(T needle)
{
for (int i = 0; i < Length; i++)
{
if (Data[i] == needle)
return i;
}
return -1;
}
bool RemoveByValue(T needle)
{
for (int i = 0; i < Length; i++)
{
if (Data[i] == needle)
{
Remove(i);
return true;
}
}
return false;
}
T& operator[](int index)
{
assert(index >= 0 && index < Length);
return Data[index];
}
};
class JitBlock
{
public:
JitBlock(u32 num, u32 literalHash, u32 numAddresses, u32 numLiterals)
{
Num = num;
NumAddresses = numAddresses;
NumLiterals = numLiterals;
Data.SetLength(numAddresses * 2 + numLiterals);
}
u32 StartAddr;
u32 StartAddrLocal;
u32 InstrHash, LiteralHash;
u8 Num;
u16 NumAddresses;
u16 NumLiterals;
JitBlockEntry EntryPoint;
u32* AddressRanges()
{ return &Data[0]; }
u32* AddressMasks()
{ return &Data[NumAddresses]; }
u32* Literals()
{ return &Data[NumAddresses * 2]; }
private:
TinyVector<u32> Data;
};
// size should be 16 bytes because I'm to lazy to use mul and whatnot
struct __attribute__((packed)) AddressRange
{
@ -214,13 +85,9 @@ typedef void (*InterpreterFunc)(ARM* cpu);
extern InterpreterFunc InterpretARM[];
extern InterpreterFunc InterpretTHUMB[];
extern TinyVector<u32> InvalidLiterals;
extern AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count];
inline bool PageContainsCode(AddressRange* range)
inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
{
for (int i = 0; i < 8; i++)
for (int i = 0; i < pageSize / 512; i++)
{
if (range[i].Blocks.Length > 0)
return true;
@ -228,11 +95,6 @@ inline bool PageContainsCode(AddressRange* range)
return false;
}
u32 LocaliseCodeAddress(u32 num, u32 addr);
template <u32 Num>
void LinkBlock(ARM* cpu, u32 codeOffset);
template <typename T, int ConsoleType> T SlowRead9(u32 addr, ARMv5* cpu);
template <typename T, int ConsoleType> void SlowWrite9(u32 addr, ARMv5* cpu, u32 val);
template <typename T, int ConsoleType> T SlowRead7(u32 addr);

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -20,62 +20,225 @@
#define ARMJIT_MEMORY
#include "types.h"
#include "MemConstants.h"
#include "ARM.h"
#ifdef JIT_ENABLED
# include <mutex>
# include "TinyVector.h"
# include "ARM.h"
# if defined(__SWITCH__)
# include <switch.h>
# elif defined(_WIN32)
#include <windows.h>
# else
# include <sys/mman.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <signal.h>
# endif
#else
# include <array>
#endif
namespace ARMJIT_Memory
namespace melonDS
{
#ifdef JIT_ENABLED
namespace Platform { struct DynamicLibrary; }
class Compiler;
class ARMJIT;
#endif
extern void* FastMem9Start;
extern void* FastMem7Start;
static constexpr u32 LargePageSize = 0x4000;
static constexpr u32 RegularPageSize = 0x1000;
void Init();
void DeInit();
void Reset();
enum
constexpr u32 RoundUp(u32 size) noexcept
{
memregion_Other = 0,
memregion_ITCM,
memregion_DTCM,
memregion_BIOS9,
memregion_MainRAM,
memregion_SharedWRAM,
memregion_IO9,
memregion_VRAM,
memregion_BIOS7,
memregion_WRAM7,
memregion_IO7,
memregion_Wifi,
memregion_VWRAM,
// DSi
memregion_BIOS9DSi,
memregion_BIOS7DSi,
memregion_NewSharedWRAM_A,
memregion_NewSharedWRAM_B,
memregion_NewSharedWRAM_C,
memregions_Count
};
int ClassifyAddress9(u32 addr);
int ClassifyAddress7(u32 addr);
bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize);
u32 LocaliseAddress(int region, u32 num, u32 addr);
bool IsFastmemCompatible(int region);
void RemapDTCM(u32 newBase, u32 newSize);
void RemapSWRAM();
void RemapNWRAM(int num);
void SetCodeProtection(int region, u32 offset, bool protect);
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size);
return (size + LargePageSize - 1) & ~(LargePageSize - 1);
}
static constexpr u32 MemBlockMainRAMOffset = 0;
static constexpr u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
static constexpr u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
static constexpr u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
static constexpr u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
static constexpr u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
static constexpr u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
static constexpr u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
class ARMJIT_Memory
{
public:
enum
{
memregion_Other = 0,
memregion_ITCM,
memregion_DTCM,
memregion_BIOS9,
memregion_MainRAM,
memregion_SharedWRAM,
memregion_IO9,
memregion_VRAM,
memregion_BIOS7,
memregion_WRAM7,
memregion_IO7,
memregion_Wifi,
memregion_VWRAM,
// DSi
memregion_BIOS9DSi,
memregion_BIOS7DSi,
memregion_NewSharedWRAM_A,
memregion_NewSharedWRAM_B,
memregion_NewSharedWRAM_C,
memregions_Count
};
#ifdef JIT_ENABLED
public:
explicit ARMJIT_Memory(melonDS::NDS& nds);
~ARMJIT_Memory() noexcept;
ARMJIT_Memory(const ARMJIT_Memory&) = delete;
ARMJIT_Memory(ARMJIT_Memory&&) = delete;
ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete;
ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete;
void Reset() noexcept;
void RemapDTCM(u32 newBase, u32 newSize) noexcept;
void RemapSWRAM() noexcept;
void RemapNWRAM(int num) noexcept;
void SetCodeProtection(int region, u32 offset, bool protect) noexcept;
[[nodiscard]] u8* GetMainRAM() noexcept { return MemoryBase + MemBlockMainRAMOffset; }
[[nodiscard]] const u8* GetMainRAM() const noexcept { return MemoryBase + MemBlockMainRAMOffset; }
[[nodiscard]] u8* GetSharedWRAM() noexcept { return MemoryBase + MemBlockSWRAMOffset; }
[[nodiscard]] const u8* GetSharedWRAM() const noexcept { return MemoryBase + MemBlockSWRAMOffset; }
[[nodiscard]] u8* GetARM7WRAM() noexcept { return MemoryBase + MemBlockARM7WRAMOffset; }
[[nodiscard]] const u8* GetARM7WRAM() const noexcept { return MemoryBase + MemBlockARM7WRAMOffset; }
[[nodiscard]] u8* GetARM9DTCM() noexcept { return MemoryBase + MemBlockDTCMOffset; }
[[nodiscard]] const u8* GetARM9DTCM() const noexcept { return MemoryBase + MemBlockDTCMOffset; }
[[nodiscard]] u8* GetNWRAM_A() noexcept { return MemoryBase + MemBlockNWRAM_AOffset; }
[[nodiscard]] const u8* GetNWRAM_A() const noexcept { return MemoryBase + MemBlockNWRAM_AOffset; }
[[nodiscard]] u8* GetNWRAM_B() noexcept { return MemoryBase + MemBlockNWRAM_BOffset; }
[[nodiscard]] const u8* GetNWRAM_B() const noexcept { return MemoryBase + MemBlockNWRAM_BOffset; }
[[nodiscard]] u8* GetNWRAM_C() noexcept { return MemoryBase + MemBlockNWRAM_COffset; }
[[nodiscard]] const u8* GetNWRAM_C() const noexcept { return MemoryBase + MemBlockNWRAM_COffset; }
int ClassifyAddress9(u32 addr) const noexcept;
int ClassifyAddress7(u32 addr) const noexcept;
bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) const noexcept;
u32 LocaliseAddress(int region, u32 num, u32 addr) const noexcept;
bool IsFastmemCompatible(int region) const noexcept;
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept;
bool MapAtAddress(u32 addr) noexcept;
static bool IsFastMemSupported();
static void RegisterFaultHandler();
static void UnregisterFaultHandler();
static u32 PageSize;
static u32 PageShift;
private:
friend class Compiler;
struct Mapping
{
u32 Addr;
u32 Size, LocalOffset;
u32 Num;
void Unmap(int region, NDS& nds) noexcept;
};
struct FaultDescription
{
u32 EmulatedFaultAddr;
u8* FaultPC;
};
static bool FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds);
bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept;
bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept;
void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept;
melonDS::NDS& NDS;
void* FastMem9Start;
void* FastMem7Start;
u8* MemoryBase = nullptr;
#if defined(__SWITCH__)
VirtmemReservation* FastMem9Reservation, *FastMem7Reservation;
u8* MemoryBaseCodeMem;
#elif defined(_WIN32)
struct VirtmemPlaceholder
{
uintptr_t Start;
size_t Size;
};
std::vector<VirtmemPlaceholder> VirtmemPlaceholders;
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
HANDLE MemoryFile = INVALID_HANDLE_VALUE;
#else
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
int MemoryFile = -1;
#endif
#ifdef ANDROID
Platform::DynamicLibrary* Libandroid = nullptr;
#endif
u8 MappingStatus9[1 << (32-12)] {};
u8 MappingStatus7[1 << (32-12)] {};
TinyVector<Mapping> Mappings[memregions_Count] {};
#else
public:
explicit ARMJIT_Memory(melonDS::NDS&) {};
~ARMJIT_Memory() = default;
ARMJIT_Memory(const ARMJIT_Memory&) = delete;
ARMJIT_Memory(ARMJIT_Memory&&) = delete;
ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete;
ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete;
void Reset() noexcept {}
void RemapDTCM(u32 newBase, u32 newSize) noexcept {}
void RemapSWRAM() noexcept {}
void RemapNWRAM(int num) noexcept {}
void SetCodeProtection(int region, u32 offset, bool protect) noexcept {}
[[nodiscard]] u8* GetMainRAM() noexcept { return MainRAM.data(); }
[[nodiscard]] const u8* GetMainRAM() const noexcept { return MainRAM.data(); }
[[nodiscard]] u8* GetSharedWRAM() noexcept { return SharedWRAM.data(); }
[[nodiscard]] const u8* GetSharedWRAM() const noexcept { return SharedWRAM.data(); }
[[nodiscard]] u8* GetARM7WRAM() noexcept { return ARM7WRAM.data(); }
[[nodiscard]] const u8* GetARM7WRAM() const noexcept { return ARM7WRAM.data(); }
[[nodiscard]] u8* GetARM9DTCM() noexcept { return DTCM.data(); }
[[nodiscard]] const u8* GetARM9DTCM() const noexcept { return DTCM.data(); }
[[nodiscard]] u8* GetNWRAM_A() noexcept { return NWRAM_A.data(); }
[[nodiscard]] const u8* GetNWRAM_A() const noexcept { return NWRAM_A.data(); }
[[nodiscard]] u8* GetNWRAM_B() noexcept { return NWRAM_B.data(); }
[[nodiscard]] const u8* GetNWRAM_B() const noexcept { return NWRAM_B.data(); }
[[nodiscard]] u8* GetNWRAM_C() noexcept { return NWRAM_C.data(); }
[[nodiscard]] const u8* GetNWRAM_C() const noexcept { return NWRAM_C.data(); }
private:
std::array<u8, MainRAMMaxSize> MainRAM {};
std::array<u8, ARM7WRAMSize> ARM7WRAM {};
std::array<u8, SharedWRAMSize> SharedWRAM {};
std::array<u8, DTCMPhysicalSize> DTCM {};
std::array<u8, NWRAMSize> NWRAM_A {};
std::array<u8, NWRAMSize> NWRAM_B {};
std::array<u8, NWRAMSize> NWRAM_C {};
#endif
};
}
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -19,16 +19,20 @@
#ifndef ARMJIT_REGCACHE_H
#define ARMJIT_REGCACHE_H
#include "ARMJIT.h"
#include "ARMJIT_Internal.h"
#include "Platform.h"
// TODO: replace this in the future
#include "dolphin/BitSet.h"
#include <assert.h>
namespace ARMJIT
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
using namespace Common;
// Imported inside the namespace so that other headers aren't polluted
template <typename T, typename Reg>
class RegisterCache
@ -80,7 +84,7 @@ public:
}
}
printf("this is a JIT bug! LoadRegister failed\n");
Log(LogLevel::Error, "this is a JIT bug! LoadRegister failed\n");
abort();
}
@ -95,7 +99,7 @@ public:
LiteralsLoaded &= ~(1 << reg);
}
bool IsLiteral(int reg)
bool IsLiteral(int reg) const
{
return LiteralsLoaded & (1 << reg);
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -17,10 +17,11 @@
*/
#include "ARMJIT_Compiler.h"
#include "../ARM.h"
using namespace Gen;
namespace ARMJIT
namespace melonDS
{
// uses RSCRATCH3
@ -128,7 +129,7 @@ OpArg Compiler::A_Comp_GetALUOp2(bool S, bool& carryUsed)
Comp_AddCycles_C();
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift);
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
carryUsed = false;
if (S && shift)
@ -227,7 +228,7 @@ void Compiler::A_Comp_Arith()
Comp_ArithTriOp(&Compiler::AND, rd, rn, op2, carryUsed, sFlag|opSymmetric|opInvertOp2);
break;
default:
printf("this is a JIT bug! %04x\n", op);
Log(LogLevel::Error, "this is a JIT bug! %04x\n", op);
abort();
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -17,10 +17,12 @@
*/
#include "ARMJIT_Compiler.h"
#include "../ARM.h"
#include "../NDS.h"
using namespace Gen;
namespace ARMJIT
namespace melonDS
{
template <typename T>
@ -119,7 +121,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1];
cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1];
CurCPU->R[15] = compileTimePC;
}
@ -131,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
u32 compileTimePC = CurCPU->R[15];
CurCPU->R[15] = newPC;
cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3];
cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3];
CurCPU->R[15] = compileTimePC;
}
@ -174,9 +176,9 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
else
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
if (Num == 0)
CALL((void*)&ARMv5JumpToTrampoline);
ABI_CallFunction(ARMv5JumpToTrampoline);
else
CALL((void*)&ARMv4JumpToTrampoline);
ABI_CallFunction(ARMv4JumpToTrampoline);
PopRegs(restoreCPSR, true);
@ -247,7 +249,7 @@ void Compiler::T_Comp_BranchXchangeReg()
{
if (Num == 1)
{
printf("BLX unsupported on ARM7!!!\n");
Log(LogLevel::Warn, "BLX unsupported on ARM7!!!\n");
return;
}
MOV(32, R(RSCRATCH), MapReg(CurInstr.A_Reg(3)));

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -18,25 +18,22 @@
#include "ARMJIT_Compiler.h"
#include "../ARMJIT.h"
#include "../ARMInterpreter.h"
#include "../NDS.h"
#include "../ARMJIT_Global.h"
#include <assert.h>
#include <stdarg.h>
#include "../dolphin/CommonFuncs.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
using namespace Gen;
using namespace Common;
extern "C" void ARM_Ret();
namespace ARMJIT
namespace melonDS
{
template <>
const X64Reg RegisterCache<Compiler, X64Reg>::NativeRegAllocOrder[] =
@ -140,7 +137,7 @@ void Compiler::A_Comp_MSR()
Comp_AddCycles_C();
OpArg val = CurInstr.Instr & (1 << 25)
? Imm32(::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
? Imm32(melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
: MapReg(CurInstr.A_Reg(0));
u32 mask = 0;
@ -219,46 +216,21 @@ void Compiler::A_Comp_MSR()
MOV(32, R(ABI_PARAM3), R(RCPSR));
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
MOV(64, R(ABI_PARAM1), R(RCPU));
CALL((void*)&UpdateModeTrampoline);
ABI_CallFunction(UpdateModeTrampoline);
PopRegs(true, true);
}
}
}
/*
We'll repurpose this .bss memory
*/
u8 CodeMemory[1024 * 1024 * 32];
Compiler::Compiler()
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
{
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
ARMJIT_Global::Init();
u64 pageSize = (u64)sysInfo.dwPageSize;
#else
u64 pageSize = sysconf(_SC_PAGE_SIZE);
#endif
CodeMemBase = static_cast<u8*>(ARMJIT_Global::AllocateCodeMem());
CodeMemSize = ARMJIT_Global::CodeMemorySliceSize;
u8* pageAligned = (u8*)(((u64)CodeMemory & ~(pageSize - 1)) + pageSize);
u64 alignedSize = (((u64)CodeMemory + sizeof(CodeMemory)) & ~(pageSize - 1)) - (u64)pageAligned;
#ifdef _WIN32
DWORD dummy;
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
#elif defined(__APPLE__)
pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
ResetStart = pageAligned;
CodeMemSize = alignedSize;
}
ResetStart = CodeMemBase;
Reset();
@ -472,6 +444,13 @@ Compiler::Compiler()
FarSize = (ResetStart + CodeMemSize) - FarStart;
}
Compiler::~Compiler()
{
ARMJIT_Global::FreeCodeMem(CodeMemBase);
ARMJIT_Global::DeInit();
}
void Compiler::LoadCPSR()
{
assert(!CPSRDirty);
@ -648,7 +627,7 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
};
#undef F
bool Compiler::CanCompile(bool thumb, u16 kind)
bool Compiler::CanCompile(bool thumb, u16 kind) const
{
return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL;
}
@ -664,7 +643,7 @@ void Compiler::Reset()
LoadStorePatches.clear();
}
bool Compiler::IsJITFault(u8* addr)
bool Compiler::IsJITFault(const u8* addr)
{
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
}
@ -681,7 +660,7 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)&ARM_Ret, true);
ABI_TailCall(ARM_Ret);
}
}
@ -711,13 +690,13 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
{
if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess...
{
printf("near reset\n");
ResetBlockCache();
Log(LogLevel::Debug, "near reset\n");
NDS.JIT.ResetBlockCache();
}
if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess...
{
printf("far reset\n");
ResetBlockCache();
Log(LogLevel::Debug, "far reset\n");
NDS.JIT.ResetBlockCache();
}
ConstantCycles = 0;
@ -843,7 +822,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)ARM_Ret, true);
ABI_TailCall(ARM_Ret);
#ifdef JIT_PROFILING_ENABLED
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
@ -861,7 +840,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
{
s32 cycles = Num ?
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
if ((!Thumb && CurInstr.Cond() < 0xE) || forceNonConstant)
@ -873,7 +852,7 @@ void Compiler::Comp_AddCycles_C(bool forceNonConstant)
void Compiler::Comp_AddCycles_CI(u32 i)
{
s32 cycles = (Num ?
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + i;
if (!Thumb && CurInstr.Cond() < 0xE)
@ -885,7 +864,7 @@ void Compiler::Comp_AddCycles_CI(u32 i)
void Compiler::Comp_AddCycles_CI(Gen::X64Reg i, int add)
{
s32 cycles = Num ?
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
if (!Thumb && CurInstr.Cond() < 0xE)
@ -910,7 +889,7 @@ void Compiler::Comp_AddCycles_CDI()
s32 cycles;
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
@ -955,7 +934,7 @@ void Compiler::Comp_AddCycles_CD()
}
else
{
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
s32 numD = CurInstr.DataCycles;
if ((CurInstr.DataRegion >> 4) == 0x02)

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -19,9 +19,10 @@
#ifndef ARMJIT_X64_COMPILER_H
#define ARMJIT_X64_COMPILER_H
#if defined(JIT_ENABLED) && defined(__x86_64__)
#include "../dolphin/x64Emitter.h"
#include "../ARMJIT.h"
#include "../ARMJIT_Internal.h"
#include "../ARMJIT_RegisterCache.h"
@ -31,9 +32,12 @@
#include <unordered_map>
namespace ARMJIT
{
namespace melonDS
{
class ARMJIT;
class ARMJIT_Memory;
class NDS;
const Gen::X64Reg RCPU = Gen::RBP;
const Gen::X64Reg RCPSR = Gen::R15;
@ -79,7 +83,8 @@ struct Op2
class Compiler : public Gen::XEmitter
{
public:
Compiler();
explicit Compiler(melonDS::NDS& nds);
~Compiler();
void Reset();
@ -88,7 +93,7 @@ public:
void LoadReg(int reg, Gen::X64Reg nativeReg);
void SaveReg(int reg, Gen::X64Reg nativeReg);
bool CanCompile(bool thumb, u16 kind);
bool CanCompile(bool thumb, u16 kind) const;
typedef void (Compiler::*CompileFunc)();
@ -167,7 +172,7 @@ public:
memop_SubtractOffset = 1 << 4
};
void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags);
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
s32 Comp_MemAccessBlock(int rn, Common::BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
@ -230,7 +235,7 @@ public:
SetCodePtr(FarCode);
}
bool IsJITFault(u8* addr);
bool IsJITFault(const u8* addr);
u8* RewriteMemAccess(u8* pc);
@ -238,44 +243,47 @@ public:
void CreateMethod(const char* namefmt, void* start, ...);
#endif
u8* FarCode;
u8* NearCode;
u32 FarSize;
u32 NearSize;
melonDS::NDS& NDS;
u8* FarCode {};
u8* NearCode {};
u32 FarSize {};
u32 NearSize {};
u8* NearStart;
u8* FarStart;
u8* NearStart {};
u8* FarStart {};
void* PatchedStoreFuncs[2][2][3][16];
void* PatchedLoadFuncs[2][2][3][2][16];
void* PatchedStoreFuncs[2][2][3][16] {};
void* PatchedLoadFuncs[2][2][3][2][16] {};
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches;
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
u8* ResetStart;
u32 CodeMemSize;
u8* CodeMemBase;
u8* ResetStart {};
u32 CodeMemSize {};
bool Exit;
bool IrregularCycles;
bool Exit {};
bool IrregularCycles {};
void* ReadBanked;
void* WriteBanked;
void* ReadBanked {};
void* WriteBanked {};
bool CPSRDirty = false;
FetchedInstr CurInstr;
FetchedInstr CurInstr {};
RegisterCache<Compiler, Gen::X64Reg> RegCache;
RegisterCache<Compiler, Gen::X64Reg> RegCache {};
bool Thumb;
u32 Num;
u32 R15;
u32 CodeRegion;
bool Thumb {};
u32 Num {};
u32 R15 {};
u32 CodeRegion {};
u32 ConstantCycles;
u32 ConstantCycles {};
ARM* CurCPU;
ARM* CurCPU {};
};
}
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team, RSDuck
Copyright 2016-2024 melonDS team, RSDuck
This file is part of melonDS.
@ -17,7 +17,7 @@
*/
#include "../ARM.h"
using namespace melonDS;
int main(int argc, char* argv[])
{
FILE* f = fopen("ARMJIT_Offsets.h", "w");

View File

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

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -17,10 +17,12 @@
*/
#include "ARMJIT_Compiler.h"
#include "../ARMJIT.h"
#include "../NDS.h"
using namespace Gen;
namespace ARMJIT
namespace melonDS
{
template <typename T>
@ -51,7 +53,7 @@ u8* Compiler::RewriteMemAccess(u8* pc)
return pc + (ptrdiff_t)patch.Offset;
}
printf("this is a JIT bug %sx\n", pc);
Log(LogLevel::Error, "this is a JIT bug %sx\n", pc);
abort();
}
@ -67,9 +69,9 @@ u8* Compiler::RewriteMemAccess(u8* pc)
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
{
u32 localAddr = LocaliseCodeAddress(Num, addr);
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
if (invalidLiteralIdx != -1)
{
return false;
@ -84,7 +86,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
if (size == 32)
{
CurCPU->DataRead32(addr & ~0x3, &val);
val = ::ROR(val, (addr & 0x3) << 3);
val = melonDS::ROR(val, (addr & 0x3) << 3);
}
else if (size == 16)
{
@ -117,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (size == 16)
addressMask = ~1;
if (LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
{
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
@ -134,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
Comp_AddCycles_CDI();
}
bool addrIsStatic = LiteralOptimizations
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
u32 staticAddress;
if (addrIsStatic)
@ -195,10 +197,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
MOV(32, rnMapped, R(finalAddr));
u32 expectedTarget = Num == 0
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
{
if (rdMapped.IsImm())
{
@ -211,12 +213,12 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
assert(rdMapped.GetSimpleReg() >= 0 && rdMapped.GetSimpleReg() < 16);
patch.PatchFunc = flags & memop_Store
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()]
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()];
? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()]
: PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()];
assert(patch.PatchFunc != NULL);
MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
X64Reg maskedAddr = RSCRATCH3;
if (size > 8)
@ -267,7 +269,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
void* func = NULL;
if (addrIsStatic)
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
if (func)
{
@ -312,26 +314,26 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
MOV(32, R(ABI_PARAM1), R(RSCRATCH3));
if (flags & memop_Store)
{
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: CALL((void*)&SlowWrite9<u32, 0>); break;
case 16: CALL((void*)&SlowWrite9<u16, 0>); break;
case 8: CALL((void*)&SlowWrite9<u8, 0>); break;
case 33: CALL((void*)&SlowWrite9<u32, 1>); break;
case 17: CALL((void*)&SlowWrite9<u16, 1>); break;
case 9: CALL((void*)&SlowWrite9<u8, 1>); break;
case 32: ABI_CallFunction(SlowWrite9<u32, 0>); break;
case 16: ABI_CallFunction(SlowWrite9<u16, 0>); break;
case 8: ABI_CallFunction(&SlowWrite9<u8, 0>); break;
case 33: ABI_CallFunction(&SlowWrite9<u32, 1>); break;
case 17: ABI_CallFunction(&SlowWrite9<u16, 1>); break;
case 9: ABI_CallFunction(&SlowWrite9<u8, 1>); break;
}
}
else
{
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: CALL((void*)&SlowRead9<u32, 0>); break;
case 16: CALL((void*)&SlowRead9<u16, 0>); break;
case 8: CALL((void*)&SlowRead9<u8, 0>); break;
case 33: CALL((void*)&SlowRead9<u32, 1>); break;
case 17: CALL((void*)&SlowRead9<u16, 1>); break;
case 9: CALL((void*)&SlowRead9<u8, 1>); break;
case 32: ABI_CallFunction(&SlowRead9<u32, 0>); break;
case 16: ABI_CallFunction(&SlowRead9<u16, 0>); break;
case 8: ABI_CallFunction(&SlowRead9<u8, 0>); break;
case 33: ABI_CallFunction(&SlowRead9<u32, 1>); break;
case 17: ABI_CallFunction(&SlowRead9<u16, 1>); break;
case 9: ABI_CallFunction(&SlowRead9<u8, 1>); break;
}
}
}
@ -343,26 +345,26 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
{
MOV(32, R(ABI_PARAM2), rdMapped);
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: CALL((void*)&SlowWrite7<u32, 0>); break;
case 16: CALL((void*)&SlowWrite7<u16, 0>); break;
case 8: CALL((void*)&SlowWrite7<u8, 0>); break;
case 33: CALL((void*)&SlowWrite7<u32, 1>); break;
case 17: CALL((void*)&SlowWrite7<u16, 1>); break;
case 9: CALL((void*)&SlowWrite7<u8, 1>); break;
case 32: ABI_CallFunction(&SlowWrite7<u32, 0>); break;
case 16: ABI_CallFunction(&SlowWrite7<u16, 0>); break;
case 8: ABI_CallFunction(&SlowWrite7<u8, 0>); break;
case 33: ABI_CallFunction(&SlowWrite7<u32, 1>); break;
case 17: ABI_CallFunction(&SlowWrite7<u16, 1>); break;
case 9: ABI_CallFunction(&SlowWrite7<u8, 1>); break;
}
}
else
{
switch (size | NDS::ConsoleType)
switch (size | NDS.ConsoleType)
{
case 32: CALL((void*)&SlowRead7<u32, 0>); break;
case 16: CALL((void*)&SlowRead7<u16, 0>); break;
case 8: CALL((void*)&SlowRead7<u8, 0>); break;
case 33: CALL((void*)&SlowRead7<u32, 1>); break;
case 17: CALL((void*)&SlowRead7<u16, 1>); break;
case 9: CALL((void*)&SlowRead7<u8, 1>); break;
case 32: ABI_CallFunction(&SlowRead7<u32, 0>); break;
case 16: ABI_CallFunction(&SlowRead7<u16, 0>); break;
case 8: ABI_CallFunction(&SlowRead7<u8, 0>); break;
case 33: ABI_CallFunction(&SlowRead7<u32, 1>); break;
case 17: ABI_CallFunction(&SlowRead7<u16, 1>); break;
case 9: ABI_CallFunction(&SlowRead7<u8, 1>); break;
}
}
}
@ -382,7 +384,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
if (!(flags & memop_Store) && rd == 15)
{
if (size < 32)
printf("!!! LDR <32 bit PC %08X %x\n", R15, CurInstr.Instr);
Log(LogLevel::Debug, "!!! LDR <32 bit PC %08X %x\n", R15, CurInstr.Instr);
{
if (Num == 1)
{
@ -421,16 +423,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
s32 offset = (regsCount * 4) * (decrement ? -1 : 1);
int expectedTarget = Num == 0
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
if (!store)
Comp_AddCycles_CDI();
else
Comp_AddCycles_CD();
bool compileFastPath = FastMemory
&& !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
&& !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
// we need to make sure that the stack stays aligned to 16 bytes
#ifdef _WIN32
@ -453,7 +455,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
u8* fastPathStart = GetWritableCodePtr();
u8* loadStoreAddr[16];
MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
ADD(64, R(RSCRATCH2), R(RSCRATCH4));
u32 offset = 0;
@ -522,12 +524,12 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (Num == 0)
MOV(64, R(ABI_PARAM4), R(RCPU));
switch (Num * 2 | NDS::ConsoleType)
switch (Num * 2 | NDS.ConsoleType)
{
case 0: CALL((void*)&SlowBlockTransfer9<false, 0>); break;
case 1: CALL((void*)&SlowBlockTransfer9<false, 1>); break;
case 2: CALL((void*)&SlowBlockTransfer7<false, 0>); break;
case 3: CALL((void*)&SlowBlockTransfer7<false, 1>); break;
case 0: ABI_CallFunction(&SlowBlockTransfer9<false, 0>); break;
case 1: ABI_CallFunction(&SlowBlockTransfer9<false, 1>); break;
case 2: ABI_CallFunction(&SlowBlockTransfer7<false, 0>); break;
case 3: ABI_CallFunction(&SlowBlockTransfer7<false, 1>); break;
}
PopRegs(false, false);
@ -626,12 +628,12 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
if (Num == 0)
MOV(64, R(ABI_PARAM4), R(RCPU));
switch (Num * 2 | NDS::ConsoleType)
switch (Num * 2 | NDS.ConsoleType)
{
case 0: CALL((void*)&SlowBlockTransfer9<true, 0>); break;
case 1: CALL((void*)&SlowBlockTransfer9<true, 1>); break;
case 2: CALL((void*)&SlowBlockTransfer7<true, 0>); break;
case 3: CALL((void*)&SlowBlockTransfer7<true, 1>); break;
case 0: ABI_CallFunction(&SlowBlockTransfer9<true, 0>); break;
case 1: ABI_CallFunction(&SlowBlockTransfer9<true, 1>); break;
case 2: ABI_CallFunction(&SlowBlockTransfer7<true, 0>); break;
case 3: ABI_CallFunction(&SlowBlockTransfer7<true, 1>); break;
}
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
@ -807,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel()
{
u32 offset = (CurInstr.Instr & 0xFF) << 2;
u32 addr = (R15 & ~0x2) + offset;
if (!LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
}

View File

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

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2022 melonDS team
Copyright 2016-2024 melonDS team
This file is part of melonDS.
@ -22,7 +22,7 @@
#include "ARMJIT.h"
namespace ARMInstrInfo
namespace melonDS::ARMInstrInfo
{
#define ak(x) ((x) << 23)
@ -315,7 +315,7 @@ const u32 T_SVC = T_BranchAlways | T_WriteR14 | tk(tk_SVC);
#include "ARM_InstrTable.h"
#undef INSTRFUNC_PROTO
Info Decode(bool thumb, u32 num, u32 instr)
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
{
const u8 FlagsReadPerCond[7] = {
flag_Z,
@ -386,7 +386,7 @@ Info Decode(bool thumb, u32 num, u32 instr)
{
if (res.Kind == tk_LDR_PCREL)
{
if (!ARMJIT::LiteralOptimizations)
if (!literaloptimizations)
res.SrcRegs |= 1 << 15;
res.SpecialKind = special_LoadLiteral;
}

View File

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

View File

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

140
src/Args.h Normal file
View File

@ -0,0 +1,140 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MELONDS_ARGS_H
#define MELONDS_ARGS_H
#include <array>
#include <optional>
#include <memory>
#include "NDSCart.h"
#include "GBACart.h"
#include "types.h"
#include "MemConstants.h"
#include "DSi_NAND.h"
#include "FATStorage.h"
#include "FreeBIOS.h"
#include "GPU3D_Soft.h"
#include "SPI_Firmware.h"
#include "SPU.h"
namespace melonDS
{
namespace NDSCart { class CartCommon; }
namespace GBACart { class CartCommon; }
template<size_t N>
constexpr std::array<u8, N> BrokenBIOS = []() constexpr {
std::array<u8, N> broken {};
for (int i = 0; i < 16; i++)
{
broken[i*4+0] = 0xE7;
broken[i*4+1] = 0xFF;
broken[i*4+2] = 0xDE;
broken[i*4+3] = 0xFF;
}
return broken;
}();
/// Arguments that configure the JIT.
/// Ignored in builds that don't have the JIT included.
struct JITArgs
{
unsigned MaxBlockSize = 32;
bool LiteralOptimizations = true;
bool BranchOptimizations = true;
/// Ignored in builds that have fast memory excluded
/// (even if the JIT is otherwise available).
/// Enabled by default, but frontends should disable this when debugging
/// so the constants segfaults don't hinder debugging.
bool FastMemory = true;
};
using ARM9BIOSImage = std::array<u8, ARM9BIOSSize>;
using ARM7BIOSImage = std::array<u8, ARM7BIOSSize>;
using DSiBIOSImage = std::array<u8, DSiBIOSSize>;
struct GDBArgs
{
u16 PortARM7 = 0;
u16 PortARM9 = 0;
bool ARM7BreakOnStartup = false;
bool ARM9BreakOnStartup = false;
};
/// Arguments to pass into the NDS constructor.
/// New fields here should have default values if possible.
struct NDSArgs
{
/// NDS ARM9 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
/// NDS ARM7 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::unique_ptr<ARM7BIOSImage> ARM7BIOS = std::make_unique<ARM7BIOSImage>(bios_arm7_bin);
/// Firmware image to install.
/// Defaults to generated NDS firmware.
/// Generated firmware is not compatible with DSi mode.
melonDS::Firmware Firmware {0};
/// How the JIT should be configured when initializing.
/// Defaults to enabled, with default settings.
/// To disable the JIT, set this to std::nullopt.
/// Ignored in builds that don't have the JIT included.
std::optional<JITArgs> JIT = JITArgs();
AudioBitDepth BitDepth = AudioBitDepth::Auto;
AudioInterpolation Interpolation = AudioInterpolation::None;
/// How the GDB stub should be handled.
/// Defaults to disabled.
/// Ignored in builds that don't have the GDB stub included.
std::optional<GDBArgs> GDB = std::nullopt;
/// The 3D renderer to initialize the DS with.
/// Defaults to the software renderer.
/// Can be changed later at any time.
std::unique_ptr<melonDS::Renderer3D> Renderer3D = std::make_unique<SoftRenderer>();
};
/// Arguments to pass into the DSi constructor.
/// New fields here should have default values if possible.
/// Contains no virtual methods, so there's no vtable.
struct DSiArgs final : public NDSArgs
{
std::unique_ptr<DSiBIOSImage> ARM9iBIOS = std::make_unique<DSiBIOSImage>(BrokenBIOS<DSiBIOSSize>);
std::unique_ptr<DSiBIOSImage> ARM7iBIOS = std::make_unique<DSiBIOSImage>(BrokenBIOS<DSiBIOSSize>);
/// NAND image to install.
/// Required, there is no default value.
DSi_NAND::NANDImage NANDImage;
/// SD card to install.
/// Defaults to std::nullopt, which means no SD card.
std::optional<FATStorage> DSiSDCard;
bool FullBIOSBoot = false;
};
}
#endif //MELONDS_ARGS_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

243
src/DMA_Timings.cpp Normal file
View File

@ -0,0 +1,243 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include "DMA_Timings.h"
#include "types.h"
namespace melonDS::DMATiming
{
// DMA timing tables
//
// DMA timings on the DS are normally straightforward, except in one case: when
// main RAM is involved.
// Main RAM to main RAM is the easy case: 16c/unit in 16bit mode, 18c/unit in 32bit
// mode.
// It gets more complicated when transferring from main RAM to somewhere else, or
// vice versa: main RAM supports burst accesses, but the rules dictating how long
// bursts can be are weird and inconsistent. Main RAM also supports parallel
// memory operations, to some extent.
// I haven't figured out the full logic behind it, let alone how to emulate it
// efficiently, so for now we will use these tables.
// A zero denotes the end of a burst pattern.
//
// Note: burst patterns only apply when the main RAM address is incrementing.
// A fixed or decrementing address results in nonsequential accesses.
//
// Note about GBA slot/wifi timings: these take into account the sequential timing
// setting. Timings are such that the nonseq setting only matters for the first
// access, and minor edge cases (like the last of a 0x20000-byte block).
extern const std::array<u8, 256> MRAMDummy = {0};
extern const std::array<u8, 256> MRAMRead16Bursts[] =
{
// main RAM to regular 16bit or 32bit bus (similar)
{7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2,
7, 3,
0},
// main RAM to GBA/wifi, seq=4
{8, 6, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
0},
// main RAM to GBA/wifi, seq=6
{10, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7,
12, 8,
0},
};
extern const std::array<u8, 256> MRAMRead32Bursts[] =
{
// main RAM to regular 16bit bus
{9, 4, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 9,
0},
// main RAM to regular 32bit bus
{9, 3, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2,
0},
// main RAM to GBA/wifi, seq=4
{14, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13, 10, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9,
13,
0},
// main RAM to GBA/wifi, seq=6
{18, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17, 14, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13,
17,
0},
};
extern const std::array<u8, 256> MRAMWrite16Bursts[] =
{
// regular 16bit or 32bit bus to main RAM (similar)
{8, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
0},
// GBA/wifi to main RAM, seq=4
{10, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
0},
// GBA/wifi to main RAM, seq=6
{9, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7,
0},
};
extern const std::array<u8, 256> MRAMWrite32Bursts[4] =
{
// regular 16bit bus to main RAM
{9, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
0},
// regular 32bit bus to main RAM
{9, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
0},
// GBA/wifi to main RAM, seq=4
{15, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10,
0},
// GBA/wifi to main RAM, seq=6
{16, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14,
0},
};
}

View File

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

File diff suppressed because it is too large Load Diff

248
src/DSi.h
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

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