Compare commits

..

1 Commits

Author SHA1 Message Date
xbzk a9cabafbdf payload based fence sync 2026-03-31 01:14:16 +02:00
90 changed files with 3275 additions and 5236 deletions
@@ -3,7 +3,8 @@ name: tx-pull
on:
# monday, wednesday, saturday at 2pm
schedule:
cron: '0 14 * * 1,3,6'
cron:
- '0 14 * * 1,3,6'
workflow_dispatch:
jobs:
@@ -58,3 +59,4 @@ jobs:
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
-H 'Content-Type: application/json' \
-d "@data.json" --fail
+4 -4
View File
@@ -37,10 +37,10 @@ set(GIT_DESC ${BUILD_VERSION})
# Auto-updater metadata! Must somewhat mirror GitHub API endpoint
if (NIGHTLY_BUILD)
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API "git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API_PATH "/api/v1/repos/")
set(BUILD_AUTO_UPDATE_REPO "eden-ci/nightly")
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
set(BUILD_AUTO_UPDATE_API "api.github.com")
set(BUILD_AUTO_UPDATE_API_PATH "/repos/")
set(BUILD_AUTO_UPDATE_REPO "Eden-CI/Nightly")
set(REPO_NAME "Eden Nightly")
else()
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
+212 -71
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 256 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 KiB

After

Width:  |  Height:  |  Size: 335 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 19 KiB

-1
View File
@@ -1 +0,0 @@
#43fcfcff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 35 KiB

+1 -1
View File
@@ -334,7 +334,7 @@ pacman -Syuu --needed --noconfirm $packages
<summary>HaikuOS</summary>
```sh
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
```
[Caveats](./Caveats.md#haikuos).
+15 -35
View File
@@ -56,7 +56,7 @@ Use this when you need to connect to a multiplayer room for LDN functionality in
- Multiplayer Options Configured in Eden Settings
- Network Access
### Steps
## Steps
There are 2 primary methods that you can use to connect to an existing room, depending on how the room is hosted.
- Joining a Public Lobby
@@ -70,7 +70,7 @@ There are 2 primary methods that you can use to connect to an existing room, dep
</aside>
## Joining a Public Lobby
### Joining a Public Lobby
1. Open Eden and navigate to *Multiplayer → Browse Public Game Lobby*.
2. The **Public Room Browser** will now open and display a list of publicly accessible rooms. Find one you want to connect to and double click it.
@@ -90,7 +90,7 @@ If the hoster has not made the lobby public, or you don't want to find it in the
---
## Hosting a Multiplayer Room
# Hosting a Multiplayer Room
Use this guide for when you want to host a multiplayer lobby to play with others in Eden. In order to have someone access the room from outside your local network, see the *Access Your Multiplayer Room Externally* section for next steps.
**Click [Here](https://evilperson1337.notion.site/Hosting-a-Multiplayer-Room-2c357c2edaf6819481dbe8a99926cea2) for a version of this guide with images & visual elements.**
@@ -100,7 +100,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
- Network Access
- Ability to allow programs through the firewall on your device.
### Steps
## Steps
1. Open Eden and navigate to *Emulation → Multiplayer → Create Room.*
2. Fill out the following information in the popup dialog box.
@@ -120,7 +120,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
---
## Access Your Multiplayer Room Externally
# Access Your Multiplayer Room Externally
Quite often the person with whom you want to play is located off of your internal network (LAN). If you want to host a room and play with them you will need to get your devices to communicate with each other. This guide will go over your options on how to do this so that you can play together.
**Click [Here](https://evilperson1337.notion.site/Access-Your-Multiplayer-Room-Externally-2c357c2edaf681c0ab2ce2ee624d809d) for a version of this guide with images & visual elements.**
@@ -129,9 +129,9 @@ Quite often the person with whom you want to play is located off of your interna
- Eden set up and Functioning
- Network Access
### Options
## Options
#### Port Forwarding
### Port Forwarding
- **Difficulty Level**: High
@@ -148,9 +148,8 @@ The process works by creating a static mapping—often called a “port-forward
For our purposes we would pick the port we want to expose (*e.g. 24872*) and we would access our router's configuration and create a port-forward rule to send the traffic from an external connection to your local machine over our specified port (*24872)*. The exact way to do so, varies greatly by router manufacturer - and sometimes require contacting your ISP to do so depending on your agreement. You can look up your router on [*portforward.com*](https://portforward.com/router.htm) which may have instructions on how to do so for your specific equipment. If it is not there, you will have to use Google/ChatGPT to determine the steps for your equipment.
Remember you can't have one port open for multiple devices at the same time - you must only host from one device (or do more convoluted networking which we will not cover here).
#### Use a Tunnelling Service
### Use a Tunnelling Service
- **Difficulty Level**: Easy
<aside>
@@ -168,7 +167,7 @@ For our purposes we would spawn the listener for the port that way chose when ho
- [*Playit.GG*](https://playit.gg/)
#### Use a VPN Service
### Use a VPN Service
- **Difficulty**: Easy
@@ -190,7 +189,7 @@ The VPN solution is a good compromise between the tunnelling solution and port f
---
## Finding the Server Information for a Multiplayer Room
# Finding the Server Information for a Multiplayer Room
Use this guide when you need to determine the connection information for the Public Multiplayer Lobby you are connected to.
**Click [Here](https://evilperson1337.notion.site/Finding-the-Server-Information-for-a-Multiplayer-Room-2c557c2edaf6809e94e8ed3429b9eb26) for a version of this guide with images & visual elements.**
@@ -199,7 +198,7 @@ Use this guide when you need to determine the connection information for the Pub
- Eden set up and configured
- Internet Access
### Steps
## Steps
### Method 1: Grabbing the Address from the Log File
1. Open Eden and Connect to the room you want to identify.
@@ -223,7 +222,7 @@ Use this guide when you need to determine the connection information for the Pub
2. Open the terminal supported by your operating system.
3. Run one of the following commands, replacing *<Name>* with the name of the server from step 1.
#### PowerShell Command [Windows Users]
### PowerShell Command [Windows Users]
```powershell
# Calls the API to get the address and port information
@@ -236,7 +235,7 @@ Use this guide when you need to determine the connection information for the Pub
#}
```
#### CURL Command [MacOS/Linux Users] **Requires jq*
### CURL Command [MacOS/Linux Users] **Requires jq*
```bash
# Calls the API to get the address and port information
@@ -253,7 +252,7 @@ Use this guide when you need to determine the connection information for the Pub
---
## Multiplayer for Local Co-Op Games
# Multiplayer for Local Co-Op Games
Use this guide when you want to play with a friend on a different system for games that only support local co-op.
**Click [Here](https://evilperson1337.notion.site/Multiplayer-for-Local-Co-Op-Games-2c657c2edaf680c59975ec6b52022a2d) for a version of this guide with images & visual elements.**
@@ -272,7 +271,7 @@ In either situation at its core, we are emulating an input device on the host ma
- Parsec is free to use for personal, non-commercial use. For instructions on how to set up an account and install the client you should refer to the Parsec documentation on it's site.
- Parsec client installed on your machine and remote (friend's) machine
### Steps
## Steps
<aside>
@@ -295,22 +294,3 @@ This guide will assume you are the one hosting the game and go over things *Pars
10. Set up the remote player's controller.
11. Hit **OK** to apply the changes.
12. Launch the game you want to play and enter the co-op mode. How this works depends on the game, so you will have to look in the menus or online to find out.
## Metaserver troubleshooting
If you can't connect to the metaserver, it's likely your ISP is blocking the requests.
### Linux and Steamdeck
Most Linux systems and Steamdeck should allow to modify the base `/etc/hosts` file, this should fix the DNS lookup issue; hence add the following to said file:
```
28.165.181.135 api.ynet-fun.xyz api.ynet-fun.xyz
```
### Zapret
In `lists/list-general.txt` add the following:
```
api.ynet-fun.xyz
ynet-fun.xyz
```
@@ -25,11 +25,6 @@ import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.navigation.NavOptions
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.utils.CustomSettingsHandler
import android.util.Rational
import android.view.InputDevice
import android.view.KeyEvent
@@ -92,28 +87,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
private val emulationViewModel: EmulationViewModel by viewModels()
private var foregroundService: Intent? = null
private val mainHandler = Handler(Looper.getMainLooper())
private var pendingRomSwapIntent: Intent? = null
private var isWaitingForRomSwapStop = false
private var romSwapNativeStopped = false
private var romSwapThreadStopped = false
private var romSwapGeneration = 0
private var hasEmulationSession = processHasEmulationSession
private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() }
private fun onRomSwapStopTimeout() {
if (!isWaitingForRomSwapStop) {
return
}
Log.warning("[EmulationActivity] ROM swap stop timed out; retrying native stop and continuing to wait")
NativeLibrary.stopEmulation()
scheduleRomSwapStopTimeout()
}
private fun scheduleRomSwapStopTimeout() {
mainHandler.removeCallbacks(romSwapStopTimeoutRunnable)
mainHandler.postDelayed(romSwapStopTimeoutRunnable, ROM_SWAP_STOP_TIMEOUT_MS)
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
@@ -155,29 +128,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
binding = ActivityEmulationBinding.inflate(layoutInflater)
setContentView(binding.root)
val launchIntent = Intent(intent)
val shouldDeferLaunchForSwap = hasEmulationSession && isSwapIntent(launchIntent)
if (shouldDeferLaunchForSwap) {
Log.info("[EmulationActivity] onCreate detected existing session; deferring new game setup for swap")
emulationViewModel.setIsEmulationStopping(true)
emulationViewModel.setEmulationStopped(false)
}
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
val initialArgs = if (shouldDeferLaunchForSwap) {
Bundle(intent.extras ?: Bundle()).apply {
processSessionGame?.let { putParcelable("game", it) }
}
} else {
intent.extras
}
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, initialArgs)
if (shouldDeferLaunchForSwap) {
mainHandler.post {
handleSwapIntent(launchIntent)
}
}
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
isActivityRecreated = savedInstanceState != null
@@ -257,7 +210,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
}
override fun onDestroy() {
mainHandler.removeCallbacks(romSwapStopTimeoutRunnable)
super.onDestroy()
inputManager.unregisterInputDeviceListener(this)
stopForegroundService(this)
@@ -276,121 +228,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleSwapIntent(intent)
nfcReader.onNewIntent(intent)
InputHandler.updateControllerData()
}
setIntent(intent)
private fun isSwapIntent(intent: Intent): Boolean {
return when {
intent.getBooleanExtra(EXTRA_OVERLAY_GAMELESS_EDIT_MODE, false) -> false
intent.action == CustomSettingsHandler.CUSTOM_CONFIG_ACTION -> true
intent.data != null -> true
else -> {
val extras = intent.extras
extras != null &&
BundleCompat.getParcelable(extras, EXTRA_SELECTED_GAME, Game::class.java) != null
}
}
}
private fun handleSwapIntent(intent: Intent) {
if (!isSwapIntent(intent)) {
return
}
pendingRomSwapIntent = Intent(intent)
if (!isWaitingForRomSwapStop) {
Log.info("[EmulationActivity] Begin ROM swap: data=${intent.data}")
isWaitingForRomSwapStop = true
romSwapNativeStopped = false
romSwapThreadStopped = false
romSwapGeneration += 1
val thisSwapGeneration = romSwapGeneration
emulationViewModel.setIsEmulationStopping(true)
emulationViewModel.setEmulationStopped(false)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as? NavHostFragment
val childFragmentManager = navHostFragment?.childFragmentManager
val stoppingFragmentForSwap =
(childFragmentManager?.primaryNavigationFragment as? EmulationFragment) ?:
childFragmentManager
?.fragments
?.asReversed()
?.firstOrNull {
it is EmulationFragment &&
it.isAdded &&
it.view != null &&
!it.isRemoving
} as? EmulationFragment
val hasSessionForSwap = hasEmulationSession || stoppingFragmentForSwap != null
if (!hasSessionForSwap) {
romSwapNativeStopped = true
romSwapThreadStopped = true
} else {
if (stoppingFragmentForSwap != null) {
stoppingFragmentForSwap.stopForRomSwap()
stoppingFragmentForSwap.notifyWhenEmulationThreadStops {
if (!isWaitingForRomSwapStop || romSwapGeneration != thisSwapGeneration) {
return@notifyWhenEmulationThreadStops
}
romSwapThreadStopped = true
Log.info("[EmulationActivity] ROM swap thread stop acknowledged")
launchPendingRomSwap(force = false)
}
} else {
Log.warning("[EmulationActivity] ROM swap stop target fragment not found; requesting native stop")
romSwapThreadStopped = true
NativeLibrary.stopEmulation()
}
scheduleRomSwapStopTimeout()
}
}
launchPendingRomSwap(force = false)
}
private fun launchPendingRomSwap(force: Boolean) {
if (!isWaitingForRomSwapStop) {
return
}
if (!force && (!romSwapNativeStopped || !romSwapThreadStopped)) {
return
}
val swapIntent = pendingRomSwapIntent ?: return
Log.info("[EmulationActivity] Launching pending ROM swap: data=${swapIntent.data}")
pendingRomSwapIntent = null
isWaitingForRomSwapStop = false
romSwapNativeStopped = false
romSwapThreadStopped = false
mainHandler.removeCallbacks(romSwapStopTimeoutRunnable)
applyGameLaunchIntent(swapIntent)
}
private fun applyGameLaunchIntent(intent: Intent) {
hasEmulationSession = true
processHasEmulationSession = true
emulationViewModel.setIsEmulationStopping(false)
emulationViewModel.setEmulationStopped(false)
setIntent(Intent(intent))
// Reset navigation graph with new intent data to recreate EmulationFragment
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
val navController = navHostFragment.navController
val startArgs = intent.extras?.let { Bundle(it) } ?: Bundle()
val navOptions = NavOptions.Builder()
.setPopUpTo(R.id.emulationFragment, true)
.build()
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
runCatching {
navController.navigate(R.id.emulationFragment, startArgs, navOptions)
}.onFailure {
Log.warning("[EmulationActivity] ROM swap navigate fallback to setGraph: ${it.message}")
navController.setGraph(R.navigation.emulation_navigation, startArgs)
}
nfcReader.onNewIntent(intent)
InputHandler.updateControllerData()
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
@@ -762,48 +608,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
}
fun onEmulationStarted() {
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post { onEmulationStarted() }
return
}
hasEmulationSession = true
processHasEmulationSession = true
emulationViewModel.setEmulationStarted(true)
emulationViewModel.setIsEmulationStopping(false)
emulationViewModel.setEmulationStopped(false)
NativeLibrary.playTimeManagerStart()
}
fun onEmulationStopped(status: Int) {
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post { onEmulationStopped(status) }
return
}
hasEmulationSession = false
processHasEmulationSession = false
if (isWaitingForRomSwapStop) {
romSwapNativeStopped = true
Log.info("[EmulationActivity] ROM swap native stop acknowledged")
launchPendingRomSwap(force = false)
} else if (status == 0 && emulationViewModel.programChanged.value == -1) {
processSessionGame = null
if (status == 0 && emulationViewModel.programChanged.value == -1) {
finish()
} else if (!isWaitingForRomSwapStop) {
processSessionGame = null
}
emulationViewModel.setEmulationStopped(true)
}
fun updateSessionGame(game: Game?) {
processSessionGame = game
}
fun onProgramChanged(programIndex: Int) {
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post { onProgramChanged(programIndex) }
return
}
emulationViewModel.setProgramChanged(programIndex)
}
@@ -827,11 +644,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
companion object {
const val EXTRA_SELECTED_GAME = "SelectedGame"
const val EXTRA_OVERLAY_GAMELESS_EDIT_MODE = "overlayGamelessEditMode"
private const val ROM_SWAP_STOP_TIMEOUT_MS = 5000L
@Volatile
private var processHasEmulationSession = false
@Volatile
private var processSessionGame: Game? = null
fun stopForegroundService(activity: Activity) {
val startIntent = Intent(activity, ForegroundService::class.java)
@@ -50,7 +50,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.navArgs
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
@@ -136,8 +135,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var intentGame: Game? = null
private var isCustomSettingsIntent = false
private var isStoppingForRomSwap = false
private var deferGameSetupUntilStopCompletes = false
private var perfStatsRunnable: Runnable? = null
private var socRunnable: Runnable? = null
@@ -241,14 +238,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
if (emulationViewModel.isEmulationStopping.value) {
deferGameSetupUntilStopCompletes = true
if (game == null) {
game = args.game ?: intentGame
}
return
}
finishGameSetup()
}
@@ -271,7 +260,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
game = gameToUse
emulationActivity?.updateSessionGame(gameToUse)
} catch (e: Exception) {
Log.error("[EmulationFragment] Error during game setup: ${e.message}")
Toast.makeText(
@@ -346,8 +334,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
emulationState = EmulationState(game!!.path) {
return@EmulationState driverViewModel.isInteractionAllowed.value &&
!isStoppingForRomSwap
return@EmulationState driverViewModel.isInteractionAllowed.value
}
}
@@ -903,12 +890,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
)
game?.let {
GameIconUtils.loadGameIcon(it, binding.loadingImage)
binding.loadingTitle.text = it.title
} ?: run {
binding.loadingTitle.text = ""
}
GameIconUtils.loadGameIcon(game!!, binding.loadingImage)
binding.loadingTitle.text = game!!.title
binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true
@@ -976,12 +959,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
ViewUtils.showView(binding.loadingIndicator)
ViewUtils.hideView(binding.inputContainer)
ViewUtils.hideView(binding.showStatsOverlayText)
} else if (deferGameSetupUntilStopCompletes) {
if (!isAdded) {
return@collect
}
deferGameSetupUntilStopCompletes = false
finishGameSetup()
}
}
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
@@ -1018,24 +995,26 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
if (it &&
!isStoppingForRomSwap &&
!NativeLibrary.isRunning() &&
!NativeLibrary.isPaused()
) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
if (it && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
startEmulation()
}
}
driverViewModel.onLaunchGame()
}
private fun startEmulation(programIndex: Int = 0) {
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val b = _binding ?: return
@@ -1396,9 +1375,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onDestroyView()
amiiboLoadJob?.cancel()
amiiboLoadJob = null
perfStatsRunnable?.let { perfStatsUpdateHandler.removeCallbacks(it) }
socRunnable?.let { socUpdateHandler.removeCallbacks(it) }
handler.removeCallbacksAndMessages(null)
clearPausedFrame()
_binding?.surfaceInputOverlay?.touchEventListener = null
_binding = null
@@ -1406,9 +1382,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
override fun onDetach() {
if (!hasNewerEmulationFragment()) {
NativeLibrary.clearEmulationActivity()
}
NativeLibrary.clearEmulationActivity()
super.onDetach()
}
@@ -1866,74 +1840,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
if (this::emulationState.isInitialized && !hasNewerEmulationFragment()) {
emulationState.clearSurface()
}
emulationState.clearSurface()
emulationStarted = false
}
private fun hasNewerEmulationFragment(): Boolean {
val activity = emulationActivity ?: return false
return try {
val navHostFragment =
activity.supportFragmentManager.findFragmentById(R.id.fragment_container) as? NavHostFragment
?: return false
val currentFragment = navHostFragment.childFragmentManager.fragments
.filterIsInstance<EmulationFragment>()
.firstOrNull()
currentFragment != null && currentFragment !== this
} catch (_: Exception) {
false
}
}
// xbzk: called from EmulationActivity when a new game is loaded while this fragment is still active,
// to wait for the emulation thread to stop before allowing the ROM swap to proceed
fun notifyWhenEmulationThreadStops(onStopped: () -> Unit) {
if (!this::emulationState.isInitialized) {
onStopped()
return
}
val emuThread = runCatching { emulationState.emulationThread }.getOrNull()
if (emuThread == null || !emuThread.isAlive) {
onStopped()
return
}
Thread({
runCatching { emuThread.join() }
Handler(Looper.getMainLooper()).post {
onStopped()
}
}, "RomSwapWait").start()
}
// xbzk: called from EmulationActivity when a new game is loaded while this
// fragment is still active, to stop the current emulation before swapping the ROM
fun stopForRomSwap() {
if (isStoppingForRomSwap) {
return
}
isStoppingForRomSwap = true
clearPausedFrame()
emulationViewModel.setIsEmulationStopping(true)
_binding?.let {
binding.loadingText.setText(R.string.shutting_down)
ViewUtils.showView(binding.loadingIndicator)
ViewUtils.hideView(binding.inputContainer)
ViewUtils.hideView(binding.showStatsOverlayText)
}
if (this::emulationState.isInitialized) {
emulationState.stop()
if (NativeLibrary.isRunning() || NativeLibrary.isPaused()) {
Log.warning("[EmulationFragment] ROM swap stop fallback: forcing native stop request.")
NativeLibrary.stopEmulation()
}
} else {
NativeLibrary.stopEmulation()
}
NativeConfig.reloadGlobalConfig()
}
private fun showOverlayOptions() {
val anchor = binding.inGameMenu.findViewById<View>(R.id.menu_overlay_controls)
val popup = PopupMenu(requireContext(), anchor)
@@ -2224,7 +2134,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
state = State.STOPPED
} else {
Log.warning("[EmulationFragment] Stop called while already stopped.")
NativeLibrary.stopEmulation()
}
}
@@ -36,7 +36,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamePropertiesBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GameProperty
import org.yuzu.yuzu_emu.model.GamesViewModel
@@ -47,7 +46,6 @@ import org.yuzu.yuzu_emu.model.SubmenuProperty
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil
@@ -63,7 +61,6 @@ class GamePropertiesFragment : Fragment() {
private val homeViewModel: HomeViewModel by activityViewModels()
private val gamesViewModel: GamesViewModel by activityViewModels()
private val addonViewModel: AddonViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
private val args by navArgs<GamePropertiesFragmentArgs>()
@@ -121,20 +118,6 @@ class GamePropertiesFragment : Fragment() {
.show(childFragmentManager, LaunchGameDialogFragment.TAG)
}
if (GameHelper.cachedGameList.isEmpty()) {
binding.buttonStart.isEnabled = false
viewLifecycleOwner.lifecycleScope.launch {
withContext(Dispatchers.IO) {
GameHelper.restoreContentForGame(args.game)
}
if (_binding == null) {
return@launch
}
addonViewModel.onAddonsViewStarted(args.game)
binding.buttonStart.isEnabled = true
}
}
reloadList()
homeViewModel.openImportSaves.collect(
@@ -100,45 +100,42 @@ class GamesViewModel : ViewModel() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
try {
if (firstStartup) {
// Retrieve list of cached games
val storedGames =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getStringSet(GameHelper.KEY_GAMES, emptySet())
if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>()
storedGames.forEach {
val game: Game
try {
game = Json.decodeFromString(it)
} catch (e: Exception) {
// We don't care about any errors related to parsing the game cache
return@forEach
}
val gameExists =
DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(game.path)
)?.exists()
if (gameExists == true) {
deserializedGames.add(game)
}
if (firstStartup) {
// Retrieve list of cached games
val storedGames =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getStringSet(GameHelper.KEY_GAMES, emptySet())
if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>()
storedGames.forEach {
val game: Game
try {
game = Json.decodeFromString(it)
} catch (e: Exception) {
// We don't care about any errors related to parsing the game cache
return@forEach
}
val gameExists =
DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(game.path)
)?.exists()
if (gameExists == true) {
deserializedGames.add(game)
}
setGames(deserializedGames.toList())
}
setGames(deserializedGames.toList())
}
}
setGames(GameHelper.getGames())
_shouldScrollAfterReload.value = true
setGames(GameHelper.getGames())
reloading.set(false)
_isReloading.value = false
_shouldScrollAfterReload.value = true
if (directoriesChanged) {
setShouldSwapData(true)
}
} finally {
reloading.set(false)
_isReloading.value = false
if (directoriesChanged) {
setShouldSwapData(true)
}
}
}
@@ -23,8 +23,8 @@ object DirectoryInitialization {
fun start() {
if (!areDirectoriesReady) {
initializeInternalStorage()
NativeConfig.initializeGlobalConfig()
NativeLibrary.initializeSystem(false)
NativeConfig.initializeGlobalConfig()
NativeLibrary.reloadProfiles()
migrateSettings()
areDirectoriesReady = true
@@ -8,11 +8,9 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
import android.net.Uri
import android.provider.DocumentsContract
import androidx.preference.PreferenceManager
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game
@@ -51,8 +49,29 @@ object GameHelper {
// Remove previous filesystem provider information so we can get up to date version info
NativeLibrary.clearFilesystemProvider()
// Scan External Content directories and register all NSP/XCI files
val externalContentDirs = NativeConfig.getExternalContentDirs()
val uniqueExternalContentDirs = linkedSetOf<String>()
externalContentDirs.forEach { externalDir ->
if (externalDir.isNotEmpty()) {
uniqueExternalContentDirs.add(externalDir)
}
}
val mountedContainerUris = mutableSetOf<String>()
mountExternalContentDirectories(mountedContainerUris)
for (externalDir in uniqueExternalContentDirs) {
if (externalDir.isNotEmpty()) {
val externalDirUri = externalDir.toUri()
if (FileUtil.isTreeUriValid(externalDirUri)) {
scanContentContainersRecursive(FileUtil.listFiles(externalDirUri), 3) {
val containerUri = it.uri.toString()
if (mountedContainerUris.add(containerUri)) {
NativeLibrary.addFileToFilesystemProvider(containerUri)
}
}
}
}
}
val badDirs = mutableListOf<Int>()
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
@@ -96,15 +115,6 @@ object GameHelper {
return games.toList()
}
fun restoreContentForGame(game: Game) {
NativeLibrary.reloadKeys()
val mountedContainerUris = mutableSetOf<String>()
mountExternalContentDirectories(mountedContainerUris)
mountGameFolderContent(Uri.parse(game.path), mountedContainerUris)
NativeLibrary.addFileToFilesystemProvider(game.path)
}
// File extensions considered as external content, buuut should
// be done better imo.
private val externalContentExtensions = setOf("nsp", "xci")
@@ -171,71 +181,6 @@ object GameHelper {
}
}
private fun mountExternalContentDirectories(mountedContainerUris: MutableSet<String>) {
val uniqueExternalContentDirs = linkedSetOf<String>()
NativeConfig.getExternalContentDirs().forEach { externalDir ->
if (externalDir.isNotEmpty()) {
uniqueExternalContentDirs.add(externalDir)
}
}
for (externalDir in uniqueExternalContentDirs) {
val externalDirUri = externalDir.toUri()
if (FileUtil.isTreeUriValid(externalDirUri)) {
scanContentContainersRecursive(FileUtil.listFiles(externalDirUri), 3) {
val containerUri = it.uri.toString()
if (mountedContainerUris.add(containerUri)) {
NativeLibrary.addFileToFilesystemProvider(containerUri)
}
}
}
}
}
private fun mountGameFolderContent(gameUri: Uri, mountedContainerUris: MutableSet<String>) {
if (gameUri.scheme == "content") {
val parentUri = getParentDocumentUri(gameUri) ?: return
scanContentContainersRecursive(FileUtil.listFiles(parentUri), 1) {
val containerUri = it.uri.toString()
if (mountedContainerUris.add(containerUri)) {
NativeLibrary.addGameFolderFileToFilesystemProvider(containerUri)
}
}
return
}
val gameFile = File(gameUri.path ?: gameUri.toString())
val parentDir = gameFile.parentFile ?: return
parentDir.listFiles()?.forEach { sibling ->
if (!sibling.isFile) {
return@forEach
}
val extension = sibling.extension.lowercase()
if (externalContentExtensions.contains(extension)) {
val containerUri = Uri.fromFile(sibling).toString()
if (mountedContainerUris.add(containerUri)) {
NativeLibrary.addGameFolderFileToFilesystemProvider(containerUri)
}
}
}
}
private fun getParentDocumentUri(uri: Uri): Uri? {
return try {
val documentId = DocumentsContract.getDocumentId(uri)
val separatorIndex = documentId.lastIndexOf('/')
if (separatorIndex == -1) {
null
} else {
val parentDocumentId = documentId.substring(0, separatorIndex)
DocumentsContract.buildDocumentUriUsingTree(uri, parentDocumentId)
}
} catch (_: Exception) {
null
}
}
fun getGame(
uri: Uri,
addedToLibrary: Boolean,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 67 KiB

@@ -1 +1 @@
<?xml version='1.0' encoding='utf-8'?><resources><color name='ic_launcher_background'>#43fcfcff</color></resources>
<?xml version='1.0' encoding='utf-8'?><resources><color name='ic_launcher_background'>#1F143C</color></resources>
-2
View File
@@ -134,8 +134,6 @@ add_library(
typed_address.h
uint128.h
unique_function.h
random.cpp
random.h
uuid.cpp
uuid.h
vector_math.h
-22
View File
@@ -1,22 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <optional>
#include <random>
#include "common/random.h"
namespace Common::Random {
[[nodiscard]] static std::random_device& GetGlobalRandomDevice() noexcept {
static std::random_device g_random_device{};
return g_random_device;
}
[[nodiscard]] u32 Random32(u32 seed) noexcept {
return GetGlobalRandomDevice()();
}
[[nodiscard]] u64 Random64(u64 seed) noexcept {
return GetGlobalRandomDevice()();
}
[[nodiscard]] std::mt19937 GetMT19937() noexcept {
return std::mt19937(GetGlobalRandomDevice()());
}
}
-13
View File
@@ -1,13 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <random>
#include "common/common_types.h"
namespace Common::Random {
[[nodiscard]] u32 Random32(u32 seed) noexcept;
[[nodiscard]] u64 Random64(u64 seed) noexcept;
[[nodiscard]] std::mt19937 GetMT19937() noexcept;
}
+7 -1
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -218,6 +218,12 @@ public:
return t0;
}
u64 GenerateRandomU64() {
const u32 lo = this->GenerateRandomU32();
const u32 hi = this->GenerateRandomU32();
return (u64{hi} << 32) | u64{lo};
}
float GenerateRandomF32() {
// Floats have 24 bits of mantissa.
constexpr u32 MantissaBits = 24;
+6 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,7 +10,6 @@
#include "common/assert.h"
#include "common/tiny_mt.h"
#include "common/uuid.h"
#include "common/random.h"
namespace Common {
@@ -179,16 +175,21 @@ u128 UUID::AsU128() const {
}
UUID UUID::MakeRandom() {
return MakeRandomWithSeed(Common::Random::Random32(0));
std::random_device device;
return MakeRandomWithSeed(device());
}
UUID UUID::MakeRandomWithSeed(u32 seed) {
// Create and initialize our RNG.
TinyMT rng;
rng.Initialize(seed);
UUID uuid;
// Populate the UUID with random bytes.
rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
return uuid;
}
+5 -2
View File
@@ -12,7 +12,6 @@
#include "common/fs/path_util.h"
#include "common/hex_util.h"
#include "common/logging.h"
#include "common/random.h"
#include "common/string_util.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
@@ -491,13 +490,17 @@ std::vector<NcaID> PlaceholderCache::List() const {
}
NcaID PlaceholderCache::Generate() {
auto gen = Common::Random::GetMT19937();
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, (std::numeric_limits<u64>::max)());
NcaID out{};
const auto v1 = distribution(gen);
const auto v2 = distribution(gen);
std::memcpy(out.data(), &v1, sizeof(u64));
std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
return out;
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -7,7 +7,6 @@
#include <random>
#include "common/literals.h"
#include "common/random.h"
#include "common/settings.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
@@ -202,8 +201,15 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, (std::numeric_limits<u64>::max)());
return distribution(gen);
}
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, Common::Random::GetMT19937());
return GenerateUniformRange(min, max, GenerateRandomU64);
}
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -36,6 +33,7 @@ public:
// Randomness.
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
// Secure Memory.
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
+2 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -14,7 +14,6 @@
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/tiny_mt.h"
#include "common/random.h"
#include "core/hle/kernel/k_system_control.h"
namespace Kernel {
@@ -24,7 +23,7 @@ public:
class RandomBitGenerator {
public:
RandomBitGenerator() {
m_rng.Initialize(u32(Common::Random::Random64(0)));
m_rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
}
u64 SelectRandomBit(u64 bitmap) {
+5 -4
View File
@@ -20,7 +20,6 @@
#include "common/fiber.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/random.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
@@ -46,7 +45,8 @@ namespace {
constexpr inline s32 TerminatingThreadPriority = Kernel::Svc::SystemThreadPriorityHighest - 1;
static void ResetThreadContext32(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point, u64 arg) {
static void ResetThreadContext32(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point,
u64 arg) {
ctx = {};
ctx.r[0] = arg;
ctx.r[15] = entry_point;
@@ -55,10 +55,11 @@ static void ResetThreadContext32(Kernel::Svc::ThreadContext& ctx, u64 stack_top,
ctx.fpsr = 0;
}
static void ResetThreadContext64(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point, u64 arg) {
static void ResetThreadContext64(Kernel::Svc::ThreadContext& ctx, u64 stack_top, u64 entry_point,
u64 arg) {
ctx = {};
ctx.r[0] = arg;
ctx.r[18] = Common::Random::Random64(0) | 1;
ctx.r[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
ctx.pc = entry_point;
ctx.sp = stack_top;
ctx.fpcr = 0;
+11 -11
View File
@@ -438,20 +438,20 @@ void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequ
template <bool Domain, typename T, typename... A>
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
const auto mgr = ctx.GetManager().get();
// Verify domain state.
if constexpr (!Domain) {
const bool is_domain = mgr ? mgr->IsDomain() : false;
ASSERT_MSG(!is_domain,
"Non-domain reply used on domain session\n"
"Service={} (TIPC={} CmdType={} Cmd=0x{:08X}\n"
"HasDomainHeader={} DomainHandlers={}\nDesc={}",
t.GetServiceName(), ctx.IsTipc(),
u32(ctx.GetCommandType()), u32(ctx.GetCommand()),
ctx.HasDomainMessageHeader(), mgr ? u32(mgr->DomainHandlerCount()) : 0u,
ctx.Description());
const auto _mgr = ctx.GetManager();
const bool _is_domain = _mgr ? _mgr->IsDomain() : false;
ASSERT_MSG(!_is_domain,
"Non-domain reply used on domain session\n"
"Service={} (TIPC={} CmdType={} Cmd=0x{:08X}\n"
"HasDomainHeader={} DomainHandlers={}\nDesc={}",
t.GetServiceName(), ctx.IsTipc(),
static_cast<u32>(ctx.GetCommandType()), static_cast<u32>(ctx.GetCommand()),
ctx.HasDomainMessageHeader(), _mgr ? static_cast<u32>(_mgr->DomainHandlerCount()) : 0u,
ctx.Description());
}
const bool is_domain = Domain ? mgr->IsDomain() : false;
const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
+11 -11
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -81,29 +78,32 @@ public:
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
IPC::CommandHeader header{};
auto const mgr = ctx.GetManager().get();
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
u32 raw_data_size = ctx.write_size = ctx.IsTipc() ? normal_params_size - 1 : normal_params_size;
u32 raw_data_size = ctx.write_size =
ctx.IsTipc() ? normal_params_size - 1 : normal_params_size;
u32 num_handles_to_move{};
u32 num_domain_objects{};
const bool always_move_handles = (u32(flags) & u32(Flags::AlwaysMoveHandles)) != 0;
if (!mgr->IsDomain() || always_move_handles) {
const bool always_move_handles{
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
if (!ctx.GetManager()->IsDomain() || always_move_handles) {
num_handles_to_move = num_objects_to_move;
} else {
num_domain_objects = num_objects_to_move;
}
if (mgr->IsDomain()) {
raw_data_size += u32(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
if (ctx.GetManager()->IsDomain()) {
raw_data_size +=
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
ctx.write_size += num_domain_objects;
}
if (ctx.IsTipc()) {
header.type.Assign(ctx.GetCommandType());
} else {
raw_data_size += u32(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 + normal_params_size);
raw_data_size += static_cast<u32>(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 +
normal_params_size);
}
header.data_size.Assign(raw_data_size);
@@ -126,7 +126,7 @@ public:
if (!ctx.IsTipc()) {
AlignWithPadding();
if (mgr->IsDomain() && ctx.HasDomainMessageHeader()) {
if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
+5 -7
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,7 +7,6 @@
#include <span>
#include "common/common_types.h"
#include "common/random.h"
#include "common/swap.h"
#include "common/uuid.h"
#include "core/hle/service/mii/mii_types.h"
@@ -69,9 +65,11 @@ public:
template <typename T>
static T GetRandomValue(T min, T max) {
std::uniform_int_distribution<u64> distribution{u64(min), u64(max)};
auto gen = Common::Random::GetMT19937();
return T(distribution(gen));
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(static_cast<u64>(min),
static_cast<u64>(max));
return static_cast<T>(distribution(gen));
}
template <typename T>
@@ -6,7 +6,6 @@
#include <cstring>
#include "common/logging.h"
#include "common/random.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
@@ -230,7 +229,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
// TODO: this is bad form of ASLR, it sucks
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue()
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
: std::rand()) * 0x734287f27) & 0xfff000;
// Setup the process code layout
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) {
+1 -2
View File
@@ -6,7 +6,6 @@
#include <cstring>
#include "common/settings.h"
#include "common/random.h"
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
@@ -91,7 +90,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
// TODO: this is bad form of ASLR, it sucks
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue()
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
: std::rand()) * 0x734287f27) & 0xfff000;
// Setup the process code layout
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) {
+1 -2
View File
@@ -11,7 +11,6 @@
#include "common/common_types.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/random.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
@@ -244,7 +243,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
// TODO: this is bad form of ASLR, it sucks
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue()
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
: std::rand()) * 0x734287f27) & 0xfff000;
// Setup the process code layout
if (process
+12 -2
View File
@@ -45,7 +45,11 @@ static inline bool AddressSpaceContains(const Common::PageTable& table, const Co
// from outside classes. This also allows modification to the internals of the memory
// subsystem without needing to rebuild all files that make use of the memory interface.
struct Memory::Impl {
explicit Impl(Core::System& system_) : system{system_} {}
explicit Impl(Core::System& system_) : system{system_} {
// Initialize thread count based on available cores for parallel memory operations
const unsigned int hw_concurrency = std::thread::hardware_concurrency();
thread_count = (std::max)(2u, (std::min)(hw_concurrency, 8u)); // Limit to 8 threads max
}
void SetCurrentPageTable(Kernel::KProcess& process) {
current_page_table = &process.GetPageTable().GetImpl();
@@ -852,7 +856,13 @@ struct Memory::Impl {
Tegra::MaxwellDeviceMemoryManager* gpu_device_memory{};
Common::PageTable* current_page_table = nullptr;
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_read_areas{};
// Number of threads to use for parallel memory operations
unsigned int thread_count = 2;
// Minimum size in bytes for which parallel processing is beneficial
//size_t PARALLEL_THRESHOLD = (L3 CACHE * NUM PHYSICAL CORES); // 64 KB
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES>
rasterizer_read_areas{};
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
std::array<Common::ScratchBuffer<u32>, Core::Hardware::NUM_CPU_CORES> scratch_buffers{};
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
+3 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -51,12 +51,10 @@ void RenderdocAPI::ToggleCapture() {
if (!rdoc_api) [[unlikely]] {
return;
}
auto* api = static_cast<RENDERDOC_API_1_6_0*>(rdoc_api);
if (!is_capturing) {
api->StartFrameCapture(NULL, NULL);
rdoc_api->StartFrameCapture(NULL, NULL);
} else {
api->EndFrameCapture(NULL, NULL);
rdoc_api->EndFrameCapture(NULL, NULL);
}
is_capturing = !is_capturing;
}
+3 -1
View File
@@ -6,6 +6,8 @@
#pragma once
struct RENDERDOC_API_1_7_0;
namespace Tools {
class RenderdocAPI {
@@ -16,7 +18,7 @@ public:
void ToggleCapture();
private:
void* rdoc_api{};
RENDERDOC_API_1_7_0* rdoc_api{};
bool is_capturing{false};
};
@@ -316,8 +316,8 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
return current_location->index;
}
ASSERT(!bool(ValueInfo(*current_location).realized));
ASSERT(bool(ValueInfo(*current_location).locked));
ASSERT(!ValueInfo(*current_location).realized);
ASSERT(ValueInfo(*current_location).locked);
if constexpr (required_kind == HostLoc::Kind::Gpr) {
const int new_location_index = AllocateRegister(gprs, gpr_order);
@@ -59,10 +59,8 @@ static Xbyak::Address MJitStateExtReg(A32::ExtReg reg) {
UNREACHABLE();
}
A32EmitContext::A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels)
: EmitContext(reg_alloc, block, shared_labels)
, conf(conf)
{}
A32EmitContext::A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block)
: EmitContext(reg_alloc, block), conf(conf) {}
A32::LocationDescriptor A32EmitContext::Location() const {
return A32::LocationDescriptor{block.Location()};
@@ -111,44 +109,27 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
gprs.reset(size_t(HostLoc::R14));
return gprs;
}(), any_xmm);
A32EmitContext ctx{conf, reg_alloc, block, shared_labels};
A32EmitContext ctx{conf, reg_alloc, block};
// Start emitting.
code.align();
const u8* const entrypoint = code.getCurr();
code.mov(code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)], rbp);
code.lea(rbp, code.ptr[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer) - 8]);
EmitCondPrelude(ctx);
typedef void (EmitX64::*EmitHandlerFn)(EmitContext& context, IR::Inst* inst);
constexpr EmitHandlerFn opcode_handlers[] = {
#define OPCODE(name, type, ...) &EmitX64::Emit##name,
#define A32OPC(name, type, ...)
#define A64OPC(name, type, ...)
#include "dynarmic/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
};
typedef void (A32EmitX64::*A32EmitHandlerFn)(A32EmitContext& context, IR::Inst* inst);
constexpr A32EmitHandlerFn a32_handlers[] = {
#define OPCODE(...)
#define A32OPC(name, type, ...) &A32EmitX64::EmitA32##name,
#define A64OPC(...)
#include "dynarmic/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
};
for (auto& inst : block.instructions) {
auto const opcode = inst.GetOpcode();
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) [[likely]] {
auto* inst = &*iter;
// Call the relevant Emit* member function.
switch (opcode) {
#define OPCODE(name, type, ...) case IR::Opcode::name: goto opcode_branch;
#define A32OPC(name, type, ...) case IR::Opcode::A32##name: goto a32_branch;
#define A64OPC(name, type, ...)
switch (inst->GetOpcode()) {
#define OPCODE(name, type, ...) \
case IR::Opcode::name: \
A32EmitX64::Emit##name(ctx, inst); \
break;
#define A32OPC(name, type, ...) \
case IR::Opcode::A32##name: \
A32EmitX64::EmitA32##name(ctx, inst);\
break;
#define A64OPC(...)
#include "dynarmic/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
@@ -156,14 +137,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
default:
UNREACHABLE();
}
opcode_branch:
(this->*opcode_handlers[size_t(opcode)])(ctx, &inst);
goto finish_this_inst;
a32_branch:
// Update with FIRST A32 instruction
(this->*a32_handlers[size_t(opcode) - size_t(IR::Opcode::A32SetCheckBit)])(ctx, &inst);
finish_this_inst:
ctx.reg_alloc.EndOfAllocScope();
reg_alloc.EndOfAllocScope();
#ifndef NDEBUG
if (conf.very_verbose_debugging_output)
EmitVerboseDebuggingOutput(reg_alloc);
@@ -172,14 +146,15 @@ finish_this_inst:
reg_alloc.AssertNoMoreUses();
if (conf.enable_cycle_counting)
if (conf.enable_cycle_counting) {
EmitAddCycles(block.CycleCount());
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
}
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
code.int3();
for (auto& deferred_emit : ctx.deferred_emits)
for (auto& deferred_emit : ctx.deferred_emits) {
deferred_emit();
}
code.int3();
const size_t size = size_t(code.getCurr() - entrypoint);
@@ -192,7 +167,6 @@ finish_this_inst:
auto const bdesc = RegisterBlock(descriptor, entrypoint, size);
code.DisableWriting();
shared_labels.clear();
return bdesc;
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@@ -29,7 +29,7 @@ namespace Dynarmic::Backend::X64 {
class RegAlloc;
struct A32EmitContext final : public EmitContext {
A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels);
A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block);
A32::LocationDescriptor Location() const;
A32::LocationDescriptor EndLocation() const;
@@ -130,7 +130,6 @@ public:
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
ankerl::unordered_dense::set<DoNotFastmemMarker> do_not_fastmem;
boost::container::stable_vector<Xbyak::Label> shared_labels;
void (*memory_read_128)() = nullptr; // Dummy
void (*memory_write_128)() = nullptr; // Dummy
const void* terminal_handler_pop_rsb_hint;
@@ -37,10 +37,8 @@ namespace Dynarmic::Backend::X64 {
using namespace Xbyak::util;
A64EmitContext::A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels)
: EmitContext(reg_alloc, block, shared_labels)
, conf(conf)
{}
A64EmitContext::A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block)
: EmitContext(reg_alloc, block), conf(conf) {}
A64::LocationDescriptor A64EmitContext::Location() const {
return A64::LocationDescriptor{block.Location()};
@@ -85,14 +83,11 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) noexcept {
gprs.reset(size_t(HostLoc::R14));
return gprs;
}(), any_xmm};
A64EmitContext ctx{conf, reg_alloc, block, shared_labels};
A64EmitContext ctx{conf, reg_alloc, block};
// Start emitting.
code.align();
const auto* const entrypoint = code.getCurr();
code.mov(code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)], rbp);
code.lea(rbp, code.ptr[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer) - 8]);
DEBUG_ASSERT(block.GetCondition() == IR::Cond::AL);
typedef void (EmitX64::*EmitHandlerFn)(EmitContext& context, IR::Inst* inst);
@@ -144,13 +139,16 @@ finish_this_inst:
}
reg_alloc.AssertNoMoreUses();
if (conf.enable_cycle_counting)
if (conf.enable_cycle_counting) {
EmitAddCycles(block.CycleCount());
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
}
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
code.int3();
for (auto& deferred_emit : ctx.deferred_emits)
for (auto& deferred_emit : ctx.deferred_emits) {
deferred_emit();
}
code.int3();
const size_t size = size_t(code.getCurr() - entrypoint);
@@ -163,7 +161,6 @@ finish_this_inst:
auto bdesc = RegisterBlock(descriptor, entrypoint, size);
code.DisableWriting();
shared_labels.clear();
return bdesc;
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@@ -27,7 +27,7 @@
namespace Dynarmic::Backend::X64 {
struct A64EmitContext final : public EmitContext {
A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels);
A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block);
A64::LocationDescriptor Location() const;
bool IsSingleStep() const;
@@ -126,7 +126,6 @@ public:
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
ankerl::unordered_dense::set<DoNotFastmemMarker> do_not_fastmem;
boost::container::stable_vector<Xbyak::Label> shared_labels;
const void* terminal_handler_pop_rsb_hint = nullptr;
const void* terminal_handler_fast_dispatch_hint = nullptr;
FastDispatchEntry& (*fast_dispatch_table_lookup)(u64) = nullptr;
@@ -32,11 +32,8 @@ namespace Dynarmic::Backend::X64 {
using namespace Xbyak::util;
EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels)
: reg_alloc(reg_alloc)
, block(block)
, shared_labels(shared_labels)
{}
EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block)
: reg_alloc(reg_alloc), block(block) {}
EmitContext::~EmitContext() = default;
@@ -16,12 +16,11 @@
#include <type_traits>
#include <vector>
#include "dynarmic/mcl/bit.hpp"
#include <ankerl/unordered_dense.h>
#include <boost/container/stable_vector.hpp>
#include "dynarmic/backend/x64/xbyak.h"
#include <boost/container/small_vector.hpp>
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/mcl/bit.hpp"
#include "dynarmic/backend/exception_handler.h"
#include "dynarmic/backend/x64/reg_alloc.h"
#include "dynarmic/common/fp/fpcr.h"
@@ -53,23 +52,24 @@ using VectorArray = std::array<T, A64FullVectorWidth::value / mcl::bitsizeof<T>>
template<typename T>
using HalfVectorArray = std::array<T, A64FullVectorWidth::value / mcl::bitsizeof<T> / 2>;
using SharedLabel = Xbyak::Label*;
struct EmitContext {
EmitContext(RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels);
EmitContext(RegAlloc& reg_alloc, IR::Block& block);
virtual ~EmitContext();
virtual FP::FPCR FPCR(bool fpcr_controlled = true) const = 0;
virtual bool HasOptimization(OptimizationFlag flag) const = 0;
[[nodiscard]] inline Xbyak::Label* GenSharedLabel() noexcept {
return &shared_labels.emplace_back();
}
std::vector<std::function<void()>> deferred_emits;
RegAlloc& reg_alloc;
IR::Block& block;
boost::container::stable_vector<Xbyak::Label>& shared_labels;
std::vector<std::function<void()>> deferred_emits;
};
using SharedLabel = std::shared_ptr<Xbyak::Label>;
inline SharedLabel GenSharedLabel() {
return std::make_shared<Xbyak::Label>();
}
class EmitX64 {
public:
struct BlockDescriptor {
@@ -136,7 +136,7 @@ void ForceToDefaultNaN(BlockOfCode& code, Xbyak::Xmm result) {
template<size_t fsize>
SharedLabel ProcessNaN(BlockOfCode& code, EmitContext& ctx, Xbyak::Xmm a) {
SharedLabel nan = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel nan = GenSharedLabel(), end = GenSharedLabel();
FCODE(ucomis)(a, a);
code.jp(*nan, code.T_NEAR);
@@ -251,7 +251,7 @@ template<size_t fsize, typename Function>
void FPTwoOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
SharedLabel end = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel();
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(code, args[0]);
@@ -304,7 +304,7 @@ void FPThreeOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn)
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr(code);
SharedLabel end = ctx.GenSharedLabel(), nan = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), nan = GenSharedLabel();
code.movaps(result, op1);
if constexpr (std::is_member_function_pointer_v<Function>) {
@@ -413,7 +413,7 @@ static void EmitFPMinMax(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, bo
DenormalsAreZero<fsize>(code, ctx, {result, operand});
SharedLabel equal = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel equal = GenSharedLabel(), end = GenSharedLabel();
FCODE(ucomis)(result, operand);
code.jz(*equal, code.T_NEAR);
@@ -484,7 +484,7 @@ static inline void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::
}
};
SharedLabel end = ctx.GenSharedLabel(), z = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), z = GenSharedLabel();
FCODE(ucomis)(op1, op2);
code.jz(*z, code.T_NEAR);
@@ -632,7 +632,7 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, bo
}
if (code.HasHostFeature(HostFeature::FMA | HostFeature::AVX)) {
SharedLabel fallback = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel fallback = GenSharedLabel(), end = GenSharedLabel();
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(code, args[0]);
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(code, args[1]);
@@ -843,7 +843,7 @@ static void EmitFPMulX(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
const Xbyak::Reg64 tmp = do_default_nan ? INVALID_REG : ctx.reg_alloc.ScratchGpr(code);
SharedLabel end = ctx.GenSharedLabel(), nan = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), nan = GenSharedLabel();
if (code.HasHostFeature(HostFeature::AVX)) {
FCODE(vmuls)(result, op1, op2);
@@ -981,7 +981,7 @@ static void EmitFPRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
}
if (code.HasHostFeature(HostFeature::FMA)) {
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(code, args[0]);
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(code, args[1]);
@@ -1129,7 +1129,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
const Xbyak::Xmm value = ctx.reg_alloc.ScratchXmm(code);
[[maybe_unused]] const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr(code).cvt32();
SharedLabel bad_values = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel bad_values = GenSharedLabel(), end = GenSharedLabel();
code.movaps(value, operand);
@@ -1296,7 +1296,7 @@ static void EmitFPRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
}
if (code.HasHostFeature(HostFeature::FMA | HostFeature::AVX)) {
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(code, args[0]);
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(code, args[1]);
@@ -1641,7 +1641,7 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm scratch = ctx.reg_alloc.ScratchXmm(code);
if (!unsigned_) {
SharedLabel saturate_max = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel saturate_max = GenSharedLabel(), end = GenSharedLabel();
ZeroIfNaN<64>(code, src, scratch);
@@ -86,7 +86,7 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
const auto wrapped_fn = read_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel abort = GenSharedLabel(), end = GenSharedLabel();
if (fastmem_marker) {
// Use fastmem
@@ -108,7 +108,7 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
conf.recompile_on_fastmem_failure,
});
EmitCheckMemoryAbort(ctx, inst, end);
EmitCheckMemoryAbort(ctx, inst, end.get());
code.jmp(*end, code.T_NEAR);
});
} else {
@@ -120,7 +120,7 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
ctx.deferred_emits.emplace_back([=, this, &ctx] {
code.L(*abort);
code.call(wrapped_fn);
EmitCheckMemoryAbort(ctx, inst, end);
EmitCheckMemoryAbort(ctx, inst, end.get());
code.jmp(*end, code.T_NEAR);
});
}
@@ -173,7 +173,7 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
const auto wrapped_fn = write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel abort = GenSharedLabel(), end = GenSharedLabel();
if (fastmem_marker) {
// Use fastmem
@@ -195,7 +195,7 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
conf.recompile_on_fastmem_failure,
});
EmitCheckMemoryAbort(ctx, inst, end);
EmitCheckMemoryAbort(ctx, inst, end.get());
code.jmp(*end, code.T_NEAR);
});
} else {
@@ -207,7 +207,7 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
ctx.deferred_emits.emplace_back([=, this, &ctx] {
code.L(*abort);
code.call(wrapped_fn);
EmitCheckMemoryAbort(ctx, inst, end);
EmitCheckMemoryAbort(ctx, inst, end.get());
code.jmp(*end, code.T_NEAR);
});
}
@@ -352,7 +352,7 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
const auto fastmem_marker = ShouldFastmem(ctx, inst);
if (fastmem_marker) {
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel abort = GenSharedLabel(), end = GenSharedLabel();
bool require_abort_handling = false;
const auto src_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling);
@@ -427,7 +427,7 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
EmitExclusiveLock(code, conf, tmp, tmp2.cvt32());
SharedLabel end = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel();
code.mov(status, u32(1));
code.movzx(tmp.cvt32(), code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)]);
@@ -460,7 +460,7 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
const auto fastmem_marker = ShouldFastmem(ctx, inst);
if (fastmem_marker) {
SharedLabel abort = ctx.GenSharedLabel();
SharedLabel abort = GenSharedLabel();
bool require_abort_handling = false;
const auto dest_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling, tmp);
@@ -54,7 +54,7 @@ void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsi
if (ctx.conf.only_detect_misalignment_via_page_table_on_page_boundary) {
const u32 page_align_mask = static_cast<u32>(page_table_const_size - 1) & ~align_mask;
SharedLabel detect_boundary = ctx.GenSharedLabel(), resume = ctx.GenSharedLabel();
SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel();
code.jnz(*detect_boundary, code.T_NEAR);
code.L(*resume);
File diff suppressed because it is too large Load Diff
@@ -24,7 +24,6 @@
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/rounding_mode.h"
#include "dynarmic/common/fp/util.h"
#include "dynarmic/interface/optimization_flags.h"
#include "dynarmic/ir/basic_block.h"
@@ -94,7 +93,7 @@ void HandleNaNs(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, std::
code.cmp(bitmask, 0);
}
SharedLabel end = ctx.GenSharedLabel(), nan = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), nan = GenSharedLabel();
code.jnz(*nan, code.T_NEAR);
code.L(*end);
@@ -189,6 +188,23 @@ void ForceToDefaultNaN(BlockOfCode& code, FP::FPCR fpcr, Xbyak::Xmm result) {
}
}
template<size_t fsize>
void ZeroIfNaN(BlockOfCode& code, Xbyak::Xmm result) {
const Xbyak::Xmm nan_mask = xmm0;
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
constexpr u32 nan_to_zero = FixupLUT(FpFixup::PosZero,
FpFixup::PosZero);
FCODE(vfixupimmp)(result, result, code.BConst<32>(ptr_b, nan_to_zero), u8(0));
} else if (code.HasHostFeature(HostFeature::AVX)) {
FCODE(vcmpordp)(nan_mask, result, result);
FCODE(vandp)(result, result, nan_mask);
} else {
code.movaps(nan_mask, result);
FCODE(cmpordp)(nan_mask, nan_mask);
code.andps(result, nan_mask);
}
}
template<size_t fsize>
void DenormalsAreZero(BlockOfCode& code, FP::FPCR fpcr, std::initializer_list<Xbyak::Xmm> to_daz, Xbyak::Xmm tmp) {
if (fpcr.FZ()) {
@@ -1314,7 +1330,7 @@ void EmitFPVectorMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
code.movaps(result, xmm_a);
@@ -1587,7 +1603,7 @@ static void EmitRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(code, args[1]);
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
code.movaps(result, GetVectorOf<fsize, false, 0, 2>(code));
@@ -1760,7 +1776,7 @@ static void EmitRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* ins
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
const Xbyak::Xmm value = ctx.reg_alloc.ScratchXmm(code);
SharedLabel bad_values = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
SharedLabel bad_values = GenSharedLabel(), end = GenSharedLabel();
code.movaps(value, operand);
@@ -1851,7 +1867,7 @@ static void EmitRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
const Xbyak::Xmm mask = ctx.reg_alloc.ScratchXmm(code);
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
code.vmovaps(result, GetVectorOf<fsize, false, 0, 3>(code));
@@ -1988,123 +2004,120 @@ void EmitX64::EmitFPVectorToHalf32(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize, bool unsigned_>
void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const size_t fbits = inst->GetArg(1).GetU8();
const auto rounding = FP::RoundingMode(inst->GetArg(2).GetU8());
const auto rounding = static_cast<FP::RoundingMode>(inst->GetArg(2).GetU8());
[[maybe_unused]] const bool fpcr_controlled = inst->GetArg(3).GetU1();
if (code.HasHostFeature(HostFeature::SSE41) && fsize != 16 && rounding != FP::RoundingMode::ToNearest_TieAwayFromZero) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm src = ctx.reg_alloc.UseScratchXmm(code, args[0]);
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
const int round_imm = [&] {
switch (rounding) {
case FP::RoundingMode::ToNearest_TieEven:
default:
return 0b00;
case FP::RoundingMode::TowardsPlusInfinity:
return 0b10;
case FP::RoundingMode::TowardsMinusInfinity:
return 0b01;
case FP::RoundingMode::TowardsZero:
return 0b11;
}
}();
const auto perform_conversion = [&code, &ctx](const Xbyak::Xmm& src) {
// MSVC doesn't allow us to use a [&] capture, so we have to do this instead.
(void)ctx;
if constexpr (fsize != 16) {
if (code.HasHostFeature(HostFeature::SSE41) && rounding != FP::RoundingMode::ToNearest_TieAwayFromZero) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if constexpr (fsize == 32) {
code.cvttps2dq(src, src);
} else {
const Xbyak::Xmm src = ctx.reg_alloc.UseScratchXmm(code, args[0]);
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
const int round_imm = [&] {
switch (rounding) {
case FP::RoundingMode::ToNearest_TieEven:
default:
return 0b00;
case FP::RoundingMode::TowardsPlusInfinity:
return 0b10;
case FP::RoundingMode::TowardsMinusInfinity:
return 0b01;
case FP::RoundingMode::TowardsZero:
return 0b11;
}
}();
const auto perform_conversion = [&code, &ctx](const Xbyak::Xmm& src) {
// MSVC doesn't allow us to use a [&] capture, so we have to do this instead.
(void)ctx;
if constexpr (fsize == 32) {
code.cvttps2dq(src, src);
} else {
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
code.vcvttpd2qq(src, src);
} else {
const Xbyak::Reg64 hi = ctx.reg_alloc.ScratchGpr(code);
const Xbyak::Reg64 lo = ctx.reg_alloc.ScratchGpr(code);
code.cvttsd2si(lo, src);
code.punpckhqdq(src, src);
code.cvttsd2si(hi, src);
code.movq(src, lo);
code.pinsrq(src, hi, 1);
ctx.reg_alloc.Release(hi);
ctx.reg_alloc.Release(lo);
}
}
};
if (fbits != 0) {
const u64 scale_factor = fsize == 32
? static_cast<u64>(fbits + 127) << 23
: static_cast<u64>(fbits + 1023) << 52;
FCODE(mulp)(src, GetVectorOf<fsize>(code, scale_factor));
}
FCODE(roundp)(src, src, static_cast<u8>(round_imm));
ZeroIfNaN<fsize>(code, src);
constexpr u64 float_upper_limit_signed = fsize == 32 ? 0x4f000000 : 0x43e0000000000000;
[[maybe_unused]] constexpr u64 float_upper_limit_unsigned = fsize == 32 ? 0x4f800000 : 0x43f0000000000000;
if constexpr (unsigned_) {
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
code.vcvttpd2qq(src, src);
// Mask positive values
code.xorps(xmm0, xmm0);
FCODE(vcmpp)(k1, src, xmm0, Cmp::GreaterEqual_OQ);
// Convert positive values to unsigned integers, write 0 anywhere else
// vcvttp*2u*q already saturates out-of-range values to (0xFFFF...)
if constexpr (fsize == 32) {
code.vcvttps2udq(src | k1 | T_z, src);
} else {
code.vcvttpd2uqq(src | k1 | T_z, src);
}
} else {
const Xbyak::Reg64 hi = ctx.reg_alloc.ScratchGpr(code);
const Xbyak::Reg64 lo = ctx.reg_alloc.ScratchGpr(code);
// Zero is minimum
code.xorps(xmm0, xmm0);
FCODE(cmplep)(xmm0, src);
FCODE(andp)(src, xmm0);
code.cvttsd2si(lo, src);
code.punpckhqdq(src, src);
code.cvttsd2si(hi, src);
code.movq(src, lo);
code.pinsrq(src, hi, 1);
// Will we exceed unsigned range?
const Xbyak::Xmm exceed_unsigned = ctx.reg_alloc.ScratchXmm(code);
code.movaps(exceed_unsigned, GetVectorOf<fsize, float_upper_limit_unsigned>(code));
FCODE(cmplep)(exceed_unsigned, src);
ctx.reg_alloc.Release(hi);
ctx.reg_alloc.Release(lo);
}
}
};
if (fbits != 0) {
const u64 scale_factor = fsize == 32
? u64(fbits + 127) << 23
: u64(fbits + 1023) << 52;
FCODE(mulp)(src, GetVectorOf<fsize>(code, scale_factor));
}
// Will be exceed signed range?
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
code.movaps(tmp, GetVectorOf<fsize, float_upper_limit_signed>(code));
code.movaps(xmm0, tmp);
FCODE(cmplep)(xmm0, src);
FCODE(andp)(tmp, xmm0);
FCODE(subp)(src, tmp);
perform_conversion(src);
ICODE(psll)(xmm0, u8(fsize - 1));
FCODE(orp)(src, xmm0);
FCODE(roundp)(src, src, u8(round_imm));
const Xbyak::Xmm nan_mask = xmm0;
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
static constexpr u32 nan_to_zero = FixupLUT(FpFixup::PosZero, FpFixup::PosZero);
FCODE(vfixupimmp)(src, src, code.BConst<32>(ptr_b, nan_to_zero), u8(0));
} else if (code.HasHostFeature(HostFeature::AVX)) {
FCODE(vcmpordp)(nan_mask, src, src);
FCODE(vandp)(src, src, nan_mask);
} else {
code.movaps(nan_mask, src);
FCODE(cmpordp)(nan_mask, nan_mask);
code.andps(src, nan_mask);
}
constexpr u64 float_upper_limit_signed = fsize == 32 ? 0x4f000000 : 0x43e0000000000000;
[[maybe_unused]] constexpr u64 float_upper_limit_unsigned = fsize == 32 ? 0x4f800000 : 0x43f0000000000000;
if constexpr (unsigned_) {
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
// Mask positive values
code.xorps(xmm0, xmm0);
FCODE(vcmpp)(k1, src, xmm0, Cmp::GreaterEqual_OQ);
// Convert positive values to unsigned integers, write 0 anywhere else
// vcvttp*2u*q already saturates out-of-range values to (0xFFFF...)
if (fsize == 32) {
code.vcvttps2udq(src | k1 | T_z, src);
} else {
code.vcvttpd2uqq(src | k1 | T_z, src);
// Saturate to max
FCODE(orp)(src, exceed_unsigned);
}
} else {
// Zero is minimum
code.xorps(xmm0, xmm0);
FCODE(cmplep)(xmm0, src);
FCODE(andp)(src, xmm0);
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
constexpr u64 integer_max = FPT((std::numeric_limits<std::conditional_t<unsigned_, FPT, std::make_signed_t<FPT>>>::max)());
// Will we exceed unsigned range?
const Xbyak::Xmm exceed_unsigned = ctx.reg_alloc.ScratchXmm(code);
code.movaps(exceed_unsigned, GetVectorOf<fsize, float_upper_limit_unsigned>(code));
FCODE(cmplep)(exceed_unsigned, src);
// Will be exceed signed range?
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
code.movaps(tmp, GetVectorOf<fsize, float_upper_limit_signed>(code));
code.movaps(xmm0, tmp);
code.movaps(xmm0, GetVectorOf<fsize, float_upper_limit_signed>(code));
FCODE(cmplep)(xmm0, src);
FCODE(andp)(tmp, xmm0);
FCODE(subp)(src, tmp);
perform_conversion(src);
ICODE(psll)(xmm0, u8(fsize - 1));
FCODE(orp)(src, xmm0);
// Saturate to max
FCODE(orp)(src, exceed_unsigned);
FCODE(blendvp)(src, GetVectorOf<fsize, integer_max>(code));
}
} else {
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
constexpr u64 integer_max = FPT((std::numeric_limits<std::conditional_t<unsigned_, FPT, std::make_signed_t<FPT>>>::max)());
code.movaps(xmm0, GetVectorOf<fsize, float_upper_limit_signed>(code));
FCODE(cmplep)(xmm0, src);
perform_conversion(src);
FCODE(blendvp)(src, GetVectorOf<fsize, integer_max>(code));
}
});
ctx.reg_alloc.DefineValue(code, inst, src);
return;
});
ctx.reg_alloc.DefineValue(code, inst, src);
return;
}
}
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@@ -176,7 +176,7 @@ struct ExceptionHandler::Impl final {
code.align(16);
const u8* exception_handler_without_cb = code.getCurr<u8*>();
code.mov(code.eax, u32(ExceptionContinueSearch));
code.mov(code.eax, static_cast<u32>(ExceptionContinueSearch));
code.ret();
code.align(16);
@@ -192,18 +192,20 @@ struct ExceptionHandler::Impl final {
code.lea(code.rsp, code.ptr[code.rsp - 8]);
code.mov(code.ABI_PARAM1, std::bit_cast<u64>(&cb));
code.mov(code.ABI_PARAM2, code.ABI_PARAM3);
code.CallLambda([](const std::function<FakeCall(u64)>& cb_, PCONTEXT ctx) {
FakeCall fc = cb_(ctx->Rip);
ctx->Rsp -= sizeof(u64);
*std::bit_cast<u64*>(ctx->Rsp) = fc.ret_rip;
ctx->Rip = fc.call_rip;
});
code.CallLambda(
[](const std::function<FakeCall(u64)>& cb_, PCONTEXT ctx) {
FakeCall fc = cb_(ctx->Rip);
ctx->Rsp -= sizeof(u64);
*std::bit_cast<u64*>(ctx->Rsp) = fc.ret_rip;
ctx->Rip = fc.call_rip;
});
code.add(code.rsp, 8);
code.mov(code.eax, u32(ExceptionContinueExecution));
code.mov(code.eax, static_cast<u32>(ExceptionContinueExecution));
code.ret();
exception_handler_without_cb_offset = ULONG(exception_handler_without_cb - code.getCode<u8*>());
exception_handler_with_cb_offset = ULONG(exception_handler_with_cb - code.getCode<u8*>());
exception_handler_without_cb_offset = static_cast<ULONG>(exception_handler_without_cb - code.getCode<u8*>());
exception_handler_with_cb_offset = static_cast<ULONG>(exception_handler_with_cb - code.getCode<u8*>());
code.align(16);
UNWIND_INFO* unwind_info = static_cast<UNWIND_INFO*>(code.AllocateFromCodeSpace(sizeof(UNWIND_INFO)));
@@ -417,8 +417,7 @@ HostLoc RegAlloc::SelectARegister(std::bitset<32> desired_locations) const noexc
// While R13 and R14 are technically available, we avoid allocating for them
// at all costs, because theoretically skipping them is better than spilling
// all over the place - i also fixes bugs with high reg pressure
// %rbp must not be trashed, so skip it as well
} else if (i == HostLoc::RBP || (i >= HostLoc::R13 && i <= HostLoc::R15)) {
} else if (i >= HostLoc::R13 && i <= HostLoc::R15) {
// skip, do not touch
// Intel recommends to reuse registers as soon as they're overwritable (DO NOT SPILL)
} else if (loc_info.IsEmpty()) {
@@ -50,7 +50,7 @@ public:
}
inline void ReadLock() noexcept {
ASSERT(size_t(is_being_used_count) + 1 < (std::numeric_limits<decltype(is_being_used_count)>::max)());
ASSERT(!bool(is_scratch));
ASSERT(!is_scratch);
is_being_used_count++;
}
inline void WriteLock() noexcept {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@@ -22,13 +22,14 @@ constexpr size_t SpillCount = 64;
#endif
struct alignas(16) StackLayout {
// Needs alignment for VMOV and XMM spills
alignas(16) std::array<std::array<u64, 2>, SpillCount> spill;
s64 cycles_remaining;
s64 cycles_to_run;
std::array<std::array<u64, 2>, SpillCount> spill;
u32 save_host_MXCSR;
bool check_bit;
u64 abi_base_pointer;
};
#ifdef _MSC_VER
@@ -36,19 +36,25 @@ inline size_t ToFastLookupIndexArm(u32 instruction) noexcept {
} // namespace detail
template<typename V>
static ArmDecodeTable<V> GetArmDecodeTable() noexcept {
ArmDecodeTable<V> table{};
for (size_t i = 0; i < table.size(); ++i) {
// PLEASE HEAP ELLIDE
for (auto const& e : std::vector<ArmMatcher<V>>{
constexpr ArmDecodeTable<V> GetArmDecodeTable() noexcept {
std::vector<ArmMatcher<V>> list = {
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
#include "./arm.inc"
#undef INST
}) {
auto const expect = detail::ToFastLookupIndexArm(e.GetExpected());
auto const mask = detail::ToFastLookupIndexArm(e.GetMask());
};
// If a matcher has more bits in its mask it is more specific, so it should come first.
std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) {
return mcl::bit::count_ones(matcher1.GetMask()) > mcl::bit::count_ones(matcher2.GetMask());
});
ArmDecodeTable<V> table{};
for (size_t i = 0; i < table.size(); ++i) {
for (auto matcher : list) {
const auto expect = detail::ToFastLookupIndexArm(matcher.GetExpected());
const auto mask = detail::ToFastLookupIndexArm(matcher.GetMask());
if ((i & mask) == expect) {
table[i].push_back(e);
table[i].push_back(matcher);
}
}
}
@@ -56,7 +62,7 @@ static ArmDecodeTable<V> GetArmDecodeTable() noexcept {
}
template<typename V>
static std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) noexcept {
std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) noexcept {
alignas(64) static const auto table = GetArmDecodeTable<V>();
const auto matches_instruction = [instruction](const auto& matcher) {
return matcher.Matches(instruction);
@@ -67,7 +73,7 @@ static std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32
}
template<typename V>
static std::optional<std::string_view> GetNameARM(u32 inst) noexcept {
std::optional<std::string_view> GetNameARM(u32 inst) noexcept {
std::vector<std::pair<std::string_view, ArmMatcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./arm.inc"
@@ -1,265 +1,316 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// DO NOT REORDER
// Barrier instructions
INST(arm_DMB, "DMB", "1111010101111111111100000101oooo") // v7
INST(arm_DSB, "DSB", "1111010101111111111100000100oooo") // v7
INST(arm_ISB, "ISB", "1111010101111111111100000110oooo") // v7
INST(arm_CLREX, "CLREX", "11110101011111111111000000011111")
INST(arm_SETEND, "SETEND", "1111000100000001000000e000000000")
INST(arm_DMB, "DMB", "1111010101111111111100000101oooo")
INST(arm_DSB, "DSB", "1111010101111111111100000100oooo")
INST(arm_ISB, "ISB", "1111010101111111111100000110oooo")
INST(arm_SEV, "SEV", "----0011001000001111000000000100")
INST(arm_SEVL, "SEVL", "----0011001000001111000000000101")
INST(arm_WFE, "WFE", "----0011001000001111000000000010")
INST(arm_WFI, "WFI", "----0011001000001111000000000011")
INST(arm_YIELD, "YIELD", "----0011001000001111000000000001")
INST(arm_NOP, "NOP", "----0011001000001111000000000000")
INST(arm_RFE, "RFE", "1111100--0-1----0000101000000000")
INST(arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm")
INST(arm_BX, "BX", "cccc000100101111111111110001mmmm")
INST(arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm")
INST(arm_NOP, "Reserved Hint", "----001100100000111100000000----")
INST(arm_MRS, "MRS", "cccc000100001111dddd000000000000")
INST(arm_SRS, "SRS", "1111100--1-0110100000101000-----")
INST(arm_CPS, "CPS", "111100010000---00000000---0-----")
INST(arm_STL, "STL", "cccc00011000nnnn111111001001tttt")
INST(arm_LDA, "LDA", "cccc00011001nnnndddd110010011111")
INST(arm_LDAEX, "LDAEX", "cccc00011001nnnndddd111010011111")
INST(arm_LDREX, "LDREX", "cccc00011001nnnndddd111110011111")
INST(arm_LDAEXD, "LDAEXD", "cccc00011011nnnndddd111010011111")
INST(arm_LDREXD, "LDREXD", "cccc00011011nnnndddd111110011111")
INST(arm_STLB, "STLB", "cccc00011100nnnn111111001001tttt")
INST(arm_LDAB, "LDAB", "cccc00011101nnnndddd110010011111")
INST(arm_LDAEXB, "LDAEXB", "cccc00011101nnnndddd111010011111")
INST(arm_LDREXB, "LDREXB", "cccc00011101nnnndddd111110011111")
INST(arm_STLH, "STLH", "cccc00011110nnnn111111001001mmmm")
INST(arm_LDAH, "LDAH", "cccc00011111nnnndddd110010011111")
INST(arm_LDAEXH, "LDAEXH", "cccc00011111nnnndddd111010011111")
INST(arm_LDREXH, "LDREXH", "cccc00011111nnnndddd111110011111")
INST(arm_CLZ, "CLZ", "cccc000101101111dddd11110001mmmm")
INST(arm_RBIT, "RBIT", "cccc011011111111dddd11110011mmmm")
INST(arm_REV, "REV", "cccc011010111111dddd11110011mmmm")
INST(arm_REV16, "REV16", "cccc011010111111dddd11111011mmmm")
INST(arm_REVSH, "REVSH", "cccc011011111111dddd11111011mmmm")
INST(arm_MSR_reg, "MSR (reg)", "cccc00010010mmmm111100000000nnnn")
INST(arm_SXTB, "SXTB", "cccc011010101111ddddrr000111mmmm")
INST(arm_SXTB16, "SXTB16", "cccc011010001111ddddrr000111mmmm")
INST(arm_SXTH, "SXTH", "cccc011010111111ddddrr000111mmmm")
INST(arm_UXTB, "UXTB", "cccc011011101111ddddrr000111mmmm")
INST(arm_UXTB16, "UXTB16", "cccc011011001111ddddrr000111mmmm")
INST(arm_UXTH, "UXTH", "cccc011011111111ddddrr000111mmmm")
INST(arm_UDF, "UDF", "111001111111------------1111----")
INST(arm_NOP, "Reserved Hint", "----0011001000001111------------")
INST(arm_SWP, "SWP", "cccc00010000nnnntttt00001001uuuu")
INST(arm_SWPB, "SWPB", "cccc00010100nnnntttt00001001uuuu")
INST(arm_STLEX, "STLEX", "cccc00011000nnnndddd11101001tttt")
INST(arm_STREX, "STREX", "cccc00011000nnnndddd11111001mmmm")
INST(arm_STLEXD, "STLEXD", "cccc00011010nnnndddd11101001mmmm")
INST(arm_STREXD, "STREXD", "cccc00011010nnnndddd11111001mmmm")
INST(arm_STLEXB, "STLEXB", "cccc00011100nnnndddd11101001mmmm")
INST(arm_STREXB, "STREXB", "cccc00011100nnnndddd11111001mmmm")
INST(arm_STLEXH, "STLEXH", "cccc00011110nnnndddd11101001mmmm")
INST(arm_STREXH, "STREXH", "cccc00011110nnnndddd11111001mmmm")
INST(arm_SEL, "SEL", "cccc01101000nnnndddd11111011mmmm")
INST(arm_USAD8, "USAD8", "cccc01111000dddd1111mmmm0001nnnn")
INST(arm_SSAT16, "SSAT16", "cccc01101010vvvvdddd11110011nnnn")
INST(arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn")
INST(arm_SDIV, "SDIV", "cccc01110001dddd1111mmmm0001nnnn")
INST(arm_UDIV, "UDIV", "cccc01110011dddd1111mmmm0001nnnn")
INST(arm_SADD8, "SADD8", "cccc01100001nnnndddd11111001mmmm")
INST(arm_SADD16, "SADD16", "cccc01100001nnnndddd11110001mmmm")
INST(arm_SASX, "SASX", "cccc01100001nnnndddd11110011mmmm")
INST(arm_SSAX, "SSAX", "cccc01100001nnnndddd11110101mmmm")
INST(arm_SSUB8, "SSUB8", "cccc01100001nnnndddd11111111mmmm")
INST(arm_SSUB16, "SSUB16", "cccc01100001nnnndddd11110111mmmm")
INST(arm_UADD8, "UADD8", "cccc01100101nnnndddd11111001mmmm")
INST(arm_UADD16, "UADD16", "cccc01100101nnnndddd11110001mmmm")
INST(arm_UASX, "UASX", "cccc01100101nnnndddd11110011mmmm")
INST(arm_USAX, "USAX", "cccc01100101nnnndddd11110101mmmm")
INST(arm_USUB8, "USUB8", "cccc01100101nnnndddd11111111mmmm")
INST(arm_USUB16, "USUB16", "cccc01100101nnnndddd11110111mmmm")
INST(arm_QADD8, "QADD8", "cccc01100010nnnndddd11111001mmmm")
INST(arm_QADD16, "QADD16", "cccc01100010nnnndddd11110001mmmm")
INST(arm_QASX, "QASX", "cccc01100010nnnndddd11110011mmmm")
INST(arm_QSAX, "QSAX", "cccc01100010nnnndddd11110101mmmm")
INST(arm_QSUB8, "QSUB8", "cccc01100010nnnndddd11111111mmmm")
INST(arm_QSUB16, "QSUB16", "cccc01100010nnnndddd11110111mmmm")
INST(arm_UQADD8, "UQADD8", "cccc01100110nnnndddd11111001mmmm")
INST(arm_UQADD16, "UQADD16", "cccc01100110nnnndddd11110001mmmm")
INST(arm_UQASX, "UQASX", "cccc01100110nnnndddd11110011mmmm")
INST(arm_UQSAX, "UQSAX", "cccc01100110nnnndddd11110101mmmm")
INST(arm_UQSUB8, "UQSUB8", "cccc01100110nnnndddd11111111mmmm")
INST(arm_UQSUB16, "UQSUB16", "cccc01100110nnnndddd11110111mmmm")
INST(arm_SHADD8, "SHADD8", "cccc01100011nnnndddd11111001mmmm")
INST(arm_SHADD16, "SHADD16", "cccc01100011nnnndddd11110001mmmm")
INST(arm_SHASX, "SHASX", "cccc01100011nnnndddd11110011mmmm")
INST(arm_SHSAX, "SHSAX", "cccc01100011nnnndddd11110101mmmm")
INST(arm_SHSUB8, "SHSUB8", "cccc01100011nnnndddd11111111mmmm")
INST(arm_SHSUB16, "SHSUB16", "cccc01100011nnnndddd11110111mmmm")
INST(arm_UHADD8, "UHADD8", "cccc01100111nnnndddd11111001mmmm")
INST(arm_UHADD16, "UHADD16", "cccc01100111nnnndddd11110001mmmm")
INST(arm_UHASX, "UHASX", "cccc01100111nnnndddd11110011mmmm")
INST(arm_UHSAX, "UHSAX", "cccc01100111nnnndddd11110101mmmm")
INST(arm_UHSUB8, "UHSUB8", "cccc01100111nnnndddd11111111mmmm")
INST(arm_UHSUB16, "UHSUB16", "cccc01100111nnnndddd11110111mmmm")
INST(arm_QADD, "QADD", "cccc00010000nnnndddd00000101mmmm")
INST(arm_QSUB, "QSUB", "cccc00010010nnnndddd00000101mmmm")
INST(arm_QDADD, "QDADD", "cccc00010100nnnndddd00000101mmmm")
INST(arm_QDSUB, "QDSUB", "cccc00010110nnnndddd00000101mmmm")
INST(arm_PLD_reg, "PLD (reg)", "11110111uz01nnnn1111iiiiitt0mmmm")
INST(arm_LDRHT, "LDRHT (A1)", "----0000-1111111--------1011----")
INST(arm_LDRHT, "LDRHT (A2)", "----0000-011--------00001011----")
INST(arm_LDRSBT, "LDRSBT (A2)", "----0000-011--------00001101----")
INST(arm_LDRSHT, "LDRSHT (A2)", "----0000-011--------00001111----")
INST(arm_STRHT, "STRHT (A2)", "----0000-010--------00001011----")
INST(arm_LDRD_lit, "LDRD (lit)", "cccc0001u1001111ttttvvvv1101vvvv")
INST(arm_LDRSB_lit, "LDRSB (lit)", "cccc0001u1011111ttttvvvv1101vvvv")
INST(arm_LDRSH_lit, "LDRSH (lit)", "cccc0001u1011111ttttvvvv1111vvvv")
INST(arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn")
INST(arm_SMULWy, "SMULWY", "cccc00010010dddd0000mmmm1y10nnnn")
INST(arm_SMMUL, "SMMUL", "cccc01110101dddd1111mmmm00R1nnnn")
INST(arm_SMUAD, "SMUAD", "cccc01110000dddd1111mmmm00M1nnnn")
INST(arm_SMUSD, "SMUSD", "cccc01110000dddd1111mmmm01M1nnnn")
INST(arm_CRC32, "CRC32", "cccc00010zz0nnnndddd00000100mmmm")
INST(arm_CRC32C, "CRC32C", "cccc00010zz0nnnndddd00100100mmmm")
INST(arm_CMN_rsr, "CMN (rsr)", "cccc00010111nnnn0000ssss0rr1mmmm")
INST(arm_CMP_rsr, "CMP (rsr)", "cccc00010101nnnn0000ssss0rr1mmmm")
INST(arm_TEQ_rsr, "TEQ (rsr)", "cccc00010011nnnn0000ssss0rr1mmmm")
INST(arm_TST_rsr, "TST (rsr)", "cccc00010001nnnn0000ssss0rr1mmmm")
INST(arm_SXTAB, "SXTAB", "cccc01101010nnnnddddrr000111mmmm")
INST(arm_SXTAB16, "SXTAB16", "cccc01101000nnnnddddrr000111mmmm")
INST(arm_SXTAH, "SXTAH", "cccc01101011nnnnddddrr000111mmmm")
INST(arm_UXTAB, "UXTAB", "cccc01101110nnnnddddrr000111mmmm")
INST(arm_UXTAB16, "UXTAB16", "cccc01101100nnnnddddrr000111mmmm")
INST(arm_UXTAH, "UXTAH", "cccc01101111nnnnddddrr000111mmmm")
INST(arm_PLD_imm, "PLD (imm)", "11110101uz01nnnn1111iiiiiiiiiiii")
INST(arm_BFC, "BFC", "cccc0111110vvvvvddddvvvvv0011111")
INST(arm_SMULxy, "SMULXY", "cccc00010110dddd0000mmmm1xy0nnnn")
INST(arm_CMN_reg, "CMN (reg)", "cccc00010111nnnn0000vvvvvrr0mmmm")
INST(arm_CMP_reg, "CMP (reg)", "cccc00010101nnnn0000vvvvvrr0mmmm")
INST(arm_MOV_rsr, "MOV (rsr)", "cccc0001101S0000ddddssss0rr1mmmm")
INST(arm_MVN_rsr, "MVN (rsr)", "cccc0001111S0000ddddssss0rr1mmmm")
INST(arm_TEQ_reg, "TEQ (reg)", "cccc00010011nnnn0000vvvvvrr0mmmm")
INST(arm_TST_reg, "TST (reg)", "cccc00010001nnnn0000vvvvvrr0mmmm")
INST(arm_LDRD_reg, "LDRD (reg)", "cccc000pu0w0nnnntttt00001101mmmm")
INST(arm_LDRH_lit, "LDRH (lit)", "cccc000pu1w11111ttttvvvv1011vvvv")
INST(arm_LDRH_reg, "LDRH (reg)", "cccc000pu0w1nnnntttt00001011mmmm")
INST(arm_LDRSB_reg, "LDRSB (reg)", "cccc000pu0w1nnnntttt00001101mmmm")
INST(arm_LDRSH_reg, "LDRSH (reg)", "cccc000pu0w1nnnntttt00001111mmmm")
INST(arm_STRD_reg, "STRD (reg)", "cccc000pu0w0nnnntttt00001111mmmm")
INST(arm_STRH_reg, "STRH (reg)", "cccc000pu0w0nnnntttt00001011mmmm")
INST(arm_CMN_imm, "CMN (imm)", "cccc00110111nnnn0000rrrrvvvvvvvv")
INST(arm_CMP_imm, "CMP (imm)", "cccc00110101nnnn0000rrrrvvvvvvvv")
INST(arm_MOV_reg, "MOV (reg)", "cccc0001101S0000ddddvvvvvrr0mmmm")
INST(arm_MVN_reg, "MVN (reg)", "cccc0001111S0000ddddvvvvvrr0mmmm")
INST(arm_TEQ_imm, "TEQ (imm)", "cccc00110011nnnn0000rrrrvvvvvvvv")
INST(arm_TST_imm, "TST (imm)", "cccc00110001nnnn0000rrrrvvvvvvvv")
INST(arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv")
INST(arm_USADA8, "USADA8", "cccc01111000ddddaaaammmm0001nnnn")
INST(arm_MLS, "MLS", "cccc00000110ddddaaaammmm1001nnnn")
INST(arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn")
INST(arm_MSR_imm, "MSR (imm)", "cccc00110010mmmm1111rrrrvvvvvvvv")
INST(arm_MOV_imm, "MOV (imm)", "cccc0011101S0000ddddrrrrvvvvvvvv")
INST(arm_MVN_imm, "MVN (imm)", "cccc0011111S0000ddddrrrrvvvvvvvv")
INST(arm_LDRHT, "LDRHT (A1)", "----0000-111------------1011----")
INST(arm_LDRSBT, "LDRSBT (A1)", "----0000-111------------1101----")
INST(arm_LDRSHT, "LDRSHT (A1)", "----0000-111------------1111----")
INST(arm_STRHT, "STRHT (A1)", "----0000-110------------1011----")
INST(arm_LDR_lit, "LDR (lit)", "cccc0101u0011111ttttvvvvvvvvvvvv")
INST(arm_LDRB_lit, "LDRB (lit)", "cccc0101u1011111ttttvvvvvvvvvvvv")
INST(arm_PKHBT, "PKHBT", "cccc01101000nnnnddddvvvvv001mmmm")
INST(arm_PKHTB, "PKHTB", "cccc01101000nnnnddddvvvvv101mmmm")
INST(arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn")
INST(arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn")
INST(arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn")
INST(arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn")
INST(arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn")
INST(arm_SMLAWy, "SMLAWY", "cccc00010010ddddaaaammmm1y00nnnn")
INST(arm_SMMLA, "SMMLA", "cccc01110101ddddaaaammmm00R1nnnn")
INST(arm_SMMLS, "SMMLS", "cccc01110101ddddaaaammmm11R1nnnn")
INST(arm_SMLAD, "SMLAD", "cccc01110000ddddaaaammmm00M1nnnn")
INST(arm_SMLALD, "SMLALD", "cccc01110100ddddaaaammmm00M1nnnn")
INST(arm_SMLSD, "SMLSD", "cccc01110000ddddaaaammmm01M1nnnn")
INST(arm_SMLSLD, "SMLSLD", "cccc01110100ddddaaaammmm01M1nnnn")
INST(arm_BFI, "BFI", "cccc0111110vvvvvddddvvvvv001nnnn")
INST(arm_SBFX, "SBFX", "cccc0111101wwwwwddddvvvvv101nnnn")
INST(arm_UBFX, "UBFX", "cccc0111111wwwwwddddvvvvv101nnnn")
INST(arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn")
INST(arm_SMLAxy, "SMLAXY", "cccc00010000ddddaaaammmm1xy0nnnn")
INST(arm_ADC_rsr, "ADC (rsr)", "cccc0000101Snnnnddddssss0rr1mmmm")
INST(arm_ADD_rsr, "ADD (rsr)", "cccc0000100Snnnnddddssss0rr1mmmm")
INST(arm_AND_rsr, "AND (rsr)", "cccc0000000Snnnnddddssss0rr1mmmm")
INST(arm_BIC_rsr, "BIC (rsr)", "cccc0001110Snnnnddddssss0rr1mmmm")
INST(arm_EOR_rsr, "EOR (rsr)", "cccc0000001Snnnnddddssss0rr1mmmm")
INST(arm_ORR_rsr, "ORR (rsr)", "cccc0001100Snnnnddddssss0rr1mmmm")
INST(arm_RSB_rsr, "RSB (rsr)", "cccc0000011Snnnnddddssss0rr1mmmm")
INST(arm_RSC_rsr, "RSC (rsr)", "cccc0000111Snnnnddddssss0rr1mmmm")
INST(arm_SBC_rsr, "SBC (rsr)", "cccc0000110Snnnnddddssss0rr1mmmm")
INST(arm_SUB_rsr, "SUB (rsr)", "cccc0000010Snnnnddddssss0rr1mmmm")
INST(arm_LDRD_imm, "LDRD (imm)", "cccc000pu1w0nnnnttttvvvv1101vvvv")
INST(arm_LDRH_imm, "LDRH (imm)", "cccc000pu1w1nnnnttttvvvv1011vvvv")
INST(arm_LDRSB_imm, "LDRSB (imm)", "cccc000pu1w1nnnnttttvvvv1101vvvv")
INST(arm_LDRSH_imm, "LDRSH (imm)", "cccc000pu1w1nnnnttttvvvv1111vvvv")
INST(arm_STRD_imm, "STRD (imm)", "cccc000pu1w0nnnnttttvvvv1111vvvv")
INST(arm_STRH_imm, "STRH (imm)", "cccc000pu1w0nnnnttttvvvv1011vvvv")
INST(arm_SSAT, "SSAT", "cccc0110101vvvvvddddvvvvvr01nnnn")
INST(arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn")
INST(arm_MCRR, "MCRR", "cccc11000100uuuuttttppppooooMMMM")
INST(arm_MRRC, "MRRC", "cccc11000101uuuuttttppppooooMMMM")
INST(arm_ADC_reg, "ADC (reg)", "cccc0000101Snnnnddddvvvvvrr0mmmm")
INST(arm_ADD_reg, "ADD (reg)", "cccc0000100Snnnnddddvvvvvrr0mmmm")
INST(arm_AND_reg, "AND (reg)", "cccc0000000Snnnnddddvvvvvrr0mmmm")
INST(arm_BIC_reg, "BIC (reg)", "cccc0001110Snnnnddddvvvvvrr0mmmm")
INST(arm_EOR_reg, "EOR (reg)", "cccc0000001Snnnnddddvvvvvrr0mmmm")
INST(arm_ORR_reg, "ORR (reg)", "cccc0001100Snnnnddddvvvvvrr0mmmm")
INST(arm_RSB_reg, "RSB (reg)", "cccc0000011Snnnnddddvvvvvrr0mmmm")
INST(arm_RSC_reg, "RSC (reg)", "cccc0000111Snnnnddddvvvvvrr0mmmm")
INST(arm_SBC_reg, "SBC (reg)", "cccc0000110Snnnnddddvvvvvrr0mmmm")
INST(arm_SUB_reg, "SUB (reg)", "cccc0000010Snnnnddddvvvvvrr0mmmm")
INST(arm_LDRBT, "LDRBT (A2)", "----0110-111---------------0----")
INST(arm_LDRT, "LDRT (A2)", "----0110-011---------------0----")
INST(arm_STRBT, "STRBT (A2)", "----0110-110---------------0----")
INST(arm_STRT, "STRT (A2)", "----0110-010---------------0----")
INST(arm_MOVT, "MOVT", "cccc00110100vvvvddddvvvvvvvvvvvv")
INST(arm_MOVW, "MOVW", "cccc00110000vvvvddddvvvvvvvvvvvv")
INST(arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv")
INST(arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv")
INST(arm_ADD_imm, "ADD (imm)", "cccc0010100Snnnnddddrrrrvvvvvvvv")
INST(arm_AND_imm, "AND (imm)", "cccc0010000Snnnnddddrrrrvvvvvvvv")
INST(arm_BIC_imm, "BIC (imm)", "cccc0011110Snnnnddddrrrrvvvvvvvv")
INST(arm_EOR_imm, "EOR (imm)", "cccc0010001Snnnnddddrrrrvvvvvvvv")
INST(arm_ORR_imm, "ORR (imm)", "cccc0011100Snnnnddddrrrrvvvvvvvv")
INST(arm_RSB_imm, "RSB (imm)", "cccc0010011Snnnnddddrrrrvvvvvvvv")
INST(arm_RSC_imm, "RSC (imm)", "cccc0010111Snnnnddddrrrrvvvvvvvv")
INST(arm_SBC_imm, "SBC (imm)", "cccc0010110Snnnnddddrrrrvvvvvvvv")
INST(arm_SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv")
INST(arm_LDRBT, "LDRBT (A1)", "----0100-111--------------------")
INST(arm_LDRT, "LDRT (A1)", "----0100-011--------------------")
INST(arm_STRBT, "STRBT (A1)", "----0100-110--------------------")
INST(arm_STRT, "STRT (A1)", "----0100-010--------------------")
INST(arm_LDM, "LDM", "cccc100010w1nnnnxxxxxxxxxxxxxxxx")
INST(arm_LDMDA, "LDMDA", "cccc100000w1nnnnxxxxxxxxxxxxxxxx")
INST(arm_LDMDB, "LDMDB", "cccc100100w1nnnnxxxxxxxxxxxxxxxx")
INST(arm_LDMIB, "LDMIB", "cccc100110w1nnnnxxxxxxxxxxxxxxxx")
INST(arm_STM, "STM", "cccc100010w0nnnnxxxxxxxxxxxxxxxx")
INST(arm_STMDA, "STMDA", "cccc100000w0nnnnxxxxxxxxxxxxxxxx")
INST(arm_STMDB, "STMDB", "cccc100100w0nnnnxxxxxxxxxxxxxxxx")
INST(arm_STMIB, "STMIB", "cccc100110w0nnnnxxxxxxxxxxxxxxxx")
INST(arm_MCR, "MCR", "cccc1110ooo0NNNNttttppppooo1MMMM")
INST(arm_MRC, "MRC", "cccc1110ooo1NNNNttttppppooo1MMMM")
INST(arm_LDR_reg, "LDR (reg)", "cccc011pu0w1nnnnttttvvvvvrr0mmmm")
INST(arm_LDRB_reg, "LDRB (reg)", "cccc011pu1w1nnnnttttvvvvvrr0mmmm")
INST(arm_STR_reg, "STR (reg)", "cccc011pu0w0nnnnttttvvvvvrr0mmmm")
INST(arm_STRB_reg, "STRB (reg)", "cccc011pu1w0nnnnttttvvvvvrr0mmmm")
INST(arm_LDM_usr, "LDM (usr reg)", "----100--101--------------------")
INST(arm_LDM_eret, "LDM (exce ret)", "----100--1-1----1---------------")
INST(arm_STM_usr, "STM (usr reg)", "----100--100--------------------")
INST(arm_CDP, "CDP", "cccc1110ooooNNNNDDDDppppooo0MMMM")
INST(arm_LDR_imm, "LDR (imm)", "cccc010pu0w1nnnnttttvvvvvvvvvvvv")
INST(arm_LDRB_imm, "LDRB (imm)", "cccc010pu1w1nnnnttttvvvvvvvvvvvv")
INST(arm_STR_imm, "STR (imm)", "cccc010pu0w0nnnnttttvvvvvvvvvvvv")
INST(arm_STRB_imm, "STRB (imm)", "cccc010pu1w0nnnnttttvvvvvvvvvvvv")
INST(arm_B, "B", "cccc1010vvvvvvvvvvvvvvvvvvvvvvvv")
INST(arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv")
INST(arm_LDC, "LDC", "cccc110pudw1nnnnDDDDppppvvvvvvvv")
INST(arm_STC, "STC", "cccc110pudw0nnnnDDDDppppvvvvvvvv")
INST(arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv")
// Branch instructions
INST(arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv") // v5
INST(arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm") // v5
INST(arm_B, "B", "cccc1010vvvvvvvvvvvvvvvvvvvvvvvv") // v1
INST(arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv") // v1
INST(arm_BX, "BX", "cccc000100101111111111110001mmmm") // v4T
INST(arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm") // v5J
// CRC32 instructions
INST(arm_CRC32, "CRC32", "cccc00010zz0nnnndddd00000100mmmm") // v8
INST(arm_CRC32C, "CRC32C", "cccc00010zz0nnnndddd00100100mmmm") // v8
// Coprocessor instructions
INST(arm_CDP, "CDP", "cccc1110ooooNNNNDDDDppppooo0MMMM") // v2 (CDP2: v5)
INST(arm_LDC, "LDC", "cccc110pudw1nnnnDDDDppppvvvvvvvv") // v2 (LDC2: v5)
INST(arm_MCR, "MCR", "cccc1110ooo0NNNNttttppppooo1MMMM") // v2 (MCR2: v5)
INST(arm_MCRR, "MCRR", "cccc11000100uuuuttttppppooooMMMM") // v5E (MCRR2: v6)
INST(arm_MRC, "MRC", "cccc1110ooo1NNNNttttppppooo1MMMM") // v2 (MRC2: v5)
INST(arm_MRRC, "MRRC", "cccc11000101uuuuttttppppooooMMMM") // v5E (MRRC2: v6)
INST(arm_STC, "STC", "cccc110pudw0nnnnDDDDppppvvvvvvvv") // v2 (STC2: v5)
// Data Processing instructions
INST(arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_ADC_reg, "ADC (reg)", "cccc0000101Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_ADC_rsr, "ADC (rsr)", "cccc0000101Snnnnddddssss0rr1mmmm") // v1
INST(arm_ADD_imm, "ADD (imm)", "cccc0010100Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_ADD_reg, "ADD (reg)", "cccc0000100Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_ADD_rsr, "ADD (rsr)", "cccc0000100Snnnnddddssss0rr1mmmm") // v1
INST(arm_AND_imm, "AND (imm)", "cccc0010000Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_AND_reg, "AND (reg)", "cccc0000000Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_AND_rsr, "AND (rsr)", "cccc0000000Snnnnddddssss0rr1mmmm") // v1
INST(arm_BIC_imm, "BIC (imm)", "cccc0011110Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_BIC_reg, "BIC (reg)", "cccc0001110Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_BIC_rsr, "BIC (rsr)", "cccc0001110Snnnnddddssss0rr1mmmm") // v1
INST(arm_CMN_imm, "CMN (imm)", "cccc00110111nnnn0000rrrrvvvvvvvv") // v1
INST(arm_CMN_reg, "CMN (reg)", "cccc00010111nnnn0000vvvvvrr0mmmm") // v1
INST(arm_CMN_rsr, "CMN (rsr)", "cccc00010111nnnn0000ssss0rr1mmmm") // v1
INST(arm_CMP_imm, "CMP (imm)", "cccc00110101nnnn0000rrrrvvvvvvvv") // v1
INST(arm_CMP_reg, "CMP (reg)", "cccc00010101nnnn0000vvvvvrr0mmmm") // v1
INST(arm_CMP_rsr, "CMP (rsr)", "cccc00010101nnnn0000ssss0rr1mmmm") // v1
INST(arm_EOR_imm, "EOR (imm)", "cccc0010001Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_EOR_reg, "EOR (reg)", "cccc0000001Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_EOR_rsr, "EOR (rsr)", "cccc0000001Snnnnddddssss0rr1mmmm") // v1
INST(arm_MOV_imm, "MOV (imm)", "cccc0011101S0000ddddrrrrvvvvvvvv") // v1
INST(arm_MOV_reg, "MOV (reg)", "cccc0001101S0000ddddvvvvvrr0mmmm") // v1
INST(arm_MOV_rsr, "MOV (rsr)", "cccc0001101S0000ddddssss0rr1mmmm") // v1
INST(arm_MVN_imm, "MVN (imm)", "cccc0011111S0000ddddrrrrvvvvvvvv") // v1
INST(arm_MVN_reg, "MVN (reg)", "cccc0001111S0000ddddvvvvvrr0mmmm") // v1
INST(arm_MVN_rsr, "MVN (rsr)", "cccc0001111S0000ddddssss0rr1mmmm") // v1
INST(arm_ORR_imm, "ORR (imm)", "cccc0011100Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_ORR_reg, "ORR (reg)", "cccc0001100Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_ORR_rsr, "ORR (rsr)", "cccc0001100Snnnnddddssss0rr1mmmm") // v1
INST(arm_RSB_imm, "RSB (imm)", "cccc0010011Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_RSB_reg, "RSB (reg)", "cccc0000011Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_RSB_rsr, "RSB (rsr)", "cccc0000011Snnnnddddssss0rr1mmmm") // v1
INST(arm_RSC_imm, "RSC (imm)", "cccc0010111Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_RSC_reg, "RSC (reg)", "cccc0000111Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_RSC_rsr, "RSC (rsr)", "cccc0000111Snnnnddddssss0rr1mmmm") // v1
INST(arm_SBC_imm, "SBC (imm)", "cccc0010110Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_SBC_reg, "SBC (reg)", "cccc0000110Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_SBC_rsr, "SBC (rsr)", "cccc0000110Snnnnddddssss0rr1mmmm") // v1
INST(arm_SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv") // v1
INST(arm_SUB_reg, "SUB (reg)", "cccc0000010Snnnnddddvvvvvrr0mmmm") // v1
INST(arm_SUB_rsr, "SUB (rsr)", "cccc0000010Snnnnddddssss0rr1mmmm") // v1
INST(arm_TEQ_imm, "TEQ (imm)", "cccc00110011nnnn0000rrrrvvvvvvvv") // v1
INST(arm_TEQ_reg, "TEQ (reg)", "cccc00010011nnnn0000vvvvvrr0mmmm") // v1
INST(arm_TEQ_rsr, "TEQ (rsr)", "cccc00010011nnnn0000ssss0rr1mmmm") // v1
INST(arm_TST_imm, "TST (imm)", "cccc00110001nnnn0000rrrrvvvvvvvv") // v1
INST(arm_TST_reg, "TST (reg)", "cccc00010001nnnn0000vvvvvrr0mmmm") // v1
INST(arm_TST_rsr, "TST (rsr)", "cccc00010001nnnn0000ssss0rr1mmmm") // v1
// Exception Generating instructions
INST(arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv") // v5
INST(arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv") // v1
INST(arm_UDF, "UDF", "111001111111------------1111----")
// Extension instructions
INST(arm_SXTB, "SXTB", "cccc011010101111ddddrr000111mmmm") // v6
INST(arm_SXTB16, "SXTB16", "cccc011010001111ddddrr000111mmmm") // v6
INST(arm_SXTH, "SXTH", "cccc011010111111ddddrr000111mmmm") // v6
INST(arm_SXTAB, "SXTAB", "cccc01101010nnnnddddrr000111mmmm") // v6
INST(arm_SXTAB16, "SXTAB16", "cccc01101000nnnnddddrr000111mmmm") // v6
INST(arm_SXTAH, "SXTAH", "cccc01101011nnnnddddrr000111mmmm") // v6
INST(arm_UXTB, "UXTB", "cccc011011101111ddddrr000111mmmm") // v6
INST(arm_UXTB16, "UXTB16", "cccc011011001111ddddrr000111mmmm") // v6
INST(arm_UXTH, "UXTH", "cccc011011111111ddddrr000111mmmm") // v6
INST(arm_UXTAB, "UXTAB", "cccc01101110nnnnddddrr000111mmmm") // v6
INST(arm_UXTAB16, "UXTAB16", "cccc01101100nnnnddddrr000111mmmm") // v6
INST(arm_UXTAH, "UXTAH", "cccc01101111nnnnddddrr000111mmmm") // v6
// Hint instructions
INST(arm_PLD_imm, "PLD (imm)", "11110101uz01nnnn1111iiiiiiiiiiii") // v5E for PLD; v7 for PLDW
INST(arm_PLD_reg, "PLD (reg)", "11110111uz01nnnn1111iiiiitt0mmmm") // v5E for PLD; v7 for PLDW
INST(arm_SEV, "SEV", "----0011001000001111000000000100") // v6K
INST(arm_SEVL, "SEVL", "----0011001000001111000000000101") // v8
INST(arm_WFE, "WFE", "----0011001000001111000000000010") // v6K
INST(arm_WFI, "WFI", "----0011001000001111000000000011") // v6K
INST(arm_YIELD, "YIELD", "----0011001000001111000000000001") // v6K
INST(arm_NOP, "Reserved Hint", "----0011001000001111------------")
INST(arm_NOP, "Reserved Hint", "----001100100000111100000000----")
// Synchronization Primitive instructions
INST(arm_CLREX, "CLREX", "11110101011111111111000000011111") // v6K
INST(arm_SWP, "SWP", "cccc00010000nnnntttt00001001uuuu") // v2S (v6: Deprecated)
INST(arm_SWPB, "SWPB", "cccc00010100nnnntttt00001001uuuu") // v2S (v6: Deprecated)
INST(arm_STL, "STL", "cccc00011000nnnn111111001001tttt") // v8
INST(arm_STLEX, "STLEX", "cccc00011000nnnndddd11101001tttt") // v8
INST(arm_STREX, "STREX", "cccc00011000nnnndddd11111001mmmm") // v6
INST(arm_LDA, "LDA", "cccc00011001nnnndddd110010011111") // v8
INST(arm_LDAEX, "LDAEX", "cccc00011001nnnndddd111010011111") // v8
INST(arm_LDREX, "LDREX", "cccc00011001nnnndddd111110011111") // v6
INST(arm_STLEXD, "STLEXD", "cccc00011010nnnndddd11101001mmmm") // v8
INST(arm_STREXD, "STREXD", "cccc00011010nnnndddd11111001mmmm") // v6K
INST(arm_LDAEXD, "LDAEXD", "cccc00011011nnnndddd111010011111") // v8
INST(arm_LDREXD, "LDREXD", "cccc00011011nnnndddd111110011111") // v6K
INST(arm_STLB, "STLB", "cccc00011100nnnn111111001001tttt") // v8
INST(arm_STLEXB, "STLEXB", "cccc00011100nnnndddd11101001mmmm") // v8
INST(arm_STREXB, "STREXB", "cccc00011100nnnndddd11111001mmmm") // v6K
INST(arm_LDAB, "LDAB", "cccc00011101nnnndddd110010011111") // v8
INST(arm_LDAEXB, "LDAEXB", "cccc00011101nnnndddd111010011111") // v8
INST(arm_LDREXB, "LDREXB", "cccc00011101nnnndddd111110011111") // v6K
INST(arm_STLH, "STLH", "cccc00011110nnnn111111001001mmmm") // v8
INST(arm_STLEXH, "STLEXH", "cccc00011110nnnndddd11101001mmmm") // v8
INST(arm_STREXH, "STREXH", "cccc00011110nnnndddd11111001mmmm") // v6K
INST(arm_LDAH, "LDAH", "cccc00011111nnnndddd110010011111") // v8
INST(arm_LDAEXH, "LDAEXH", "cccc00011111nnnndddd111010011111") // v8
INST(arm_LDREXH, "LDREXH", "cccc00011111nnnndddd111110011111") // v6K
// Load/Store instructions
INST(arm_LDRBT, "LDRBT (A1)", "----0100-111--------------------") // v1
INST(arm_LDRBT, "LDRBT (A2)", "----0110-111---------------0----") // v1
INST(arm_LDRHT, "LDRHT (A1)", "----0000-111------------1011----") // v6T2
INST(arm_LDRHT, "LDRHT (A1)", "----0000-1111111--------1011----") // v6T2
INST(arm_LDRHT, "LDRHT (A2)", "----0000-011--------00001011----") // v6T2
INST(arm_LDRSBT, "LDRSBT (A1)", "----0000-111------------1101----") // v6T2
INST(arm_LDRSBT, "LDRSBT (A2)", "----0000-011--------00001101----") // v6T2
INST(arm_LDRSHT, "LDRSHT (A1)", "----0000-111------------1111----") // v6T2
INST(arm_LDRSHT, "LDRSHT (A2)", "----0000-011--------00001111----") // v6T2
INST(arm_LDRT, "LDRT (A1)", "----0100-011--------------------") // v1
INST(arm_LDRT, "LDRT (A2)", "----0110-011---------------0----") // v1
INST(arm_STRBT, "STRBT (A1)", "----0100-110--------------------") // v1
INST(arm_STRBT, "STRBT (A2)", "----0110-110---------------0----") // v1
INST(arm_STRHT, "STRHT (A1)", "----0000-110------------1011----") // v6T2
INST(arm_STRHT, "STRHT (A2)", "----0000-010--------00001011----") // v6T2
INST(arm_STRT, "STRT (A1)", "----0100-010--------------------") // v1
INST(arm_STRT, "STRT (A2)", "----0110-010---------------0----") // v1
INST(arm_LDR_lit, "LDR (lit)", "cccc0101u0011111ttttvvvvvvvvvvvv") // v1
INST(arm_LDR_imm, "LDR (imm)", "cccc010pu0w1nnnnttttvvvvvvvvvvvv") // v1
INST(arm_LDR_reg, "LDR (reg)", "cccc011pu0w1nnnnttttvvvvvrr0mmmm") // v1
INST(arm_LDRB_lit, "LDRB (lit)", "cccc0101u1011111ttttvvvvvvvvvvvv") // v1
INST(arm_LDRB_imm, "LDRB (imm)", "cccc010pu1w1nnnnttttvvvvvvvvvvvv") // v1
INST(arm_LDRB_reg, "LDRB (reg)", "cccc011pu1w1nnnnttttvvvvvrr0mmmm") // v1
INST(arm_LDRD_lit, "LDRD (lit)", "cccc0001u1001111ttttvvvv1101vvvv") // v5E
INST(arm_LDRD_imm, "LDRD (imm)", "cccc000pu1w0nnnnttttvvvv1101vvvv") // v5E
INST(arm_LDRD_reg, "LDRD (reg)", "cccc000pu0w0nnnntttt00001101mmmm") // v5E
INST(arm_LDRH_lit, "LDRH (lit)", "cccc000pu1w11111ttttvvvv1011vvvv") // v4
INST(arm_LDRH_imm, "LDRH (imm)", "cccc000pu1w1nnnnttttvvvv1011vvvv") // v4
INST(arm_LDRH_reg, "LDRH (reg)", "cccc000pu0w1nnnntttt00001011mmmm") // v4
INST(arm_LDRSB_lit, "LDRSB (lit)", "cccc0001u1011111ttttvvvv1101vvvv") // v4
INST(arm_LDRSB_imm, "LDRSB (imm)", "cccc000pu1w1nnnnttttvvvv1101vvvv") // v4
INST(arm_LDRSB_reg, "LDRSB (reg)", "cccc000pu0w1nnnntttt00001101mmmm") // v4
INST(arm_LDRSH_lit, "LDRSH (lit)", "cccc0001u1011111ttttvvvv1111vvvv") // v4
INST(arm_LDRSH_imm, "LDRSH (imm)", "cccc000pu1w1nnnnttttvvvv1111vvvv") // v4
INST(arm_LDRSH_reg, "LDRSH (reg)", "cccc000pu0w1nnnntttt00001111mmmm") // v4
INST(arm_STR_imm, "STR (imm)", "cccc010pu0w0nnnnttttvvvvvvvvvvvv") // v1
INST(arm_STR_reg, "STR (reg)", "cccc011pu0w0nnnnttttvvvvvrr0mmmm") // v1
INST(arm_STRB_imm, "STRB (imm)", "cccc010pu1w0nnnnttttvvvvvvvvvvvv") // v1
INST(arm_STRB_reg, "STRB (reg)", "cccc011pu1w0nnnnttttvvvvvrr0mmmm") // v1
INST(arm_STRD_imm, "STRD (imm)", "cccc000pu1w0nnnnttttvvvv1111vvvv") // v5E
INST(arm_STRD_reg, "STRD (reg)", "cccc000pu0w0nnnntttt00001111mmmm") // v5E
INST(arm_STRH_imm, "STRH (imm)", "cccc000pu1w0nnnnttttvvvv1011vvvv") // v4
INST(arm_STRH_reg, "STRH (reg)", "cccc000pu0w0nnnntttt00001011mmmm") // v4
// Load/Store Multiple instructions
INST(arm_LDM, "LDM", "cccc100010w1nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_LDMDA, "LDMDA", "cccc100000w1nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_LDMDB, "LDMDB", "cccc100100w1nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_LDMIB, "LDMIB", "cccc100110w1nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_LDM_usr, "LDM (usr reg)", "----100--101--------------------") // v1
INST(arm_LDM_eret, "LDM (exce ret)", "----100--1-1----1---------------") // v1
INST(arm_STM, "STM", "cccc100010w0nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_STMDA, "STMDA", "cccc100000w0nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_STMDB, "STMDB", "cccc100100w0nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_STMIB, "STMIB", "cccc100110w0nnnnxxxxxxxxxxxxxxxx") // v1
INST(arm_STM_usr, "STM (usr reg)", "----100--100--------------------") // v1
// Miscellaneous instructions
INST(arm_BFC, "BFC", "cccc0111110vvvvvddddvvvvv0011111") // v6T2
INST(arm_BFI, "BFI", "cccc0111110vvvvvddddvvvvv001nnnn") // v6T2
INST(arm_CLZ, "CLZ", "cccc000101101111dddd11110001mmmm") // v5
INST(arm_MOVT, "MOVT", "cccc00110100vvvvddddvvvvvvvvvvvv") // v6T2
INST(arm_MOVW, "MOVW", "cccc00110000vvvvddddvvvvvvvvvvvv") // v6T2
INST(arm_NOP, "NOP", "----0011001000001111000000000000") // v6K
INST(arm_SBFX, "SBFX", "cccc0111101wwwwwddddvvvvv101nnnn") // v6T2
INST(arm_SEL, "SEL", "cccc01101000nnnndddd11111011mmmm") // v6
INST(arm_UBFX, "UBFX", "cccc0111111wwwwwddddvvvvv101nnnn") // v6T2
// Unsigned Sum of Absolute Differences instructions
INST(arm_USAD8, "USAD8", "cccc01111000dddd1111mmmm0001nnnn") // v6
INST(arm_USADA8, "USADA8", "cccc01111000ddddaaaammmm0001nnnn") // v6
// Packing instructions
INST(arm_PKHBT, "PKHBT", "cccc01101000nnnnddddvvvvv001mmmm") // v6K
INST(arm_PKHTB, "PKHTB", "cccc01101000nnnnddddvvvvv101mmmm") // v6K
// Reversal instructions
INST(arm_RBIT, "RBIT", "cccc011011111111dddd11110011mmmm") // v6T2
INST(arm_REV, "REV", "cccc011010111111dddd11110011mmmm") // v6
INST(arm_REV16, "REV16", "cccc011010111111dddd11111011mmmm") // v6
INST(arm_REVSH, "REVSH", "cccc011011111111dddd11111011mmmm") // v6
// Saturation instructions
INST(arm_SSAT, "SSAT", "cccc0110101vvvvvddddvvvvvr01nnnn") // v6
INST(arm_SSAT16, "SSAT16", "cccc01101010vvvvdddd11110011nnnn") // v6
INST(arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn") // v6
INST(arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn") // v6
// Divide instructions
INST(arm_SDIV, "SDIV", "cccc01110001dddd1111mmmm0001nnnn") // v7a
INST(arm_UDIV, "UDIV", "cccc01110011dddd1111mmmm0001nnnn") // v7a
// Multiply (Normal) instructions
INST(arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn") // v2
INST(arm_MLS, "MLS", "cccc00000110ddddaaaammmm1001nnnn") // v6T2
INST(arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn") // v2
// Multiply (Long) instructions
INST(arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn") // v3M
INST(arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn") // v3M
INST(arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn") // v6
INST(arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn") // v3M
INST(arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn") // v3M
// Multiply (Halfword) instructions
INST(arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn") // v5xP
INST(arm_SMLAxy, "SMLAXY", "cccc00010000ddddaaaammmm1xy0nnnn") // v5xP
INST(arm_SMULxy, "SMULXY", "cccc00010110dddd0000mmmm1xy0nnnn") // v5xP
// Multiply (Word by Halfword) instructions
INST(arm_SMLAWy, "SMLAWY", "cccc00010010ddddaaaammmm1y00nnnn") // v5xP
INST(arm_SMULWy, "SMULWY", "cccc00010010dddd0000mmmm1y10nnnn") // v5xP
// Multiply (Most Significant Word) instructions
INST(arm_SMMUL, "SMMUL", "cccc01110101dddd1111mmmm00R1nnnn") // v6
INST(arm_SMMLA, "SMMLA", "cccc01110101ddddaaaammmm00R1nnnn") // v6
INST(arm_SMMLS, "SMMLS", "cccc01110101ddddaaaammmm11R1nnnn") // v6
// Multiply (Dual) instructions
INST(arm_SMLAD, "SMLAD", "cccc01110000ddddaaaammmm00M1nnnn") // v6
INST(arm_SMLALD, "SMLALD", "cccc01110100ddddaaaammmm00M1nnnn") // v6
INST(arm_SMLSD, "SMLSD", "cccc01110000ddddaaaammmm01M1nnnn") // v6
INST(arm_SMLSLD, "SMLSLD", "cccc01110100ddddaaaammmm01M1nnnn") // v6
INST(arm_SMUAD, "SMUAD", "cccc01110000dddd1111mmmm00M1nnnn") // v6
INST(arm_SMUSD, "SMUSD", "cccc01110000dddd1111mmmm01M1nnnn") // v6
// Parallel Add/Subtract (Modulo) instructions
INST(arm_SADD8, "SADD8", "cccc01100001nnnndddd11111001mmmm") // v6
INST(arm_SADD16, "SADD16", "cccc01100001nnnndddd11110001mmmm") // v6
INST(arm_SASX, "SASX", "cccc01100001nnnndddd11110011mmmm") // v6
INST(arm_SSAX, "SSAX", "cccc01100001nnnndddd11110101mmmm") // v6
INST(arm_SSUB8, "SSUB8", "cccc01100001nnnndddd11111111mmmm") // v6
INST(arm_SSUB16, "SSUB16", "cccc01100001nnnndddd11110111mmmm") // v6
INST(arm_UADD8, "UADD8", "cccc01100101nnnndddd11111001mmmm") // v6
INST(arm_UADD16, "UADD16", "cccc01100101nnnndddd11110001mmmm") // v6
INST(arm_UASX, "UASX", "cccc01100101nnnndddd11110011mmmm") // v6
INST(arm_USAX, "USAX", "cccc01100101nnnndddd11110101mmmm") // v6
INST(arm_USUB8, "USUB8", "cccc01100101nnnndddd11111111mmmm") // v6
INST(arm_USUB16, "USUB16", "cccc01100101nnnndddd11110111mmmm") // v6
// Parallel Add/Subtract (Saturating) instructions
INST(arm_QADD8, "QADD8", "cccc01100010nnnndddd11111001mmmm") // v6
INST(arm_QADD16, "QADD16", "cccc01100010nnnndddd11110001mmmm") // v6
INST(arm_QASX, "QASX", "cccc01100010nnnndddd11110011mmmm") // v6
INST(arm_QSAX, "QSAX", "cccc01100010nnnndddd11110101mmmm") // v6
INST(arm_QSUB8, "QSUB8", "cccc01100010nnnndddd11111111mmmm") // v6
INST(arm_QSUB16, "QSUB16", "cccc01100010nnnndddd11110111mmmm") // v6
INST(arm_UQADD8, "UQADD8", "cccc01100110nnnndddd11111001mmmm") // v6
INST(arm_UQADD16, "UQADD16", "cccc01100110nnnndddd11110001mmmm") // v6
INST(arm_UQASX, "UQASX", "cccc01100110nnnndddd11110011mmmm") // v6
INST(arm_UQSAX, "UQSAX", "cccc01100110nnnndddd11110101mmmm") // v6
INST(arm_UQSUB8, "UQSUB8", "cccc01100110nnnndddd11111111mmmm") // v6
INST(arm_UQSUB16, "UQSUB16", "cccc01100110nnnndddd11110111mmmm") // v6
// Parallel Add/Subtract (Halving) instructions
INST(arm_SHADD8, "SHADD8", "cccc01100011nnnndddd11111001mmmm") // v6
INST(arm_SHADD16, "SHADD16", "cccc01100011nnnndddd11110001mmmm") // v6
INST(arm_SHASX, "SHASX", "cccc01100011nnnndddd11110011mmmm") // v6
INST(arm_SHSAX, "SHSAX", "cccc01100011nnnndddd11110101mmmm") // v6
INST(arm_SHSUB8, "SHSUB8", "cccc01100011nnnndddd11111111mmmm") // v6
INST(arm_SHSUB16, "SHSUB16", "cccc01100011nnnndddd11110111mmmm") // v6
INST(arm_UHADD8, "UHADD8", "cccc01100111nnnndddd11111001mmmm") // v6
INST(arm_UHADD16, "UHADD16", "cccc01100111nnnndddd11110001mmmm") // v6
INST(arm_UHASX, "UHASX", "cccc01100111nnnndddd11110011mmmm") // v6
INST(arm_UHSAX, "UHSAX", "cccc01100111nnnndddd11110101mmmm") // v6
INST(arm_UHSUB8, "UHSUB8", "cccc01100111nnnndddd11111111mmmm") // v6
INST(arm_UHSUB16, "UHSUB16", "cccc01100111nnnndddd11110111mmmm") // v6
// Saturated Add/Subtract instructions
INST(arm_QADD, "QADD", "cccc00010000nnnndddd00000101mmmm") // v5xP
INST(arm_QSUB, "QSUB", "cccc00010010nnnndddd00000101mmmm") // v5xP
INST(arm_QDADD, "QDADD", "cccc00010100nnnndddd00000101mmmm") // v5xP
INST(arm_QDSUB, "QDSUB", "cccc00010110nnnndddd00000101mmmm") // v5xP
// Status Register Access instructions
INST(arm_CPS, "CPS", "111100010000---00000000---0-----") // v6
INST(arm_SETEND, "SETEND", "1111000100000001000000e000000000") // v6
INST(arm_MRS, "MRS", "cccc000100001111dddd000000000000") // v3
INST(arm_MSR_imm, "MSR (imm)", "cccc00110010mmmm1111rrrrvvvvvvvv") // v3
INST(arm_MSR_reg, "MSR (reg)", "cccc00010010mmmm111100000000nnnn") // v3
INST(arm_RFE, "RFE", "1111100--0-1----0000101000000000") // v6
INST(arm_SRS, "SRS", "1111100--1-0110100000101000-----") // v6
@@ -27,12 +27,50 @@ template<typename Visitor>
using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
static std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 instruction) noexcept {
alignas(64) static const auto table = std::array{
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() noexcept {
std::vector<std::pair<const char*, ASIMDMatcher<V>>> table = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./asimd.inc"
#undef INST
};
// Exceptions to the rule of thumb.
const std::set<std::string> comes_first{
"VBIC, VMOV, VMVN, VORR (immediate)",
"VEXT",
"VTBL",
"VTBX",
"VDUP (scalar)",
};
const std::set<std::string> comes_last{
"VMLA (scalar)",
"VMLAL (scalar)",
"VQDMLAL/VQDMLSL (scalar)",
"VMUL (scalar)",
"VMULL (scalar)",
"VQDMULL (scalar)",
"VQDMULH (scalar)",
"VQRDMULH (scalar)",
};
const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& e) {
return comes_first.count(e.first) > 0;
});
const auto sort_end = std::stable_partition(table.begin(), table.end(), [&](const auto& e) {
return comes_last.count(e.first) == 0;
});
// If a matcher has more bits in its mask it is more specific, so it should come first.
std::stable_sort(sort_begin, sort_end, [](const auto& a, const auto& b) {
return mcl::bit::count_ones(a.second.GetMask()) > mcl::bit::count_ones(b.second.GetMask());
});
std::vector<ASIMDMatcher<V>> final_table;
std::transform(table.cbegin(), table.cend(), std::back_inserter(final_table), [](auto const& e) {
return e.second;
});
return final_table;
}
template<typename V>
std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 instruction) noexcept {
alignas(64) static const auto table = GetASIMDDecodeTable<V>();
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
return matcher.Matches(instruction);
});
@@ -40,7 +78,7 @@ static std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(
}
template<typename V>
static std::optional<std::string_view> GetNameASIMD(u32 inst) noexcept {
std::optional<std::string_view> GetNameASIMD(u32 inst) noexcept {
std::vector<std::pair<std::string_view, ASIMDMatcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./asimd.inc"
@@ -1,151 +1,172 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// DO NOT REORDER
// Three registers of the same length
INST(asimd_VHADD, "VHADD", "1111001U0Dzznnnndddd0000NQM0mmmm") // ASIMD
INST(asimd_VQADD, "VQADD", "1111001U0Dzznnnndddd0000NQM1mmmm") // ASIMD
INST(asimd_VRHADD, "VRHADD", "1111001U0Dzznnnndddd0001NQM0mmmm") // ASIMD
INST(asimd_VAND_reg, "VAND (register)", "111100100D00nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBIC_reg, "VBIC (register)", "111100100D01nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VORR_reg, "VORR (register)", "111100100D10nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VORN_reg, "VORN (register)", "111100100D11nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VEOR_reg, "VEOR (register)", "111100110D00nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBSL, "VBSL", "111100110D01nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBIT, "VBIT", "111100110D10nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBIF, "VBIF", "111100110D11nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VHSUB, "VHSUB", "1111001U0Dzznnnndddd0010NQM0mmmm") // ASIMD
INST(asimd_VQSUB, "VQSUB", "1111001U0Dzznnnndddd0010NQM1mmmm") // ASIMD
INST(asimd_VCGT_reg, "VCGT (register)", "1111001U0Dzznnnndddd0011NQM0mmmm") // ASIMD
INST(asimd_VCGE_reg, "VCGE (register)", "1111001U0Dzznnnndddd0011NQM1mmmm") // ASIMD
INST(asimd_VSHL_reg, "VSHL (register)", "1111001U0Dzznnnndddd0100NQM0mmmm") // ASIMD
INST(asimd_VQSHL_reg, "VQSHL (register)", "1111001U0Dzznnnndddd0100NQM1mmmm") // ASIMD
INST(asimd_VRSHL, "VRSHL", "1111001U0Dzznnnndddd0101NQM0mmmm") // ASIMD
//INST(asimd_VQRSHL, "VQRSHL", "1111001U0-CC--------0101---1----") // ASIMD
INST(asimd_VMAX, "VMAX/VMIN (integer)", "1111001U0Dzznnnnmmmm0110NQMommmm") // ASIMD
INST(asimd_VABD, "VABD", "1111001U0Dzznnnndddd0111NQM0mmmm") // ASIMD
INST(asimd_VABA, "VABA", "1111001U0Dzznnnndddd0111NQM1mmmm") // ASIMD
INST(asimd_VADD_int, "VADD (integer)", "111100100Dzznnnndddd1000NQM0mmmm") // ASIMD
INST(asimd_VSUB_int, "VSUB (integer)", "111100110Dzznnnndddd1000NQM0mmmm") // ASIMD
INST(asimd_VTST, "VTST", "111100100Dzznnnndddd1000NQM1mmmm") // ASIMD
INST(asimd_VCEQ_reg, "VCEG (register)", "111100110Dzznnnndddd1000NQM1mmmm") // ASIMD
INST(asimd_VMLA, "VMLA/VMLS", "1111001o0Dzznnnndddd1001NQM0mmmm") // ASIMD
INST(asimd_VMUL, "VMUL", "1111001P0Dzznnnndddd1001NQM1mmmm") // ASIMD
INST(asimd_VPMAX_int, "VPMAX/VPMIN (integer)", "1111001U0Dzznnnndddd1010NQMommmm") // ASIMD
INST(v8_VMAXNM, "VMAXNM", "111100110D0znnnndddd1111NQM1mmmm") // v8
INST(v8_VMINNM, "VMINNM", "111100110D1znnnndddd1111NQM1mmmm") // v8
INST(asimd_VQDMULH, "VQDMULH", "111100100Dzznnnndddd1011NQM0mmmm") // ASIMD
INST(asimd_VQRDMULH, "VQRDMULH", "111100110Dzznnnndddd1011NQM0mmmm") // ASIMD
INST(asimd_VPADD, "VPADD", "111100100Dzznnnndddd1011NQM1mmmm") // ASIMD
INST(asimd_VFMA, "VFMA", "111100100D0znnnndddd1100NQM1mmmm") // ASIMD
INST(asimd_VFMS, "VFMS", "111100100D1znnnndddd1100NQM1mmmm") // ASIMD
INST(asimd_VADD_float, "VADD (floating-point)", "111100100D0znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VSUB_float, "VSUB (floating-point)", "111100100D1znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VPADD_float, "VPADD (floating-point)", "111100110D0znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VABD_float, "VABD (floating-point)", "111100110D1znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VMLA_float, "VMLA (floating-point)", "111100100D0znnnndddd1101NQM1mmmm") // ASIMD
INST(asimd_VMLS_float, "VMLS (floating-point)", "111100100D1znnnndddd1101NQM1mmmm") // ASIMD
INST(asimd_VMUL_float, "VMUL (floating-point)", "111100110D0znnnndddd1101NQM1mmmm") // ASIMD
INST(asimd_VCEQ_reg_float, "VCEQ (register)", "111100100D0znnnndddd1110NQM0mmmm") // ASIMD
INST(asimd_VCGE_reg_float, "VCGE (register)", "111100110D0znnnndddd1110NQM0mmmm") // ASIMD
INST(asimd_VCGT_reg_float, "VCGT (register)", "111100110D1znnnndddd1110NQM0mmmm") // ASIMD
INST(asimd_VACGE, "VACGE", "111100110Doznnnndddd1110NQM1mmmm") // ASIMD
INST(asimd_VMAX_float, "VMAX (floating-point)", "111100100D0znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VMIN_float, "VMIN (floating-point)", "111100100D1znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VPMAX_float, "VPMAX (floating-point)", "111100110D0znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VPMIN_float, "VPMIN (floating-point)", "111100110D1znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VRECPS, "VRECPS", "111100100D0znnnndddd1111NQM1mmmm") // ASIMD
INST(asimd_VRSQRTS, "VRSQRTS", "111100100D1znnnndddd1111NQM1mmmm") // ASIMD
INST(v8_SHA256H, "SHA256H", "111100110D00nnnndddd1100NQM0mmmm") // v8
INST(v8_SHA256H2, "SHA256H2", "111100110D01nnnndddd1100NQM0mmmm") // v8
INST(v8_SHA256SU1, "SHA256SU1", "111100110D10nnnndddd1100NQM0mmmm") // v8
INST(asimd_VMOV_imm, "VBIC, VMOV, VMVN, VORR (immediate)", "1111001a1D000bcdVVVVmmmm0Qo1efgh")
INST(asimd_VEXT, "VEXT", "111100101D11nnnnddddiiiiNQM0mmmm")
INST(asimd_VTBL, "VTBL", "111100111D11nnnndddd10zzN0M0mmmm")
INST(asimd_VTBX, "VTBX", "111100111D11nnnndddd10zzN1M0mmmm")
INST(asimd_VDUP_scalar, "VDUP (scalar)", "111100111D11iiiidddd11000QM0mmmm")
INST(asimd_VSWP, "VSWP", "111100111D110010dddd00000QM0mmmm")
INST(asimd_VMOVN, "VMOVN", "111100111D11zz10dddd001000M0mmmm")
INST(asimd_VQMOVUN, "VQMOVUN", "111100111D11zz10dddd001001M0mmmm")
INST(asimd_VSHLL_max, "VSHLL_max", "111100111D11zz10dddd001100M0mmmm")
INST(v8_AESE, "AESE", "111100111D11zz00dddd001100M0mmmm")
INST(v8_AESD, "AESD", "111100111D11zz00dddd001101M0mmmm")
INST(v8_AESMC, "AESMC", "111100111D11zz00dddd001110M0mmmm")
INST(v8_AESIMC, "AESIMC", "111100111D11zz00dddd001111M0mmmm")
INST(arm_UDF, "UNALLOCATED", "111100111-11--01----001010-0----")
INST(arm_UDF, "UNALLOCATED (SHA1H)", "111100111-11--01----001011-0----")
INST(arm_UDF, "UNALLOCATED (SHA1SU1)", "111100111-11--10----001110-0----")
INST(v8_SHA256SU0, "SHA256SU0", "111100111D11zz10dddd001111M0mmmm")
INST(asimd_VCLS, "VCLS", "111100111D11zz00dddd01000QM0mmmm")
INST(asimd_VCLZ, "VCLZ", "111100111D11zz00dddd01001QM0mmmm")
INST(asimd_VCNT, "VCNT", "111100111D11zz00dddd01010QM0mmmm")
INST(asimd_VMVN_reg, "VMVN_reg", "111100111D11zz00dddd01011QM0mmmm")
INST(asimd_VQABS, "VQABS", "111100111D11zz00dddd01110QM0mmmm")
INST(asimd_VQNEG, "VQNEG", "111100111D11zz00dddd01111QM0mmmm")
INST(arm_UDF, "UNALLOCATED", "111100111-11--01----01101--0----")
INST(arm_UDF, "UNALLOCATED", "111100111-11--10----00000--0----")
INST(asimd_VTRN, "VTRN", "111100111D11zz10dddd00001QM0mmmm")
INST(asimd_VUZP, "VUZP", "111100111D11zz10dddd00010QM0mmmm")
INST(asimd_VZIP, "VZIP", "111100111D11zz10dddd00011QM0mmmm")
INST(asimd_VQMOVN, "VQMOVN", "111100111D11zz10dddd00101oM0mmmm")
INST(v8_VRINTN, "VRINTN", "111100111D11zz10dddd01000QM0mmmm")
INST(v8_VRINTX, "VRINTX", "111100111D11zz10dddd01001QM0mmmm")
INST(v8_VRINTA, "VRINTA", "111100111D11zz10dddd01010QM0mmmm")
INST(v8_VRINTZ, "VRINTZ", "111100111D11zz10dddd01011QM0mmmm")
INST(v8_VRINTM, "VRINTM", "111100111D11zz10dddd01101QM0mmmm")
INST(v8_VRINTP, "VRINTP", "111100111D11zz10dddd01111QM0mmmm")
INST(asimd_VCVT_half, "VCVT (half-precision)", "111100111D11zz10dddd011o00M0mmmm")
INST(arm_UDF, "UNALLOCATED", "111100111-11--10----011-01-0----")
INST(asimd_VPADDL, "VPADDL", "111100111D11zz00dddd0010oQM0mmmm")
INST(asimd_VPADAL, "VPADAL", "111100111D11zz00dddd0110oQM0mmmm")
INST(asimd_VCGT_zero, "VCGT (zero)", "111100111D11zz01dddd0F000QM0mmmm")
INST(asimd_VCGE_zero, "VCGE (zero)", "111100111D11zz01dddd0F001QM0mmmm")
INST(asimd_VCEQ_zero, "VCEQ (zero)", "111100111D11zz01dddd0F010QM0mmmm")
INST(asimd_VCLE_zero, "VCLE (zero)", "111100111D11zz01dddd0F011QM0mmmm")
INST(asimd_VCLT_zero, "VCLT (zero)", "111100111D11zz01dddd0F100QM0mmmm")
INST(asimd_VABS, "VABS", "111100111D11zz01dddd0F110QM0mmmm")
INST(asimd_VNEG, "VNEG", "111100111D11zz01dddd0F111QM0mmmm")
INST(v8_VCVTA, "VCVTA", "111100111D11zz11dddd0000oQM0mmmm")
INST(v8_VCVTN, "VCVTN", "111100111D11zz11dddd0001oQM0mmmm")
INST(v8_VCVTP, "VCVTP", "111100111D11zz11dddd0010oQM0mmmm")
INST(v8_VCVTM, "VCVTM", "111100111D11zz11dddd0011oQM0mmmm")
INST(asimd_VRECPE, "VRECPE", "111100111D11zz11dddd010F0QM0mmmm")
INST(asimd_VRSQRTE, "VRSQRTE", "111100111D11zz11dddd010F1QM0mmmm")
INST(asimd_VREV, "VREV{16,32,64}", "111100111D11zz00dddd000ooQM0mmmm")
INST(asimd_VCVT_integer, "VCVT (integer)", "111100111D11zz11dddd011oUQM0mmmm")
INST(asimd_VAND_reg, "VAND (register)", "111100100D00nnnndddd0001NQM1mmmm")
INST(asimd_VBIC_reg, "VBIC (register)", "111100100D01nnnndddd0001NQM1mmmm")
INST(asimd_VORR_reg, "VORR (register)", "111100100D10nnnndddd0001NQM1mmmm")
INST(asimd_VORN_reg, "VORN (register)", "111100100D11nnnndddd0001NQM1mmmm")
INST(asimd_VEOR_reg, "VEOR (register)", "111100110D00nnnndddd0001NQM1mmmm")
INST(asimd_VBSL, "VBSL", "111100110D01nnnndddd0001NQM1mmmm")
INST(asimd_VBIT, "VBIT", "111100110D10nnnndddd0001NQM1mmmm")
INST(asimd_VBIF, "VBIF", "111100110D11nnnndddd0001NQM1mmmm")
INST(v8_SHA256H, "SHA256H", "111100110D00nnnndddd1100NQM0mmmm")
INST(v8_SHA256H2, "SHA256H2", "111100110D01nnnndddd1100NQM0mmmm")
INST(v8_SHA256SU1, "SHA256SU1", "111100110D10nnnndddd1100NQM0mmmm")
INST(asimd_VSHRN, "VSHRN", "111100101Diiiiiidddd100000M1mmmm")
INST(asimd_VRSHRN, "VRSHRN", "111100101Diiiiiidddd100001M1mmmm")
INST(asimd_VQSHRUN, "VQSHRUN", "111100111Diiiiiidddd100000M1mmmm")
INST(asimd_VQRSHRUN, "VQRSHRUN", "111100111Diiiiiidddd100001M1mmmm")
INST(v8_VMAXNM, "VMAXNM", "111100110D0znnnndddd1111NQM1mmmm")
INST(v8_VMINNM, "VMINNM", "111100110D1znnnndddd1111NQM1mmmm")
INST(asimd_VFMA, "VFMA", "111100100D0znnnndddd1100NQM1mmmm")
INST(asimd_VFMS, "VFMS", "111100100D1znnnndddd1100NQM1mmmm")
INST(asimd_VADD_float, "VADD (floating-point)", "111100100D0znnnndddd1101NQM0mmmm")
INST(asimd_VSUB_float, "VSUB (floating-point)", "111100100D1znnnndddd1101NQM0mmmm")
INST(asimd_VPADD_float, "VPADD (floating-point)", "111100110D0znnnndddd1101NQM0mmmm")
INST(asimd_VABD_float, "VABD (floating-point)", "111100110D1znnnndddd1101NQM0mmmm")
INST(asimd_VMLA_float, "VMLA (floating-point)", "111100100D0znnnndddd1101NQM1mmmm")
INST(asimd_VMLS_float, "VMLS (floating-point)", "111100100D1znnnndddd1101NQM1mmmm")
INST(asimd_VMUL_float, "VMUL (floating-point)", "111100110D0znnnndddd1101NQM1mmmm")
INST(asimd_VCEQ_reg_float, "VCEQ (register)", "111100100D0znnnndddd1110NQM0mmmm")
INST(asimd_VCGE_reg_float, "VCGE (register)", "111100110D0znnnndddd1110NQM0mmmm")
INST(asimd_VCGT_reg_float, "VCGT (register)", "111100110D1znnnndddd1110NQM0mmmm")
INST(asimd_VMAX_float, "VMAX (floating-point)", "111100100D0znnnndddd1111NQM0mmmm")
INST(asimd_VMIN_float, "VMIN (floating-point)", "111100100D1znnnndddd1111NQM0mmmm")
INST(asimd_VPMAX_float, "VPMAX (floating-point)", "111100110D0znnnndddd1111NQM0mmmm")
INST(asimd_VPMIN_float, "VPMIN (floating-point)", "111100110D1znnnndddd1111NQM0mmmm")
INST(asimd_VRECPS, "VRECPS", "111100100D0znnnndddd1111NQM1mmmm")
INST(asimd_VRSQRTS, "VRSQRTS", "111100100D1znnnndddd1111NQM1mmmm")
INST(asimd_VQSHRN, "VQSHRN", "1111001U1Diiiiiidddd100100M1mmmm")
INST(asimd_VQRSHRN, "VQRSHRN", "1111001U1Diiiiiidddd100101M1mmmm")
INST(asimd_VSHLL, "VSHLL", "1111001U1Diiiiiidddd101000M1mmmm")
INST(asimd_VADD_int, "VADD (integer)", "111100100Dzznnnndddd1000NQM0mmmm")
INST(asimd_VSUB_int, "VSUB (integer)", "111100110Dzznnnndddd1000NQM0mmmm")
INST(asimd_VTST, "VTST", "111100100Dzznnnndddd1000NQM1mmmm")
INST(asimd_VCEQ_reg, "VCEG (register)", "111100110Dzznnnndddd1000NQM1mmmm")
INST(asimd_VQDMULH, "VQDMULH", "111100100Dzznnnndddd1011NQM0mmmm")
INST(asimd_VQRDMULH, "VQRDMULH", "111100110Dzznnnndddd1011NQM0mmmm")
INST(asimd_VPADD, "VPADD", "111100100Dzznnnndddd1011NQM1mmmm")
INST(asimd_VACGE, "VACGE", "111100110Doznnnndddd1110NQM1mmmm")
INST(asimd_VABAL, "VABAL", "1111001U1Dzznnnndddd0101N0M0mmmm")
INST(asimd_VABDL, "VABDL", "1111001U1Dzznnnndddd0111N0M0mmmm")
INST(asimd_VSRI, "VSRI", "111100111Diiiiiidddd0100LQM1mmmm")
INST(asimd_VSHL, "VSHL", "111100101Diiiiiidddd0101LQM1mmmm")
INST(asimd_VSLI, "VSLI", "111100111Diiiiiidddd0101LQM1mmmm")
INST(arm_UDF, "UNALLOCATED", "111100111-11--------11-----0----")
INST(arm_UDF, "UNALLOCATED", "111101000--0--------1011--------")
INST(asimd_VHADD, "VHADD", "1111001U0Dzznnnndddd0000NQM0mmmm")
INST(asimd_VQADD, "VQADD", "1111001U0Dzznnnndddd0000NQM1mmmm")
INST(asimd_VRHADD, "VRHADD", "1111001U0Dzznnnndddd0001NQM0mmmm")
INST(asimd_VHSUB, "VHSUB", "1111001U0Dzznnnndddd0010NQM0mmmm")
INST(asimd_VQSUB, "VQSUB", "1111001U0Dzznnnndddd0010NQM1mmmm")
INST(asimd_VCGT_reg, "VCGT (register)", "1111001U0Dzznnnndddd0011NQM0mmmm")
INST(asimd_VCGE_reg, "VCGE (register)", "1111001U0Dzznnnndddd0011NQM1mmmm")
INST(asimd_VSHL_reg, "VSHL (register)", "1111001U0Dzznnnndddd0100NQM0mmmm")
INST(asimd_VQSHL_reg, "VQSHL (register)", "1111001U0Dzznnnndddd0100NQM1mmmm")
INST(asimd_VRSHL, "VRSHL", "1111001U0Dzznnnndddd0101NQM0mmmm")
INST(asimd_VABD, "VABD", "1111001U0Dzznnnndddd0111NQM0mmmm")
INST(asimd_VABA, "VABA", "1111001U0Dzznnnndddd0111NQM1mmmm")
INST(asimd_VMLA, "VMLA/VMLS", "1111001o0Dzznnnndddd1001NQM0mmmm")
INST(asimd_VMUL, "VMUL", "1111001P0Dzznnnndddd1001NQM1mmmm")
INST(asimd_VADDL, "VADDL/VADDW", "1111001U1Dzznnnndddd000oN0M0mmmm")
INST(asimd_VSUBL, "VSUBL/VSUBW", "1111001U1Dzznnnndddd001oN0M0mmmm")
INST(asimd_VMLAL, "VMLAL/VMLSL", "1111001U1Dzznnnndddd10o0N0M0mmmm")
INST(asimd_VMULL, "VMULL", "1111001U1Dzznnnndddd11P0N0M0mmmm")
INST(asimd_SHR, "SHR", "1111001U1Diiiiiidddd0000LQM1mmmm")
INST(asimd_SRA, "SRA", "1111001U1Diiiiiidddd0001LQM1mmmm")
INST(asimd_VRSHR, "VRSHR", "1111001U1Diiiiiidddd0010LQM1mmmm")
INST(asimd_VRSRA, "VRSRA", "1111001U1Diiiiiidddd0011LQM1mmmm")
INST(asimd_VCVT_fixed, "VCVT (fixed-point)", "1111001U1Diiiiiidddd111o0QM1mmmm")
INST(arm_UDF, "UNALLOCATED", "111101001-00--------11----------")
INST(v8_VLD_all_lanes, "VLD{1-4} (all lanes)", "111101001D10nnnndddd11nnzzTammmm")
INST(asimd_VMAX, "VMAX/VMIN (integer)", "1111001U0Dzznnnnmmmm0110NQMommmm")
INST(asimd_VPMAX_int, "VPMAX/VPMIN (integer)", "1111001U0Dzznnnndddd1010NQMommmm")
INST(asimd_VQSHL, "VQSHL", "1111001U1Diiiiiidddd011oLQM1mmmm")
INST(arm_UDF, "UNALLOCATED", "111101000--0--------11----------")
INST(v8_VST_multiple, "VST{1-4} (multiple)", "111101000D00nnnnddddxxxxzzaammmm")
INST(v8_VLD_multiple, "VLD{1-4} (multiple)", "111101000D10nnnnddddxxxxzzaammmm")
INST(v8_VST_single, "VST{1-4} (single)", "111101001D00nnnnddddzzNNaaaammmm")
INST(v8_VLD_single, "VLD{1-4} (single)", "111101001D10nnnnddddzzNNaaaammmm")
INST(asimd_VMLA_scalar, "VMLA (scalar)", "1111001Q1Dzznnnndddd0o0FN1M0mmmm")
INST(asimd_VMLAL_scalar, "VMLAL (scalar)", "1111001U1dzznnnndddd0o10N1M0mmmm")
INST(asimd_VMUL_scalar, "VMUL (scalar)", "1111001Q1Dzznnnndddd100FN1M0mmmm")
INST(asimd_VMULL_scalar, "VMULL (scalar)", "1111001U1Dzznnnndddd1010N1M0mmmm")
INST(asimd_VQDMULL_scalar, "VQDMULL (scalar)", "111100101Dzznnnndddd1011N1M0mmmm")
INST(asimd_VQDMULH_scalar, "VQDMULH (scalar)", "1111001Q1Dzznnnndddd1100N1M0mmmm")
INST(asimd_VQRDMULH_scalar, "VQRDMULH (scalar)", "1111001Q1Dzznnnndddd1101N1M0mmmm")
// Three registers of different lengths
INST(asimd_VADDL, "VADDL/VADDW", "1111001U1Dzznnnndddd000oN0M0mmmm") // ASIMD
INST(asimd_VSUBL, "VSUBL/VSUBW", "1111001U1Dzznnnndddd001oN0M0mmmm") // ASIMD
//INST(asimd_VADDHN, "VADDHN", "111100101-----------0100-0-0----") // ASIMD
//INST(asimd_VRADDHN, "VRADDHN", "111100111-----------0100-0-0----") // ASIMD
INST(asimd_VABAL, "VABAL", "1111001U1Dzznnnndddd0101N0M0mmmm") // ASIMD
//INST(asimd_VSUBHN, "VSUBHN", "111100101-----------0110-0-0----") // ASIMD
//INST(asimd_VRSUBHN, "VRSUBHN", "111100111-----------0110-0-0----") // ASIMD
INST(asimd_VABDL, "VABDL", "1111001U1Dzznnnndddd0111N0M0mmmm") // ASIMD
INST(asimd_VMLAL, "VMLAL/VMLSL", "1111001U1Dzznnnndddd10o0N0M0mmmm") // ASIMD
//INST(asimd_VQDMLAL, "VQDMLAL", "111100101-----------10-1-0-0----") // ASIMD
INST(asimd_VMULL, "VMULL", "1111001U1Dzznnnndddd11P0N0M0mmmm") // ASIMD
//INST(asimd_VQDMULL, "VQDMULL", "111100101-----------1101-0-0----") // ASIMD
// Two registers and a scalar
INST(asimd_VMLA_scalar, "VMLA (scalar)", "1111001Q1Dzznnnndddd0o0FN1M0mmmm") // ASIMD
INST(asimd_VMLAL_scalar, "VMLAL (scalar)", "1111001U1dzznnnndddd0o10N1M0mmmm") // ASIMD
//INST(asimd_VQDMLAL_scalar, "VQDMLAL/VQDMLSL (scalar)", "111100101-BB--------0x11-1-0----") // ASIMD
INST(asimd_VMUL_scalar, "VMUL (scalar)", "1111001Q1Dzznnnndddd100FN1M0mmmm") // ASIMD
INST(asimd_VMULL_scalar, "VMULL (scalar)", "1111001U1Dzznnnndddd1010N1M0mmmm") // ASIMD
INST(asimd_VQDMULL_scalar, "VQDMULL (scalar)", "111100101Dzznnnndddd1011N1M0mmmm") // ASIMD
INST(asimd_VQDMULH_scalar, "VQDMULH (scalar)", "1111001Q1Dzznnnndddd1100N1M0mmmm") // ASIMD
INST(asimd_VQRDMULH_scalar, "VQRDMULH (scalar)", "1111001Q1Dzznnnndddd1101N1M0mmmm") // ASIMD
// Two registers and a shift amount
INST(asimd_SHR, "SHR", "1111001U1Diiiiiidddd0000LQM1mmmm") // ASIMD
INST(asimd_SRA, "SRA", "1111001U1Diiiiiidddd0001LQM1mmmm") // ASIMD
INST(asimd_VRSHR, "VRSHR", "1111001U1Diiiiiidddd0010LQM1mmmm") // ASIMD
INST(asimd_VRSRA, "VRSRA", "1111001U1Diiiiiidddd0011LQM1mmmm") // ASIMD
INST(asimd_VSRI, "VSRI", "111100111Diiiiiidddd0100LQM1mmmm") // ASIMD
INST(asimd_VSHL, "VSHL", "111100101Diiiiiidddd0101LQM1mmmm") // ASIMD
INST(asimd_VSLI, "VSLI", "111100111Diiiiiidddd0101LQM1mmmm") // ASIMD
INST(asimd_VQSHL, "VQSHL" , "1111001U1Diiiiiidddd011oLQM1mmmm") // ASIMD
INST(asimd_VSHRN, "VSHRN", "111100101Diiiiiidddd100000M1mmmm") // ASIMD
INST(asimd_VRSHRN, "VRSHRN", "111100101Diiiiiidddd100001M1mmmm") // ASIMD
INST(asimd_VQSHRUN, "VQSHRUN", "111100111Diiiiiidddd100000M1mmmm") // ASIMD
INST(asimd_VQRSHRUN, "VQRSHRUN", "111100111Diiiiiidddd100001M1mmmm") // ASIMD
INST(asimd_VQSHRN, "VQSHRN", "1111001U1Diiiiiidddd100100M1mmmm") // ASIMD
INST(asimd_VQRSHRN, "VQRSHRN", "1111001U1Diiiiiidddd100101M1mmmm") // ASIMD
INST(asimd_VSHLL, "VSHLL", "1111001U1Diiiiiidddd101000M1mmmm") // ASIMD
INST(asimd_VCVT_fixed, "VCVT (fixed-point)", "1111001U1Diiiiiidddd111o0QM1mmmm") // ASIMD
// Two registers, miscellaneous
INST(asimd_VREV, "VREV{16,32,64}", "111100111D11zz00dddd000ooQM0mmmm") // ASIMD
INST(asimd_VPADDL, "VPADDL", "111100111D11zz00dddd0010oQM0mmmm") // ASIMD
INST(asimd_VCLS, "VCLS", "111100111D11zz00dddd01000QM0mmmm") // ASIMD
INST(asimd_VCLZ, "VCLZ", "111100111D11zz00dddd01001QM0mmmm") // ASIMD
INST(asimd_VCNT, "VCNT", "111100111D11zz00dddd01010QM0mmmm") // ASIMD
INST(asimd_VMVN_reg, "VMVN_reg", "111100111D11zz00dddd01011QM0mmmm") // ASIMD
INST(asimd_VPADAL, "VPADAL", "111100111D11zz00dddd0110oQM0mmmm") // ASIMD
INST(asimd_VQABS, "VQABS", "111100111D11zz00dddd01110QM0mmmm") // ASIMD
INST(asimd_VQNEG, "VQNEG", "111100111D11zz00dddd01111QM0mmmm") // ASIMD
INST(asimd_VCGT_zero, "VCGT (zero)", "111100111D11zz01dddd0F000QM0mmmm") // ASIMD
INST(asimd_VCGE_zero, "VCGE (zero)", "111100111D11zz01dddd0F001QM0mmmm") // ASIMD
INST(asimd_VCEQ_zero, "VCEQ (zero)", "111100111D11zz01dddd0F010QM0mmmm") // ASIMD
INST(asimd_VCLE_zero, "VCLE (zero)", "111100111D11zz01dddd0F011QM0mmmm") // ASIMD
INST(asimd_VCLT_zero, "VCLT (zero)", "111100111D11zz01dddd0F100QM0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--01----01101--0----") // v8
INST(asimd_VABS, "VABS", "111100111D11zz01dddd0F110QM0mmmm") // ASIMD
INST(asimd_VNEG, "VNEG", "111100111D11zz01dddd0F111QM0mmmm") // ASIMD
INST(asimd_VSWP, "VSWP", "111100111D110010dddd00000QM0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--10----00000--0----") // ASIMD
INST(asimd_VTRN, "VTRN", "111100111D11zz10dddd00001QM0mmmm") // ASIMD
INST(asimd_VUZP, "VUZP", "111100111D11zz10dddd00010QM0mmmm") // ASIMD
INST(asimd_VZIP, "VZIP", "111100111D11zz10dddd00011QM0mmmm") // ASIMD
INST(asimd_VMOVN, "VMOVN", "111100111D11zz10dddd001000M0mmmm") // ASIMD
INST(asimd_VQMOVUN, "VQMOVUN", "111100111D11zz10dddd001001M0mmmm") // ASIMD
INST(asimd_VQMOVN, "VQMOVN", "111100111D11zz10dddd00101oM0mmmm") // ASIMD
INST(asimd_VSHLL_max, "VSHLL_max", "111100111D11zz10dddd001100M0mmmm") // ASIMD
INST(v8_VRINTN, "VRINTN", "111100111D11zz10dddd01000QM0mmmm") // v8
INST(v8_VRINTX, "VRINTX", "111100111D11zz10dddd01001QM0mmmm") // v8
INST(v8_VRINTA, "VRINTA", "111100111D11zz10dddd01010QM0mmmm") // v8
INST(v8_VRINTZ, "VRINTZ", "111100111D11zz10dddd01011QM0mmmm") // v8
INST(v8_VRINTM, "VRINTM", "111100111D11zz10dddd01101QM0mmmm") // v8
INST(v8_VRINTP, "VRINTP", "111100111D11zz10dddd01111QM0mmmm") // v8
INST(asimd_VCVT_half, "VCVT (half-precision)", "111100111D11zz10dddd011o00M0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--10----011-01-0----") // ASIMD
INST(v8_VCVTA, "VCVTA", "111100111D11zz11dddd0000oQM0mmmm") // v8
INST(v8_VCVTN, "VCVTN", "111100111D11zz11dddd0001oQM0mmmm") // v8
INST(v8_VCVTP, "VCVTP", "111100111D11zz11dddd0010oQM0mmmm") // v8
INST(v8_VCVTM, "VCVTM", "111100111D11zz11dddd0011oQM0mmmm") // v8
INST(asimd_VRECPE, "VRECPE", "111100111D11zz11dddd010F0QM0mmmm") // ASIMD
INST(asimd_VRSQRTE, "VRSQRTE", "111100111D11zz11dddd010F1QM0mmmm") // ASIMD
INST(asimd_VCVT_integer, "VCVT (integer)", "111100111D11zz11dddd011oUQM0mmmm") // ASIMD
// Two registers, cryptography
INST(v8_AESE, "AESE", "111100111D11zz00dddd001100M0mmmm") // v8
INST(v8_AESD, "AESD", "111100111D11zz00dddd001101M0mmmm") // v8
INST(v8_AESMC, "AESMC", "111100111D11zz00dddd001110M0mmmm") // v8
INST(v8_AESIMC, "AESIMC", "111100111D11zz00dddd001111M0mmmm") // v8
INST(arm_UDF, "UNALLOCATED", "111100111-11--01----001010-0----") // v8
INST(arm_UDF, "UNALLOCATED (SHA1H)", "111100111-11--01----001011-0----") // v8
INST(arm_UDF, "UNALLOCATED (SHA1SU1)", "111100111-11--10----001110-0----") // v8
INST(v8_SHA256SU0, "SHA256SU0", "111100111D11zz10dddd001111M0mmmm") // v8
// One register and modified immediate
INST(asimd_VMOV_imm, "VBIC, VMOV, VMVN, VORR (immediate)", "1111001a1D000bcdVVVVmmmm0Qo1efgh") // ASIMD
// Miscellaneous
INST(asimd_VEXT, "VEXT", "111100101D11nnnnddddiiiiNQM0mmmm") // ASIMD
INST(asimd_VTBL, "VTBL", "111100111D11nnnndddd10zzN0M0mmmm") // ASIMD
INST(asimd_VTBX, "VTBX", "111100111D11nnnndddd10zzN1M0mmmm") // ASIMD
INST(asimd_VDUP_scalar, "VDUP (scalar)", "111100111D11iiiidddd11000QM0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--------11-----0----") // ASIMD
// Advanced SIMD load/store structures
INST(v8_VST_multiple, "VST{1-4} (multiple)", "111101000D00nnnnddddxxxxzzaammmm") // v8
INST(v8_VLD_multiple, "VLD{1-4} (multiple)", "111101000D10nnnnddddxxxxzzaammmm") // v8
INST(arm_UDF, "UNALLOCATED", "111101000--0--------1011--------") // v8
INST(arm_UDF, "UNALLOCATED", "111101000--0--------11----------") // v8
INST(arm_UDF, "UNALLOCATED", "111101001-00--------11----------") // v8
INST(v8_VLD_all_lanes, "VLD{1-4} (all lanes)", "111101001D10nnnndddd11nnzzTammmm") // v8
INST(v8_VST_single, "VST{1-4} (single)", "111101001D00nnnnddddzzNNaaaammmm") // v8
INST(v8_VLD_single, "VLD{1-4} (single)", "111101001D10nnnnddddzzNNaaaammmm") // v8
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@@ -24,8 +24,8 @@ template<typename Visitor>
using Thumb16Matcher = Decoder::Matcher<Visitor, u16>;
template<typename V>
static std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
alignas(64) static const auto table = std::array{
std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
alignas(64) static const std::vector<Thumb16Matcher<V>> table = {
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)),
#include "./thumb16.inc"
#undef INST
@@ -37,7 +37,7 @@ static std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThum
}
template<typename V>
static std::optional<std::string_view> GetNameThumb16(u32 inst) noexcept {
std::optional<std::string_view> GetNameThumb16(u32 inst) noexcept {
std::vector<std::pair<std::string_view, Thumb16Matcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)) },
#include "./thumb16.inc"
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@@ -23,8 +23,8 @@ template<typename Visitor>
using Thumb32Matcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
static std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
alignas(64) static const auto table = std::array{
std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
alignas(64) static const std::vector<Thumb32Matcher<V>> table = {
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
#include "./thumb32.inc"
#undef INST
@@ -36,7 +36,7 @@ static std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThum
}
template<typename V>
static std::optional<std::string_view> GetNameThumb32(u32 inst) noexcept {
std::optional<std::string_view> GetNameThumb32(u32 inst) noexcept {
std::vector<std::pair<std::string_view, Thumb32Matcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./thumb32.inc"
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@@ -24,7 +24,7 @@ template<typename Visitor>
using VFPMatcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
static std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
using Table = std::vector<VFPMatcher<V>>;
alignas(64) static const struct Tables {
Table unconditional;
@@ -52,7 +52,7 @@ static std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32
}
template<typename V>
static std::optional<std::string_view> GetNameVFP(u32 inst) noexcept {
std::optional<std::string_view> GetNameVFP(u32 inst) noexcept {
std::vector<std::pair<std::string_view, VFPMatcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./vfp.inc"
@@ -36,19 +36,33 @@ inline size_t ToFastLookupIndex(u32 instruction) {
} // namespace detail
template<typename V>
inline DecodeTable<V> GetDecodeTable() {
DecodeTable<V> table{};
for (size_t i = 0; i < table.size(); ++i) {
// PLEASE HEAP ELLIDE
for (auto const& e : std::vector<Matcher<V>>{
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
constexpr DecodeTable<V> GetDecodeTable() {
std::vector<std::pair<const char*, Matcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./a64.inc"
#undef INST
}) {
const auto expect = detail::ToFastLookupIndex(e.GetExpected());
const auto mask = detail::ToFastLookupIndex(e.GetMask());
if ((i & mask) == expect)
table[i].push_back(e);
};
// If a matcher has more bits in its mask it is more specific, so it should come first.
std::stable_sort(list.begin(), list.end(), [](const auto& a, const auto& b) {
// If a matcher has more bits in its mask it is more specific, so it should come first.
return mcl::bit::count_ones(a.second.GetMask()) > mcl::bit::count_ones(b.second.GetMask());
});
// Exceptions to the above rule of thumb.
std::stable_partition(list.begin(), list.end(), [&](const auto& e) {
return std::set<std::string>{
"MOVI, MVNI, ORR, BIC (vector, immediate)",
"FMOV (vector, immediate)",
"Unallocated SIMD modified immediate",
}.count(e.first) > 0;
});
DecodeTable<V> table{};
for (size_t i = 0; i < table.size(); ++i) {
for (auto const& e : list) {
const auto expect = detail::ToFastLookupIndex(e.second.GetExpected());
const auto mask = detail::ToFastLookupIndex(e.second.GetMask());
if ((i & mask) == expect) {
table[i].push_back(e.second);
}
}
}
return table;
@@ -56,7 +70,7 @@ inline DecodeTable<V> GetDecodeTable() {
/// In practice it must always suceed, otherwise something else unrelated would have gone awry
template<typename V>
inline std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
alignas(64) static const auto table = GetDecodeTable<V>();
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
auto iter = std::find_if(subtable.begin(), subtable.end(), [instruction](const auto& matcher) {
@@ -68,7 +82,7 @@ inline std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instru
}
template<typename V>
inline std::optional<std::string_view> GetName(u32 inst) noexcept {
std::optional<std::string_view> GetName(u32 inst) noexcept {
std::vector<std::pair<std::string_view, Matcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./a64.inc"
File diff suppressed because it is too large Load Diff
-5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// First we list common shared opcodes
// Since we give priority to A64 performance, we include them first, this is so we
// can discard all A32 opcodes instead of having a "hole" in our checks
@@ -713,8 +710,6 @@ A64OPC(ExclusiveWriteMemory32, U32, U64,
A64OPC(ExclusiveWriteMemory64, U32, U64, U64, U64, AccType )
A64OPC(ExclusiveWriteMemory128, U32, U64, U64, U128, AccType )
// Remember to update:
// - a32_emit_x64.cpp
// A32 Context getters/setters
A32OPC(SetCheckBit, Void, U1 )
-99
View File
@@ -415,105 +415,6 @@ TEST_CASE("A64: URSHL", "[a64]") {
CHECK(jit.GetVector(9) == Vector{0x0000000000000002, 0x12db8b8280e0ba});
}
TEST_CASE("A64: SQSHLU", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SQSHLU(V8.B16(), V0.B16(), 1);
code.SQSHLU(V9.H8(), V1.H8(), 2);
code.SQSHLU(V10.S4(), V2.S4(), 28);
code.SQSHLU(V11.D2(), V3.D2(), 4);
code.SQSHLU(V12.S4(), V0.S4(), 1);
code.SQSHLU(V13.S4(), V1.S4(), 3);
code.SQSHLU(V14.S4(), V2.S4(), 0);
code.SQSHLU(V15.S4(), V3.S4(), 0);
jit.SetVector(0, Vector{0xffffffff'18ba6a6a, 0x7fffffff'943b954f});
jit.SetVector(1, Vector{0x0000000b'0000000f, 0xffffffff'ffffffff});
jit.SetVector(2, Vector{0x00000001'000000ff, 0x00000010'0000007f});
jit.SetVector(3, Vector{0xffffffffffffffff, 0x96dc5c140705cd04});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(8) == Vector{0x3000d4d4, 0xfe0000000076009e});
CHECK(jit.GetVector(9) == Vector{0x2c0000003c, 0});
CHECK(jit.GetVector(10) == Vector{0x10000000'ffffffff, 0xffffffff'ffffffff});
CHECK(jit.GetVector(11) == Vector{0, 0});
CHECK(jit.GetVector(12) == Vector{0x3174d4d4, 0xfffffffe00000000});
CHECK(jit.GetVector(13) == Vector{0x5800000078, 0});
CHECK(jit.GetVector(14) == Vector{0x1000000ff, 0x100000007f});
CHECK(jit.GetVector(15) == Vector{0, 0x705cd04});
}
TEST_CASE("A64: SMIN", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SMIN(V8.B16(), V0.B16(), V3.B16());
code.SMIN(V9.H8(), V1.H8(), V2.H8());
code.SMIN(V10.S4(), V2.S4(), V3.S4());
code.SMIN(V11.S4(), V3.S4(), V3.S4());
code.SMIN(V12.S4(), V0.S4(), V3.S4());
code.SMIN(V13.S4(), V1.S4(), V2.S4());
code.SMIN(V14.S4(), V2.S4(), V1.S4());
code.SMIN(V15.S4(), V3.S4(), V0.S4());
jit.SetPC(0);
jit.SetVector(0, Vector{0xffffffff'18ba6a6a, 0x7fffffff'943b954f});
jit.SetVector(1, Vector{0x0000000b'0000000f, 0xffffffff'ffffffff});
jit.SetVector(2, Vector{0x00000001'000000ff, 0x00000010'0000007f});
jit.SetVector(3, Vector{0xffffffff'ffffffff, 0x96dc5c14'0705cd04});
env.ticks_left = 4;
CheckedRun([&]() { jit.Run(); });
REQUIRE(jit.GetVector(8) == Vector{0xffffffffffbaffff, 0x96dcffff94059504});
REQUIRE(jit.GetVector(9) == Vector{0x10000000f, 0xffffffffffffffff});
REQUIRE(jit.GetVector(10) == Vector{0xffffffffffffffff, 0x96dc5c140000007f});
}
TEST_CASE("A64: SMINP", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SMINP(V8.B16(), V0.B16(), V3.B16());
code.SMINP(V9.H8(), V1.H8(), V2.H8());
code.SMINP(V10.S4(), V2.S4(), V1.S4());
code.SMINP(V11.S4(), V3.S4(), V3.S4());
code.SMINP(V12.S4(), V0.S4(), V3.S4());
code.SMINP(V13.S4(), V1.S4(), V2.S4());
code.SMINP(V14.S4(), V2.S4(), V1.S4());
code.SMINP(V15.S4(), V3.S4(), V0.S4());
jit.SetPC(0);
jit.SetVector(0, Vector{0xffffffff'18ba6a6a, 0x7fffffff'943b954f});
jit.SetVector(1, Vector{0x0000000b'0000000f, 0xffffffff'ffffffff});
jit.SetVector(2, Vector{0x00000001'000000ff, 0x00000010'0000007f});
jit.SetVector(3, Vector{0xffffffff'ffffffff, 0x96dc5c14'0705cd04});
env.ticks_left = 4;
CheckedRun([&]() { jit.Run(); });
REQUIRE(jit.GetVector(8) == Vector{0xffff9495ffffba6a, 0x961405cdffffffff});
REQUIRE(jit.GetVector(9) == Vector{0xffffffff00000000, 0});
REQUIRE(jit.GetVector(10) == Vector{0x1000000001, 0xffffffff0000000b});
REQUIRE(jit.GetVector(11) == Vector{0x96dc5c14ffffffff, 0x96dc5c14ffffffff});
REQUIRE(jit.GetVector(12) == Vector{0x943b954fffffffff, 0x96dc5c14ffffffff});
REQUIRE(jit.GetVector(13) == Vector{0xffffffff0000000b, 0x1000000001});
REQUIRE(jit.GetVector(14) == Vector{0x1000000001, 0xffffffff0000000b});
REQUIRE(jit.GetVector(15) == Vector{0x96dc5c14ffffffff, 0x943b954fffffffff});
}
TEST_CASE("A64: XTN", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
+6 -3
View File
@@ -4,21 +4,22 @@
#include <random>
#include <frozen/string.h>
#include "common/settings.h"
#include "common/random.h"
#include "settings_generator.h"
namespace FrontendCommon {
void GenerateSettings() {
auto gen = Common::Random::GetMT19937();
static std::random_device rd;
// Web Token
if (Settings::values.eden_token.GetValue().empty()) {
static constexpr const size_t token_length = 48;
static constexpr const frozen::string token_set = "abcdefghijklmnopqrstuvwxyz";
static std::uniform_int_distribution<int> token_dist(0, token_set.size() - 1);
std::string result;
for (size_t i = 0; i < token_length; ++i) {
size_t idx = token_dist(gen);
size_t idx = token_dist(rd);
result += token_set[idx];
}
Settings::values.eden_token.SetValue(result);
@@ -26,6 +27,8 @@ void GenerateSettings() {
// Randomly generated number because, well, we fill the rest automagically ;)
// Other serial parts are filled by Region_Index
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u32> distribution(1, (std::numeric_limits<u32>::max)());
if (Settings::values.serial_unit.GetValue() == 0)
Settings::values.serial_unit.SetValue(distribution(gen));
+8 -11
View File
@@ -1,13 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <random>
#include "common/random.h"
#include "common/input.h"
#include "hid_core/frontend/input_converter.h"
@@ -123,14 +119,15 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
.properties = properties,
};
if (TransformToButton(callback).value) {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<s16> distribution(-5000, 5000);
auto gen = Common::Random::GetMT19937();
status.accel.x.raw_value = f32(distribution(gen)) * 0.001f;
status.accel.y.raw_value = f32(distribution(gen)) * 0.001f;
status.accel.z.raw_value = f32(distribution(gen)) * 0.001f;
status.gyro.x.raw_value = f32(distribution(gen)) * 0.001f;
status.gyro.y.raw_value = f32(distribution(gen)) * 0.001f;
status.gyro.z.raw_value = f32(distribution(gen)) * 0.001f;
status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
}
break;
}
+6 -2
View File
@@ -11,7 +11,6 @@
#include "common/logging.h"
#include "common/param_package.h"
#include "common/random.h"
#include "common/settings.h"
#include "input_common/drivers/udp_client.h"
#include "input_common/helpers/udp_protocol.h"
@@ -32,7 +31,7 @@ public:
explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
: callback(std::move(callback_)), timer(io_context),
socket(io_context, udp::endpoint(udp::v4(), 0)), client_id(Common::Random::Random32(0)) {
socket(io_context, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) {
boost::system::error_code ec{};
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
if (ec.value() != boost::system::errc::success) {
@@ -65,6 +64,11 @@ public:
}
private:
u32 GenerateRandomClientId() const {
std::random_device device;
return device();
}
void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
switch (*type) {
+3 -1
View File
@@ -23,6 +23,8 @@ namespace Network {
class Room::RoomImpl {
public:
std::mt19937 random_gen; ///< Random number generator. Used for GenerateFakeIPAddress
ENetHost* server = nullptr; ///< Network interface.
std::atomic<State> state{State::Closed}; ///< Current state of the room.
@@ -49,7 +51,7 @@ public:
IPBanList ip_ban_list; ///< List of banned IP addresses
mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists
RoomImpl() {}
RoomImpl() : random_gen(std::random_device()()) {}
/// Thread that receives and dispatches network packets
std::optional<std::jthread> room_thread;
@@ -118,6 +118,8 @@ if (NOT GLSLANG_ERROR STREQUAL "")
set(QUIET_FLAG "")
endif()
# Shader files must depend on their directory otherwise *BSD make will spontaneously combust
file(MAKE_DIRECTORY "${SHADER_DIR}")
foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES})
get_filename_component(FILENAME ${SOURCE_FILE} NAME)
string(REPLACE "." "_" SHADER_NAME ${FILENAME})
@@ -133,6 +135,7 @@ foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES})
${SOURCE_FILE}
DEPENDS
${INPUT_FILE}
${SHADER_DIR}
# HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
)
set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
@@ -148,6 +151,8 @@ foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES})
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} --target-env ${SPIR_V_VERSION}
MAIN_DEPENDENCY
${SOURCE_FILE}
DEPENDS
${SHADER_DIR}
)
set(SHADER_HEADERS ${SHADER_HEADERS} ${SPIRV_HEADER_FILE})
endif()
@@ -167,6 +172,7 @@ foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
${SOURCE_FILE}
DEPENDS
${INPUT_FILE}
${SHADER_DIR}
# HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
)
set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
+10 -3
View File
@@ -260,7 +260,8 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
};
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
bool is_synced = !Settings::IsGPULevelHigh() && is_fence;
const bool is_payload = counter_type == QueryType::Payload;
const bool is_synced = !(Settings::getDebugKnobAt(0) && is_payload) && !Settings::IsGPULevelHigh() && is_fence;
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
pointer, pointer_timestamp] {
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
@@ -292,9 +293,15 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
}
});
if (is_fence) {
impl->rasterizer.SignalFence(std::move(operation));
if (Settings::getDebugKnobAt(0) && is_payload) {
impl->rasterizer.SyncOperation(std::move(operation));
std::function<void()> noop([] {});
impl->rasterizer.SignalFence(std::move(noop));
} else {
impl->rasterizer.SignalFence(std::move(operation));
}
} else {
if (!Settings::IsGPULevelHigh() && counter_type == QueryType::Payload) {
if (!Settings::IsGPULevelHigh() && is_payload) {
if (has_timestamp) {
u64 timestamp = impl->gpu.GetTicks();
u64 value = static_cast<u64>(payload);
@@ -49,7 +49,7 @@ using VideoCore::Surface::PixelFormatFromDepthFormat;
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
constexpr size_t NUM_STAGES = Maxwell::MaxShaderStage;
constexpr size_t INLINE_IMAGE_ELEMENTS = 64;
constexpr size_t MAX_IMAGE_ELEMENTS = 64;
DescriptorLayoutBuilder MakeBuilder(const Device& device, std::span<const Shader::Info> infos) {
DescriptorLayoutBuilder builder{device};
@@ -264,11 +264,7 @@ GraphicsPipeline::GraphicsPipeline(
stage_infos[stage] = *info;
enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
num_image_elements += Shader::NumDescriptors(info->texture_buffer_descriptors);
num_image_elements += Shader::NumDescriptors(info->image_buffer_descriptors);
num_textures += Shader::NumDescriptors(info->texture_descriptors);
num_image_elements += Shader::NumDescriptors(info->texture_descriptors);
num_image_elements += Shader::NumDescriptors(info->image_descriptors);
}
fragment_has_color0_output = stage_infos[NUM_STAGES - 1].stores_frag_color[0];
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
@@ -314,10 +310,10 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
template <typename Spec>
bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
small_vector<VideoCommon::ImageViewInOut, INLINE_IMAGE_ELEMENTS> views;
small_vector<VideoCommon::SamplerId, INLINE_IMAGE_ELEMENTS> samplers;
views.reserve(num_image_elements);
samplers.reserve(num_textures);
std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers;
size_t sampler_index{};
size_t view_index{};
texture_cache.SynchronizeGraphicsDescriptors();
@@ -362,11 +358,11 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE {
for (u32 index = 0; index < desc.count; ++index) {
const auto handle{read_handle(desc, index)};
views.push_back({
views[view_index++] = {
.index = handle.first,
.blacklist = blacklist,
.id = {}
});
};
}
}};
if constexpr (Spec::has_texture_buffers) {
@@ -382,10 +378,10 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
for (const auto& desc : info.texture_descriptors) {
for (u32 index = 0; index < desc.count; ++index) {
const auto handle{read_handle(desc, index)};
views.push_back({handle.first});
views[view_index++] = {handle.first};
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
samplers.push_back(sampler);
samplers[sampler_index++] = sampler;
}
}
if constexpr (Spec::has_images) {
@@ -411,9 +407,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
if constexpr (Spec::enabled_stages[4]) {
config_stage(4);
}
ASSERT(views.size() == num_image_elements);
ASSERT(samplers.size() == num_textures);
texture_cache.FillGraphicsImageViews<Spec::has_images>(std::span(views.data(), views.size()));
texture_cache.FillGraphicsImageViews<Spec::has_images>(std::span(views.data(), view_index));
VideoCommon::ImageViewInOut* texture_buffer_it{views.data()};
const auto bind_stage_info{[&](size_t stage) LAMBDA_FORCEINLINE {
@@ -507,8 +501,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
buffer_cache.any_buffer_uploaded = false;
}
texture_cache.UpdateRenderTargets(false);
texture_cache.CheckFeedbackLoop(std::span<const VideoCommon::ImageViewInOut>{views.data(),
views.size()});
texture_cache.CheckFeedbackLoop(views);
ConfigureDraw(rescaling, render_area);
return true;
@@ -994,7 +987,7 @@ void GraphicsPipeline::Validate() {
num_images += Shader::NumDescriptors(info.texture_descriptors);
num_images += Shader::NumDescriptors(info.image_descriptors);
}
ASSERT(num_images == num_image_elements);
ASSERT(num_images <= MAX_IMAGE_ELEMENTS);
}
} // namespace Vulkan
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -159,7 +159,6 @@ private:
std::array<Shader::Info, NUM_STAGES> stage_infos;
std::array<u32, 5> enabled_uniform_buffer_masks{};
VideoCommon::UniformBufferSizes uniform_buffer_sizes{};
size_t num_image_elements{};
u32 num_textures{};
bool fragment_has_color0_output{};
+9 -25
View File
@@ -18,25 +18,8 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
// Padding, dimensions, alignment...
const int column = index.row() % m_columns;
const int cell_width = option.rect.width();
const int fixed_card_width = cell_width - m_padding;
const int margins = 8;
// The gist of it is that this anchors the left and right sides to the edges,
// while maintaining an even gap between each card.
// I just smashed random keys into my keyboard until something worked.
// Don't even bother trying to figure out what the hell this is doing.
const auto total_row_width = m_columns * cell_width;
const auto total_gap_space = total_row_width - (margins * 2) - (m_columns * fixed_card_width);
const auto gap = (m_columns > 1) ? (total_gap_space / (m_columns - 1)) : 0;
const auto relative_x = margins + (column * (fixed_card_width + gap));
const auto x_pos = option.rect.left() - (column * cell_width) + static_cast<int>(relative_x);
// also, add some additional padding here to prevent card overlap
QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8, option.rect.height() - margins);
// padding
QRect cardRect = option.rect.adjusted(4 + m_padding / 2, 4, -4 - m_padding / 2, -4);
// colors
QPalette palette = option.palette;
@@ -58,6 +41,8 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->setPen(QPen(borderColor, 1));
painter->drawRoundedRect(cardRect, 10, 10);
static constexpr const int padding = 8;
// icon
int _iconsize = UISettings::values.game_icon_size.GetValue();
QSize iconSize(_iconsize, _iconsize);
@@ -69,7 +54,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
scaledSize.scale(iconSize, Qt::KeepAspectRatio);
int x = cardRect.left() + (cardRect.width() - scaledSize.width()) / 2;
int y = cardRect.top() + margins;
int y = cardRect.top() + padding;
iconRect = QRect(x, y, scaledSize.width(), scaledSize.height());
@@ -88,7 +73,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->restore();
} else {
// if there is no icon just draw a blank rect
iconRect = QRect(cardRect.left() + margins, cardRect.top() + margins, _iconsize, _iconsize);
iconRect = QRect(cardRect.left() + padding, cardRect.top() + padding, _iconsize, _iconsize);
}
if (UISettings::values.show_game_name.GetValue()) {
@@ -97,8 +82,8 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
// padding + text
QRect textRect = cardRect;
textRect.setTop(iconRect.bottom() + margins);
textRect.adjust(margins, 0, -margins, -margins);
textRect.setTop(iconRect.bottom() + 8);
textRect.adjust(padding, 0, -padding, -padding);
// We are already crammed on space, ignore the row 2
QString title = index.data(Qt::DisplayRole).toString();
@@ -126,8 +111,7 @@ QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex&
return m_size;
}
void GameCard::setSize(const QSize& newSize, const int padding, const int columns) {
void GameCard::setSize(const QSize& newSize, const int padding) {
m_size = newSize;
m_padding = padding;
m_columns = columns;
}
+1 -2
View File
@@ -18,10 +18,9 @@ public:
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setSize(const QSize& newSize, const int padding, const int columns);
void setSize(const QSize& newSize, const int padding);
private:
QSize m_size;
int m_padding;
int m_columns;
};
+4 -4
View File
@@ -411,7 +411,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
list_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
list_view->setContextMenuPolicy(Qt::CustomContextMenu);
list_view->setGridSize(QSize(140, 160));
m_gameCard->setSize(list_view->gridSize(), 0, 4);
m_gameCard->setSize(list_view->gridSize(), 0);
list_view->setSpacing(10);
list_view->setWordWrap(true);
@@ -1051,8 +1051,8 @@ void GameList::UpdateIconSize() {
// And now stretch it a bit to fill out remaining space.
// Not perfect but works well enough for now
int columns = std::max(1, (view_width - 16) / min_item_width);
int stretched_width = ((view_width) - (spacing * (columns - 1))) / columns;
int columns = std::max(1, view_width / min_item_width);
int stretched_width = (view_width - (spacing * (columns - 1))) / columns;
// only updates things if grid size is changed
QSize grid_size(stretched_width, icon_size + heightMargin);
@@ -1060,7 +1060,7 @@ void GameList::UpdateIconSize() {
list_view->setUpdatesEnabled(false);
list_view->setGridSize(grid_size);
m_gameCard->setSize(grid_size, stretched_width - min_item_width, columns);
m_gameCard->setSize(grid_size, stretched_width - min_item_width);
list_view->setUpdatesEnabled(true);
}
-1646
View File
File diff suppressed because it is too large Load Diff