Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 104d8af2b4 | |||
| 3ce5463d2d | |||
| cae70c30fa | |||
| bcceced96d | |||
| 82e374f66c | |||
| 6e76014824 | |||
| bb71ace365 | |||
| 21f9db1c27 | |||
| b4a485e244 | |||
| 81a344f3db | |||
| c0fbb2526d | |||
| c3afd2fabd | |||
| ee2891c55e | |||
| dd91b41a78 | |||
| e9f4541069 | |||
| cf7086de7c | |||
| 8e14f07a69 | |||
| 0b179517b3 | |||
| 7a8176f63f |
@@ -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 |
@@ -30,6 +30,7 @@
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded",
|
||||
"git_version": "0.37.0",
|
||||
"version": "0.18.7",
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"patches": [
|
||||
"0001-mingw.patch",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 |
@@ -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>
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -11,11 +8,10 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioCore::AudioCore(Core::System& system) {
|
||||
audio_manager.emplace();
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
|
||||
CreateSinks();
|
||||
// Must be created after the sinks
|
||||
adsp.emplace(system, *output_sink);
|
||||
adsp = std::make_unique<ADSP::ADSP>(system, *output_sink);
|
||||
}
|
||||
|
||||
AudioCore ::~AudioCore() {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +15,10 @@ class System;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
class AudioManager;
|
||||
/**
|
||||
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
*/
|
||||
class AudioCore {
|
||||
public:
|
||||
explicit AudioCore(Core::System& system);
|
||||
@@ -50,22 +50,27 @@ public:
|
||||
*/
|
||||
Sink::Sink& GetInputSink();
|
||||
|
||||
/// @brief Get the ADSP.
|
||||
/// @return Ref to the ADSP.
|
||||
/**
|
||||
* Get the ADSP.
|
||||
*
|
||||
* @return Ref to the ADSP.
|
||||
*/
|
||||
ADSP::ADSP& ADSP();
|
||||
|
||||
private:
|
||||
/// @brief Create the sinks on startup.
|
||||
/**
|
||||
* Create the sinks on startup.
|
||||
*/
|
||||
void CreateSinks();
|
||||
|
||||
/// Main audio manager for audio in/out
|
||||
std::optional<AudioManager> audio_manager;
|
||||
std::unique_ptr<AudioManager> audio_manager;
|
||||
/// Sink used for audio renderer and audio out
|
||||
std::unique_ptr<Sink::Sink> output_sink;
|
||||
/// Sink used for audio input
|
||||
std::unique_ptr<Sink::Sink> input_sink;
|
||||
/// The ADSP in the sysmodule
|
||||
std::optional<ADSP::ADSP> adsp;
|
||||
std::unique_ptr<ADSP::ADSP> adsp;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -44,7 +41,8 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,77 +1,81 @@
|
||||
// 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
|
||||
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioManager::AudioManager() {
|
||||
thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("AudioManager");
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
while (!stop_token.stop_requested()) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = Event::Type(i);
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
thread = std::jthread([this]() { ThreadFunc(); });
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
running = false;
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
if (thread.joinable()) {
|
||||
thread.request_stop();
|
||||
thread.join();
|
||||
}
|
||||
thread.join();
|
||||
}
|
||||
|
||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
||||
events.SetAudioEvent(type, signalled);
|
||||
}
|
||||
|
||||
void AudioManager::ThreadFunc() {
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = static_cast<Event::Type>(i);
|
||||
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -69,6 +66,13 @@ public:
|
||||
void SetEvent(Event::Type type, bool signalled);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Main thread, waiting on a manager signal and calling the registered function.
|
||||
*/
|
||||
void ThreadFunc();
|
||||
|
||||
/// Is the main thread running?
|
||||
std::atomic<bool> running{};
|
||||
/// Unused
|
||||
bool needs_update{};
|
||||
/// Events to be set and signalled
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -43,7 +40,8 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -347,7 +347,7 @@ struct System::Impl {
|
||||
|
||||
// Register with applet manager
|
||||
// All threads are started, begin main process execution, now that we're in the clear
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -268,7 +268,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
@@ -36,15 +34,16 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
|
||||
|
||||
} // namespace
|
||||
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
: m_window_system(window_system) {
|
||||
// TODO: am reads this from the home button state in hid, which is controller-agnostic.
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this, &window_system](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
std::unique_lock lk{m_mutex};
|
||||
OnButtonStateChanged(window_system);
|
||||
}
|
||||
},
|
||||
.on_change =
|
||||
[this](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
this->OnButtonStateChanged();
|
||||
}
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
|
||||
@@ -53,35 +52,25 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
|
||||
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
m_player1_key = m_player1->SetCallback(engine_callback);
|
||||
|
||||
m_thread = std::jthread([this, &window_system](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("ButtonPoller");
|
||||
while (!stop_token.stop_requested()) {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (stop_token.stop_requested())
|
||||
break;
|
||||
OnButtonStateChanged(window_system);
|
||||
std::this_thread::sleep_for(5ms);
|
||||
}
|
||||
});
|
||||
m_thread = std::thread([this] { this->ThreadLoop(); });
|
||||
}
|
||||
|
||||
ButtonPoller::~ButtonPoller() {
|
||||
m_handheld->DeleteCallback(m_handheld_key);
|
||||
m_player1->DeleteCallback(m_player1_key);
|
||||
m_stop = true;
|
||||
m_cv.notify_all();
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.request_stop();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
auto const home_button = m_handheld->GetHomeButtons().home.Value()
|
||||
|| m_player1->GetHomeButtons().home.Value();
|
||||
auto const capture_button = m_handheld->GetCaptureButtons().capture.Value()
|
||||
|| m_player1->GetCaptureButtons().capture.Value();
|
||||
void ButtonPoller::OnButtonStateChanged() {
|
||||
std::lock_guard lk{m_mutex};
|
||||
const bool home_button =
|
||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
||||
m_player1->GetCaptureButtons().capture.Value();
|
||||
|
||||
// Buttons pressed which were not previously pressed
|
||||
if (home_button && !m_home_button_press_start) {
|
||||
@@ -101,7 +90,7 @@ void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
m_home_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
@@ -109,7 +98,7 @@ void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
m_capture_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
@@ -118,8 +107,9 @@ void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
if (!home_button && m_home_button_press_start) {
|
||||
if(!m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::HomeButtonShortPressing : SystemButtonType::HomeButtonLongPressing);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing
|
||||
: SystemButtonType::HomeButtonLongPressing);
|
||||
}
|
||||
m_home_button_press_start = std::nullopt;
|
||||
m_home_button_long_sent = false;
|
||||
@@ -127,8 +117,9 @@ void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
if (!capture_button && m_capture_button_press_start) {
|
||||
if (!m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::CaptureButtonShortPressing : SystemButtonType::CaptureButtonLongPressing);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing
|
||||
: SystemButtonType::CaptureButtonLongPressing);
|
||||
}
|
||||
m_capture_button_press_start = std::nullopt;
|
||||
m_capture_button_long_sent = false;
|
||||
@@ -139,4 +130,16 @@ void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
// }
|
||||
}
|
||||
|
||||
void ButtonPoller::ThreadLoop() {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
while (!m_stop) {
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (m_stop) break;
|
||||
lk.unlock();
|
||||
OnButtonStateChanged();
|
||||
lk.lock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -30,23 +30,31 @@ class ButtonPoller {
|
||||
public:
|
||||
explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
|
||||
~ButtonPoller();
|
||||
void OnButtonStateChanged(WindowSystem& window_system);
|
||||
|
||||
private:
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::jthread m_thread;
|
||||
void OnButtonStateChanged();
|
||||
void ThreadLoop();
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
int m_handheld_key{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int m_player1_key{};
|
||||
|
||||
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
||||
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int32_t m_handheld_key{};
|
||||
int32_t m_player1_key{};
|
||||
bool m_home_button_long_sent : 1 = false;
|
||||
bool m_capture_button_long_sent : 1 = false;
|
||||
bool m_power_button_long_sent : 1 = false;
|
||||
bool m_home_button_long_sent{};
|
||||
bool m_capture_button_long_sent{};
|
||||
bool m_power_button_long_sent{};
|
||||
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_stop{false};
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,24 +15,12 @@ enum class UserDataTag : u32 {
|
||||
};
|
||||
|
||||
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
||||
: m_system(system), m_context(system, "am:EventObserver")
|
||||
, m_window_system(window_system)
|
||||
, m_wakeup_event(m_context)
|
||||
, m_wakeup_holder(m_wakeup_event.GetHandle())
|
||||
{
|
||||
: m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system),
|
||||
m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) {
|
||||
m_window_system.SetEventObserver(this);
|
||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
||||
m_thread = std::thread([this] {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
while (true) {
|
||||
auto* signaled_holder = this->WaitSignaled();
|
||||
if (!signaled_holder) {
|
||||
break;
|
||||
}
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
});
|
||||
m_thread = std::thread([&] { this->ThreadFunc(); });
|
||||
}
|
||||
|
||||
EventObserver::~EventObserver() {
|
||||
@@ -161,4 +146,17 @@ void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
|
||||
delete holder;
|
||||
}
|
||||
|
||||
void EventObserver::ThreadFunc() {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
|
||||
while (true) {
|
||||
auto* signaled_holder = this->WaitSignaled();
|
||||
if (!signaled_holder) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -44,6 +41,9 @@ private:
|
||||
private:
|
||||
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
|
||||
private:
|
||||
// System reference and context.
|
||||
Core::System& m_system;
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
@@ -20,7 +16,7 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||
if (!slot.has_value()) {
|
||||
return FileSys::StorageId::None;
|
||||
@@ -40,23 +36,31 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (out_loader) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_optional<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
if (!out_loader) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::nullopt;
|
||||
|
||||
// Try to load the process.
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
@@ -66,7 +70,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
@@ -75,8 +79,9 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
||||
nca.GetKeyGeneration());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,32 +90,42 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
} else {
|
||||
out_control.resize(sizeof(FileSys::RawNACP));
|
||||
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
||||
}
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process->GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
auto process =
|
||||
CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index);
|
||||
if (!process) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::nullopt;
|
||||
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
} else {
|
||||
out_control.resize(sizeof(FileSys::RawNACP));
|
||||
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
||||
}
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process->GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(
|
||||
storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(
|
||||
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -27,7 +24,12 @@ class Process;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -21,7 +21,8 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor, Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor,
|
||||
Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
// Get the program NCA from storage.
|
||||
@@ -34,10 +35,11 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
auto process =
|
||||
CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
@@ -45,7 +47,8 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
|
||||
window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -87,10 +90,12 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
auto process =
|
||||
CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
@@ -98,7 +103,8 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
|
||||
m_window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -121,23 +121,26 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
};
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2100);
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = mode;
|
||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
window_system.TrackApplet(applet, false);
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
if (!process) {
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
}
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = mode;
|
||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
window_system.TrackApplet(applet, false);
|
||||
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
}
|
||||
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -14,9 +14,7 @@ namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -43,7 +43,7 @@ private:
|
||||
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::optional<AudioCore::AudioOut::Manager> impl;
|
||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,9 +15,7 @@ namespace Service::Audio {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioRendererManager::IAudioRendererManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -33,7 +30,7 @@ private:
|
||||
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
||||
u32 revision, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::optional<AudioCore::Renderer::Manager> impl;
|
||||
std::unique_ptr<AudioCore::Renderer::Manager> impl;
|
||||
u32 num_audio_devices{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>
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,6 +10,14 @@
|
||||
|
||||
namespace Service {
|
||||
|
||||
Process::Process(Core::System& system)
|
||||
: m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
|
||||
m_process_started() {}
|
||||
|
||||
Process::~Process() {
|
||||
this->Finalize();
|
||||
}
|
||||
|
||||
bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) {
|
||||
// First, ensure we are not holding another process.
|
||||
this->Finalize();
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -25,8 +22,8 @@ namespace Service {
|
||||
|
||||
class Process {
|
||||
public:
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
explicit Process(Core::System& system);
|
||||
~Process();
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
@@ -53,8 +50,8 @@ public:
|
||||
private:
|
||||
Core::System& m_system;
|
||||
Kernel::KProcess* m_process{};
|
||||
u64 m_main_thread_stack_size{};
|
||||
s32 m_main_thread_priority{};
|
||||
u64 m_main_thread_stack_size{};
|
||||
bool m_process_started{};
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -54,7 +54,7 @@ void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsi
|
||||
if (ctx.conf.only_detect_misalignment_via_page_table_on_page_boundary) {
|
||||
const u32 page_align_mask = static_cast<u32>(page_table_const_size - 1) & ~align_mask;
|
||||
|
||||
SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel();
|
||||
SharedLabel detect_boundary = ctx.GenSharedLabel(), resume = ctx.GenSharedLabel();
|
||||
|
||||
code.jnz(*detect_boundary, code.T_NEAR);
|
||||
code.L(*resume);
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
@@ -23,8 +23,8 @@ template<typename Visitor>
|
||||
using Thumb32Matcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
|
||||
alignas(64) static const std::vector<Thumb32Matcher<V>> table = {
|
||||
static std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
|
||||
alignas(64) static const auto table = std::array{
|
||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
||||
#include "./thumb32.inc"
|
||||
#undef INST
|
||||
@@ -36,7 +36,7 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::string_view> GetNameThumb32(u32 inst) noexcept {
|
||||
static std::optional<std::string_view> GetNameThumb32(u32 inst) noexcept {
|
||||
std::vector<std::pair<std::string_view, Thumb32Matcher<V>>> list = {
|
||||
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||
#include "./thumb32.inc"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 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,7 +24,7 @@ template<typename Visitor>
|
||||
using VFPMatcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
|
||||
static std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
|
||||
using Table = std::vector<VFPMatcher<V>>;
|
||||
alignas(64) static const struct Tables {
|
||||
Table unconditional;
|
||||
@@ -52,7 +52,7 @@ std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruc
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::string_view> GetNameVFP(u32 inst) noexcept {
|
||||
static std::optional<std::string_view> GetNameVFP(u32 inst) noexcept {
|
||||
std::vector<std::pair<std::string_view, VFPMatcher<V>>> list = {
|
||||
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||
#include "./vfp.inc"
|
||||
|
||||
@@ -36,33 +36,19 @@ inline size_t ToFastLookupIndex(u32 instruction) {
|
||||
} // namespace detail
|
||||
|
||||
template<typename V>
|
||||
constexpr DecodeTable<V> GetDecodeTable() {
|
||||
std::vector<std::pair<const char*, Matcher<V>>> list = {
|
||||
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||
#include "./a64.inc"
|
||||
#undef INST
|
||||
};
|
||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||
std::stable_sort(list.begin(), list.end(), [](const auto& a, const auto& b) {
|
||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||
return mcl::bit::count_ones(a.second.GetMask()) > mcl::bit::count_ones(b.second.GetMask());
|
||||
});
|
||||
// Exceptions to the above rule of thumb.
|
||||
std::stable_partition(list.begin(), list.end(), [&](const auto& e) {
|
||||
return std::set<std::string>{
|
||||
"MOVI, MVNI, ORR, BIC (vector, immediate)",
|
||||
"FMOV (vector, immediate)",
|
||||
"Unallocated SIMD modified immediate",
|
||||
}.count(e.first) > 0;
|
||||
});
|
||||
inline DecodeTable<V> GetDecodeTable() {
|
||||
DecodeTable<V> table{};
|
||||
for (size_t i = 0; i < table.size(); ++i) {
|
||||
for (auto const& e : list) {
|
||||
const auto expect = detail::ToFastLookupIndex(e.second.GetExpected());
|
||||
const auto mask = detail::ToFastLookupIndex(e.second.GetMask());
|
||||
if ((i & mask) == expect) {
|
||||
table[i].push_back(e.second);
|
||||
}
|
||||
// PLEASE HEAP ELLIDE
|
||||
for (auto const& e : std::vector<Matcher<V>>{
|
||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
||||
#include "./a64.inc"
|
||||
#undef INST
|
||||
}) {
|
||||
const auto expect = detail::ToFastLookupIndex(e.GetExpected());
|
||||
const auto mask = detail::ToFastLookupIndex(e.GetMask());
|
||||
if ((i & mask) == expect)
|
||||
table[i].push_back(e);
|
||||
}
|
||||
}
|
||||
return table;
|
||||
@@ -70,7 +56,7 @@ constexpr DecodeTable<V> GetDecodeTable() {
|
||||
|
||||
/// In practice it must always suceed, otherwise something else unrelated would have gone awry
|
||||
template<typename V>
|
||||
std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
|
||||
inline std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
|
||||
alignas(64) static const auto table = GetDecodeTable<V>();
|
||||
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
|
||||
auto iter = std::find_if(subtable.begin(), subtable.end(), [instruction](const auto& matcher) {
|
||||
@@ -82,7 +68,7 @@ std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction)
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
std::optional<std::string_view> GetName(u32 inst) noexcept {
|
||||
inline std::optional<std::string_view> GetName(u32 inst) noexcept {
|
||||
std::vector<std::pair<std::string_view, Matcher<V>>> list = {
|
||||
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||
#include "./a64.inc"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// First we list common shared opcodes
|
||||
// Since we give priority to A64 performance, we include them first, this is so we
|
||||
// can discard all A32 opcodes instead of having a "hole" in our checks
|
||||
@@ -710,6 +713,8 @@ A64OPC(ExclusiveWriteMemory32, U32, U64,
|
||||
A64OPC(ExclusiveWriteMemory64, U32, U64, U64, U64, AccType )
|
||||
A64OPC(ExclusiveWriteMemory128, U32, U64, U64, U128, AccType )
|
||||
|
||||
// Remember to update:
|
||||
// - a32_emit_x64.cpp
|
||||
|
||||
// A32 Context getters/setters
|
||||
A32OPC(SetCheckBit, Void, U1 )
|
||||
|
||||
@@ -415,6 +415,105 @@ TEST_CASE("A64: URSHL", "[a64]") {
|
||||
CHECK(jit.GetVector(9) == Vector{0x0000000000000002, 0x12db8b8280e0ba});
|
||||
}
|
||||
|
||||
TEST_CASE("A64: SQSHLU", "[a64]") {
|
||||
A64TestEnv env;
|
||||
A64::UserConfig jit_user_config{};
|
||||
jit_user_config.callbacks = &env;
|
||||
A64::Jit jit{jit_user_config};
|
||||
|
||||
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
|
||||
code.SQSHLU(V8.B16(), V0.B16(), 1);
|
||||
code.SQSHLU(V9.H8(), V1.H8(), 2);
|
||||
code.SQSHLU(V10.S4(), V2.S4(), 28);
|
||||
code.SQSHLU(V11.D2(), V3.D2(), 4);
|
||||
code.SQSHLU(V12.S4(), V0.S4(), 1);
|
||||
code.SQSHLU(V13.S4(), V1.S4(), 3);
|
||||
code.SQSHLU(V14.S4(), V2.S4(), 0);
|
||||
code.SQSHLU(V15.S4(), V3.S4(), 0);
|
||||
|
||||
jit.SetVector(0, Vector{0xffffffff'18ba6a6a, 0x7fffffff'943b954f});
|
||||
jit.SetVector(1, Vector{0x0000000b'0000000f, 0xffffffff'ffffffff});
|
||||
jit.SetVector(2, Vector{0x00000001'000000ff, 0x00000010'0000007f});
|
||||
jit.SetVector(3, Vector{0xffffffffffffffff, 0x96dc5c140705cd04});
|
||||
|
||||
env.ticks_left = env.code_mem.size();
|
||||
CheckedRun([&]() { jit.Run(); });
|
||||
|
||||
CHECK(jit.GetVector(8) == Vector{0x3000d4d4, 0xfe0000000076009e});
|
||||
CHECK(jit.GetVector(9) == Vector{0x2c0000003c, 0});
|
||||
CHECK(jit.GetVector(10) == Vector{0x10000000'ffffffff, 0xffffffff'ffffffff});
|
||||
CHECK(jit.GetVector(11) == Vector{0, 0});
|
||||
CHECK(jit.GetVector(12) == Vector{0x3174d4d4, 0xfffffffe00000000});
|
||||
CHECK(jit.GetVector(13) == Vector{0x5800000078, 0});
|
||||
CHECK(jit.GetVector(14) == Vector{0x1000000ff, 0x100000007f});
|
||||
CHECK(jit.GetVector(15) == Vector{0, 0x705cd04});
|
||||
}
|
||||
|
||||
TEST_CASE("A64: SMIN", "[a64]") {
|
||||
A64TestEnv env;
|
||||
A64::UserConfig jit_user_config{};
|
||||
jit_user_config.callbacks = &env;
|
||||
A64::Jit jit{jit_user_config};
|
||||
|
||||
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
|
||||
code.SMIN(V8.B16(), V0.B16(), V3.B16());
|
||||
code.SMIN(V9.H8(), V1.H8(), V2.H8());
|
||||
code.SMIN(V10.S4(), V2.S4(), V3.S4());
|
||||
code.SMIN(V11.S4(), V3.S4(), V3.S4());
|
||||
code.SMIN(V12.S4(), V0.S4(), V3.S4());
|
||||
code.SMIN(V13.S4(), V1.S4(), V2.S4());
|
||||
code.SMIN(V14.S4(), V2.S4(), V1.S4());
|
||||
code.SMIN(V15.S4(), V3.S4(), V0.S4());
|
||||
|
||||
jit.SetPC(0);
|
||||
jit.SetVector(0, Vector{0xffffffff'18ba6a6a, 0x7fffffff'943b954f});
|
||||
jit.SetVector(1, Vector{0x0000000b'0000000f, 0xffffffff'ffffffff});
|
||||
jit.SetVector(2, Vector{0x00000001'000000ff, 0x00000010'0000007f});
|
||||
jit.SetVector(3, Vector{0xffffffff'ffffffff, 0x96dc5c14'0705cd04});
|
||||
|
||||
env.ticks_left = 4;
|
||||
CheckedRun([&]() { jit.Run(); });
|
||||
|
||||
REQUIRE(jit.GetVector(8) == Vector{0xffffffffffbaffff, 0x96dcffff94059504});
|
||||
REQUIRE(jit.GetVector(9) == Vector{0x10000000f, 0xffffffffffffffff});
|
||||
REQUIRE(jit.GetVector(10) == Vector{0xffffffffffffffff, 0x96dc5c140000007f});
|
||||
}
|
||||
|
||||
TEST_CASE("A64: SMINP", "[a64]") {
|
||||
A64TestEnv env;
|
||||
A64::UserConfig jit_user_config{};
|
||||
jit_user_config.callbacks = &env;
|
||||
A64::Jit jit{jit_user_config};
|
||||
|
||||
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
|
||||
code.SMINP(V8.B16(), V0.B16(), V3.B16());
|
||||
code.SMINP(V9.H8(), V1.H8(), V2.H8());
|
||||
code.SMINP(V10.S4(), V2.S4(), V1.S4());
|
||||
code.SMINP(V11.S4(), V3.S4(), V3.S4());
|
||||
code.SMINP(V12.S4(), V0.S4(), V3.S4());
|
||||
code.SMINP(V13.S4(), V1.S4(), V2.S4());
|
||||
code.SMINP(V14.S4(), V2.S4(), V1.S4());
|
||||
code.SMINP(V15.S4(), V3.S4(), V0.S4());
|
||||
|
||||
jit.SetPC(0);
|
||||
jit.SetVector(0, Vector{0xffffffff'18ba6a6a, 0x7fffffff'943b954f});
|
||||
jit.SetVector(1, Vector{0x0000000b'0000000f, 0xffffffff'ffffffff});
|
||||
jit.SetVector(2, Vector{0x00000001'000000ff, 0x00000010'0000007f});
|
||||
jit.SetVector(3, Vector{0xffffffff'ffffffff, 0x96dc5c14'0705cd04});
|
||||
|
||||
env.ticks_left = 4;
|
||||
CheckedRun([&]() { jit.Run(); });
|
||||
|
||||
REQUIRE(jit.GetVector(8) == Vector{0xffff9495ffffba6a, 0x961405cdffffffff});
|
||||
REQUIRE(jit.GetVector(9) == Vector{0xffffffff00000000, 0});
|
||||
REQUIRE(jit.GetVector(10) == Vector{0x1000000001, 0xffffffff0000000b});
|
||||
REQUIRE(jit.GetVector(11) == Vector{0x96dc5c14ffffffff, 0x96dc5c14ffffffff});
|
||||
REQUIRE(jit.GetVector(12) == Vector{0x943b954fffffffff, 0x96dc5c14ffffffff});
|
||||
REQUIRE(jit.GetVector(13) == Vector{0xffffffff0000000b, 0x1000000001});
|
||||
REQUIRE(jit.GetVector(14) == Vector{0x1000000001, 0xffffffff0000000b});
|
||||
REQUIRE(jit.GetVector(15) == Vector{0x96dc5c14ffffffff, 0x943b954fffffffff});
|
||||
}
|
||||
|
||||
TEST_CASE("A64: XTN", "[a64]") {
|
||||
A64TestEnv env;
|
||||
A64::UserConfig jit_user_config{};
|
||||
|
||||
@@ -4,22 +4,21 @@
|
||||
#include <random>
|
||||
#include <frozen/string.h>
|
||||
#include "common/settings.h"
|
||||
#include "common/random.h"
|
||||
#include "settings_generator.h"
|
||||
|
||||
namespace FrontendCommon {
|
||||
|
||||
void GenerateSettings() {
|
||||
static std::random_device rd;
|
||||
|
||||
auto gen = Common::Random::GetMT19937();
|
||||
// Web Token
|
||||
if (Settings::values.eden_token.GetValue().empty()) {
|
||||
static constexpr const size_t token_length = 48;
|
||||
static constexpr const frozen::string token_set = "abcdefghijklmnopqrstuvwxyz";
|
||||
static std::uniform_int_distribution<int> token_dist(0, token_set.size() - 1);
|
||||
std::string result;
|
||||
|
||||
for (size_t i = 0; i < token_length; ++i) {
|
||||
size_t idx = token_dist(rd);
|
||||
size_t idx = token_dist(gen);
|
||||
result += token_set[idx];
|
||||
}
|
||||
Settings::values.eden_token.SetValue(result);
|
||||
@@ -27,8 +26,6 @@ void GenerateSettings() {
|
||||
|
||||
// Randomly generated number because, well, we fill the rest automagically ;)
|
||||
// Other serial parts are filled by Region_Index
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u32> distribution(1, (std::numeric_limits<u32>::max)());
|
||||
if (Settings::values.serial_unit.GetValue() == 0)
|
||||
Settings::values.serial_unit.SetValue(distribution(gen));
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#include "common/random.h"
|
||||
#include "common/input.h"
|
||||
#include "hid_core/frontend/input_converter.h"
|
||||
|
||||
@@ -119,15 +123,14 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
|
||||
.properties = properties,
|
||||
};
|
||||
if (TransformToButton(callback).value) {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<s16> distribution(-5000, 5000);
|
||||
status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
auto gen = Common::Random::GetMT19937();
|
||||
status.accel.x.raw_value = f32(distribution(gen)) * 0.001f;
|
||||
status.accel.y.raw_value = f32(distribution(gen)) * 0.001f;
|
||||
status.accel.z.raw_value = f32(distribution(gen)) * 0.001f;
|
||||
status.gyro.x.raw_value = f32(distribution(gen)) * 0.001f;
|
||||
status.gyro.y.raw_value = f32(distribution(gen)) * 0.001f;
|
||||
status.gyro.z.raw_value = f32(distribution(gen)) * 0.001f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/random.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/helpers/udp_protocol.h"
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
|
||||
explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
|
||||
: callback(std::move(callback_)), timer(io_context),
|
||||
socket(io_context, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) {
|
||||
socket(io_context, udp::endpoint(udp::v4(), 0)), client_id(Common::Random::Random32(0)) {
|
||||
boost::system::error_code ec{};
|
||||
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
|
||||
if (ec.value() != boost::system::errc::success) {
|
||||
@@ -65,11 +65,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
u32 GenerateRandomClientId() const {
|
||||
std::random_device device;
|
||||
return device();
|
||||
}
|
||||
|
||||
void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
|
||||
if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
|
||||
switch (*type) {
|
||||
@@ -136,7 +131,6 @@ private:
|
||||
};
|
||||
|
||||
static void SocketLoop(Socket* socket) {
|
||||
Common::SetCurrentThreadName("cemuhookWorker");
|
||||
socket->StartReceive();
|
||||
socket->StartSend(Socket::clock::now());
|
||||
socket->Loop();
|
||||
@@ -332,11 +326,9 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
|
||||
}
|
||||
|
||||
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
|
||||
SocketCallback callback{
|
||||
[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }
|
||||
};
|
||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }};
|
||||
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
|
||||
clients[client].uuid = GetHostUUID(host);
|
||||
clients[client].host = host;
|
||||
@@ -574,7 +566,9 @@ bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) {
|
||||
void TestCommunication(const std::string& host, u16 port,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback) {
|
||||
std::thread([=] {
|
||||
Common::Event success_event;
|
||||
SocketCallback callback{
|
||||
@@ -607,38 +601,40 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y);
|
||||
min_x = (std::min)(min_x, u16(data.touch[0].x));
|
||||
min_y = (std::min)(min_y, u16(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = (std::min)(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = (std::min)(min_y, static_cast<u16>(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
|
||||