diff --git a/assets/tbas/basic.js b/assets/tbas/basic.js index 64570ef..94f833a 100644 --- a/assets/tbas/basic.js +++ b/assets/tbas/basic.js @@ -253,6 +253,21 @@ basicInterpreterStatus.builtin = { if (isNaN(lh) || isNaN(rh)) throw lang.illegalType(lnum); sys.poke(lh, rh); +}, +"GOTO" : function(lnum, args) { + if (args.length != 1) throw lang.syntaxfehler(lnum); + var lh = resolve(args[0]); + if (lh === undefined) throw lang.refError(lnum); + if (isNaN(lh)) throw lang.illegalType(lnum); + return lh; +}, +"GOSUB" : function(lnum, args) { + if (args.length != 1) throw lang.syntaxfehler(lnum); + var lh = resolve(args[0]); + if (lh === undefined || rh === undefined) throw lang.refError(lnum); + if (isNaN(lh)) throw lang.illegalType(lnum); + basicInterpreterStatus.gosubStack.push(lnum + 1); + return lh; } }; Object.freeze(basicInterpreterStatus.builtin); @@ -757,7 +772,7 @@ for input "DEFUN sinc(x) = sin(x) / x" "quote" == state || "number" == state || "bool" == state || "literal" == state; } - var _debugSyntaxAnalysis = true; + var _debugSyntaxAnalysis = false; if (_debugSyntaxAnalysis) println("@@ SYNTAX ANALYSIS @@"); @@ -1012,8 +1027,14 @@ function JStoBASICtype(object) { else if (object === undefined) return "null"; else throw "InternalError: un-translatable object with typeof "+(typeof object)+"\n"+object; } +function SyntaxTreeReturnObj(type, value, nextLine) { + this.type = type; + this.value = value; + this.nextLine = nextLine; +} +basicFunctions._gotoCmds = { GOTO:1, GOSUB:1 }; basicFunctions._executeSyntaxTree = function(lnum, syntaxTree, recDepth) { - var _debugExec = true; + var _debugExec = false; var recWedge = "> ".repeat(recDepth); if (_debugExec) serial.println(recWedge+"@@ EXECUTE @@"); @@ -1023,26 +1044,32 @@ basicFunctions._executeSyntaxTree = function(lnum, syntaxTree, recDepth) { else if (syntaxTree.type == "function" || syntaxTree.type == "operator") { if (_debugExec) serial.println(recWedge+"function|operator"); if (_debugExec) serial.println(recWedge+syntaxTree.toString()); - var func = basicInterpreterStatus.builtin[syntaxTree.value.toUpperCase()]; + var funcName = syntaxTree.value.toUpperCase(); + var func = basicInterpreterStatus.builtin[funcName]; var args = syntaxTree.leaves.map(function(it) { return basicFunctions._executeSyntaxTree(lnum, it, recDepth + 1); }); if (_debugExec) { - serial.println(recWedge+"fn call name: "+syntaxTree.value.toUpperCase()); + serial.println(recWedge+"fn call name: "+funcName); serial.println(recWedge+"fn call args: "+(args.map(function(it) { return it.type+" "+it.value; })).join(", ")); } var funcCallResult = func(lnum, args); - return { type:JStoBASICtype(funcCallResult), value:funcCallResult }; + + return new SyntaxTreeReturnObj( + JStoBASICtype(funcCallResult), + funcCallResult, + (basicFunctions._gotoCmds[funcName] !== undefined) ? funcCallResult : lnum + 1 + ); } else if (syntaxTree.type == "number") { if (_debugExec) serial.println(recWedge+"number"); - return { type:syntaxTree.type, value:+(syntaxTree.value) }; + return new SyntaxTreeReturnObj(syntaxTree.type, +(syntaxTree.value), lnum + 1); } else if (syntaxTree.type == "string" || syntaxTree.type == "literal") { - if (_debugExec) serial.println(recWedge+"string|operator"); - return { type:syntaxTree.type, value:syntaxTree.value }; + if (_debugExec) serial.println(recWedge+"string|literal"); + return new SyntaxTreeReturnObj(syntaxTree.type, syntaxTree.value, lnum + 1); } else if (syntaxTree.type == "null") { - return basicFunctions._executeSyntaxTree(lnum, syntaxTree.leaves[0], recDepth + 1); + return new basicFunctions._executeSyntaxTree(lnum, syntaxTree.leaves[0], recDepth + 1); } else { serial.println(recWedge+"Parse error in "+lnum); @@ -1052,7 +1079,7 @@ basicFunctions._executeSyntaxTree = function(lnum, syntaxTree, recDepth) { }; // @returns: line number for the next command, normally (lnum + 1); if GOTO or GOSUB was met, returns its line number basicFunctions._interpretLine = function(lnum, cmd) { - var _debugprintHighestLevel = true; + var _debugprintHighestLevel = false; // TOKENISE var tokenisedObject = basicFunctions._tokenise(lnum, cmd); @@ -1068,12 +1095,12 @@ basicFunctions._interpretLine = function(lnum, cmd) { if (_debugprintHighestLevel) serial.println("Final syntax tree:"); if (_debugprintHighestLevel) serial.println(syntaxTree.toString()); - basicFunctions._executeSyntaxTree(lnum, syntaxTree, 0); + var execResult = basicFunctions._executeSyntaxTree(lnum, syntaxTree, 0); // EXECUTE - return lnum + 1; + return execResult.nextLine; }; // end INTERPRETLINE @@ -1143,6 +1170,9 @@ basicFunctions.fre = function(args) { basicFunctions.run = function(args) { // RUN function var linenumber = 1; var oldnum = 1; + + var countup = 0; + do { if (cmdbuf[linenumber] !== undefined) { oldnum = linenumber; @@ -1151,7 +1181,8 @@ basicFunctions.run = function(args) { // RUN function else { linenumber += 1; } - if (con.hitterminate()) { + countup += 1; + if (con.hitterminate() || countup >= 100) { println("Break in "+oldnum); break; } diff --git a/guicursor.kra b/guicursor.kra index 1d35fb9..f19c277 100644 --- a/guicursor.kra +++ b/guicursor.kra @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0df1e8c678058e829058c3244e535363a3e552f43918d94df13ecb726b74050 -size 63938 +oid sha256:92a751f926661ee1a57e7949a7693d6421b908e5fe5986352fc4f30e8fb1e9ca +size 495180 diff --git a/src/net/torvald/tsvm/peripheral/TTY.kt b/src/net/torvald/tsvm/peripheral/TTY.kt new file mode 100644 index 0000000..1269b4d --- /dev/null +++ b/src/net/torvald/tsvm/peripheral/TTY.kt @@ -0,0 +1,245 @@ +package net.torvald.tsvm.peripheral + +import com.badlogic.gdx.graphics.Texture +import net.torvald.UnsafeHelper +import net.torvald.tsvm.VM +import java.io.InputStream +import java.io.OutputStream + +class TTY(val vm: VM) : GlassTty(TEXT_ROWS, TEXT_COLS), PeriBase { + + companion object { + const val TEXT_ROWS = 25 + const val TEXT_COLS = 80 + } + + private val chrrom = Texture("./tty.png") + private val textBuffer = UnsafeHelper.allocate(TEXT_ROWS * TEXT_COLS * 2L) + override var rawCursorPos = 0 + + private val TEXT_AREA_SIZE = TEXT_COLS * TEXT_ROWS + private val memTextOffset = 0L + private val memTextAttrOffset = TEXT_AREA_SIZE.toLong() + + override var ttyFore = 0 // 0: normal, 1: intense, 2: dim + override var ttyBack: Int + get() = 0 + set(value) {} + var ttyInv = false + override var blinkCursor = true + override var ttyRawMode = false + + override fun getCursorPos() = rawCursorPos % TEXT_COLS to rawCursorPos / TEXT_COLS + /** + * Think of it as a real paper tty; + * setCursorPos must "wrap" the cursor properly when x-value goes out of screen bound. + * For y-value, only when y < 0, set y to zero and don't care about the y-value goes out of bound. + */ + override fun setCursorPos(x: Int, y: Int) { + var newx = x + var newy = y + + if (newx >= TEXT_COLS) { + newx = 0 + newy += 1 + } + else if (newx < 0) { + newx = 0 + } + + if (newy < 0) { + newy = 0 // DON'T SCROLL when cursor goes ABOVE the screen + } + + rawCursorPos = toTtyTextOffset(newx, newy) + } + private fun toTtyTextOffset(x: Int, y: Int) = y * TEXT_COLS + x + + + override fun peek(addr: Long): Byte? { + TODO("Not yet implemented") + } + + override fun poke(addr: Long, byte: Byte) { + TODO("Not yet implemented") + } + + override fun mmio_read(addr: Long): Byte? { + TODO("Not yet implemented") + } + + override fun mmio_write(addr: Long, byte: Byte) { + TODO("Not yet implemented") + } + + override fun getVM() = vm + + override fun putChar(x: Int, y: Int, text: Byte, foreColour: Byte, backColour: Byte) { + val textOff = toTtyTextOffset(x, y) + textBuffer[memTextAttrOffset + textOff] = 0 + textBuffer[memTextOffset + textOff] = text + } + + override fun resetTtyStatus() { + ttyFore = 0 + ttyInv = false + } + + override fun cursorUp(arg: Int) { + val (x, y) = getCursorPos() + setCursorPos(x, y - arg) + } + + override fun cursorDown(arg: Int) { + val (x, y) = getCursorPos() + val newy = y + arg + setCursorPos(x, if (newy >= TEXT_ROWS) TEXT_ROWS - 1 else newy) + } + + override fun cursorFwd(arg: Int) { + val (x, y) = getCursorPos() + setCursorPos(x + arg, y) + } + + override fun cursorBack(arg: Int) { + val (x, y) = getCursorPos() + setCursorPos(x - arg, y) + } + + override fun cursorNextLine(arg: Int) { + val (_, y) = getCursorPos() + val newy = y + arg + setCursorPos(0, if (newy >= TEXT_ROWS) TEXT_ROWS - 1 else newy) + if (newy >= TEXT_ROWS) { + scrollUp(newy - TEXT_ROWS + 1) + } + } + + override fun cursorPrevLine(arg: Int) { + val (_, y) = getCursorPos() + setCursorPos(0, y - arg) + } + + override fun cursorX(arg: Int) { + val (_, y) = getCursorPos() + setCursorPos(arg, y) + } + + override fun eraseInDisp(arg: Int) { + when (arg) { + else -> TODO() + } + } + + override fun eraseInLine(arg: Int) { + when (arg) { + else -> TODO() + } + } + + override fun sgrOneArg(arg: Int) { + TODO("Not yet implemented") + } + + override fun sgrTwoArg(arg1: Int, arg2: Int) { + TODO("Not yet implemented") + } + + override fun sgrThreeArg(arg1: Int, arg2: Int, arg3: Int) { + TODO("Not yet implemented") + } + + override fun cursorXY(arg1: Int, arg2: Int) { + TODO("Not yet implemented") + } + + override fun ringBell() { + TODO("Not yet implemented") + } + + override fun insertTab() { + TODO("Not yet implemented") + } + + override fun crlf() { + TODO("Not yet implemented") + } + + override fun backspace() { + TODO("Not yet implemented") + } + + override fun privateSeqH(arg: Int) { + TODO("Not yet implemented") + } + + override fun privateSeqL(arg: Int) { + TODO("Not yet implemented") + } + + override fun getPrintStream(): OutputStream { + TODO("Not yet implemented") + } + + override fun getErrorStream(): OutputStream { + TODO("Not yet implemented") + } + + override fun getInputStream(): InputStream { + TODO("Not yet implemented") + } + + override fun putKey(key: Int) { + vm.poke(-39, key.toByte()) + } + + /** + * @return key code in 0..255 (TODO: JInput Keycode or ASCII-Code?) + */ + override fun takeKey(): Int { + return vm.peek(-38)!!.toInt().and(255) + } + + /** New lines are added at the bottom */ + override fun scrollUp(arg: Int) { + val displacement = arg.toLong() * TEXT_COLS + UnsafeHelper.memcpy( + textBuffer.ptr + memTextOffset + displacement, + textBuffer.ptr + memTextOffset, + TEXT_AREA_SIZE - displacement + ) + UnsafeHelper.memcpy( + textBuffer.ptr + memTextAttrOffset + displacement, + textBuffer.ptr + memTextAttrOffset, + TEXT_AREA_SIZE - displacement + ) + for (i in 0 until displacement) { + textBuffer[memTextOffset + TEXT_AREA_SIZE - displacement + i] = 0 + textBuffer[memTextAttrOffset + TEXT_AREA_SIZE - displacement + i] = ttyFore.toByte() + } + } + + /** New lines are added at the top */ + override fun scrollDown(arg: Int) { + val displacement = arg.toLong() * TEXT_COLS + UnsafeHelper.memcpy( + textBuffer.ptr + memTextOffset, + textBuffer.ptr + memTextOffset + displacement, + TEXT_AREA_SIZE - displacement + ) + UnsafeHelper.memcpy( + textBuffer.ptr + memTextAttrOffset, + textBuffer.ptr + memTextAttrOffset + displacement, + TEXT_AREA_SIZE - displacement + ) + for (i in 0 until displacement) { + textBuffer[memTextOffset + TEXT_AREA_SIZE + i] = 0 + textBuffer[memTextAttrOffset + TEXT_AREA_SIZE + i] = ttyFore.toByte() + } + } + + + override fun dispose() { + chrrom.dispose() + } +} \ No newline at end of file diff --git a/tty.kra b/tty.kra new file mode 100644 index 0000000..4f90460 --- /dev/null +++ b/tty.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd6045f47f85bea71f4eb6896bd15618e0931abb258eaa948c72dba713eede84 +size 534281 diff --git a/tty.png b/tty.png new file mode 100644 index 0000000..e0e42b1 Binary files /dev/null and b/tty.png differ