diff --git a/assets/JS_INIT.js b/assets/JS_INIT.js new file mode 100644 index 0000000..470b64d --- /dev/null +++ b/assets/JS_INIT.js @@ -0,0 +1,40 @@ +// standard print functions +function print(s) { + vm.print(s); +} +function println(s) { + if (typeof s == "undefined") + vm.print("\n"); + else + vm.println(s); +} +function read() { + return vm.read(); +} +// ncurses-like terminal control +var con = new Object(); +con.getch = function() { + return vm.readKey(); +}; +con.move = function(y, x) { + print(String.fromCharCode(27,91)+y+";"+x+"H"); +}; +con.addch = function(c) { + graphics.putSymbol(c); +}; +con.mvaddch = function(y, x, c) { + move(y, x); addch(c); +}; +con.getmaxyx = function() { + return graphics.getTermDimension(); +}; +con.getyx = function() { + return graphics.getCursorYX(); +}; +Object.freeze(con); +// system management function +var sys = new Object(); +sys.maxmem = function() { + return vm.peek(-65) | (vm.peek(-66) << 8) | (vm.peek(-67) << 16) | (vm.peek(-68) << 24); +}; +Object.freeze(sys); \ No newline at end of file diff --git a/assets/jscon.js b/assets/jscon.js index 594b9e2..d3d4a95 100644 --- a/assets/jscon.js +++ b/assets/jscon.js @@ -1,68 +1,70 @@ println("JS Console"); -var cmdHistory = []; // zeroth element is the oldest -var cmdHistoryScroll = 0; // 0 for outside-of-buffer, 1 for most recent +var _cmdHistory = []; // zeroth element is the oldest +var _cmdHistoryScroll = 0; // 0 for outside-of-buffer, 1 for most recent while (true) { print("JS> "); - var cmdbuf = ""; + var _cmdbuf = ""; while (true) { - var key = vm.readKey(); + var key = con.getch(); // printable chars if (key >= 32 && key <= 126) { - var s = String.fromCharCode(key); - cmdbuf += s; - print(s); + var __sss = String.fromCharCode(key); + _cmdbuf += __sss; + print(__sss); } // backspace - else if (key === 8 && cmdbuf.length > 0) { - cmdbuf = cmdbuf.substring(0, cmdbuf.length - 1); + else if (key === 8 && _cmdbuf.length > 0) { + _cmdbuf = _cmdbuf.substring(0, _cmdbuf.length - 1); print(String.fromCharCode(key)); } // enter else if (key === 10 || key === 13) { println(); try { - println(eval(cmdbuf)); + println(eval(_cmdbuf)); } catch (e) { println(e); } finally { - if (cmdbuf.trim().length > 0) - cmdHistory.push(cmdbuf); + if (_cmdbuf.trim().length > 0) + _cmdHistory.push(_cmdbuf); - cmdHistoryScroll = 0; + _cmdHistoryScroll = 0; break; } } // up arrow - else if (key === 19 && cmdHistory.length > 0 && cmdHistoryScroll < cmdHistory.length) { - cmdHistoryScroll += 1; + else if (key === 19 && _cmdHistory.length > 0 && _cmdHistoryScroll < _cmdHistory.length) { + _cmdHistoryScroll += 1; // back the cursor in order to type new cmd - for (xx = 0; xx < cmdbuf.length; xx++) print(String.fromCharCode(8)); - cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll]; + var __xx = 0; + for (__xx = 0; __xx < _cmdbuf.length; __xx++) print(String.fromCharCode(8)); + _cmdbuf = _cmdHistory[_cmdHistory.length - _cmdHistoryScroll]; // re-type the new command - print(cmdbuf); + print(_cmdbuf); } // down arrow else if (key === 20) { - if (cmdHistoryScroll > 0) { + if (_cmdHistoryScroll > 0) { // back the cursor in order to type new cmd - for (xx = 0; xx < cmdbuf.length; xx++) print(String.fromCharCode(8)); - cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll]; + var __xx = 0; + for (__xx = 0; __xx < _cmdbuf.length; __xx++) print(String.fromCharCode(8)); + _cmdbuf = _cmdHistory[_cmdHistory.length - _cmdHistoryScroll]; // re-type the new command - print(cmdbuf); + print(_cmdbuf); - cmdHistoryScroll -= 1; + _cmdHistoryScroll -= 1; } else { // back the cursor in order to type new cmd - for (xx = 0; xx < cmdbuf.length; xx++) print(String.fromCharCode(8)); - cmdbuf = ""; + for (__xx = 0; __xx < _cmdbuf.length; __xx++) print(String.fromCharCode(8)); + _cmdbuf = ""; } } } diff --git a/assets/tbas/basic.js b/assets/tbas/basic.js new file mode 100644 index 0000000..6bfcd54 --- /dev/null +++ b/assets/tbas/basic.js @@ -0,0 +1,56 @@ +var vmemsize = 60300; +var vmemused = 0; +var cmdbuf = []; // index: line number +var prompt = "Ok"; + +var linenumRe = /^[0-9]+ +[^0-9]/; + +println("Terran BASIC 1.0 "+vmemsize+" bytes free"); +println(prompt); + +var basicFunctions = new Object(); +basicFunctions._basicList = function(v, i, arr) { + if (i < 100) print(" "); + print(i); + print(" "); + println(v); +}; +basicFunctions.list = function(args) { + if (args.length == 1) { + cmdbuf.forEach(basicFunctions._basicList); + } + else if (args.length == 2) { + if (typeof cmdbuf[args[1]] != "undefined") + basicFunctions._basicList(cmdbuf[args[1]], args[1], undefined); + } + else { + var lastIndex = (args[2] === ".") ? cmdbuf.length - 1 : (args[2] | 0); + var i = 0; + for (i = args[1]; i <= lastIndex; i++) { + var cmd = cmdbuf[i]; + if (typeof cmd != "undefined") { + basicFunctions._basicList(cmd, i, cmdbuf); + } + } + } +}; + +while (true) { + var line = vm.read(); + line = line.trim(); + + if (linenumRe.test(line)) { + var i = line.indexOf(" "); + cmdbuf[line.slice(0, i)] = line.slice(i + 1, line.length); + } + else if (line.length > 0) { + try { + var cmd = line.split(" "); + basicFunctions[cmd[0]](cmd); + } + catch (e) { + println("Syntax error"); + } + println(prompt); + } +} \ No newline at end of file diff --git a/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt b/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt index 4a28852..3a76e4b 100644 --- a/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt +++ b/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt @@ -47,6 +47,35 @@ class GraphicsJSR223Delegate(val vm: VM) { } } + fun getPixelDimension(): IntArray { + getFirstGPU()?.let { return intArrayOf(it.framebuffer.width, it.framebuffer.height) } + return intArrayOf(-1, -1) + } + + fun getTermDimension(): IntArray { + getFirstGPU()?.let { return intArrayOf(it.TEXT_ROWS, it.TEXT_COLS) } + return intArrayOf(-1, -1) + } + + fun getCursorYX(): IntArray { + getFirstGPU()?.let { + val (cx, cy) = it.getCursorPos() + return intArrayOf(cy + 1, cx + 1) + } + return intArrayOf(-1, -1) + } + + /** + * prints a char as-is; won't interpret them as an escape sequence + */ + fun putSymbol(char: Byte) { + getFirstGPU()?.let { + val (cx, cy) = it.getCursorPos() + it.putChar(cx, cy, char) + it.setCursorPos(cx + 1, cy) + } + } + private fun GraphicsAdapter._loadbulk(fromAddr: Int, toAddr: Int, length: Int) { UnsafeHelper.memcpy( vm.usermem.ptr + fromAddr, diff --git a/src/net/torvald/tsvm/VM.kt b/src/net/torvald/tsvm/VM.kt index 2250df6..48b19ab 100644 --- a/src/net/torvald/tsvm/VM.kt +++ b/src/net/torvald/tsvm/VM.kt @@ -54,7 +54,7 @@ import kotlin.random.Random */ class VM( - _memsize: Int + _memsize: Long ) { val id = java.util.Random().nextInt() diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index 63b1488..279f346 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -16,7 +16,7 @@ import java.io.StringReader class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() { - val vm = VM(8192) + val vm = VM(1024.kB()) lateinit var gpu: GraphicsAdapter lateinit var batch: SpriteBatch @@ -52,7 +52,8 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() // TEST PRG //val fr = FileReader("./assets/tvdos/command.js") - val fr = FileReader("./assets/jscon.js") + //val fr = FileReader("./assets/jscon.js") + val fr = FileReader("./assets/tbas/basic.js") val prg = fr.readText() fr.close() diff --git a/src/net/torvald/tsvm/VMJSR223Delegate.kt b/src/net/torvald/tsvm/VMJSR223Delegate.kt index 4503030..06aba54 100644 --- a/src/net/torvald/tsvm/VMJSR223Delegate.kt +++ b/src/net/torvald/tsvm/VMJSR223Delegate.kt @@ -50,11 +50,35 @@ class VMJSR223Delegate(val vm: VM) { in 0x20..0x7E -> sb.append(key.toChar()) } } while (key != 13 && key != 10) - this.println() + this.print("\n") // printout \n inputStream.close() return sb.toString() } + + /** + * Read series of key inputs until Enter/Return key is pressed. Backspace will work but any other non-printable + * characters (e.g. arrow keys) won't work. + */ + fun readNoEcho(): String { + val inputStream = vm.getInputStream() + val sb = StringBuilder() + var key: Int + do { + key = inputStream.read() + + when (key) { + 8 -> if (sb.isNotEmpty()) sb.deleteCharAt(sb.lastIndex) + in 0x20..0x7E -> sb.append(key.toChar()) + } + } while (key != 13 && key != 10) + this.println() // printout \n + + inputStream.close() + return sb.toString() + } + + } class VMSerialDebugger(val vm: VM) { diff --git a/src/net/torvald/tsvm/VMRunnerFactory.kt b/src/net/torvald/tsvm/VMRunnerFactory.kt index 7d3d92e..df1bc8c 100644 --- a/src/net/torvald/tsvm/VMRunnerFactory.kt +++ b/src/net/torvald/tsvm/VMRunnerFactory.kt @@ -1,5 +1,6 @@ package net.torvald.tsvm +import java.io.FileInputStream import java.io.FileReader import javax.script.Compilable import javax.script.ScriptContext @@ -56,13 +57,16 @@ object VMRunnerFactory { bind.put("serial", VMSerialDebugger(vm)) if (extension == "js") { - engine.eval(toSingleLine(JS_INIT), context) + val fr = FileReader("./assets/JS_INIT.js") + val prg = fr.readText() + fr.close() + engine.eval(toSingleLine(prg), context) } } override suspend fun executeCommand(command: String) { //(engine as Compilable).compile(command).eval(context) // compiling does not work with bindings in kts - engine.eval(command, context) + engine.eval("\"use strict\";{$command}", context) } } } @@ -70,20 +74,6 @@ object VMRunnerFactory { } } - private val JS_INIT = """ -function print(s) { - vm.print(s); -} -function println(s) { - if (typeof s == "undefined") - vm.print("\n"); - else - vm.println(s); -} -function read() { - return vm.read(); -} -""" } fun toSingleLine(code: String) = code.replace(Regex("//[^\\n]*"), "").replace('\n', ' ') diff --git a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index 4c6e2b9..036e4f4 100644 --- a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -336,6 +336,18 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa } override fun sgrTwoArg(arg1: Int, arg2: Int) { + /* + Color table for default palette + + Black 240 + Red 160 + Green 30 + Yellow 230 + Blue 19 + Magenta 199 + Cyan 74 + White 254 + */ TODO("Not yet implemented") } @@ -347,7 +359,7 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa * @param arg1 y-position (row) * @param arg2 x-position (column) */ override fun cursorXY(arg1: Int, arg2: Int) { - setCursorPos(arg2, arg1) + setCursorPos(arg2 - 1, arg1 - 1) } override fun ringBell() { diff --git a/terranmon.txt b/terranmon.txt index bdf84aa..a6ebb53 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -56,6 +56,11 @@ MMIO usually unnecessary as such action must be automatically managed via LibGDX input processing. Stores ASCII code representing the character, plus: + (1..26: Ctrl+[alph]) + 3 : Ctrl+C + 4 : Ctrl+D + 8 : Backspace + (13: Return) 19: Up arrow 20: Down arrow 21: Left arrow