aboutsummaryrefslogtreecommitdiff
path: root/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js')
-rw-r--r--src/js/mklayout.js4
-rw-r--r--src/js/tinyvm.ts33
2 files changed, 31 insertions, 6 deletions
diff --git a/src/js/mklayout.js b/src/js/mklayout.js
index cf95091..8e41f41 100644
--- a/src/js/mklayout.js
+++ b/src/js/mklayout.js
@@ -38,8 +38,8 @@ for (let i = 0; i < args.length; i += 2) {
const mapPath = opt('--map') || path.join(root, 'build', 'core', 'map', 'memmap.h');
const outDir = opt('--out') || path.join(root, 'build', 'js');
-if (!fs.existsSync(mapPath)) {
- console.error('mklayout: no memory map at ' + mapPath + '; run `make` first');
+if (!fs.existsSync(mapPath) || !fs.statSync(mapPath).isFile()) {
+ console.error('mklayout: no memory map header at ' + mapPath + '; run `make` first');
process.exit(1);
}
fs.mkdirSync(outDir, { recursive: true });
diff --git a/src/js/tinyvm.ts b/src/js/tinyvm.ts
index 56fab70..f779487 100644
--- a/src/js/tinyvm.ts
+++ b/src/js/tinyvm.ts
@@ -135,6 +135,10 @@ export interface VMOptions {
buffered suspends the program instead of reading the end. resume() goes
on from there. eof() is true only once close() has been called. */
streaming?: boolean;
+ /** files: the file syscalls (open, read, write, close) reach the host's file
+ system through node's fs. off by default, so a vm inside the editor cannot
+ touch files; the command line turns it on, like the native vm. */
+ files?: boolean;
input?: string | Uint8Array;
onOutput?: (byte: number) => void;
onMessage?: (msg: string) => void;
@@ -181,6 +185,7 @@ export class TinyVM {
private enh: Enhancement[] = [];
public L: Layout;
private streaming: boolean = false;
+ private files: boolean = false;
private closed: boolean = false;
private blocked: boolean = false;
private rp: number = 0;
@@ -201,6 +206,7 @@ export class TinyVM {
throw new RangeError('invalid initial memory or growth limit');
}
this.streaming = !!options.streaming;
+ this.files = !!options.files && !!fs;
const buffer = new ArrayBuffer(this.mw * 8);
this.mem = new BigInt64Array(buffer);
this.bytes = new Uint8Array(buffer);
@@ -396,15 +402,33 @@ export class TinyVM {
private syscall(op: bigint, a: bigint, b: bigint, c: bigint): bigint {
const opNum = Number(op);
switch (opNum) {
- case 1: // open
- return -1n;
+ case 1: { // open(path, 0 read or 1 create/truncate for write): path is one character per word
+ if (!this.files) return -1n;
+ let path = '';
+ for (let at = Number(a); at >= 0 && at < this.mw && this.mem[at] !== 0n && path.length < 4096; at++) path += String.fromCharCode(Number(this.mem[at] & 0xFFn));
+ try { return BigInt(fs.openSync(path, b === 1n ? 'w' : 'r')); } catch { return -1n; }
+ }
case 2: // close
+ if (this.files && a > 2n) { try { fs.closeSync(Number(a)); } catch { return -1n; } }
return 0n;
- case 3: // read
- return 0n;
+ case 3: { // read(fd, buf, n): bytes stored one per word
+ if (!this.files || a <= 2n) return 0n;
+ const n = Number(c), at = Number(b);
+ if (n <= 0 || at < 0 || at > this.mw - n) return -1n;
+ const chunk = Buffer.alloc(n);
+ let got: number;
+ try { got = fs.readSync(Number(a), chunk, 0, n, null); } catch { return -1n; }
+ for (let i = 0; i < got; i++) this.mem[at + i] = BigInt(chunk[i]);
+ return BigInt(got);
+ }
case 4: { // write(fd, buf, n)
const n = Number(c);
const bufAddr = Number(b);
+ if (this.files && a > 2n) {
+ const chunk = Buffer.alloc(n);
+ for (let i = 0; i < n; i++) chunk[i] = Number(this.mem[bufAddr + i] & 0xFFn);
+ try { return BigInt(fs.writeSync(Number(a), chunk)); } catch { return -1n; }
+ }
for (let i = 0; i < n; i++) {
const ch = Number(this.mem[bufAddr + i] & 0xFFn);
this.put_c(ch);
@@ -974,6 +998,7 @@ if (typeof require === 'function' && typeof module !== 'undefined' && require.ma
return Number(value);
};
const vm = new TinyVM({
+ files: true,
memoryWords: environmentWords('TINYVM_MEMORY_WORDS'),
maxMemoryWords: environmentWords('TINYVM_MAX_MEMORY_WORDS'),
onOutput: (byte: number) => process.stdout.write(Buffer.from([byte])),