basic interpreter wip

This commit is contained in:
minjaesong
2020-05-20 05:37:36 +09:00
parent a1e053c53e
commit 904c080731
10 changed files with 205 additions and 46 deletions

40
assets/JS_INIT.js Normal file
View File

@@ -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);

View File

@@ -1,68 +1,70 @@
println("JS Console"); println("JS Console");
var cmdHistory = []; // zeroth element is the oldest var _cmdHistory = []; // zeroth element is the oldest
var cmdHistoryScroll = 0; // 0 for outside-of-buffer, 1 for most recent var _cmdHistoryScroll = 0; // 0 for outside-of-buffer, 1 for most recent
while (true) { while (true) {
print("JS> "); print("JS> ");
var cmdbuf = ""; var _cmdbuf = "";
while (true) { while (true) {
var key = vm.readKey(); var key = con.getch();
// printable chars // printable chars
if (key >= 32 && key <= 126) { if (key >= 32 && key <= 126) {
var s = String.fromCharCode(key); var __sss = String.fromCharCode(key);
cmdbuf += s; _cmdbuf += __sss;
print(s); print(__sss);
} }
// backspace // backspace
else if (key === 8 && cmdbuf.length > 0) { else if (key === 8 && _cmdbuf.length > 0) {
cmdbuf = cmdbuf.substring(0, cmdbuf.length - 1); _cmdbuf = _cmdbuf.substring(0, _cmdbuf.length - 1);
print(String.fromCharCode(key)); print(String.fromCharCode(key));
} }
// enter // enter
else if (key === 10 || key === 13) { else if (key === 10 || key === 13) {
println(); println();
try { try {
println(eval(cmdbuf)); println(eval(_cmdbuf));
} }
catch (e) { catch (e) {
println(e); println(e);
} }
finally { finally {
if (cmdbuf.trim().length > 0) if (_cmdbuf.trim().length > 0)
cmdHistory.push(cmdbuf); _cmdHistory.push(_cmdbuf);
cmdHistoryScroll = 0; _cmdHistoryScroll = 0;
break; break;
} }
} }
// up arrow // up arrow
else if (key === 19 && cmdHistory.length > 0 && cmdHistoryScroll < cmdHistory.length) { else if (key === 19 && _cmdHistory.length > 0 && _cmdHistoryScroll < _cmdHistory.length) {
cmdHistoryScroll += 1; _cmdHistoryScroll += 1;
// back the cursor in order to type new cmd // back the cursor in order to type new cmd
for (xx = 0; xx < cmdbuf.length; xx++) print(String.fromCharCode(8)); var __xx = 0;
cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll]; for (__xx = 0; __xx < _cmdbuf.length; __xx++) print(String.fromCharCode(8));
_cmdbuf = _cmdHistory[_cmdHistory.length - _cmdHistoryScroll];
// re-type the new command // re-type the new command
print(cmdbuf); print(_cmdbuf);
} }
// down arrow // down arrow
else if (key === 20) { else if (key === 20) {
if (cmdHistoryScroll > 0) { if (_cmdHistoryScroll > 0) {
// back the cursor in order to type new cmd // back the cursor in order to type new cmd
for (xx = 0; xx < cmdbuf.length; xx++) print(String.fromCharCode(8)); var __xx = 0;
cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll]; for (__xx = 0; __xx < _cmdbuf.length; __xx++) print(String.fromCharCode(8));
_cmdbuf = _cmdHistory[_cmdHistory.length - _cmdHistoryScroll];
// re-type the new command // re-type the new command
print(cmdbuf); print(_cmdbuf);
cmdHistoryScroll -= 1; _cmdHistoryScroll -= 1;
} }
else { else {
// back the cursor in order to type new cmd // back the cursor in order to type new cmd
for (xx = 0; xx < cmdbuf.length; xx++) print(String.fromCharCode(8)); for (__xx = 0; __xx < _cmdbuf.length; __xx++) print(String.fromCharCode(8));
cmdbuf = ""; _cmdbuf = "";
} }
} }
} }

56
assets/tbas/basic.js Normal file
View File

@@ -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);
}
}

View File

@@ -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) { private fun GraphicsAdapter._loadbulk(fromAddr: Int, toAddr: Int, length: Int) {
UnsafeHelper.memcpy( UnsafeHelper.memcpy(
vm.usermem.ptr + fromAddr, vm.usermem.ptr + fromAddr,

View File

@@ -54,7 +54,7 @@ import kotlin.random.Random
*/ */
class VM( class VM(
_memsize: Int _memsize: Long
) { ) {
val id = java.util.Random().nextInt() val id = java.util.Random().nextInt()

View File

@@ -16,7 +16,7 @@ import java.io.StringReader
class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() { class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() {
val vm = VM(8192) val vm = VM(1024.kB())
lateinit var gpu: GraphicsAdapter lateinit var gpu: GraphicsAdapter
lateinit var batch: SpriteBatch lateinit var batch: SpriteBatch
@@ -52,7 +52,8 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
// TEST PRG // TEST PRG
//val fr = FileReader("./assets/tvdos/command.js") //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() val prg = fr.readText()
fr.close() fr.close()

View File

@@ -50,11 +50,35 @@ class VMJSR223Delegate(val vm: VM) {
in 0x20..0x7E -> sb.append(key.toChar()) in 0x20..0x7E -> sb.append(key.toChar())
} }
} while (key != 13 && key != 10) } while (key != 13 && key != 10)
this.println() this.print("\n") // printout \n
inputStream.close() inputStream.close()
return sb.toString() 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) { class VMSerialDebugger(val vm: VM) {

View File

@@ -1,5 +1,6 @@
package net.torvald.tsvm package net.torvald.tsvm
import java.io.FileInputStream
import java.io.FileReader import java.io.FileReader
import javax.script.Compilable import javax.script.Compilable
import javax.script.ScriptContext import javax.script.ScriptContext
@@ -56,13 +57,16 @@ object VMRunnerFactory {
bind.put("serial", VMSerialDebugger(vm)) bind.put("serial", VMSerialDebugger(vm))
if (extension == "js") { 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) { override suspend fun executeCommand(command: String) {
//(engine as Compilable).compile(command).eval(context) // compiling does not work with bindings in kts //(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', ' ') fun toSingleLine(code: String) = code.replace(Regex("//[^\\n]*"), "").replace('\n', ' ')

View File

@@ -336,6 +336,18 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
} }
override fun sgrTwoArg(arg1: Int, arg2: Int) { 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") 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 arg1 y-position (row)
* @param arg2 x-position (column) */ * @param arg2 x-position (column) */
override fun cursorXY(arg1: Int, arg2: Int) { override fun cursorXY(arg1: Int, arg2: Int) {
setCursorPos(arg2, arg1) setCursorPos(arg2 - 1, arg1 - 1)
} }
override fun ringBell() { override fun ringBell() {

View File

@@ -56,6 +56,11 @@ MMIO
usually unnecessary as such action must be automatically managed via LibGDX usually unnecessary as such action must be automatically managed via LibGDX
input processing. input processing.
Stores ASCII code representing the character, plus: Stores ASCII code representing the character, plus:
(1..26: Ctrl+[alph])
3 : Ctrl+C
4 : Ctrl+D
8 : Backspace
(13: Return)
19: Up arrow 19: Up arrow
20: Down arrow 20: Down arrow
21: Left arrow 21: Left arrow