/**
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies AG makes no warranties
* regarding the provided information. XLOG Technologies AG assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies AG.
*
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies AG. If the company was not the originator of some
* excerpts, XLOG Technologies AG has at least obtained the right to
* reproduce, change and translate the information.
*
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
*
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
*
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies AG.
*/
const MAX_TILT = 64;
let fs;
let workers;
if (typeof WorkerGlobalScope === 'undefined') {
fs = await import("node:fs");
workers = await import("node:worker_threads");
} else {
fs = undefined;
workers = undefined;
}
function CpuMap() {
this.code = null;
this.offset = null;
this.state = null;
}
function CpuWarp() {
this.map = null;
this.from = 0;
this.to = 0;
this.id = 0;
this.tilt = 0;
this.cmd = false;
this.buf = null;
}
let warp = new CpuWarp();
function run_warp(warp, from, to) {
let active = true;
while (active) {
active = false;
for (let i = (warp.cmd ? warp.id : from); i < to; i++) {
if (run_id(warp, i)) {
if (warp.cmd) {
warp.id = i;
return;
} else {
active = true;
}
}
}
}
}
function run_id(warp, id) {
let pi = warp.map;
let base = pi.offset[id];
let pc = pi.state[base-2];
let accu = pi.state[base-1];
for (let k = (warp.cmd ? warp.tilt : 0); k < MAX_TILT; k++) {
if (pc < pi.code.length) {
let instr = pi.code[pc];
let value = hack_get(warp, pi, instr, base, pc);
if (warp.cmd) {
pi.state[base-2] = pc;
pi.state[base-1] = accu;
warp.tilt = k;
return true;
} else {
accu = hack_fun(instr, accu, value);
hack_set(pi, instr, accu, base);
pc = pc+1+hack_jump(instr, accu);
}
} else {
pi.state[base-2] = pc;
pi.state[base-1] = accu;
return false;
}
}
pi.state[base-2] = pc;
pi.state[base-1] = accu;
return true;
}
function hack_get(warp, pi, instr, base, pc) {
let act = instr >> 20;
let obj = ((instr >> 10) & 0x03FF) - 512;
switch ((act >> 4) & 0x0F) {
case 0: /* get */
return pi.state[base+obj];
case 1: /* cst */
return obj;
case 2: /* tbl */
return pi.code[pc+1+obj];
case 3: /* set */
return 0;
case 4: /* out */
return 0;
case 5: /* in */
extra_outline(warp, pi, obj, base);
return 0;
default:
throw new Error("illegal mode");
}
}
function hack_fun(instr, accu, value) {
let act = instr >> 20;
switch (act >> 8) {
case 0: /* val */
return value;
case 1: /* acc */
return accu;
case 2: /* add */
return (value + accu) | 0;
case 3: /* sub */
return (value - accu) | 0;
case 4: /* mul */
return Math.imul(value, accu);
case 5: /* zdiv */
return (value / accu) | 0;
case 6: /* rem */
return value % accu;
default:
throw new Error("illegal fun");
}
}
function hack_set(pi, instr, accu, base) {
let act = instr >> 20;
let obj = ((instr >> 10) & 0x03FF) - 512;
switch ((act >> 4) & 0x0F) {
case 0: /* get */
return;
case 1: /* cst */
return;
case 2: /* tbl */
return;
case 3: /* set */
pi.state[base+obj] = accu;
return;
case 4: /* out */
thread_send({cmd: "output", arg: load_ints(obj, pi.state, base)});
return;
case 5: /* in */
return;
default:
throw new Error("illegal mode");
}
}
function hack_jump(instr, accu) {
let act = instr >> 20;
let rel = (instr & 0x03FF) - 512;
switch (act & 0x0F) {
case 0: /* true */
return rel;
case 1: /* eq */
return (accu === 0 ? rel : 0);
case 2: /* nq */
return (accu !== 0 ? rel : 0);
case 3: /* ls */
return (accu < 0 ? rel : 0);
case 4: /* gr */
return (accu > 0 ? rel : 0);
case 5: /* lq */
return (accu <= 0 ? rel : 0);
case 6: /* gq */
return (accu >= 0 ? rel : 0);
default:
throw new Error("illegal cond");
}
}
/******************************************************************/
/* π-WAM Store/Load */
/******************************************************************/
function store_ints(res, max, state, base) {
for (let i = 0; i < max; i++) {
if (i < res.length) {
state[base+i] = res[i];
} else {
state[base+i] = 0;
}
}
}
function load_ints(max, state, base) {
let res = new Array(max);
for (let i = 0; i < max; i++)
res[i] = pi.state[base+i]
return res;
}
function result_promise() {
return new Promise((resolve) => {
if (res !== undefined) {
let val = res;
res = undefined;
resolve(val);
} else {
func = resolve;
}
});
}
function extra_outline(warp, pi, obj, base) {
if (warp.cmd) {
store_ints(warp.buf, obj, pi.state, base);
warp.buf = null;
warp.cmd = false;
} else {
warp.cmd = true;
}
}
/******************************************************************/
/* Worker Utility */
/******************************************************************/
let pi = new CpuMap();
let func = undefined;
let res = undefined;
function thread_send(msg) {
if (fs !== undefined) {
workers.parentPort.postMessage(msg);
} else {
self.postMessage(msg);
}
}
async function thread_handler(msg) {
switch (msg.cmd) {
case "init":
pi.code = msg.code;
pi.offset = msg.offset;
pi.state = msg.state;
break;
case "run":
warp.map = pi;
warp.from = msg.at;
warp.to = msg.at+msg.len;
for (let i = warp.from; i < warp.to; i++)
warp.map.state[warp.map.offset[i]-3] = i;
run_warp(warp, warp.from, warp.to);
while (warp.cmd) {
thread_send({cmd: "input"});
warp.buf = await result_promise();
run_warp(warp, warp.from, warp.to);
}
thread_send({cmd: "finish"});
break;
case "result":
if (func !== undefined) {
let temp = func;
func = undefined;
temp(msg.arg);
} else {
res = msg.arg;
}
break;
default:
throw new Error("illegal cmd");
}
}
if (fs !== undefined) {
workers.parentPort.on('message', thread_handler);
} else {
self.onmessage = async (event) => thread_handler(event.data);
}