mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
TEVD DOM changes committing to the physical disk
This commit is contained in:
@@ -25,6 +25,7 @@ class VM(
|
||||
val worldInterface: WorldInterface,
|
||||
val roms: Array<VMProgramRom>, // first ROM must contain the BIOS
|
||||
_peripheralSlots: Int = 8,
|
||||
val watchdogs: HashMap<String, VMWatchdog>
|
||||
) {
|
||||
|
||||
val peripheralSlots = _peripheralSlots.coerceIn(1,8)
|
||||
|
||||
56
tsvm_core/src/net/torvald/tsvm/VMWatchdog.kt
Normal file
56
tsvm_core/src/net/torvald/tsvm/VMWatchdog.kt
Normal file
@@ -0,0 +1,56 @@
|
||||
package net.torvald.tsvm
|
||||
|
||||
import com.badlogic.gdx.utils.Queue
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VDUtil
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VirtualDisk
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2022-12-18.
|
||||
*/
|
||||
abstract class VMWatchdog {
|
||||
|
||||
/** Seconds between sleep */
|
||||
abstract val interval: Float
|
||||
protected var akku = 0f
|
||||
|
||||
open fun update(delta: Float) {
|
||||
akku += delta
|
||||
while (akku > interval) {
|
||||
consumeMessages()
|
||||
println("[${this.javaClass.simpleName}] boop!")
|
||||
akku -= interval
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun consumeMessages()
|
||||
abstract fun addMessage(message: Array<Any?>)
|
||||
|
||||
}
|
||||
|
||||
|
||||
object TevdSyncWatchdog : VMWatchdog() {
|
||||
override val interval = 5f
|
||||
|
||||
private val messageQueue = ArrayList<Pair<File, VirtualDisk>>()
|
||||
|
||||
override fun consumeMessages() {
|
||||
synchronized(this) {
|
||||
messageQueue.forEach { (outfile, dom) ->
|
||||
VDUtil.dumpToRealMachine(dom, outfile)
|
||||
println("[${this.javaClass.simpleName}] dump ${outfile.path}")
|
||||
}
|
||||
messageQueue.clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun addMessage(message: Array<Any?>) {
|
||||
val file = message[0] as File
|
||||
val dom = message[1] as VirtualDisk
|
||||
|
||||
val hasDup = messageQueue.fold(false) { acc, pair -> acc or (pair.first.path == file.path) }
|
||||
if (!hasDup) {
|
||||
messageQueue.add(file to dom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,42 +37,18 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
|
||||
private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please
|
||||
private var blockSendBuffer = ByteArray(1)
|
||||
private var blockSendCount = 0
|
||||
|
||||
private val hasChanges = AtomicBoolean(false)
|
||||
|
||||
|
||||
init {
|
||||
statusCode.set(TestDiskDrive.STATE_CODE_STANDBY)
|
||||
|
||||
if (!tevdPath.exists()) {
|
||||
throw FileNotFoundException("Disk file '${theTevdPath}' not found")
|
||||
}
|
||||
|
||||
Thread {
|
||||
while (vm.isRunning) {
|
||||
println("TevdCommitWatchdog ping")
|
||||
if (hasChanges.compareAndExchangeAcquire(true, false)) {
|
||||
printdbg("Disk has changes, committing... $theTevdPath")
|
||||
commit()
|
||||
}
|
||||
else {
|
||||
printdbg("Disk has no changes, skipping... $theTevdPath")
|
||||
}
|
||||
Thread.sleep(1000L * COMMIT_INTERVAL)
|
||||
}
|
||||
}.let {
|
||||
it.start()
|
||||
vm.contexts.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
/** How often the changes in DOM (disk object model) should be saved to the physical drive when there are changes. Seconds. */
|
||||
const val COMMIT_INTERVAL = 5
|
||||
}
|
||||
|
||||
fun commit() {
|
||||
VDUtil.dumpToRealMachine(DOM, tevdPath)
|
||||
fun notifyDiskCommit() {
|
||||
vm.watchdogs["TEVD_SYNC"]?.addMessage(arrayOf(tevdPath, DOM))
|
||||
}
|
||||
|
||||
|
||||
@@ -140,8 +116,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
|
||||
writeMode = false
|
||||
appendMode = false
|
||||
|
||||
printdbg("Raising HasChanges flag (end of write)")
|
||||
hasChanges.set(true)
|
||||
printdbg("Notifying disk commit (end of write)")
|
||||
notifyDiskCommit()
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -229,8 +205,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
|
||||
}
|
||||
try {
|
||||
file.delete()
|
||||
printdbg("Raising HasChanges flag (file deleted)")
|
||||
hasChanges.set(true)
|
||||
printdbg("Notifying disk commit (file deleted)")
|
||||
notifyDiskCommit()
|
||||
}
|
||||
catch (e: SecurityException) {
|
||||
statusCode.set(TestDiskDrive.STATE_CODE_SYSTEM_SECURITY_ERROR)
|
||||
@@ -365,8 +341,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
|
||||
val status = file.mkdir()
|
||||
statusCode.set(if (status) 0 else 1)
|
||||
if (status) {
|
||||
printdbg("Raising HasChanges flag (mkdir)")
|
||||
hasChanges.set(true)
|
||||
printdbg("Notifying disk commit (mkdir)")
|
||||
notifyDiskCommit()
|
||||
}
|
||||
}
|
||||
catch (e: SecurityException) {
|
||||
@@ -386,8 +362,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
|
||||
val f1 = file.createNewFile()
|
||||
statusCode.set(if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED)
|
||||
if (f1) {
|
||||
printdbg("Raising HasChanges flag (mkfile)")
|
||||
hasChanges.set(true)
|
||||
printdbg("Notifying disk commit (mkfile)")
|
||||
notifyDiskCommit()
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -411,8 +387,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
|
||||
val f1 = file.setLastModified(vm.worldInterface.currentTimeInMills())
|
||||
statusCode.set(if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED)
|
||||
if (f1) {
|
||||
printdbg("Raising HasChanges flag (touch)")
|
||||
hasChanges.set(true)
|
||||
printdbg("Notifying disk commit (touch)")
|
||||
notifyDiskCommit()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
|
||||
|
||||
class V2kRunTest : ApplicationAdapter() {
|
||||
|
||||
val vm = VM("./assets", 64.kB(), TheRealWorld(), arrayOf())
|
||||
val vm = VM("./assets", 64.kB(), TheRealWorld(), arrayOf(), watchdogs = hashMapOf())
|
||||
lateinit var gpu: GraphicsAdapter
|
||||
|
||||
lateinit var batch: SpriteBatch
|
||||
|
||||
@@ -8,6 +8,7 @@ import kotlin.collections.CollectionsKt;
|
||||
import net.torvald.tsvm.peripheral.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class AppLoader {
|
||||
|
||||
@@ -29,6 +30,8 @@ public class AppLoader {
|
||||
|
||||
appConfig.setWindowedMode(WIDTH, HEIGHT);
|
||||
|
||||
HashMap<String, VMWatchdog> watchdogs = new HashMap<>();
|
||||
watchdogs.put("TEVD_SYNC", TevdSyncWatchdog.INSTANCE);
|
||||
|
||||
|
||||
String diskPath = "assets/disk0";
|
||||
@@ -36,11 +39,11 @@ public class AppLoader {
|
||||
|
||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, BasicRom.INSTANCE});
|
||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE});
|
||||
// VM vm = new VM("./assets", 64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE}, 2);
|
||||
// VM vm = new VM("./assets", 64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE}, 2, watchdogs);
|
||||
// VM vm = new VM(128 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, WPBios.INSTANCE});
|
||||
VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{TsvmBios.INSTANCE}, 8);
|
||||
// VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{OpenBios.INSTANCE}, 8);
|
||||
// VM pipvm = new VM("./assets", 4096, new TheRealWorld(), new VMProgramRom[]{PipBios.INSTANCE, PipROM.INSTANCE}, 8);
|
||||
VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{TsvmBios.INSTANCE}, 8, watchdogs);
|
||||
// VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{OpenBios.INSTANCE}, 8, watchdogs);
|
||||
// VM pipvm = new VM("./assets", 4096, new TheRealWorld(), new VMProgramRom[]{PipBios.INSTANCE, PipROM.INSTANCE}, 8, watchdogs);
|
||||
|
||||
vm.getIO().getBlockTransferPorts()[0].attachDevice(new TestDiskDrive(vm, 0, diskPath));
|
||||
vm.getIO().getBlockTransferPorts()[1].attachDevice(new HttpModem(vm, 1024, -1));
|
||||
|
||||
@@ -5,14 +5,10 @@ import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.badlogic.gdx.graphics.*
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import com.badlogic.gdx.utils.JsonReader
|
||||
import com.badlogic.gdx.utils.JsonValue
|
||||
import com.badlogic.gdx.utils.JsonWriter
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import net.torvald.terrarum.FlippingSpriteBatch
|
||||
import net.torvald.terrarum.imagefont.TinyAlphNum
|
||||
import net.torvald.terrarum.utils.JsonFetcher
|
||||
@@ -64,6 +60,9 @@ class VMEmuExecutableWrapper(val windowWidth: Int, val windowHeight: Int, var pa
|
||||
*/
|
||||
class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: Int, var panelsY: Int, val diskPathRoot: String) : ApplicationAdapter() {
|
||||
|
||||
val TEVD_SYNC = TevdSyncWatchdog
|
||||
|
||||
val watchdogs = hashMapOf<String, VMWatchdog>("TEVD_SYNC" to TEVD_SYNC)
|
||||
|
||||
private data class VMRunnerInfo(val vm: VM, val name: String)
|
||||
|
||||
@@ -235,7 +234,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
|
||||
super.render()
|
||||
|
||||
val dt = Gdx.graphics.rawDeltaTime
|
||||
val dt = Gdx.graphics.deltaTime
|
||||
updateAkku += dt
|
||||
|
||||
var i = 0L
|
||||
@@ -246,6 +245,8 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
}
|
||||
|
||||
renderGame(dt)
|
||||
|
||||
watchdogs.forEach { (_, watchdog) -> watchdog.update(dt) }
|
||||
}
|
||||
|
||||
private fun reboot(vm: VM) {
|
||||
@@ -487,7 +488,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
val cardslots = json.getInt("cardslots")
|
||||
val roms = json.get("roms").iterator().map { VMProgramRom(it.asString()) }.toTypedArray()
|
||||
|
||||
val vm = VM(assetsDir, ramsize, TheRealWorld(), roms, cardslots)
|
||||
val vm = VM(assetsDir, ramsize, TheRealWorld(), roms, cardslots, watchdogs)
|
||||
|
||||
// install peripherals
|
||||
listOf("com1", "com2", "com3", "com4").map { json.get(it) }.forEachIndexed { index, jsonValue ->
|
||||
|
||||
@@ -144,7 +144,7 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
|
||||
super.render()
|
||||
|
||||
val dt = Gdx.graphics.rawDeltaTime
|
||||
val dt = Gdx.graphics.deltaTime
|
||||
updateAkku += dt
|
||||
|
||||
var i = 0L
|
||||
@@ -155,6 +155,8 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
}
|
||||
|
||||
renderGame(dt)
|
||||
|
||||
vm.watchdogs.forEach { (_, watchdog) -> watchdog.update(dt) }
|
||||
}
|
||||
|
||||
private fun updateGame(delta: Float) {
|
||||
|
||||
Reference in New Issue
Block a user