Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ce420af30 | |||
| 9ace6742d7 | |||
| 79f29abcba | |||
| 3ce5463d2d | |||
| cae70c30fa | |||
| bcceced96d | |||
| 82e374f66c | |||
| 6e76014824 | |||
| bb71ace365 | |||
| 21f9db1c27 | |||
| b4a485e244 | |||
| 81a344f3db | |||
| c0fbb2526d | |||
| c3afd2fabd | |||
| ee2891c55e | |||
| dd91b41a78 | |||
| e9f4541069 | |||
| cf7086de7c | |||
| 8e14f07a69 | |||
| 0b179517b3 | |||
| 7a8176f63f | |||
| 5322bce4b8 | |||
| 276dcdd8ea | |||
| 59254cd1e7 | |||
| 9a3af3a6a3 | |||
| b473c18d6e | |||
| c984c387d7 | |||
| 5856beac54 | |||
| 16e7e034d7 |
@@ -3,8 +3,7 @@ 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:
|
||||
@@ -59,4 +58,3 @@ jobs:
|
||||
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "@data.json" --fail
|
||||
|
||||
@@ -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://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(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(REPO_NAME "Eden Nightly")
|
||||
else()
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 335 KiB After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1 @@
|
||||
#43fcfcff
|
||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 15 KiB |
@@ -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.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
|
||||
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
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#haikuos).
|
||||
|
||||
@@ -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,8 +148,9 @@ 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>
|
||||
@@ -167,7 +168,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
|
||||
|
||||
@@ -189,7 +190,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.**
|
||||
@@ -198,7 +199,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.
|
||||
@@ -222,7 +223,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
|
||||
@@ -235,7 +236,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
|
||||
@@ -252,7 +253,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.**
|
||||
@@ -271,7 +272,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>
|
||||
|
||||
@@ -294,3 +295,22 @@ 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,6 +25,11 @@ 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
|
||||
@@ -87,6 +92,28 @@ 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))
|
||||
@@ -128,9 +155,29 @@ 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
|
||||
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
isActivityRecreated = savedInstanceState != null
|
||||
|
||||
@@ -210,6 +257,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
mainHandler.removeCallbacks(romSwapStopTimeoutRunnable)
|
||||
super.onDestroy()
|
||||
inputManager.unregisterInputDeviceListener(this)
|
||||
stopForegroundService(this)
|
||||
@@ -228,17 +276,123 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
setIntent(intent)
|
||||
|
||||
// Reset navigation graph with new intent data to recreate EmulationFragment
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
||||
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
|
||||
|
||||
handleSwapIntent(intent)
|
||||
nfcReader.onNewIntent(intent)
|
||||
InputHandler.updateControllerData()
|
||||
}
|
||||
|
||||
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))
|
||||
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()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
|
||||
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
||||
@@ -608,19 +762,48 @@ 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 (status == 0 && emulationViewModel.programChanged.value == -1) {
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -644,6 +827,11 @@ 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)
|
||||
|
||||
@@ -69,8 +69,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
MY_PAGE_APPLET("my_page_applet_mode"),
|
||||
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide"),
|
||||
OVERLAY_GRID_SIZE("overlay_grid_size"),
|
||||
GPU_LOG_RING_BUFFER_SIZE("gpu_log_ring_buffer_size"),
|
||||
PRODUCT_MODEL("product_model")
|
||||
GPU_LOG_RING_BUFFER_SIZE("gpu_log_ring_buffer_size")
|
||||
;
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
|
||||
|
||||
@@ -965,16 +965,6 @@ abstract class SettingsItem(
|
||||
max = 4096
|
||||
)
|
||||
)
|
||||
put(
|
||||
SpinBoxSetting(
|
||||
IntSetting.PRODUCT_MODEL,
|
||||
titleId = R.string.product_model,
|
||||
descriptionId = R.string.product_model_description,
|
||||
valueHint = R.string.product_model,
|
||||
min = 1,
|
||||
max = 2
|
||||
)
|
||||
)
|
||||
|
||||
val fastmem = object : AbstractBooleanSetting {
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
||||
|
||||
@@ -1265,7 +1265,6 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
add(IntSetting.PRODUCT_MODEL.key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ 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
|
||||
@@ -135,6 +136,8 @@ 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
|
||||
@@ -238,6 +241,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
if (emulationViewModel.isEmulationStopping.value) {
|
||||
deferGameSetupUntilStopCompletes = true
|
||||
if (game == null) {
|
||||
game = args.game ?: intentGame
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
finishGameSetup()
|
||||
}
|
||||
|
||||
@@ -260,6 +271,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
game = gameToUse
|
||||
emulationActivity?.updateSessionGame(gameToUse)
|
||||
} catch (e: Exception) {
|
||||
Log.error("[EmulationFragment] Error during game setup: ${e.message}")
|
||||
Toast.makeText(
|
||||
@@ -334,7 +346,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
emulationState = EmulationState(game!!.path) {
|
||||
return@EmulationState driverViewModel.isInteractionAllowed.value
|
||||
return@EmulationState driverViewModel.isInteractionAllowed.value &&
|
||||
!isStoppingForRomSwap
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,8 +903,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
)
|
||||
|
||||
GameIconUtils.loadGameIcon(game!!, binding.loadingImage)
|
||||
binding.loadingTitle.text = game!!.title
|
||||
game?.let {
|
||||
GameIconUtils.loadGameIcon(it, binding.loadingImage)
|
||||
binding.loadingTitle.text = it.title
|
||||
} ?: run {
|
||||
binding.loadingTitle.text = ""
|
||||
}
|
||||
binding.loadingTitle.isSelected = true
|
||||
binding.loadingText.isSelected = true
|
||||
|
||||
@@ -959,6 +976,12 @@ 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) {
|
||||
@@ -995,26 +1018,24 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
|
||||
if (it && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
||||
startEmulation()
|
||||
if (it &&
|
||||
!isStoppingForRomSwap &&
|
||||
!NativeLibrary.isRunning() &&
|
||||
!NativeLibrary.isPaused()
|
||||
) {
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
}
|
||||
|
||||
updateScreenLayout()
|
||||
|
||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1375,6 +1396,9 @@ 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
|
||||
@@ -1382,7 +1406,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
NativeLibrary.clearEmulationActivity()
|
||||
if (!hasNewerEmulationFragment()) {
|
||||
NativeLibrary.clearEmulationActivity()
|
||||
}
|
||||
super.onDetach()
|
||||
}
|
||||
|
||||
@@ -1840,10 +1866,74 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
emulationState.clearSurface()
|
||||
if (this::emulationState.isInitialized && !hasNewerEmulationFragment()) {
|
||||
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)
|
||||
@@ -2134,6 +2224,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
state = State.STOPPED
|
||||
} else {
|
||||
Log.warning("[EmulationFragment] Stop called while already stopped.")
|
||||
NativeLibrary.stopEmulation()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ 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
|
||||
@@ -46,6 +47,7 @@ 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
|
||||
@@ -61,6 +63,7 @@ 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>()
|
||||
@@ -118,6 +121,20 @@ 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(
|
||||
@@ -133,8 +150,11 @@ class GamePropertiesFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
val isChangingConfigurations = activity?.isChangingConfigurations == true
|
||||
super.onDestroy()
|
||||
gamesViewModel.reloadGames(true)
|
||||
if (!isChangingConfigurations) {
|
||||
gamesViewModel.reloadGames(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPlayTime() {
|
||||
|
||||
@@ -100,42 +100,45 @@ class GamesViewModel : ViewModel() {
|
||||
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
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
|
||||
}
|
||||
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)
|
||||
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())
|
||||
reloading.set(false)
|
||||
_isReloading.value = false
|
||||
_shouldScrollAfterReload.value = true
|
||||
setGames(GameHelper.getGames())
|
||||
_shouldScrollAfterReload.value = true
|
||||
|
||||
if (directoriesChanged) {
|
||||
setShouldSwapData(true)
|
||||
if (directoriesChanged) {
|
||||
setShouldSwapData(true)
|
||||
}
|
||||
} finally {
|
||||
reloading.set(false)
|
||||
_isReloading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ object DirectoryInitialization {
|
||||
fun start() {
|
||||
if (!areDirectoriesReady) {
|
||||
initializeInternalStorage()
|
||||
NativeLibrary.initializeSystem(false)
|
||||
NativeConfig.initializeGlobalConfig()
|
||||
NativeLibrary.initializeSystem(false)
|
||||
NativeLibrary.reloadProfiles()
|
||||
migrateSettings()
|
||||
areDirectoriesReady = true
|
||||
|
||||
@@ -8,9 +8,11 @@ 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
|
||||
@@ -49,29 +51,8 @@ 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>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mountExternalContentDirectories(mountedContainerUris)
|
||||
|
||||
val badDirs = mutableListOf<Int>()
|
||||
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
|
||||
@@ -115,6 +96,15 @@ 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")
|
||||
@@ -181,6 +171,71 @@ 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,
|
||||
|
||||
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 44 KiB |
@@ -473,8 +473,6 @@
|
||||
<string name="renderer_accuracy_description">يتحكم في وضع محاكاة وحدة معالجة الرسومات. تعمل معظم الألعاب بشكل جيد مع وضعي سريع أو متوازن، لكن الوضع الدقيق لا يزال مطلوبًا لبعض الألعاب. تميل الجسيمات إلى العرض بشكل صحيح فقط عند استخدام الوضع الدقيق.</string>
|
||||
<string name="dma_accuracy">دقة DMA</string>
|
||||
<string name="dma_accuracy_description">يتحكم في دقة DMA. يمكن أن تؤدي الدقة الآمنة إلى حل المشكلات في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا لم تكن متأكدًا، فاترك هذا الخيار على الإعداد الافتراضي.</string>
|
||||
<string name="frame_pacing_mode">وضع توقيت الإطارات</string>
|
||||
<string name="frame_pacing_mode_description">يتحكم في كيفية إدارة المحاكي لسرعة الإطارات لتقليل التقطع وجعل معدل الإطارات أكثر سلاسة واتساقًا.</string>
|
||||
<string name="anisotropic_filtering">تصفية متباينة الخواص</string>
|
||||
<string name="anisotropic_filtering_description">يحسن جودة الأنسجة عند عرضها بزوايا مائلة</string>
|
||||
<string name="vram_usage_mode">وضع استخدام ذاكرة VRAM</string>
|
||||
@@ -999,13 +997,6 @@
|
||||
<string name="dma_accuracy_unsafe">غير آمن</string>
|
||||
<string name="dma_accuracy_safe">آمن</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">تلقائي</string>
|
||||
<string name="frame_pacing_mode_target_30">30 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_60">60 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_90">90 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_120">120 إطارًا في الثانية</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">غير مضغوط</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -452,8 +452,6 @@
|
||||
<string name="renderer_accuracy_description">Určuje režim emulovaného GPU. Většina her běží bez problémů v rychlém, nebo vyváženém režimu, ale některé stále vyžadují přesný režim. Částicové efekty se většinou zobrazují korektně pouze v přesném režimu. </string>
|
||||
<string name="dma_accuracy">Přesnost DMA</string>
|
||||
<string name="dma_accuracy_description">Ovládá přesnost DMA. Bezpečná přesnost může vyřešit problémy v některých hrách, ale v některých případech může také ovlivnit výkon. Pokud si nejste jisti, použijte výchozí nastavení.</string>
|
||||
<string name="frame_pacing_mode">Režim Framepacingu</string>
|
||||
<string name="frame_pacing_mode_description">Řídí způsob jakým emulátor spravuje časování snímku aby snížil trhání a zlepšení plynulost a stabilitu snímkové frekvence.</string>
|
||||
<string name="anisotropic_filtering">Anizotropní filtrování</string>
|
||||
<string name="anisotropic_filtering_description">Zlepšuje kvalitu textur při pohledu pod úhlem</string>
|
||||
<string name="vram_usage_mode">Režim využití VRAM</string>
|
||||
|
||||
@@ -467,8 +467,6 @@
|
||||
<string name="renderer_accuracy_description">Controla el modo de la emulación de la GPU. La mayoría de los juegos se renderizan correctamente en los modos Rápido o Equilibrado, pero algunos requieren Preciso. Las partículas tienden a renderizarse correctamente solo con el modo Preciso.</string>
|
||||
<string name="dma_accuracy">Precisión de DMA</string>
|
||||
<string name="dma_accuracy_description">Controla la precisión de DMA. La precisión segura puede solucionar problemas en algunos juegos, pero también puede afectar al rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.</string>
|
||||
<string name="frame_pacing_mode">Modo de ritmo de fotogramas</string>
|
||||
<string name="frame_pacing_mode_description">Controla cómo el emulador gestiona el ritmo de los fotogramas para reducir los tirones y hacer que la velocidad de los fotogramas sea más suave y consistente.</string>
|
||||
<string name="anisotropic_filtering">Filtrado anisotrópico</string>
|
||||
<string name="anisotropic_filtering_description">Mejora la calidad de las texturas al ser observadas desde ángulos oblicuos</string>
|
||||
<string name="vram_usage_mode">Modo de uso de VRAM</string>
|
||||
@@ -993,13 +991,6 @@
|
||||
<string name="dma_accuracy_unsafe">Inseguro</string>
|
||||
<string name="dma_accuracy_safe">Seguro</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Automático</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Sin compresión</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -468,8 +468,6 @@
|
||||
<string name="renderer_accuracy_description">Управляет режимом эмуляции графического процессора. Большинство игр нормально отображаются в режимах «Быстрый» или «Сбалансированный», но для некоторых требуется режим «Точный». Частицы обычно корректно отображаются только в режиме «Точный».</string>
|
||||
<string name="dma_accuracy">Точность DMA</string>
|
||||
<string name="dma_accuracy_description">Управляет точностью DMA. Безопасная точность может исправить проблемы в некоторых играх, но в некоторых случаях также может повлиять на производительность. Если не уверены, оставьте значение По умолчанию.</string>
|
||||
<string name="frame_pacing_mode">Режим синхронизации кадров</string>
|
||||
<string name="frame_pacing_mode_description">Управляет синхронизацией кадров в эмуляторе для уменьшения рывков и обеспечения более плавной и стабильной частоты кадров.</string>
|
||||
<string name="anisotropic_filtering">Анизотропная фильтрация</string>
|
||||
<string name="anisotropic_filtering_description">Улучшает качество текстур под углом</string>
|
||||
<string name="vram_usage_mode">Режим VRAM</string>
|
||||
@@ -994,13 +992,6 @@
|
||||
<string name="dma_accuracy_unsafe">Небезопасно</string>
|
||||
<string name="dma_accuracy_safe">Безопасный</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Авто</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Без сжатия</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -469,8 +469,6 @@
|
||||
<string name="renderer_accuracy_description">Керує режимом емуляції ГП. Більшість ігор добре візуалізуються з режимами «Швидко» або «Збалансовано», але деякі ігри можуть потребувати режиму «Точно». Частинки зазвичай правильно візуалізуються лише з режимом «Точно».</string>
|
||||
<string name="dma_accuracy">Точність DMA</string>
|
||||
<string name="dma_accuracy_description">Керує точністю DMA. Безпечна точність може виправити проблеми в деяких іграх, але в деяких випадках також може вплинути на продуктивність. Якщо не впевнені, залиште це значення за замовчуванням.</string>
|
||||
<string name="frame_pacing_mode">Режим виведення кадрів</string>
|
||||
<string name="frame_pacing_mode_description">Керує тим, як емулятор виконує виведення кадрів, щоб зменшити затримки й забезпечити плавнішу й стабільнішу частоту кадрів.</string>
|
||||
<string name="anisotropic_filtering">Анізотропне фільтрування</string>
|
||||
<string name="anisotropic_filtering_description">Покращує якість текстур під кутом.</string>
|
||||
<string name="vram_usage_mode">Режим використання VRAM</string>
|
||||
@@ -995,13 +993,6 @@
|
||||
<string name="dma_accuracy_unsafe">Небезпечно</string>
|
||||
<string name="dma_accuracy_safe">Безпечно</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Автоматично</string>
|
||||
<string name="frame_pacing_mode_target_30">30 к/с</string>
|
||||
<string name="frame_pacing_mode_target_60">60 к/с</string>
|
||||
<string name="frame_pacing_mode_target_90">90 к/с</string>
|
||||
<string name="frame_pacing_mode_target_120">120 к/с</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Без стиснення</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -462,8 +462,6 @@
|
||||
<string name="renderer_accuracy_description">控制 GPU 模拟的精确度。大部分游戏在性能或平衡模式下可以正常渲染,但部分游戏需要设置为精确。粒子效果通常只有在精确模式下才能正确显示。</string>
|
||||
<string name="dma_accuracy">DMA 精度</string>
|
||||
<string name="dma_accuracy_description">控制 DMA 精度。安全精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。</string>
|
||||
<string name="frame_pacing_mode">帧同步模式</string>
|
||||
<string name="frame_pacing_mode_description">控制模拟器如何管理帧同步,以减少卡顿,使帧率表现更加平稳顺滑。</string>
|
||||
<string name="anisotropic_filtering">各向异性过滤</string>
|
||||
<string name="anisotropic_filtering_description">提高斜角的纹理质量</string>
|
||||
<string name="vram_usage_mode">显存使用模式</string>
|
||||
@@ -988,13 +986,6 @@
|
||||
<string name="dma_accuracy_unsafe">不安全</string>
|
||||
<string name="dma_accuracy_safe">安全</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">自动</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">不压缩</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -533,20 +533,6 @@
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="framePacingModeNames">
|
||||
<item>@string/frame_pacing_mode_target_Auto</item>
|
||||
<item>@string/frame_pacing_mode_target_30</item>
|
||||
<item>@string/frame_pacing_mode_target_60</item>
|
||||
<item>@string/frame_pacing_mode_target_90</item>
|
||||
<item>@string/frame_pacing_mode_target_120</item>
|
||||
</string-array>
|
||||
<integer-array name="framePacingModeValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="appletEntries">
|
||||
<item>@string/applet_hle</item>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<?xml version='1.0' encoding='utf-8'?><resources><color name='ic_launcher_background'>#1F143C</color></resources>
|
||||
<?xml version='1.0' encoding='utf-8'?><resources><color name='ic_launcher_background'>#43fcfcff</color></resources>
|
||||
|
||||
@@ -479,8 +479,6 @@
|
||||
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
|
||||
<string name="dma_accuracy">DMA Accuracy</string>
|
||||
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
|
||||
<string name="frame_pacing_mode">Frame Pacing Mode</string>
|
||||
<string name="frame_pacing_mode_description">Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent.</string>
|
||||
<string name="anisotropic_filtering">Anisotropic filtering</string>
|
||||
<string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
|
||||
<string name="vram_usage_mode">VRAM Usage Mode</string>
|
||||
@@ -586,9 +584,6 @@
|
||||
<string name="gpu_log_ring_buffer_size_description">Number of recent Vulkan calls to track (default: 512)</string>
|
||||
<string name="gpu_log_ring_buffer_size_hint">64 to 4096 entries</string>
|
||||
|
||||
<string name="product_model">Product model</string>
|
||||
<string name="product_model_description">Reported product model by HLE firmware</string>
|
||||
|
||||
<string name="general">General</string>
|
||||
|
||||
<!-- Audio settings strings -->
|
||||
@@ -1041,13 +1036,6 @@
|
||||
<string name="dma_accuracy_unsafe">Unsafe</string>
|
||||
<string name="dma_accuracy_safe">Safe</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Auto</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Decoding Method Choices -->
|
||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||
<string name="accelerate_astc_gpu" translatable="false">GPU</string>
|
||||
|
||||
@@ -134,6 +134,8 @@ add_library(
|
||||
typed_address.h
|
||||
uint128.h
|
||||
unique_function.h
|
||||
random.cpp
|
||||
random.h
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <sys/random.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/mach.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
// FreeBSD
|
||||
@@ -503,8 +505,7 @@ public:
|
||||
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||
#elif defined(__OpenBSD__)
|
||||
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||
#elif defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
#elif defined(__FreeBSD__)
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#elif defined(__APPLE__)
|
||||
// macOS doesn't have memfd_create, use anonymous temporary file
|
||||
@@ -571,9 +572,9 @@ public:
|
||||
if (True(perms & MemoryPermission::Execute))
|
||||
prot_flags |= PROT_EXEC;
|
||||
#endif
|
||||
int flags = (fd > 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {} {}", strerror(errno), fd);
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
@@ -587,9 +588,8 @@ public:
|
||||
auto [merged_pointer, merged_size] =
|
||||
free_manager.FreeBlock(virtual_base + virtual_offset, length);
|
||||
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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()());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -186,7 +186,7 @@ bool IsFastmemEnabled() {
|
||||
// Only 4kb systems support host MMU right now
|
||||
// TODO: Support this
|
||||
return getpagesize() == 4096;
|
||||
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__)
|
||||
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__) && !defined(__FreeBSD__)
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
|
||||
@@ -640,7 +640,6 @@ struct Values {
|
||||
SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto, "time_zone_index", Category::System};
|
||||
Setting<u32> serial_battery{linkage, 0, "serial_battery", Category::System};
|
||||
Setting<u32> serial_unit{linkage, 0, "serial_unit", Category::System};
|
||||
Setting<u32> product_model{linkage, 1, "product_model", Category::System};
|
||||
// Measured in seconds since epoch
|
||||
SwitchableSetting<bool> custom_rtc_enabled{linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
|
||||
SwitchableSetting<s64> custom_rtc{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -218,12 +218,6 @@ 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;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -10,6 +13,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/tiny_mt.h"
|
||||
#include "common/uuid.h"
|
||||
#include "common/random.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -175,21 +179,16 @@ u128 UUID::AsU128() const {
|
||||
}
|
||||
|
||||
UUID UUID::MakeRandom() {
|
||||
std::random_device device;
|
||||
|
||||
return MakeRandomWithSeed(device());
|
||||
return MakeRandomWithSeed(Common::Random::Random32(0));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
|
||||
config.fastmem_pointer = page_table->fastmem_arena ?
|
||||
std::optional<uintptr_t>{reinterpret_cast<uintptr_t>(page_table->fastmem_arena)} :
|
||||
std::optional<uintptr_t>{uintptr_t(page_table->fastmem_arena)} :
|
||||
std::nullopt;
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
@@ -286,10 +286,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
// Curated optimizations
|
||||
case Settings::CpuAccuracy::Auto:
|
||||
config.unsafe_optimizations = true;
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__) || defined(__NetBSD__)
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
#endif
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
@@ -304,6 +300,10 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!Settings::IsFastmemEnabled()) {
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
m_jit.emplace(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -338,10 +338,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
// Safe optimisations
|
||||
case Settings::CpuAccuracy::Auto:
|
||||
config.unsafe_optimizations = true;
|
||||
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
#endif
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
@@ -355,6 +351,10 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!Settings::IsFastmemEnabled()) {
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
m_jit.emplace(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -773,7 +773,11 @@ std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, m
|
||||
bool was_executed = false;
|
||||
|
||||
auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction);
|
||||
was_executed = decoder.get().call(visitor, instruction);
|
||||
if (decoder) {
|
||||
was_executed = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
was_executed = false;
|
||||
}
|
||||
return was_executed ? std::optional<u64>(pc + 4) : std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#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"
|
||||
@@ -490,17 +491,13 @@ std::vector<NcaID> PlaceholderCache::List() const {
|
||||
}
|
||||
|
||||
NcaID PlaceholderCache::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
auto gen = Common::Random::GetMT19937();
|
||||
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 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -7,6 +7,7 @@
|
||||
#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"
|
||||
@@ -201,15 +202,8 @@ 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, GenerateRandomU64);
|
||||
return GenerateUniformRange(min, max, Common::Random::GetMT19937());
|
||||
}
|
||||
|
||||
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -33,7 +36,6 @@ public:
|
||||
|
||||
// Randomness.
|
||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||
static u64 GenerateRandomU64();
|
||||
|
||||
// Secure Memory.
|
||||
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -14,6 +14,7 @@
|
||||
#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 {
|
||||
@@ -23,7 +24,7 @@ public:
|
||||
class RandomBitGenerator {
|
||||
public:
|
||||
RandomBitGenerator() {
|
||||
m_rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
|
||||
m_rng.Initialize(u32(Common::Random::Random64(0)));
|
||||
}
|
||||
|
||||
u64 SelectRandomBit(u64 bitmap) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#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"
|
||||
@@ -45,8 +46,7 @@ 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,11 +55,10 @@ 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] = Kernel::KSystemControl::GenerateRandomU64() | 1;
|
||||
ctx.r[18] = Common::Random::Random64(0) | 1;
|
||||
ctx.pc = entry_point;
|
||||
ctx.sp = stack_top;
|
||||
ctx.fpcr = 0;
|
||||
|
||||
@@ -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 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 = 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 bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
|
||||
const bool is_domain = Domain ? mgr->IsDomain() : false;
|
||||
|
||||
static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
|
||||
using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -78,32 +81,29 @@ 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{
|
||||
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
|
||||
if (!ctx.GetManager()->IsDomain() || always_move_handles) {
|
||||
const bool always_move_handles = (u32(flags) & u32(Flags::AlwaysMoveHandles)) != 0;
|
||||
if (!mgr->IsDomain() || always_move_handles) {
|
||||
num_handles_to_move = num_objects_to_move;
|
||||
} else {
|
||||
num_domain_objects = num_objects_to_move;
|
||||
}
|
||||
|
||||
if (ctx.GetManager()->IsDomain()) {
|
||||
raw_data_size +=
|
||||
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
|
||||
if (mgr->IsDomain()) {
|
||||
raw_data_size += 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 += static_cast<u32>(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 +
|
||||
normal_params_size);
|
||||
raw_data_size += 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 (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
if (mgr->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -7,6 +10,7 @@
|
||||
#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"
|
||||
@@ -65,11 +69,9 @@ public:
|
||||
|
||||
template <typename T>
|
||||
static T GetRandomValue(T min, T max) {
|
||||
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));
|
||||
std::uniform_int_distribution<u64> distribution{u64(min), u64(max)};
|
||||
auto gen = Common::Random::GetMT19937();
|
||||
return T(distribution(gen));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -1158,8 +1158,8 @@ Result ISystemSettingsServer::SetDeviceNickName(
|
||||
|
||||
Result ISystemSettingsServer::GetProductModel(Out<u32> out_product_model) {
|
||||
// Most certainly should be 1 -- definitely should not be 2, but it's worth tinkering with anyways
|
||||
auto const product_model = ::Settings::values.product_model.GetValue();
|
||||
LOG_INFO(Service_SET, "called, product_model={}", product_model);
|
||||
u32 const product_model = 1;
|
||||
LOG_WARNING(Service_SET, "(STUBBED) called, product_model={}", product_model);
|
||||
*out_product_model = product_model;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#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"
|
||||
@@ -229,7 +230,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()
|
||||
: std::rand()) * 0x734287f27) & 0xfff000;
|
||||
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#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"
|
||||
@@ -90,7 +91,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()
|
||||
: std::rand()) * 0x734287f27) & 0xfff000;
|
||||
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#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"
|
||||
@@ -243,7 +244,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()
|
||||
: std::rand()) * 0x734287f27) & 0xfff000;
|
||||
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
|
||||
|
||||
// Setup the process code layout
|
||||
if (process
|
||||
|
||||
@@ -45,11 +45,7 @@ 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_} {
|
||||
// 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
|
||||
}
|
||||
explicit Impl(Core::System& system_) : system{system_} {}
|
||||
|
||||
void SetCurrentPageTable(Kernel::KProcess& process) {
|
||||
current_page_table = &process.GetPageTable().GetImpl();
|
||||
@@ -856,13 +852,7 @@ struct Memory::Impl {
|
||||
Tegra::MaxwellDeviceMemoryManager* gpu_device_memory{};
|
||||
Common::PageTable* current_page_table = nullptr;
|
||||
|
||||
// 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<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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -51,10 +51,12 @@ void RenderdocAPI::ToggleCapture() {
|
||||
if (!rdoc_api) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* api = static_cast<RENDERDOC_API_1_6_0*>(rdoc_api);
|
||||
if (!is_capturing) {
|
||||
rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
api->StartFrameCapture(NULL, NULL);
|
||||
} else {
|
||||
rdoc_api->EndFrameCapture(NULL, NULL);
|
||||
api->EndFrameCapture(NULL, NULL);
|
||||
}
|
||||
is_capturing = !is_capturing;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
struct RENDERDOC_API_1_7_0;
|
||||
|
||||
namespace Tools {
|
||||
|
||||
class RenderdocAPI {
|
||||
@@ -18,7 +16,7 @@ public:
|
||||
void ToggleCapture();
|
||||
|
||||
private:
|
||||
RENDERDOC_API_1_7_0* rdoc_api{};
|
||||
void* rdoc_api{};
|
||||
bool is_capturing{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -642,7 +642,7 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(oaknut::CodeGenerator& code, Emi
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -708,7 +708,7 @@ void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext&
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto& operand_arg = args[0];
|
||||
auto& shift_arg = args[1];
|
||||
|
||||
@@ -209,9 +209,9 @@ void CallbackOnlyEmitExclusiveWriteMemory(oaknut::CodeGenerator& code, EmitConte
|
||||
ctx.reg_alloc.DefineAsRegister(inst, X0);
|
||||
}
|
||||
|
||||
constexpr size_t page_bits = 12;
|
||||
constexpr size_t page_size = 1 << page_bits;
|
||||
constexpr size_t page_mask = (1 << page_bits) - 1;
|
||||
constexpr size_t page_table_const_bits = 12;
|
||||
constexpr size_t page_table_const_size = 1 << page_table_const_bits;
|
||||
constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
|
||||
|
||||
// This function may use Xscratch0 as a scratch register
|
||||
// Trashes NZCV
|
||||
@@ -242,28 +242,28 @@ void EmitDetectMisalignedVAddr(oaknut::CodeGenerator& code, EmitContext& ctx, oa
|
||||
code.TST(Xaddr, align_mask);
|
||||
code.B(NE, *fallback);
|
||||
} else {
|
||||
// If (addr & page_mask) > page_size - byte_size, use fallback.
|
||||
code.AND(Xscratch0, Xaddr, page_mask);
|
||||
code.CMP(Xscratch0, page_size - bitsize / 8);
|
||||
// If (addr & page_table_const_mask) > page_table_const_size - byte_size, use fallback.
|
||||
code.AND(Xscratch0, Xaddr, page_table_const_mask);
|
||||
code.CMP(Xscratch0, page_table_const_size - bitsize / 8);
|
||||
code.B(HI, *fallback);
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs Xscratch0 = page_table[addr >> page_bits]
|
||||
// Outputs Xscratch0 = page_table[addr >> page_table_const_bits]
|
||||
// May use Xscratch1 as scratch register
|
||||
// Address to read/write = [ret0 + ret1], ret0 is always Xscratch0 and ret1 is either Xaddr or Xscratch1
|
||||
// Trashes NZCV
|
||||
template<size_t bitsize>
|
||||
std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::CodeGenerator& code, EmitContext& ctx, oaknut::XReg Xaddr, const SharedLabel& fallback) {
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits;
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
EmitDetectMisalignedVAddr<bitsize>(code, ctx, Xaddr, fallback);
|
||||
|
||||
if (ctx.conf.silently_mirror_page_table || unused_top_bits == 0) {
|
||||
code.UBFX(Xscratch0, Xaddr, page_bits, valid_page_index_bits);
|
||||
code.UBFX(Xscratch0, Xaddr, page_table_const_bits, valid_page_index_bits);
|
||||
} else {
|
||||
code.LSR(Xscratch0, Xaddr, page_bits);
|
||||
code.LSR(Xscratch0, Xaddr, page_table_const_bits);
|
||||
code.TST(Xscratch0, u64(~u64(0)) << valid_page_index_bits);
|
||||
code.B(NE, *fallback);
|
||||
}
|
||||
@@ -283,7 +283,7 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return std::make_pair(Xscratch0, Xaddr);
|
||||
}
|
||||
code.AND(Xscratch1, Xaddr, page_mask);
|
||||
code.AND(Xscratch1, Xaddr, page_table_const_mask);
|
||||
return std::make_pair(Xscratch0, Xscratch1);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ constexpr size_t xmrx(size_t x) noexcept {
|
||||
}
|
||||
|
||||
struct DoNotFastmemMarkerHash {
|
||||
[[nodiscard]] constexpr size_t operator()(const DoNotFastmemMarker& value) const noexcept {
|
||||
[[nodiscard]] size_t operator()(const DoNotFastmemMarker& value) const noexcept {
|
||||
return xmrx(std::get<0>(value).Value() ^ u64(std::get<1>(value)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -316,8 +316,8 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
return current_location->index;
|
||||
}
|
||||
|
||||
ASSERT(!ValueInfo(*current_location).realized);
|
||||
ASSERT(ValueInfo(*current_location).locked);
|
||||
ASSERT(!bool(ValueInfo(*current_location).realized));
|
||||
ASSERT(bool(ValueInfo(*current_location).locked));
|
||||
|
||||
if constexpr (required_kind == HostLoc::Kind::Gpr) {
|
||||
const int new_location_index = AllocateRegister(gprs, gpr_order);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2024 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -164,12 +167,12 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(biscuit::Assembler&, EmitContext
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,10 @@ static Xbyak::Address MJitStateExtReg(A32::ExtReg reg) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
A32EmitContext::A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block)
|
||||
: EmitContext(reg_alloc, block), conf(conf) {}
|
||||
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)
|
||||
{}
|
||||
|
||||
A32::LocationDescriptor A32EmitContext::Location() const {
|
||||
return A32::LocationDescriptor{block.Location()};
|
||||
@@ -109,35 +111,59 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||
gprs.reset(size_t(HostLoc::R14));
|
||||
return gprs;
|
||||
}(), any_xmm);
|
||||
A32EmitContext ctx{conf, reg_alloc, block};
|
||||
|
||||
A32EmitContext ctx{conf, reg_alloc, block, shared_labels};
|
||||
|
||||
// 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);
|
||||
|
||||
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) [[likely]] {
|
||||
auto* inst = &*iter;
|
||||
// Call the relevant Emit* member function.
|
||||
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;
|
||||
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();
|
||||
// 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, ...)
|
||||
#include "dynarmic/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
#undef A32OPC
|
||||
#undef A64OPC
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
reg_alloc.EndOfAllocScope();
|
||||
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();
|
||||
#ifndef NDEBUG
|
||||
if (conf.very_verbose_debugging_output)
|
||||
EmitVerboseDebuggingOutput(reg_alloc);
|
||||
@@ -146,15 +172,14 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||
|
||||
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);
|
||||
@@ -167,6 +192,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||
|
||||
auto const bdesc = RegisterBlock(descriptor, entrypoint, size);
|
||||
code.DisableWriting();
|
||||
shared_labels.clear();
|
||||
return bdesc;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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);
|
||||
A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels);
|
||||
|
||||
A32::LocationDescriptor Location() const;
|
||||
A32::LocationDescriptor EndLocation() const;
|
||||
@@ -130,6 +130,7 @@ 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,8 +37,10 @@ namespace Dynarmic::Backend::X64 {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
A64EmitContext::A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block)
|
||||
: EmitContext(reg_alloc, block), conf(conf) {}
|
||||
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)
|
||||
{}
|
||||
|
||||
A64::LocationDescriptor A64EmitContext::Location() const {
|
||||
return A64::LocationDescriptor{block.Location()};
|
||||
@@ -83,11 +85,14 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) noexcept {
|
||||
gprs.reset(size_t(HostLoc::R14));
|
||||
return gprs;
|
||||
}(), any_xmm};
|
||||
A64EmitContext ctx{conf, reg_alloc, block};
|
||||
|
||||
A64EmitContext ctx{conf, reg_alloc, block, shared_labels};
|
||||
|
||||
// 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);
|
||||
@@ -139,16 +144,13 @@ 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);
|
||||
@@ -161,6 +163,7 @@ finish_this_inst:
|
||||
|
||||
auto bdesc = RegisterBlock(descriptor, entrypoint, size);
|
||||
code.DisableWriting();
|
||||
shared_labels.clear();
|
||||
return bdesc;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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);
|
||||
A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels);
|
||||
|
||||
A64::LocationDescriptor Location() const;
|
||||
bool IsSingleStep() const;
|
||||
@@ -126,6 +126,7 @@ 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,8 +32,11 @@ namespace Dynarmic::Backend::X64 {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block)
|
||||
: reg_alloc(reg_alloc), block(block) {}
|
||||
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() = default;
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
#include <boost/container/stable_vector.hpp>
|
||||
#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"
|
||||
@@ -52,24 +53,23 @@ 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);
|
||||
EmitContext(RegAlloc& reg_alloc, IR::Block& block, boost::container::stable_vector<Xbyak::Label>& shared_labels);
|
||||
virtual ~EmitContext();
|
||||
virtual FP::FPCR FPCR(bool fpcr_controlled = true) const = 0;
|
||||
virtual bool HasOptimization(OptimizationFlag flag) const = 0;
|
||||
|
||||
RegAlloc& reg_alloc;
|
||||
IR::Block& block;
|
||||
[[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;
|
||||
};
|
||||
|
||||
using SharedLabel = std::shared_ptr<Xbyak::Label>;
|
||||
|
||||
inline SharedLabel GenSharedLabel() {
|
||||
return std::make_shared<Xbyak::Label>();
|
||||
}
|
||||
|
||||
class EmitX64 {
|
||||
public:
|
||||
struct BlockDescriptor {
|
||||
|
||||
@@ -663,7 +663,7 @@ void EmitX64::EmitArithmeticShiftRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitBitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -736,7 +736,7 @@ void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitBitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto& operand_arg = args[0];
|
||||
auto& shift_arg = args[1];
|
||||
|
||||
@@ -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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel nan = ctx.GenSharedLabel(), end = ctx.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 = GenSharedLabel();
|
||||
SharedLabel end = ctx.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 = GenSharedLabel(), nan = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), nan = ctx.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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel equal = ctx.GenSharedLabel(), end = ctx.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 = GenSharedLabel(), z = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), z = ctx.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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel fallback = ctx.GenSharedLabel(), end = ctx.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 = GenSharedLabel(), nan = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), nan = ctx.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 = GenSharedLabel(), fallback = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel bad_values = ctx.GenSharedLabel(), end = ctx.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 = GenSharedLabel(), fallback = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel saturate_max = ctx.GenSharedLabel(), end = ctx.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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.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.get());
|
||||
EmitCheckMemoryAbort(ctx, inst, end);
|
||||
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.get());
|
||||
EmitCheckMemoryAbort(ctx, inst, end);
|
||||
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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.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.get());
|
||||
EmitCheckMemoryAbort(ctx, inst, end);
|
||||
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.get());
|
||||
EmitCheckMemoryAbort(ctx, inst, end);
|
||||
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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.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 = GenSharedLabel();
|
||||
SharedLabel end = ctx.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 = GenSharedLabel();
|
||||
SharedLabel abort = ctx.GenSharedLabel();
|
||||
bool require_abort_handling = false;
|
||||
|
||||
const auto dest_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling, tmp);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
@@ -22,9 +24,9 @@ namespace {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
constexpr size_t page_bits = 12;
|
||||
constexpr size_t page_size = 1 << page_bits;
|
||||
constexpr size_t page_mask = (1 << page_bits) - 1;
|
||||
constexpr size_t page_table_const_bits = 12;
|
||||
constexpr size_t page_table_const_size = 1 << page_table_const_bits;
|
||||
constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
|
||||
|
||||
template<typename EmitContext>
|
||||
void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr, Xbyak::Reg64 tmp) {
|
||||
@@ -50,9 +52,9 @@ void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsi
|
||||
code.test(vaddr, align_mask);
|
||||
|
||||
if (ctx.conf.only_detect_misalignment_via_page_table_on_page_boundary) {
|
||||
const u32 page_align_mask = static_cast<u32>(page_size - 1) & ~align_mask;
|
||||
const u32 page_align_mask = static_cast<u32>(page_table_const_size - 1) & ~align_mask;
|
||||
|
||||
SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel();
|
||||
SharedLabel detect_boundary = ctx.GenSharedLabel(), resume = ctx.GenSharedLabel();
|
||||
|
||||
code.jnz(*detect_boundary, code.T_NEAR);
|
||||
code.L(*resume);
|
||||
@@ -83,7 +85,7 @@ template<>
|
||||
// TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
|
||||
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
||||
@@ -96,13 +98,13 @@ template<>
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.and_(tmp, static_cast<u32>(page_mask));
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
return page + tmp.cvt64();
|
||||
}
|
||||
|
||||
template<>
|
||||
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A64EmitContext>(BlockOfCode& code, A64EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits;
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||
@@ -112,29 +114,29 @@ template<>
|
||||
|
||||
if (unused_top_bits == 0) {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
} else if (ctx.conf.silently_mirror_page_table) {
|
||||
if (valid_page_index_bits >= 32) {
|
||||
if (code.HasHostFeature(HostFeature::BMI2)) {
|
||||
const Xbyak::Reg64 bit_count = ctx.reg_alloc.ScratchGpr(code);
|
||||
code.mov(bit_count, unused_top_bits);
|
||||
code.bzhi(tmp, vaddr, bit_count);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
ctx.reg_alloc.Release(bit_count);
|
||||
} else {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shl(tmp, int(unused_top_bits));
|
||||
code.shr(tmp, int(unused_top_bits + page_bits));
|
||||
code.shr(tmp, int(unused_top_bits + page_table_const_bits));
|
||||
}
|
||||
} else {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.and_(tmp, u32((1 << valid_page_index_bits) - 1));
|
||||
}
|
||||
} else {
|
||||
ASSERT(valid_page_index_bits < 32);
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.test(tmp, u32(-(1 << valid_page_index_bits)));
|
||||
code.jnz(abort, code.T_NEAR);
|
||||
}
|
||||
@@ -151,7 +153,7 @@ template<>
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr);
|
||||
code.and_(tmp, static_cast<u32>(page_mask));
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
return page + tmp;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ using namespace Xbyak::util;
|
||||
|
||||
namespace {
|
||||
|
||||
enum class Op {
|
||||
enum class SaturationOp {
|
||||
Add,
|
||||
Sub,
|
||||
};
|
||||
|
||||
template<Op op, size_t size, bool has_overflow_inst = false>
|
||||
template<SaturationOp op, size_t size, bool has_overflow_inst = false>
|
||||
void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
@@ -51,7 +51,7 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
|
||||
|
||||
// overflow now contains 0x7F... if a was positive, or 0x80... if a was negative
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == SaturationOp::Add) {
|
||||
code.add(result, addend);
|
||||
} else {
|
||||
code.sub(result, addend);
|
||||
@@ -75,16 +75,16 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
}
|
||||
|
||||
template<Op op, size_t size>
|
||||
template<SaturationOp op, size_t size>
|
||||
void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
Xbyak::Reg op_result = ctx.reg_alloc.UseScratchGpr(code, args[0]).changeBit(size);
|
||||
Xbyak::Reg addend = ctx.reg_alloc.UseScratchGpr(code, args[1]).changeBit(size);
|
||||
|
||||
constexpr u64 boundary = op == Op::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
|
||||
constexpr u64 boundary = op == SaturationOp::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == SaturationOp::Add) {
|
||||
code.add(op_result, addend);
|
||||
} else {
|
||||
code.sub(op_result, addend);
|
||||
@@ -106,11 +106,11 @@ void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst
|
||||
} // anonymous namespace
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAddWithFlag32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 32, true>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 32, true>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSubWithFlag32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 32, true>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 32, true>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -192,19 +192,19 @@ void EmitX64::EmitUnsignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 8>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 16>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 32>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 64>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh16(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -256,51 +256,51 @@ void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh32(EmitContext& ctx,
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 8>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 16>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 8>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 16>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 32>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 64>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 8>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 16>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#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"
|
||||
@@ -93,7 +94,7 @@ void HandleNaNs(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, std::
|
||||
code.cmp(bitmask, 0);
|
||||
}
|
||||
|
||||
SharedLabel end = GenSharedLabel(), nan = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), nan = ctx.GenSharedLabel();
|
||||
|
||||
code.jnz(*nan, code.T_NEAR);
|
||||
code.L(*end);
|
||||
@@ -188,23 +189,6 @@ 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()) {
|
||||
@@ -1330,7 +1314,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 = GenSharedLabel(), fallback = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
|
||||
|
||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||
code.movaps(result, xmm_a);
|
||||
@@ -1603,7 +1587,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 = GenSharedLabel(), fallback = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
|
||||
|
||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||
code.movaps(result, GetVectorOf<fsize, false, 0, 2>(code));
|
||||
@@ -1776,7 +1760,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 = GenSharedLabel(), end = GenSharedLabel();
|
||||
SharedLabel bad_values = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
|
||||
|
||||
code.movaps(value, operand);
|
||||
|
||||
@@ -1867,7 +1851,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 = GenSharedLabel(), fallback = GenSharedLabel();
|
||||
SharedLabel end = ctx.GenSharedLabel(), fallback = ctx.GenSharedLabel();
|
||||
|
||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||
code.vmovaps(result, GetVectorOf<fsize, false, 0, 3>(code));
|
||||
@@ -2004,120 +1988,123 @@ 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 = static_cast<FP::RoundingMode>(inst->GetArg(2).GetU8());
|
||||
const auto rounding = FP::RoundingMode(inst->GetArg(2).GetU8());
|
||||
[[maybe_unused]] const bool fpcr_controlled = inst->GetArg(3).GetU1();
|
||||
|
||||
if constexpr (fsize != 16) {
|
||||
if (code.HasHostFeature(HostFeature::SSE41) && 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 == 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));
|
||||
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;
|
||||
|
||||
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 constexpr (fsize == 32) {
|
||||
code.cvttps2dq(src, src);
|
||||
} else {
|
||||
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 constexpr (fsize == 32) {
|
||||
code.vcvttps2udq(src | k1 | T_z, src);
|
||||
} else {
|
||||
code.vcvttpd2uqq(src | k1 | T_z, src);
|
||||
}
|
||||
code.vcvttpd2qq(src, src);
|
||||
} else {
|
||||
// Zero is minimum
|
||||
code.xorps(xmm0, xmm0);
|
||||
FCODE(cmplep)(xmm0, src);
|
||||
FCODE(andp)(src, xmm0);
|
||||
const Xbyak::Reg64 hi = ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg64 lo = ctx.reg_alloc.ScratchGpr(code);
|
||||
|
||||
// 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);
|
||||
code.cvttsd2si(lo, src);
|
||||
code.punpckhqdq(src, src);
|
||||
code.cvttsd2si(hi, src);
|
||||
code.movq(src, lo);
|
||||
code.pinsrq(src, hi, 1);
|
||||
|
||||
// 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);
|
||||
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));
|
||||
}
|
||||
|
||||
// Saturate to max
|
||||
FCODE(orp)(src, exceed_unsigned);
|
||||
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);
|
||||
}
|
||||
} 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));
|
||||
// Zero is minimum
|
||||
code.xorps(xmm0, xmm0);
|
||||
FCODE(cmplep)(xmm0, src);
|
||||
perform_conversion(src);
|
||||
FCODE(blendvp)(src, GetVectorOf<fsize, integer_max>(code));
|
||||
}
|
||||
});
|
||||
FCODE(andp)(src, xmm0);
|
||||
|
||||
ctx.reg_alloc.DefineValue(code, inst, src);
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -52,12 +52,12 @@ void EmitVectorSaturatedNative(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
}
|
||||
|
||||
enum class Op {
|
||||
enum class VectorSaturationOp {
|
||||
Add,
|
||||
Sub,
|
||||
};
|
||||
|
||||
template<Op op, size_t esize>
|
||||
template<VectorSaturationOp op, size_t esize>
|
||||
void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
static_assert(esize == 32 || esize == 64);
|
||||
constexpr u64 msb_mask = esize == 32 ? 0x8000000080000000 : 0x8000000000000000;
|
||||
@@ -72,7 +72,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
|
||||
code.movaps(xmm0, operand1);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
code.vpternlogd(xmm0, result, operand2, 0b00100100);
|
||||
} else {
|
||||
@@ -102,7 +102,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
|
||||
|
||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
} else {
|
||||
ICODE(vpsub)(result, operand1, operand2);
|
||||
@@ -112,7 +112,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
} else {
|
||||
code.movaps(xmm0, operand1);
|
||||
code.movaps(tmp, operand1);
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(padd)(result, operand2);
|
||||
} else {
|
||||
ICODE(psub)(result, operand2);
|
||||
@@ -121,7 +121,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
code.pxor(tmp, result);
|
||||
}
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
code.pandn(xmm0, tmp);
|
||||
} else {
|
||||
code.pand(xmm0, tmp);
|
||||
@@ -165,7 +165,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
}
|
||||
}
|
||||
|
||||
template<Op op, size_t esize>
|
||||
template<VectorSaturationOp op, size_t esize>
|
||||
void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
static_assert(esize == 32 || esize == 64);
|
||||
|
||||
@@ -177,7 +177,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
ICODE(vpcmpu)(k1, result, operand1, CmpInt::LessThan);
|
||||
ICODE(vpternlog)(result | k1, result, result, u8(0xFF));
|
||||
@@ -201,7 +201,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
|
||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||
code.vpxor(xmm0, operand1, operand2);
|
||||
code.vpand(tmp, operand1, operand2);
|
||||
@@ -250,7 +250,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
code.setnz(overflow);
|
||||
code.or_(code.byte[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_qc], overflow);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
code.por(result, tmp);
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
} else {
|
||||
@@ -270,11 +270,11 @@ void EmitX64::EmitVectorSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Add, 32>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Add, 64>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -286,11 +286,11 @@ void EmitX64::EmitVectorSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -302,11 +302,11 @@ void EmitX64::EmitVectorUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst)
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Add, 32>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Add, 64>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -318,11 +318,11 @@ void EmitX64::EmitVectorUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst)
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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, static_cast<u32>(ExceptionContinueSearch));
|
||||
code.mov(code.eax, u32(ExceptionContinueSearch));
|
||||
code.ret();
|
||||
|
||||
code.align(16);
|
||||
@@ -192,20 +192,18 @@ 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, static_cast<u32>(ExceptionContinueExecution));
|
||||
code.mov(code.eax, u32(ExceptionContinueExecution));
|
||||
code.ret();
|
||||
|
||||
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*>());
|
||||
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*>());
|
||||
|
||||
code.align(16);
|
||||
UNWIND_INFO* unwind_info = static_cast<UNWIND_INFO*>(code.AllocateFromCodeSpace(sizeof(UNWIND_INFO)));
|
||||
|
||||
@@ -417,7 +417,8 @@ 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
|
||||
} else if (i >= HostLoc::R13 && i <= HostLoc::R15) {
|
||||
// %rbp must not be trashed, so skip it as well
|
||||
} else if (i == HostLoc::RBP || (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(!is_scratch);
|
||||
ASSERT(!bool(is_scratch));
|
||||
is_being_used_count++;
|
||||
}
|
||||
inline void WriteLock() noexcept {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -22,14 +22,13 @@ 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
|
||||
|
||||
@@ -35,7 +35,7 @@ FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2) {
|
||||
return std::make_tuple(exponent, value);
|
||||
}();
|
||||
|
||||
if (product_value == 0) {
|
||||
if (product_value == u128(0, 0)) {
|
||||
return addend;
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2) {
|
||||
}
|
||||
|
||||
// addend < product
|
||||
const u128 result = product_value + StickyLogicalShiftRight(addend.mantissa, exp_diff - normalized_point_position);
|
||||
const u128 result = product_value + StickyLogicalShiftRight(u128(addend.mantissa, 0), exp_diff - normalized_point_position);
|
||||
return ReduceMantissa(product_sign, product_exponent, result);
|
||||
}
|
||||
|
||||
// Subtraction
|
||||
|
||||
const u128 addend_long = u128(addend.mantissa) << normalized_point_position;
|
||||
const u128 addend_long = u128(addend.mantissa, 0) << normalized_point_position;
|
||||
|
||||
bool result_sign;
|
||||
u128 result;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -137,7 +137,7 @@ u128 StickyLogicalShiftRight(u128 operand, int amount) {
|
||||
}
|
||||
|
||||
if (operand.lower != 0 || operand.upper != 0) {
|
||||
return u128(1);
|
||||
return u128(1, 0);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -22,22 +22,13 @@ struct u128 {
|
||||
u128(u128&&) = default;
|
||||
u128& operator=(const u128&) = default;
|
||||
u128& operator=(u128&&) = default;
|
||||
|
||||
u128(u64 lower_, u64 upper_)
|
||||
: lower(lower_), upper(upper_) {}
|
||||
|
||||
template<typename T>
|
||||
/* implicit */ u128(T value)
|
||||
: lower(value), upper(0) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
static_assert(mcl::bitsizeof<T> <= mcl::bitsizeof<u64>);
|
||||
}
|
||||
explicit u128(u64 lower_, u64 upper_) : lower(lower_), upper(upper_) {}
|
||||
|
||||
u64 lower = 0;
|
||||
u64 upper = 0;
|
||||
|
||||
template<size_t bit_position>
|
||||
bool Bit() const {
|
||||
[[nodiscard]] inline bool Bit() const {
|
||||
static_assert(bit_position < 128);
|
||||
if constexpr (bit_position < 64) {
|
||||
return mcl::bit::get_bit<bit_position>(lower);
|
||||
|
||||
@@ -36,25 +36,19 @@ inline size_t ToFastLookupIndexArm(u32 instruction) noexcept {
|
||||
} // namespace detail
|
||||
|
||||
template<typename V>
|
||||
constexpr ArmDecodeTable<V> GetArmDecodeTable() noexcept {
|
||||
std::vector<ArmMatcher<V>> list = {
|
||||
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>>{
|
||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
||||
#include "./arm.inc"
|
||||
#undef INST
|
||||
};
|
||||
|
||||
// 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());
|
||||
}) {
|
||||
auto const expect = detail::ToFastLookupIndexArm(e.GetExpected());
|
||||
auto const mask = detail::ToFastLookupIndexArm(e.GetMask());
|
||||
if ((i & mask) == expect) {
|
||||
table[i].push_back(matcher);
|
||||
table[i].push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +56,7 @@ constexpr ArmDecodeTable<V> GetArmDecodeTable() noexcept {
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) noexcept {
|
||||
static 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);
|
||||
@@ -73,7 +67,7 @@ std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruc
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::string_view> GetNameARM(u32 inst) noexcept {
|
||||
static 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,316 +1,265 @@
|
||||
// Barrier instructions
|
||||
INST(arm_DMB, "DMB", "1111010101111111111100000101oooo") // v7
|
||||
INST(arm_DSB, "DSB", "1111010101111111111100000100oooo") // v7
|
||||
INST(arm_ISB, "ISB", "1111010101111111111100000110oooo") // v7
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// DO NOT REORDER
|
||||
|
||||
// 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
|
||||
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")
|
||||
@@ -27,50 +27,12 @@ template<typename Visitor>
|
||||
using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
template<typename V>
|
||||
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)) },
|
||||
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)),
|
||||
#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);
|
||||
});
|
||||
@@ -78,7 +40,7 @@ std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 ins
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::string_view> GetNameASIMD(u32 inst) noexcept {
|
||||
static 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,172 +1,151 @@
|
||||
// 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
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// DO NOT REORDER
|
||||
|
||||
// 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
|
||||
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")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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>
|
||||
std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
|
||||
alignas(64) static const std::vector<Thumb16Matcher<V>> table = {
|
||||
static std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
|
||||
alignas(64) static const auto table = std::array{
|
||||
#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 @@ std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::string_view> GetNameThumb16(u32 inst) noexcept {
|
||||
static 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"
|
||||
|
||||