Refactor PlatformSpecific functions with time and setTimeout, optimize RewindStep substeps handling, update MTMCClock timing logic, enhance frame processing, and integrate updateJsDisplay for dynamic updates in Main.

This commit is contained in:
2025-08-23 17:44:25 +02:00
parent f14f316e38
commit 4dcfe3a6ff
8 changed files with 63 additions and 35 deletions

View File

@@ -3,6 +3,8 @@ package mtmc.emulator
import mtmc.emulator.MonTanaMiniComputer.ComputerStatus import mtmc.emulator.MonTanaMiniComputer.ComputerStatus
import mtmc.util.currentTimeMillis import mtmc.util.currentTimeMillis
import mtmc.util.requestAnimationFrame import mtmc.util.requestAnimationFrame
import mtmc.util.setTimeout
import mtmc.util.time
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -27,21 +29,22 @@ class MTMCClock(
var frame = 0 var frame = 0
fun run() { fun run() {
requestAnimationFrame { handleFrame(it) } requestAnimationFrame { handleFrame() }
} }
fun handleFrame(time: Double) { fun handleFrame() {
// figure out how many instructions to execute this 'time' duration // figure out how many instructions to execute this 'time' duration
// maximize time so we don't hang if the emulator is too slow // maximize time so we don't hang if the emulator is too slow
val time = time()
if (lastFrame == 0.0) { if (lastFrame == 0.0) {
lastFrame = time lastFrame = time
requestAnimationFrame { handleFrame(it) } requestAnimationFrame { handleFrame() }
return return
} }
val delta = time - lastFrame val delta = time - lastFrame
lastFrame = time lastFrame = time
val actualTime = min(delta / 1000.0, 0.0166) val timeToProcess = min(delta, 16.0)
// assume 1Hz = 1 instruction/second // assume 1Hz = 1 instruction/second
if (computer.getStatus() == ComputerStatus.EXECUTING) { if (computer.getStatus() == ComputerStatus.EXECUTING) {
@@ -50,23 +53,28 @@ class MTMCClock(
speed = 1L speed = 1L
} }
instructionsToRun += actualTime * speed instructionsToRun += timeToProcess * speed / 1000.0
val pulse: Long = instructionsToRun.toLong() val pulse: Long = instructionsToRun.toLong() + 1
instructionsToRun -= pulse instructionsToRun -= pulse
val time = currentTimeMillis() val start = time()
val ir = computer.pulse(pulse) val ir = computer.pulse(pulse)
instructions += ir instructions += ir
val duration = (time() - start)
val actual = ir / (delta / 1000.0)
if (frame % 100 == 0) { if (frame % 100 == 0) {
val duration = currentTimeMillis() - time println("Instructions ran: $ir (delta = ${delta.toFloat()}, timeToProcess = ${timeToProcess.toFloat()}, speed = $speed (actual=${actual.toLong()}), duration = $duration)")
println("Instructions ran: $ir (delta = ${delta.toFloat()}, actualTime = ${actualTime.toFloat()}, speed = $speed (actual=${(ir / delta * 1000).toLong()}), duration = $duration)")
} }
virtual += instructions virtual += instructions
ips = (instructions / actualTime).toLong() ips = (instructions / timeToProcess).toLong()
frame++ frame++
requestAnimationFrame { handleFrame(it) } if (duration > timeToProcess) {
setTimeout({ handleFrame() })
} else {
requestAnimationFrame { handleFrame() }
}
} }
//println("Executed " + instructions + " instructions at a rate of " + ips + " ips (speed = " + speed + ")") //println("Executed " + instructions + " instructions at a rate of " + ips + " ips (speed = " + speed + ")")

View File

@@ -32,7 +32,7 @@ class MonTanaMiniComputer {
var clock: MTMCClock = MTMCClock(this) var clock: MTMCClock = MTMCClock(this)
var fileSystem: FileSystem = FileSystem(this) var fileSystem: FileSystem = FileSystem(this)
var rewindSteps = Array<RewindStep?>(MAX_REWIND_STEPS) { null } var rewindSteps = Array(MAX_REWIND_STEPS) { RewindStep() }
var rewindIndex = -1 var rewindIndex = -1
// listeners // listeners
@@ -133,11 +133,9 @@ class MonTanaMiniComputer {
} }
fun fetchAndExecute() { fun fetchAndExecute() {
currentRewindStep = RewindStep() rewindIndex = (rewindIndex + 1) % rewindSteps.size
currentRewindStep?.let { rewindSteps[rewindIndex].index = 0
rewindIndex = (rewindIndex + 1) % rewindSteps.size
rewindSteps.set(rewindIndex, it)
}
fetchCurrentInstruction() fetchCurrentInstruction()
val instruction = getRegisterValue(Register.IR) val instruction = getRegisterValue(Register.IR)
if (isDoubleWordInstruction(instruction)) { if (isDoubleWordInstruction(instruction)) {

View File

@@ -3,13 +3,16 @@ package mtmc.emulator
import mtmc.util.Runnable import mtmc.util.Runnable
class RewindStep { class RewindStep {
var subSteps: MutableList<Runnable?> = mutableListOf() var subSteps: Array<Runnable> = Array(10) { {} }
var index = 0
fun rewind() { fun rewind() {
subSteps.reversed().forEach({ obj: Runnable? -> obj!!.invoke() }) subSteps.reversed().forEach({ obj: Runnable? -> obj!!.invoke() })
} }
fun addSubStep(subStep: Runnable?) { fun addSubStep(subStep: Runnable?) {
subSteps.add(subStep) if (subStep != null && index < subSteps.size) {
subSteps[index++] = subStep
}
} }
} }

View File

@@ -2,6 +2,10 @@ package mtmc.util
expect fun currentTimeMillis(): Double expect fun currentTimeMillis(): Double
expect fun time(): Double
expect fun requestAnimationFrame(action: (Double) -> Unit) expect fun requestAnimationFrame(action: (Double) -> Unit)
expect fun setTimeout(action: () -> Unit)
expect fun immediateTimeout(action: (Double) -> Unit): Int expect fun immediateTimeout(action: (Double) -> Unit): Int

View File

@@ -1,7 +1,9 @@
package mtmc package mtmc
import kotlinx.browser.document import kotlinx.browser.document
import kotlinx.browser.window
import mtmc.emulator.MonTanaMiniComputer import mtmc.emulator.MonTanaMiniComputer
import mtmc.util.currentTimeMillis
import mtmc.view.DisplayView import mtmc.view.DisplayView
import mtmc.view.MTMCView import mtmc.view.MTMCView
import nl.astraeus.komp.Komponent import nl.astraeus.komp.Komponent
@@ -21,4 +23,21 @@ fun main() {
mainView.requestUpdate() mainView.requestUpdate()
display.requestUpdate() display.requestUpdate()
window.requestAnimationFrame { updateJsDisplay() }
}
var lastMemoryUpdate = currentTimeMillis()
var updateState = true
fun updateJsDisplay() {
display.requestUpdate()
if (currentTimeMillis() - lastMemoryUpdate > 125) {
if (updateState) {
mainView.registerView.requestUpdate()
mainView.memoryView.requestUpdate()
}
lastMemoryUpdate = currentTimeMillis()
}
window.requestAnimationFrame { updateJsDisplay() }
} }

View File

@@ -1,27 +1,19 @@
package mtmc.util package mtmc.util
import kotlinx.browser.window import kotlinx.browser.window
import mtmc.display
import mtmc.mainView
import kotlin.js.Date import kotlin.js.Date
var lastMemoryUpdate = currentTimeMillis()
var updateState = true
actual fun currentTimeMillis(): Double = Date().getTime() actual fun currentTimeMillis(): Double = Date().getTime()
actual fun requestAnimationFrame(action: (Double) -> Unit) { actual fun requestAnimationFrame(action: (Double) -> Unit) {
window.requestAnimationFrame { window.requestAnimationFrame {
action(it) action(it)
display.requestUpdate()
if (currentTimeMillis() - lastMemoryUpdate > 125) {
if (updateState) {
mainView.registerView.requestUpdate()
mainView.memoryView.requestUpdate()
}
lastMemoryUpdate = currentTimeMillis()
}
} }
} }
actual fun immediateTimeout(action: (Double) -> Unit): Int = window.setTimeout(action, 0) actual fun immediateTimeout(action: (Double) -> Unit): Int = window.setTimeout(action, 0)
actual fun time(): Double = window.performance.now()
actual fun setTimeout(action: () -> Unit) {
window.setTimeout(action)
}

View File

@@ -14,7 +14,7 @@ import kotlinx.html.span
import mtmc.display import mtmc.display
import mtmc.emulator.MonTanaMiniComputer import mtmc.emulator.MonTanaMiniComputer
import mtmc.mainView import mtmc.mainView
import mtmc.util.updateState import mtmc.updateState
import nl.astraeus.komp.HtmlBuilder import nl.astraeus.komp.HtmlBuilder
import nl.astraeus.komp.Komponent import nl.astraeus.komp.Komponent
import org.w3c.dom.HTMLSelectElement import org.w3c.dom.HTMLSelectElement

View File

@@ -5,4 +5,8 @@ actual fun requestAnimationFrame(action: (Double) -> Unit) {
error("requestAnimationFrame is not supported on JVM") error("requestAnimationFrame is not supported on JVM")
} }
actual fun immediateTimeout(action: (Double) -> Unit): Int = 0 actual fun immediateTimeout(action: (Double) -> Unit): Int = 0
actual fun time(): Double = System.nanoTime() * 1000.0
actual fun setTimeout(action: () -> Unit) {}