mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-08 12:11:51 +09:00
emulator: malloc viewer
This commit is contained in:
24
tsvm_executable/src/net/torvald/tsvm/DummyMenu.kt
Normal file
24
tsvm_executable/src/net/torvald/tsvm/DummyMenu.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package net.torvald.tsvm
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2023-01-02.
|
||||
*/
|
||||
class DummyMenu(parent: VMEmuExecutable, x: Int, y: Int, w: Int, h: Int) : EmuMenu(parent, x, y, w, h) {
|
||||
|
||||
override fun show() {
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
}
|
||||
|
||||
override fun update() {
|
||||
}
|
||||
|
||||
override fun render(batch: SpriteBatch) {
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,6 @@ abstract class EmuMenu(val parent: VMEmuExecutable, val x: Int, val y: Int, val
|
||||
abstract fun hide()
|
||||
abstract fun update()
|
||||
abstract fun render(batch: SpriteBatch)
|
||||
abstract fun dispose()
|
||||
|
||||
}
|
||||
123
tsvm_executable/src/net/torvald/tsvm/MMUMenu.kt
Normal file
123
tsvm_executable/src/net/torvald/tsvm/MMUMenu.kt
Normal file
@@ -0,0 +1,123 @@
|
||||
package net.torvald.tsvm
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.tsvm.EmulatorGuiToolkit.Theme.COL_LAND
|
||||
import net.torvald.tsvm.EmulatorGuiToolkit.Theme.COL_WELL
|
||||
import net.torvald.tsvm.VMEmuExecutableWrapper.Companion.FONT
|
||||
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2023-01-02.
|
||||
*/
|
||||
class MMUMenu(parent: VMEmuExecutable, x: Int, y: Int, w: Int, h: Int) : EmuMenu(parent, x, y, w, h) {
|
||||
|
||||
override fun show() {
|
||||
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
}
|
||||
|
||||
override fun update() {
|
||||
}
|
||||
|
||||
override fun render(batch: SpriteBatch) {
|
||||
batch.color = Color.WHITE
|
||||
|
||||
val vmInfo = parent.getCurrentlySelectedVM()
|
||||
|
||||
if (vmInfo == null) {
|
||||
batch.inUse {
|
||||
FONT.draw(batch, "Please select a VM", 12f, 11f + 0* FONT.H)
|
||||
}
|
||||
}
|
||||
else vmInfo.let { (vm, vmName) ->
|
||||
batch.inUse {
|
||||
FONT.draw(batch, "Allocated size: ${vm.allocatedBlockCount * vm.MALLOC_UNIT}", 12f, 11f + 0* FONT.H)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
drawAllocMap(batch, vm, 62f, 2f * FONT.H)
|
||||
}
|
||||
}
|
||||
|
||||
private val plotColset = intArrayOf(
|
||||
0xea5545ff.toInt(), 0xf46a9bff.toInt(), 0xef9b20ff.toInt(), 0xedbf33ff.toInt(), 0xede15bff.toInt(), 0xbdcf32ff.toInt(),
|
||||
0x87bc45ff.toInt(), 0x27aeefff.toInt(), 0xb33dc6ff.toInt(), 0xe60049ff.toInt(), 0x0bb4ffff.toInt(), 0x50e991ff.toInt(),
|
||||
0xe6d800ff.toInt(), 0x9b19f5ff.toInt(), 0xffa300ff.toInt(), 0xdc0ab4ff.toInt(), 0xb3d4ffff.toInt(), 0x00bfa0ff.toInt(),
|
||||
)
|
||||
private val plotColours = plotColset.map { Color(it) }
|
||||
|
||||
private val memmapPixmap = Pixmap(512, 256, Pixmap.Format.RGBA8888)
|
||||
|
||||
private var mallocMap: List<Pair<Int, Int>> = listOf()
|
||||
|
||||
private fun drawAllocMap(batch: SpriteBatch, vm: VM, x: Float, y: Float) {
|
||||
|
||||
// clear the memmapPixmap
|
||||
memmapPixmap.setColor(0)
|
||||
memmapPixmap.fill()
|
||||
|
||||
// unallocated map as black
|
||||
for (i in 0 until vm.memsize / vm.MALLOC_UNIT) {
|
||||
paintPixel(i.toInt(), 255)
|
||||
}
|
||||
|
||||
try {
|
||||
// try to update the mallocMap
|
||||
mallocMap = vm.javaClass.getDeclaredField("mallocSizes").let {
|
||||
it.isAccessible = true
|
||||
it.get(vm) as HashMap<Int, Int>
|
||||
}.entries.map { it.key to it.value }.sortedBy { it.first }
|
||||
}
|
||||
catch (e: ConcurrentModificationException) { /* skip update for this frame */ }
|
||||
|
||||
// allocated map
|
||||
mallocMap.forEachIndexed { index, (ptr, size) ->
|
||||
for (i in 0 until size) {
|
||||
paintPixel(ptr + i, plotColset[ptr % plotColset.size])
|
||||
}
|
||||
}
|
||||
|
||||
val memmapTex = Texture(memmapPixmap)
|
||||
|
||||
batch.inUse {
|
||||
// draw allocation map
|
||||
batch.color = COL_WELL
|
||||
batch.fillRect(x, y, 512, 256)
|
||||
batch.color = Color.WHITE
|
||||
batch.draw(memmapTex, x, y)
|
||||
|
||||
// draw textual list
|
||||
mallocMap.forEachIndexed { index, (ptr, size) ->
|
||||
// hackishly draw textual list
|
||||
if (index < 52) {
|
||||
val xoff = 15f + 155f * (index / 13)
|
||||
val yoff = 286f + ((index % 13) * FONT.H)
|
||||
|
||||
batch.color = plotColours[ptr % plotColset.size]
|
||||
batch.fillRect(xoff, yoff + 1, 10, 10)
|
||||
batch.color = Color.WHITE
|
||||
FONT.draw(batch, " $size at $ptr", xoff, yoff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memmapTex.dispose()
|
||||
|
||||
}
|
||||
|
||||
private fun paintPixel(index: Int, colour: Int) {
|
||||
memmapPixmap.setColor(colour)
|
||||
memmapPixmap.drawPixel(index / 256, index % 256)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
memmapPixmap.dispose()
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,10 @@ class ProfilesMenu(parent: VMEmuExecutable, x: Int, y: Int, w: Int, h: Int) : Em
|
||||
}
|
||||
}
|
||||
|
||||
private fun SpriteBatch.setColourBy(colourIfTrue: Color = EmulatorGuiToolkit.Theme.COL_ACTIVE3, colourIfFalse: Color = Color.WHITE, predicate: () -> Boolean) {
|
||||
this.color = if (predicate()) colourIfTrue else colourIfFalse
|
||||
override fun dispose() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun SpriteBatch.setColourBy(colourIfTrue: Color = EmulatorGuiToolkit.Theme.COL_ACTIVE3, colourIfFalse: Color = Color.WHITE, predicate: () -> Boolean) {
|
||||
this.color = if (predicate()) colourIfTrue else colourIfFalse
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.torvald.tsvm
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.Input.Buttons
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.badlogic.gdx.graphics.*
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
@@ -64,11 +65,11 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
|
||||
val watchdogs = hashMapOf<String, VMWatchdog>("TEVD_SYNC" to TEVD_SYNC)
|
||||
|
||||
private data class VMRunnerInfo(val vm: VM, val name: String)
|
||||
data class VMRunnerInfo(val vm: VM, val name: String)
|
||||
|
||||
private val vms = arrayOfNulls<VMRunnerInfo>(this.panelsX * this.panelsY - 1) // index: # of the window where the reboot was requested
|
||||
|
||||
private var currentVMselection: Int? = 0 // null: emulator menu is selected
|
||||
var currentVMselection: Int? = 0 // null: emulator menu is selected
|
||||
|
||||
lateinit var batch: SpriteBatch
|
||||
lateinit var fbatch: FlippingSpriteBatch
|
||||
@@ -117,6 +118,8 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
vms[index] = VMRunnerInfo(vm, profileName)
|
||||
}
|
||||
|
||||
internal fun getCurrentlySelectedVM(): VMRunnerInfo? = if (currentVMselection == null) null else vms[currentVMselection!!]
|
||||
|
||||
private fun writeProfilesToFile(outFile: FileHandle) {
|
||||
val out = StringBuilder()
|
||||
out.append('{')
|
||||
@@ -376,6 +379,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
tabs.forEach { it.dispose() }
|
||||
batch.dispose()
|
||||
fbatch.dispose()
|
||||
fullscreenQuad.dispose()
|
||||
@@ -390,9 +394,16 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
private val menuTabX = windowWidth * (panelsX-1) + 2
|
||||
private val menuTabY =windowHeight * (panelsY-1) + FONT.H + 2
|
||||
|
||||
private val menuTabs = listOf("Profiles", "Machine", "COMs", "Cards", "Setup")
|
||||
private val menuTabs = listOf("Profiles", "MMU", "Machine", "COMs", "Cards", "Setup")
|
||||
private val tabPos = (menuTabs + "").mapIndexed { index, _ -> 1 + menuTabs.subList(0, index).sumBy { it.length } + 2 * index }
|
||||
private val tabs = listOf(ProfilesMenu(this, menuTabX, menuTabY, menuTabW, menuTabH))
|
||||
private val tabs = listOf(
|
||||
ProfilesMenu(this, menuTabX, menuTabY, menuTabW, menuTabH),
|
||||
MMUMenu(this, menuTabX, menuTabY, menuTabW, menuTabH),
|
||||
DummyMenu(this, menuTabX, menuTabY, menuTabW, menuTabH),
|
||||
DummyMenu(this, menuTabX, menuTabY, menuTabW, menuTabH),
|
||||
DummyMenu(this, menuTabX, menuTabY, menuTabW, menuTabH),
|
||||
DummyMenu(this, menuTabX, menuTabY, menuTabW, menuTabH),
|
||||
)
|
||||
private var menuTabSel = 0
|
||||
|
||||
private var tabChangeRequested: Int? = 0 // null: not requested
|
||||
@@ -423,7 +434,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
FONT.draw(batch, menuTabs[k], textX, y)
|
||||
}
|
||||
else {
|
||||
batch.color = EmulatorGuiToolkit.Theme.COL_TAB_NOT_SELECTED
|
||||
batch.color = if (k % 2 == 0) EmulatorGuiToolkit.Theme.COL_TAB_NOT_SELECTED else EmulatorGuiToolkit.Theme.COL_TAB_NOT_SELECTED2
|
||||
batch.fillRect(textX - FONT.W, y, FONT.W * (menuTabs[k].length + 2f), FONT.H.toFloat())
|
||||
|
||||
batch.color = EmulatorGuiToolkit.Theme.COL_ACTIVE2
|
||||
@@ -447,7 +458,22 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
|
||||
private fun updateMenu() {
|
||||
// update the tab
|
||||
|
||||
var tabSelected = -1
|
||||
val x = (panelsX - 1) * windowWidth
|
||||
val y = (panelsY - 1) * windowHeight
|
||||
val mx = Gdx.input.x
|
||||
val my = Gdx.input.y
|
||||
if (Gdx.input.isButtonPressed(Buttons.LEFT) && my in y until y + FONT.H) {
|
||||
for (k in menuTabs.indices) {
|
||||
val textX = x + FONT.W * tabPos[k]
|
||||
if (mx in textX - FONT.W until textX - FONT.W + FONT.W * (menuTabs[k].length + 2)) {
|
||||
tabSelected = k
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tabSelected >= 0 && tabSelected != menuTabSel) {
|
||||
tabChangeRequested = tabSelected
|
||||
}
|
||||
|
||||
// actually update the view within the tabs
|
||||
tabs[menuTabSel].update()
|
||||
@@ -584,11 +610,12 @@ object EmulatorGuiToolkit {
|
||||
val COL_HIGHLIGHT = Color(0xe43380ff.toInt()) // magenta
|
||||
val COL_DISABLED = Color(0xaaaaaaff.toInt())
|
||||
|
||||
val COL_TAB_NOT_SELECTED = Color(0x503cd4ff) // dark blue
|
||||
val COL_TAB_NOT_SELECTED = Color(0x4d39cbff) // dark blue
|
||||
val COL_TAB_NOT_SELECTED2 = Color(0x5949e0ff) // dark blue
|
||||
|
||||
val COL_LAND = Color(0x6b8ba2ff.toInt())
|
||||
val COL_WELL = Color(0x374854ff.toInt())
|
||||
val COL_WELL2 = Color(0x3e5261ff.toInt())
|
||||
val COL_WELL2 = Color(0x3f5360ff.toInt())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user