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");
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 = "";
}
}
}

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) {
UnsafeHelper.memcpy(
vm.usermem.ptr + fromAddr,

View File

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

View File

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

View File

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

View File

@@ -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', ' ')

View File

@@ -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() {

View File

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