mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-14 15:06:05 +09:00
preemption proof-of-concept
This commit is contained in:
@@ -25,7 +25,7 @@ public class TerranBASIC {
|
|||||||
|
|
||||||
appConfig.setWindowedMode(WIDTH, HEIGHT);
|
appConfig.setWindowedMode(WIDTH, HEIGHT);
|
||||||
|
|
||||||
VM tbasvm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TBASRelBios.INSTANCE});
|
VM tbasvm = new VM("./assets", 64 << 10, new TheRealWorld(), new VMProgramRom[]{TBASRelBios.INSTANCE});
|
||||||
EmulInstance tbasrunner = new EmulInstance(tbasvm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", "assets/disk0", 560, 448);
|
EmulInstance tbasrunner = new EmulInstance(tbasvm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", "assets/disk0", 560, 448);
|
||||||
new Lwjgl3Application(new VMGUI(tbasrunner, WIDTH, HEIGHT), appConfig);
|
new Lwjgl3Application(new VMGUI(tbasrunner, WIDTH, HEIGHT), appConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
1
assets/disk1/!BOOTSEC
Normal file
1
assets/disk1/!BOOTSEC
Normal file
@@ -0,0 +1 @@
|
|||||||
|
let p=_BIOS.FIRST_BOOTABLE_PORT;com.sendMessage(p[0],"DEVRST\x17");com.sendMessage(p[0],'OPENR"WORKBENCH",'+p[1]);let r=com.getStatusCode(p[0]);if(0==r)if(com.sendMessage(p[0],"READ"),r=com.getStatusCode(p[0]),0==r){let g=com.pullMessage(p[0]);eval(g)}else println("I/O Error");else println("WORKBENCH not found");println("Shutting down...");println("It is now safe to turn off the power");
|
||||||
33
assets/disk1/WORKBENCH
Normal file
33
assets/disk1/WORKBENCH
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
con.move(2,1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let prog1 = "while(1){print(1);sys.sleep(600);}"
|
||||||
|
let prog2 = "while(1){print(2);sys.sleep(500);}"
|
||||||
|
|
||||||
|
let context1 = parallel.spawnNewContext()
|
||||||
|
let context2 = parallel.spawnNewContext()
|
||||||
|
|
||||||
|
let runner1 = parallel.attachProgram(context1, prog1)
|
||||||
|
let runner2 = parallel.attachProgram(context2, prog2)
|
||||||
|
|
||||||
|
con.move(2,1)
|
||||||
|
parallel.launch(runner1)
|
||||||
|
parallel.launch(runner2)
|
||||||
|
|
||||||
|
|
||||||
|
let exit = false
|
||||||
|
|
||||||
|
while (!exit) {
|
||||||
|
|
||||||
|
parallel.suspend(runner1)
|
||||||
|
parallel.suspend(runner2)
|
||||||
|
let [y,x] = con.getyx()
|
||||||
|
con.move(1,2)
|
||||||
|
print(`Used mem: ${sys.getUsedMem()} ; ${Math.random()} `)
|
||||||
|
con.move(y,x)
|
||||||
|
parallel.resume(runner1)
|
||||||
|
parallel.resume(runner2)
|
||||||
|
|
||||||
|
sys.spin()
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.torvald.tsvm
|
package net.torvald.tsvm
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import net.torvald.UnsafeHelper
|
import net.torvald.UnsafeHelper
|
||||||
import net.torvald.UnsafePtr
|
import net.torvald.UnsafePtr
|
||||||
import net.torvald.tsvm.peripheral.IOSpace
|
import net.torvald.tsvm.peripheral.IOSpace
|
||||||
@@ -19,6 +20,7 @@ class ErrorIllegalAccess(vm: VM, addr: Long) : RuntimeException("Segmentation fa
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class VM(
|
class VM(
|
||||||
|
val assetsDir: String,
|
||||||
_memsize: Long,
|
_memsize: Long,
|
||||||
val worldInterface: WorldInterface,
|
val worldInterface: WorldInterface,
|
||||||
val roms: Array<VMProgramRom?> // first ROM must contain the BIOS
|
val roms: Array<VMProgramRom?> // first ROM must contain the BIOS
|
||||||
@@ -26,8 +28,10 @@ class VM(
|
|||||||
|
|
||||||
val id = java.util.Random().nextInt()
|
val id = java.util.Random().nextInt()
|
||||||
|
|
||||||
|
internal val contexts = ArrayList<Thread>()
|
||||||
|
|
||||||
val memsize = minOf(USER_SPACE_SIZE, _memsize.toLong())
|
val memsize = minOf(USER_SPACE_SIZE, _memsize.toLong())
|
||||||
private val MALLOC_UNIT = 64
|
val MALLOC_UNIT = 64
|
||||||
private val mallocBlockSize = (memsize / MALLOC_UNIT).toInt()
|
private val mallocBlockSize = (memsize / MALLOC_UNIT).toInt()
|
||||||
|
|
||||||
internal val usermem = UnsafeHelper.allocate(memsize)
|
internal val usermem = UnsafeHelper.allocate(memsize)
|
||||||
@@ -57,7 +61,15 @@ class VM(
|
|||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun killAllContexts() {
|
||||||
|
contexts.forEach { it.interrupt() }
|
||||||
|
contexts.clear()
|
||||||
|
}
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
|
killAllContexts()
|
||||||
|
|
||||||
|
|
||||||
peripheralTable[0] = PeripheralEntry(
|
peripheralTable[0] = PeripheralEntry(
|
||||||
IOSpace(this),
|
IOSpace(this),
|
||||||
HW_RESERVE_SIZE,
|
HW_RESERVE_SIZE,
|
||||||
@@ -83,6 +95,7 @@ class VM(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun dispose() {
|
fun dispose() {
|
||||||
|
killAllContexts()
|
||||||
usermem.destroy()
|
usermem.destroy()
|
||||||
peripheralTable.forEach { it.peripheral?.dispose() }
|
peripheralTable.forEach { it.peripheral?.dispose() }
|
||||||
}
|
}
|
||||||
@@ -151,6 +164,7 @@ class VM(
|
|||||||
|
|
||||||
private val mallocMap = BitSet(mallocBlockSize)
|
private val mallocMap = BitSet(mallocBlockSize)
|
||||||
private val mallocSizes = HashMap<Int, Int>() // HashMap<Block Index, Block Count>
|
private val mallocSizes = HashMap<Int, Int>() // HashMap<Block Index, Block Count>
|
||||||
|
var allocatedBlockCount = 0; private set
|
||||||
|
|
||||||
private fun findEmptySpace(blockSize: Int): Int? {
|
private fun findEmptySpace(blockSize: Int): Int? {
|
||||||
var cursorHead = 0
|
var cursorHead = 0
|
||||||
@@ -180,6 +194,7 @@ class VM(
|
|||||||
val allocBlocks = ceil(size.toDouble() / MALLOC_UNIT).toInt()
|
val allocBlocks = ceil(size.toDouble() / MALLOC_UNIT).toInt()
|
||||||
val blockStart = findEmptySpace(allocBlocks) ?: throw OutOfMemoryError()
|
val blockStart = findEmptySpace(allocBlocks) ?: throw OutOfMemoryError()
|
||||||
|
|
||||||
|
allocatedBlockCount += allocBlocks
|
||||||
mallocSizes[blockStart] = allocBlocks
|
mallocSizes[blockStart] = allocBlocks
|
||||||
return blockStart * MALLOC_UNIT
|
return blockStart * MALLOC_UNIT
|
||||||
}
|
}
|
||||||
@@ -190,6 +205,7 @@ class VM(
|
|||||||
|
|
||||||
mallocMap.set(index, index + count, false)
|
mallocMap.set(index, index + count, false)
|
||||||
mallocSizes.remove(index)
|
mallocSizes.remove(index)
|
||||||
|
allocatedBlockCount -= count
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class VMNativePtr(val address: Int, val size: Int)
|
internal data class VMNativePtr(val address: Int, val size: Int)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package net.torvald.tsvm
|
package net.torvald.tsvm
|
||||||
|
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUlong
|
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUlong
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
@@ -140,6 +143,7 @@ class VMJSR223Delegate(val vm: VM) {
|
|||||||
}
|
}
|
||||||
fun waitForMemChg(addr: Int, andMask: Int) = waitForMemChg(addr, andMask, 0)
|
fun waitForMemChg(addr: Int, andMask: Int) = waitForMemChg(addr, andMask, 0)
|
||||||
|
|
||||||
|
fun getUsedMem() = vm.allocatedBlockCount * vm.MALLOC_UNIT
|
||||||
}
|
}
|
||||||
|
|
||||||
class VMSerialDebugger(val vm: VM) {
|
class VMSerialDebugger(val vm: VM) {
|
||||||
@@ -147,3 +151,24 @@ class VMSerialDebugger(val vm: VM) {
|
|||||||
fun println(s: Any?) = System.out.println("$s")
|
fun println(s: Any?) = System.out.println("$s")
|
||||||
fun printerr(s: Any?) = System.err.println("$s")
|
fun printerr(s: Any?) = System.err.println("$s")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Parallel(val vm: VM) {
|
||||||
|
fun spawnNewContext(): VMRunner {
|
||||||
|
return VMRunnerFactory(vm.assetsDir, vm, "js")
|
||||||
|
}
|
||||||
|
fun attachProgram(context: VMRunner, program: String): Thread {
|
||||||
|
Thread { context.eval(program) }.let {
|
||||||
|
vm.contexts.add(it)
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun launch(thread: Thread) {
|
||||||
|
thread.start()
|
||||||
|
}
|
||||||
|
fun suspend(thread: Thread) {
|
||||||
|
thread.suspend()
|
||||||
|
}
|
||||||
|
fun resume(thread: Thread) {
|
||||||
|
thread.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import javax.script.ScriptEngineManager
|
|||||||
abstract class VMRunner(val extension: String) {
|
abstract class VMRunner(val extension: String) {
|
||||||
abstract suspend fun executeCommand(command: String)
|
abstract suspend fun executeCommand(command: String)
|
||||||
abstract suspend fun evalGlobal(command: String)
|
abstract suspend fun evalGlobal(command: String)
|
||||||
|
abstract fun eval(command: String)
|
||||||
abstract fun close()
|
abstract fun close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ object VMRunnerFactory {
|
|||||||
bind.putMember("base64", Base64Delegate)
|
bind.putMember("base64", Base64Delegate)
|
||||||
bind.putMember("com", SerialHelperDelegate(vm))
|
bind.putMember("com", SerialHelperDelegate(vm))
|
||||||
bind.putMember("dma", DMADelegate(vm))
|
bind.putMember("dma", DMADelegate(vm))
|
||||||
|
bind.putMember("parallel", Parallel(vm))
|
||||||
|
|
||||||
val fr = FileReader("$assetsRoot/JS_INIT.js")
|
val fr = FileReader("$assetsRoot/JS_INIT.js")
|
||||||
val prg = fr.readText()
|
val prg = fr.readText()
|
||||||
@@ -83,6 +85,16 @@ object VMRunnerFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun eval(command: String) {
|
||||||
|
try {
|
||||||
|
context.eval("js", encapsulateJS(sanitiseJS(command)))
|
||||||
|
}
|
||||||
|
catch (e: javax.script.ScriptException) {
|
||||||
|
System.err.println("ScriptException from the script:")
|
||||||
|
System.err.println(command.substring(0, minOf(1024, command.length)))
|
||||||
|
throw e
|
||||||
|
} }
|
||||||
|
|
||||||
override suspend fun evalGlobal(command: String) {
|
override suspend fun evalGlobal(command: String) {
|
||||||
context.eval("js", "\"use strict\";" + sanitiseJS(command))
|
context.eval("js", "\"use strict\";" + sanitiseJS(command))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,15 +32,17 @@ public class AppLoader {
|
|||||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE});
|
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE});
|
||||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE});
|
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE});
|
||||||
// VM vm = new VM(128 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, WPBios.INSTANCE});
|
// VM vm = new VM(128 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, WPBios.INSTANCE});
|
||||||
VM vm = new VM(2048 << 10, new TheRealWorld(), new VMProgramRom[]{TsvmBios.INSTANCE});
|
VM vm = new VM("./assets", 2048 << 10, new TheRealWorld(), new VMProgramRom[]{TsvmBios.INSTANCE});
|
||||||
VM pipvm = new VM(4096, new TheRealWorld(), new VMProgramRom[]{PipBios.INSTANCE, PipROM.INSTANCE});
|
VM pipvm = new VM("./assets", 4096, new TheRealWorld(), new VMProgramRom[]{PipBios.INSTANCE, PipROM.INSTANCE});
|
||||||
|
|
||||||
EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", "assets/disk0", 560, 448);
|
String diskPath = "assets/disk1";
|
||||||
EmulInstance reference2 = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceLikeLCD", "assets/disk0", 560, 448);
|
|
||||||
EmulInstance term = new EmulInstance(vm, "net.torvald.tsvm.peripheral.Term", "assets/disk0", 720, 480);
|
EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", diskPath, 560, 448);
|
||||||
EmulInstance portable = new EmulInstance(vm, "net.torvald.tsvm.peripheral.CharacterLCDdisplay", "assets/disk0", 628, 302);
|
EmulInstance reference2 = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceLikeLCD", diskPath, 560, 448);
|
||||||
|
EmulInstance term = new EmulInstance(vm, "net.torvald.tsvm.peripheral.Term", diskPath, 720, 480);
|
||||||
|
EmulInstance portable = new EmulInstance(vm, "net.torvald.tsvm.peripheral.CharacterLCDdisplay", diskPath, 628, 302);
|
||||||
EmulInstance wp = new EmulInstance(vm, "net.torvald.tsvm.peripheral.WpTerm", "assets/wpdisk", 810, 360);
|
EmulInstance wp = new EmulInstance(vm, "net.torvald.tsvm.peripheral.WpTerm", "assets/wpdisk", 810, 360);
|
||||||
EmulInstance pip = new EmulInstance(pipvm, null, "assets/disk0", 640, 480, CollectionsKt.listOf(new Pair(1, new PeripheralEntry2(
|
EmulInstance pip = new EmulInstance(pipvm, null, diskPath, 640, 480, CollectionsKt.listOf(new Pair(1, new PeripheralEntry2(
|
||||||
32768L,
|
32768L,
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
|||||||
if (usememvwr) memvwr = Memvwr(vm)
|
if (usememvwr) memvwr = Memvwr(vm)
|
||||||
|
|
||||||
|
|
||||||
vmRunner = VMRunnerFactory("./assets", vm, "js")
|
vmRunner = VMRunnerFactory(vm.assetsDir, vm, "js")
|
||||||
coroutineJob = GlobalScope.launch {
|
coroutineJob = GlobalScope.launch {
|
||||||
vmRunner.executeCommand(vm.roms[0]!!.readAll())
|
vmRunner.executeCommand(vm.roms[0]!!.readAll())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user