mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
basic interpreter wip
This commit is contained in:
40
assets/JS_INIT.js
Normal file
40
assets/JS_INIT.js
Normal 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);
|
||||
@@ -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
56
assets/tbas/basic.js
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -54,7 +54,7 @@ import kotlin.random.Random
|
||||
*/
|
||||
|
||||
class VM(
|
||||
_memsize: Int
|
||||
_memsize: Long
|
||||
) {
|
||||
|
||||
val id = java.util.Random().nextInt()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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', ' ')
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user