diff --git a/FontROM7x14.kra b/FontROM7x14.kra index 30495fe..4c52aa9 100644 Binary files a/FontROM7x14.kra and b/FontROM7x14.kra differ diff --git a/FontROM7x7.kra b/FontROM7x7.kra new file mode 100644 index 0000000..beeadc7 --- /dev/null +++ b/FontROM7x7.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b2f064bae765a5bfa8ab3276ddc14d1c489bf6da7dafdce9feece509fcd8647 +size 54694 diff --git a/assets/JS_INIT.js b/assets/JS_INIT.js index be93bb3..21bd7ee 100644 --- a/assets/JS_INIT.js +++ b/assets/JS_INIT.js @@ -60,6 +60,12 @@ con.color_pair = function(fore, back) { // 0..255 print(String.fromCharCode(27,91)+"38;5;"+fore+"m"); print(String.fromCharCode(27,91)+"48;5;"+back+"m"); }; +con.clear = function() { + print(String.fromCharCode(27,91)+"2J"); +}; +con.curs_set = function(arg) { + print(String.fromCharCode(27,91)+"?25"+((arg == 0) ? "l" : "h")); +}; Object.freeze(con); // system management function var system = {}; diff --git a/assets/tvdos/fsh.js b/assets/tvdos/fsh.js new file mode 100644 index 0000000..e21abf5 --- /dev/null +++ b/assets/tvdos/fsh.js @@ -0,0 +1,10 @@ +graphics.setBackground(3,3,3); + +var _fsh = {}; +_fsh.titlebartex = new GL.Tex(2, 14, [254, 239, 254, 254, 253, 254, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 252, 253, 253, 252, 252, 252, 252, 251, 251, 251]); + +GL.drawTexPattern(_fsh.titlebartex, 0, 0, 560, 14); + +con.color_pair(254, 255); +con.clear(); +con.curs_set(0); diff --git a/assets/tvdos/gl.js b/assets/tvdos/gl.js new file mode 100644 index 0000000..bd23bdf --- /dev/null +++ b/assets/tvdos/gl.js @@ -0,0 +1,26 @@ +/* +TVDOS Graphics Library + +Has no affiliation with OpenGL by Khronos Group + */ + +var GL = {}; +GL.Tex = function(w, h, bytes) { + this.width = w; + this.height = h; + this.texData = bytes; +}; +GL.drawTexPattern = function(tex, x, y, width, height) { + for (var yy = 0; yy < height; yy++) { + for (var xx = 0; xx < width; xx++) { + var tx = xx % tex.width; + var ty = yy % tex.height; + var c = tex.texData[ty * tex.width + tx]; + graphics.plotPixel(x + xx, y + yy, c); + } + } +}; +GL.drawTexImage = function(tex, x, y) { + GL.drawTexPattern(tex, x, y, tex.width, tex.height); +}; +Object.freeze(GL); diff --git a/shell_mockup.kra b/shell_mockup.kra new file mode 100644 index 0000000..c1b55fd --- /dev/null +++ b/shell_mockup.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec7d818b19632a258a312d26a2a9cb35a2bd47a1a3f25650972d03c2e574f436 +size 293378 diff --git a/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt b/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt index 5af84b8..f3eb4f5 100644 --- a/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt +++ b/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt @@ -65,6 +65,20 @@ class GraphicsJSR223Delegate(val vm: VM) { return intArrayOf(-1, -1) } + fun setBackground(r: Int, g: Int, b: Int) { + getFirstGPU()?.let { + it.poke(250880, r.toByte()) + it.poke(250881, g.toByte()) + it.poke(250882, b.toByte()) + } + } + + fun clearText() { + getFirstGPU()?.let { + it.eraseInDisp(2) + } + } + /** * prints a char as-is; won't interpret them as an escape sequence */ diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index b4018d6..894c3df 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -58,16 +58,23 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() val bios = fr1.readText() fr1.close() + + val fr2 = FileReader("./assets/tvdos/gl.js") + val tvgl = fr2.readText() + fr2.close() + + //val fr = FileReader("./assets/tvdos/command.js") - val fr = FileReader("./assets/tbas/basic.js") + val fr = FileReader("./assets/tvdos/fsh.js") + //val fr = FileReader("./assets/tbas/basic.js") //val fr = FileReader("./assets/jscon.js") val prg = fr.readText() fr.close() vmRunner = VMRunnerFactory(vm, "js") coroutineJob = GlobalScope.launch { - vmRunner.executeCommand(sanitiseJS(bios)) - vmRunner.executeCommand(sanitiseJS(prg)) + vmRunner.evalGlobal(bios + "\n" + tvgl) + vmRunner.executeCommand(prg) } diff --git a/src/net/torvald/tsvm/VMRunnerFactory.kt b/src/net/torvald/tsvm/VMRunnerFactory.kt index 584128c..0e06f8c 100644 --- a/src/net/torvald/tsvm/VMRunnerFactory.kt +++ b/src/net/torvald/tsvm/VMRunnerFactory.kt @@ -11,6 +11,7 @@ import kotlin.test.assertNotNull abstract class VMRunner(val extension: String) { abstract suspend fun executeCommand(command: String) + abstract suspend fun evalGlobal(command: String) } @@ -36,6 +37,10 @@ object VMRunnerFactory { override suspend fun executeCommand(command: String) { vmLua.lua.load(command).call() } + + override suspend fun evalGlobal(command: String) { + TODO("Not yet implemented") + } } } "vt2" -> { @@ -47,6 +52,10 @@ object VMRunnerFactory { override suspend fun executeCommand(command: String) { engine.eval(command) } + + override suspend fun evalGlobal(command: String) { + TODO("Not yet implemented") + } } } else -> { @@ -76,7 +85,11 @@ object VMRunnerFactory { } override suspend fun executeCommand(command: String) { - engine.eval("\"use strict\";{$command}", context) + engine.eval("\"use strict\";" + sanitiseJS(toSingleLine(command)), context) + } + + override suspend fun evalGlobal(command: String) { + engine.eval("\"use strict\";" + toSingleLine(command), context) } } } @@ -84,7 +97,8 @@ object VMRunnerFactory { } } -} -fun toSingleLine(code: String) = code.replace(Regex("//[^\\n]*"), "").replace('\n', ' ') -fun sanitiseJS(code: String) = "eval('${toSingleLine(code).replace("\\", "\\\\")}')" + private fun toSingleLine(code: String) = code.replace(Regex("//[^\\n]*"), "").replace('\n', ' ') + private fun sanitiseJS(code: String) = "eval('${toSingleLine(code).replace("\\", "\\\\")}')" + +} diff --git a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index ee9fca8..4d8d902 100644 --- a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -276,6 +276,16 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false, lcdInvert: Boole override fun eraseInDisp(arg: Int) { when (arg) { + 2 -> { + val foreBits = ttyFore or ttyFore.shl(8) or ttyFore.shl(16) or ttyFore.shl(24) + val backBits = ttyBack or ttyBack.shl(8) or ttyBack.shl(16) or ttyBack.shl(24) + for (i in 0 until TEXT_COLS * TEXT_ROWS step 4) { + spriteAndTextArea.setInt(memTextForeOffset + i, foreBits) + spriteAndTextArea.setInt(memTextBackOffset + i, backBits) + spriteAndTextArea.setInt(memTextOffset + i, -1) + } + spriteAndTextArea.setShort(memTextCursorPosOffset, 0) + } else -> TODO() } } diff --git a/src/net/torvald/tsvm/vdc/Videotron2K.kt b/src/net/torvald/tsvm/vdc/Videotron2K.kt index b902dd9..eb7b2d5 100644 --- a/src/net/torvald/tsvm/vdc/Videotron2K.kt +++ b/src/net/torvald/tsvm/vdc/Videotron2K.kt @@ -224,6 +224,8 @@ class Videotron2K(var gpu: GraphicsAdapter?) { val wordsUpper = stmtUpper.split(reTokenizer) if (stmtUpper.startsWith("SCENE_")) { // indexed scene + if (currentScene != 0L) throw IllegalStateException("Line $lnum: Scenes cannot be nested") + val scenenumStr = stmt.substring(6) try { val scenenum = scenenumStr.toLong() @@ -235,6 +237,8 @@ class Videotron2K(var gpu: GraphicsAdapter?) { } } else if (stmtUpper.startsWith("SCENE ")) { // named scene + if (currentScene != 0L) throw IllegalStateException("Line $lnum: Scenes cannot be nested") + val sceneName = wordsUpper[1] if (sceneName.isNullOrBlank()) { throw IllegalArgumentException("Line $lnum: Illegal scene name on $stmt") @@ -246,9 +250,7 @@ class Videotron2K(var gpu: GraphicsAdapter?) { currentScene = registerNewVariable(sceneName) // scenes use same addr space as vars, to make things easier on the backend } else if (wordsUpper[0] == "END" && wordsUpper[1] == "SCENE") { // END SCENE - if (currentScene == 0L) { - throw IllegalArgumentException("Line $lnum: END SCENE is called without matching SCENE definition") - } + if (currentScene == 0L) throw IllegalArgumentException("Line $lnum: END SCENE is called without matching SCENE definition") scenes[currentScene] = sceneStatements.toTypedArray() @@ -532,7 +534,7 @@ object Command { instance.sleepLatch = true val timeTook = (System.nanoTime() - instance.performanceCounterTmr).toDouble() - instance.statsFrameTime = timeTook / 1000000000.0 + instance.statsFrameTime = timeTook * 0.000001 instance.performanceCounterTmr = System.nanoTime() } instSet[CMP shr 3] = { instance, args -> // CMP rA rB rC diff --git a/terranmon.txt b/terranmon.txt index 916c757..8e7bf7d 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -136,15 +136,15 @@ From the start of the memory space: write to this address FIRST and then write to "command" to execute the command 86 bytes *Unused* -IF graphics_mode THEN - (41 sprites : 260 bytes each -> 10660 bytes) - 0th sprite is always the GUI cursor +IF hires_text THEN + 418 bytes + *Unused* 2 bytes - Ob hv0000xy yyyyyyyy (h: horizontal flip, v: vertical flip, x: show/hide, y: y-position) - 2 bytes - 0b rr0000xx xxxxxxxx (r: rotation, x: x-position) - 256 bytes - 16x16 texture for the sprite + Cursor position in: (y*64 + x) + 5120 bytes + Text fore/back colours, 16 colours, LSB is foreground + 5120 bytes + Text buffer of 80x64 (7x7 character size) ELSE 2978 bytes *Unused* @@ -155,12 +155,24 @@ ELSE 2560 bytes Text background colours 2560 bytes - Text buffer of 70x32 (8x14 character size, and yes: actual character data is on the bottom) + Text buffer of 80x32 (7x14 character size, and yes: actual character data is on the bottom) FI 512 bytes Palette stored in following pattern: 0b rrrr gggg, 0b bbbb aaaa, .... Palette number 255 is always full transparent (bits being all zero) +Optional Sprite Card +16 bytes + Reserved +65520 bytes: + (0th sprite is always the GUI cursor, thus 251 sprites) + 2 bytes + Ob hv000pxy yyyyyyyy (p: 0 for above-text, 1 for below-text, h: horizontal flip, v: vertical flip, x: show/hide, y: y-position) + 2 bytes + 0b rr0000xx xxxxxxxx (r: rotation, x: x-position) + 256 bytes + 16x16 texture for the sprite + MMIO 0..1 RO