aboutsummaryrefslogtreecommitdiff
path: root/src/micro/micro.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/micro/micro.c')
-rw-r--r--src/micro/micro.c643
1 files changed, 643 insertions, 0 deletions
diff --git a/src/micro/micro.c b/src/micro/micro.c
new file mode 100644
index 0000000..4542bd8
--- /dev/null
+++ b/src/micro/micro.c
@@ -0,0 +1,643 @@
+/* portable microcore. the base machine from core.c — memory, the two stacks,
+ the 22 opcodes, call/ret, and the genome — without the jit, llvm, o3, or
+ the enhancement optimizer.
+
+ `/` `%` `<<` and every other custom opcode are installed at runtime
+ (opcode 21). the genome's own idiv, imod, ishl, lfa and outside arrive
+ that way, and so do programs that declare enhance. this file lowers a
+ recipe or a straight-line vm body into a flat list of micro-ops and runs
+ that list. there is no constant folding and no multiply-high rewrite of
+ division; the results match, the list is just longer. install kind 2 is
+ an o3 region: it is accepted and ignored, and the ordinary bytecode of
+ that region runs instead, which is the same program the full interpreter
+ would run if the region did not lift.
+
+ no libc in this file. stdint.h is freestanding. 64-bit division is the
+ compiler's runtime. bss must be zero before micro_run.
+
+ memory size and the map come from memmap.h (the default is 1,048,576
+ words). a smaller board compiles this file against the headers src/tools/genmap
+ writes, and against the genome built for that same map. */
+#include <stdint.h>
+#ifdef USE_LSP_GENOME
+#include "lsp_genome.h"
+#elif defined(GENOME_H)
+#include GENOME_H
+#else
+#include "genome.h"
+#endif
+#include "micro_port.h"
+#include "fops.h"
+#include "bops.h"
+#define NENH 512
+#define XOP0 22
+#define XPOOL 4096
+#define MAXOPS 128
+#define MAXV 256
+
+enum {
+ U_LI, U_MOV, U_ADD, U_SUB, U_MUL, U_SDIV, U_UDIV, U_SREM, U_UREM,
+ U_AND, U_OR, U_XOR, U_SHL, U_SHR, U_SAR, U_NEG, U_NOT,
+ U_LT, U_LTU, U_EQ, U_NE, U_MIN, U_MAX, U_MINU, U_MAXU,
+ U_MULHS, U_MULHU, U_CLZ, U_ABS, U_SEL, U_LD, U_ST,
+ U_CTZ, U_POPCNT, U_BSWAP, U_ROTL, U_ROTR, U_F2S, U_S2F, U_N
+};
+static const char *const uname[U_N] = {
+ "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem",
+ "and", "or", "xor", "shl", "shr", "sar", "neg", "not",
+ "lt", "ltu", "eq", "ne", "min", "max", "minu", "maxu",
+ "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st",
+ "ctz", "popcnt", "bswap", "rotl", "rotr", "f2s", "s2f"
+};
+static const signed char uarity[U_N] = {
+ 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2,
+ 1, 1, 1, 2, 2, 1, 1
+};
+
+typedef struct {
+ unsigned char op, a, b, c;
+ unsigned short d;
+ Cell imm;
+} Xop;
+typedef struct {
+ unsigned char live, nin, nout;
+ unsigned short n, at, nv;
+ unsigned short out[8];
+} Enh;
+
+/* the machine: m is mw words, and grows on demand through the port (micro_mem_alloc).
+ every access goes through m and mw, which the port's memory may move under. */
+static Cell *m;
+static UCell mw;
+static UCell memory_initial = MICRO_MW_INIT, memory_limit = MICRO_MW_LIMIT;
+int micro_memory_config(UCell initial, UCell limit)
+{
+ if (m || initial < MICRO_MW_INIT || limit < initial || limit > 0x7fffffff) return 0;
+ memory_initial = initial; memory_limit = limit; return 1;
+}
+static Xop xops[XPOOL];
+static int xuse;
+static Enh enh[NENH];
+static Xop build[MAXOPS];
+static int bn, bnv, berr;
+static Cell xv[MAXV];
+static int jflag;
+static int jknown; /* 0 until something asks for the end-of-input flag: the probe reads the port, so it waits for the first ask */
+
+UCell micro_mem_words(void) { return mw; }
+static int inmem(Cell a) { return (UCell)a < mw; }
+static Cell ld(Cell a) { return inmem(a) ? m[a] : 0; }
+static Cell b_load(Cell addr, int n)
+{
+ if (!bop_ok(addr, n, mw)) return 0;
+ return bop_load(m, addr, n);
+}
+static void b_store(Cell addr, Cell val, int n)
+{
+ Cell out[2], w0;
+ int nw, i;
+ if (!bop_ok(addr, n, mw)) return;
+ nw = bop_merge(m, addr, val, n, out, &w0);
+ for (i = 0; i < nw; i++) if (inmem(w0 + i)) m[w0 + i] = out[i];
+}
+
+/* the run's sentinel pcs: each names why a run stopped that is not a halt. they
+ are negative, so no address in memory is one of them. */
+#define PC_DATA_OVERFLOW (-3)
+#define PC_RETURN_OVERFLOW (-4)
+#define PC_PAST_END (-5)
+#define PC_BAD_OP (-6)
+#define PC_HALT (-7) /* the zero word: the program's halt, not an error */
+static int bad_op;
+
+/* grows the machine to at least words words, through the port. the contents stay,
+ and the new words are zero. 1 on success, 0 when the port has no more memory. */
+static int mem_grow_words(UCell words)
+{
+ Cell *n;
+ if (words <= mw && m) return 1;
+ if (words > memory_limit) return 0;
+ n = micro_mem_alloc(words);
+ if (!n) return 0;
+ m = n;
+ mw = words;
+ return 1;
+}
+
+/* the heap asks for more memory (syscall 9, a = 4, b = words). answers the new
+ size in words, or -1 when the port cannot give it */
+static Cell mem_grow_request(Cell words)
+{
+ if (words < 0) return -1;
+ if (!mem_grow_words((UCell)words)) return -1;
+ return (Cell)mw;
+}
+static int stack_memory_grow(UCell words) { return mem_grow_words(words); }
+#include "stack.h"
+
+static Cell mulhu(UCell a, UCell b)
+{
+ UCell a0 = a & 0xffffffffull, a1 = a >> 32, b0 = b & 0xffffffffull, b1 = b >> 32;
+ UCell p0 = a0 * b0, p1 = a1 * b0, p2 = a0 * b1, p3 = a1 * b1;
+ UCell mid = (p0 >> 32) + (p1 & 0xffffffffull) + (p2 & 0xffffffffull);
+ return (Cell)(p3 + (p1 >> 32) + (p2 >> 32) + (mid >> 32));
+}
+static Cell mulhs(Cell a, Cell b)
+{
+ Cell hi = mulhu((UCell)a, (UCell)b);
+ if (a < 0) hi -= b;
+ if (b < 0) hi -= a;
+ return hi;
+}
+static Cell sar(Cell v, int n)
+{
+ if (n <= 0) return v;
+ if (v < 0) return (Cell)(((UCell)v >> n) | (~(UCell)0 << (64 - n)));
+ return (Cell)((UCell)v >> n);
+}
+static Cell ev(int op, Cell a, Cell b, Cell c)
+{
+ UCell ua = (UCell)a, ub = (UCell)b;
+ int sh = (int)(b & 63);
+ switch (op) {
+ case U_MOV: return a;
+ case U_ADD: return (Cell)(ua + ub);
+ case U_SUB: return (Cell)(ua - ub);
+ case U_MUL: return (Cell)(ua * ub);
+ case U_SDIV: return b == 0 ? 0 : (a == (Cell)((UCell)1 << 63) && b == -1) ? a : a / b;
+ case U_UDIV: return b == 0 ? 0 : (Cell)(ua / ub);
+ case U_SREM: return b == 0 ? a : (a == (Cell)((UCell)1 << 63) && b == -1) ? 0 : a % b;
+ case U_UREM: return b == 0 ? a : (Cell)(ua % ub);
+ case U_AND: return a & b;
+ case U_OR: return a | b;
+ case U_XOR: return a ^ b;
+ case U_SHL: return (Cell)(ua << sh);
+ case U_SHR: return (Cell)(ua >> sh);
+ case U_SAR: return sar(a, sh);
+ case U_NEG: return (Cell)(0ull - ua);
+ case U_NOT: return ~a;
+ case U_LT: return a < b;
+ case U_LTU: return ua < ub;
+ case U_EQ: return a == b;
+ case U_NE: return a != b;
+ case U_MIN: return a < b ? a : b;
+ case U_MAX: return a > b ? a : b;
+ case U_MINU: return ua < ub ? a : b;
+ case U_MAXU: return ua > ub ? a : b;
+ case U_MULHS: return mulhs(a, b);
+ case U_MULHU: return mulhu(ua, ub);
+ case U_CLZ: {
+ int n = 0;
+ if (!ua) return 64;
+ while (!(ua & ((UCell)1 << 63))) { ua <<= 1; n++; }
+ return n;
+ }
+ case U_ABS: return a < 0 ? (Cell)(0ull - ua) : a;
+ case U_CTZ: { int n = 0; if (!ua) return 64; while (!(ua & 1)) { ua >>= 1; n++; } return n; }
+ case U_POPCNT: { int n = 0; while (ua) { ua &= ua - 1; n++; } return n; }
+ case U_BSWAP: { UCell r = 0; int i; for (i = 0; i < 8; i++) { r = (r << 8) | (ua & 0xff); ua >>= 8; } return (Cell)r; }
+ case U_ROTL: return (Cell)((ua << sh) | (ua >> ((64 - sh) & 63)));
+ case U_ROTR: return (Cell)((ua >> sh) | (ua << ((64 - sh) & 63)));
+ case U_F2S: return (Cell)fop_f2s((uint64_t)a);
+ case U_S2F: return (Cell)fop_s2f((uint64_t)a);
+ case U_SEL: return a ? b : c;
+ }
+ return 0;
+}
+
+static int vnew(int op, int a, int b, int c, Cell imm)
+{
+ Xop *u;
+ if (berr) return 0;
+ if (bn >= MAXOPS || bnv >= MAXV) { berr = -5; return 0; }
+ u = &build[bn++];
+ u->op = (unsigned char)op;
+ u->a = (unsigned char)a;
+ u->b = (unsigned char)b;
+ u->c = (unsigned char)c;
+ u->d = (unsigned short)bnv;
+ u->imm = imm;
+ return bnv++;
+}
+static int seq(const char *a, const char *b)
+{
+ while (*a && *a == *b) { a++; b++; }
+ return *a == *b;
+}
+static int findop(const char *s)
+{
+ int i;
+ for (i = 0; i < U_N; i++) if (seq(uname[i], s)) return i;
+ return -1;
+}
+static int parse_num(const char *t, Cell *o)
+{
+ int sign = 1, base = 10, d;
+ UCell n = 0;
+ if (*t == '-') { sign = -1; t++; }
+ else if (*t == '+') t++;
+ if (*t == '0' && (t[1] == 'x' || t[1] == 'X')) { base = 16; t += 2; }
+ else if (*t == '0' && t[1]) base = 8;
+ if (!*t) return -1;
+ for (; *t; t++) {
+ if (*t >= '0' && *t <= '9') d = *t - '0';
+ else if (*t >= 'a' && *t <= 'f') d = *t - 'a' + 10;
+ else if (*t >= 'A' && *t <= 'F') d = *t - 'A' + 10;
+ else return -1;
+ if (d >= base) return -1;
+ n = n * (UCell)base + (UCell)d;
+ }
+ *o = sign < 0 ? (Cell)(0ull - n) : (Cell)n;
+ return 0;
+}
+static int regn(const char *t)
+{
+ int r = 0;
+ if (t[0] != 'r' || t[1] < '0' || t[1] > '9') return -1;
+ t++;
+ while (*t >= '0' && *t <= '9') {
+ r = r * 10 + *t++ - '0';
+ if (r > 15) return -2;
+ }
+ return r;
+}
+static int named(Cell names, int nin, const char *t)
+{
+ int i, j;
+ Cell s;
+ for (i = 0; i < nin && names; i++) {
+ s = ld(names + i);
+ j = 0;
+ while (ld(s + j) && ld(s + j) == (unsigned char)t[j]) j++;
+ if (!ld(s + j) && !t[j]) return i;
+ }
+ return -1;
+}
+static int rget(int *cur, int r)
+{
+ if (cur[r] < 0) cur[r] = vnew(U_LI, 0, 0, 0, 0);
+ return cur[r];
+}
+
+static int parse_recipe(Cell src, Cell names, int nin, int nout, int *out)
+{
+ int cur[16], i, n, k, op, args[3], na, dst, isst, nt, rr, v;
+ char tok[8][32];
+ Cell p = src, x;
+ for (i = 0; i < 16; i++) cur[i] = i < nin ? i : -1;
+ bn = 0; bnv = nin; berr = 0;
+ for (;;) {
+ nt = 0;
+ for (;;) {
+ int c = (int)ld(p);
+ while (c == ' ' || c == '\t' || c == '\r') c = (int)ld(++p);
+ if (!c || c == '\n' || c == ';') break;
+ if (c == '=') {
+ if (nt < 8) { tok[nt][0] = '='; tok[nt][1] = 0; nt++; }
+ p++;
+ continue;
+ }
+ n = 0;
+ while (c && c != ' ' && c != '\t' && c != '\n' && c != ';' && c != '=' && c != '\r') {
+ if (n < 31) tok[nt < 8 ? nt : 7][n++] = (char)c;
+ c = (int)ld(++p);
+ }
+ if (nt < 8) tok[nt++][n] = 0;
+ }
+ if (nt) {
+ isst = seq(tok[0], "st");
+ if (!isst && (nt < 3 || !seq(tok[1], "="))) return -2;
+ dst = -1;
+ if (!isst) {
+ rr = regn(tok[0]);
+ if (rr == -2) return -2;
+ dst = rr >= 0 ? rr : named(names, nin, tok[0]);
+ if (dst < 0 || dst > 15) return -2;
+ }
+ k = isst ? 1 : 2;
+ op = -1;
+ if (!isst && nt > 3) {
+ op = findop(tok[k]);
+ if (op < 0) return -2;
+ k++;
+ }
+ if (isst) op = U_ST;
+ na = 0;
+ for (; k < nt; k++) {
+ char *t = tok[k];
+ v = -1;
+ if (na >= 3) return -2;
+ rr = regn(t);
+ if (rr == -2) return -2;
+ if (rr >= 0) v = rget(cur, rr);
+ else if ((t[0] >= '0' && t[0] <= '9') || t[0] == '-' || t[0] == '+') {
+ if (parse_num(t, &x)) return -2;
+ v = vnew(U_LI, 0, 0, 0, x);
+ } else {
+ i = named(names, nin, t);
+ if (i < 0) return -2;
+ v = rget(cur, i);
+ }
+ if (berr) return berr;
+ args[na++] = v;
+ }
+ if (op < 0) {
+ if (na != 1) return -2;
+ cur[dst] = args[0];
+ } else {
+ if (na != (op == U_LI ? 0 : uarity[op])) {
+ if (!(op == U_LI && na == 1)) return -2;
+ }
+ if (op == U_LI) cur[dst] = na ? args[0] : vnew(U_LI, 0, 0, 0, 0);
+ else if (op == U_MOV) { if (na != 1) return -2; cur[dst] = args[0]; }
+ else if (op == U_ST) vnew(U_ST, args[0], args[1], 0, 0);
+ else cur[dst] = vnew(op, args[0], na > 1 ? args[1] : 0, na > 2 ? args[2] : 0, 0);
+ if (berr) return berr;
+ }
+ }
+ if (!ld(p)) break;
+ p++;
+ }
+ for (i = 0; i < nout; i++) {
+ if (cur[i] < 0) cur[i] = vnew(U_LI, 0, 0, 0, 0);
+ out[i] = cur[i];
+ }
+ return berr;
+}
+
+static int lower_vm(Cell a, Cell len, int nin, int nout, int *out)
+{
+ static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT };
+ int st[256], sp = 0, i, k, x, y, j;
+ Cell w;
+ if ((UCell)len > 100000) return -5;
+ bn = 0; bnv = nin; berr = 0;
+ for (i = 0; i < nin; i++) st[sp++] = i;
+ for (i = 0; i < (int)len; i++) {
+ w = ld(a + i);
+ if (w & 1) {
+ if (sp >= 256) return -4;
+ st[sp++] = vnew(U_LI, 0, 0, 0, w >> 1);
+ if (berr) return berr;
+ continue;
+ }
+ k = (int)(w >> 1);
+ if (k == 9) break;
+ switch (k) {
+ case 1:
+ if (sp < 1) return -4;
+ st[sp - 1] = vnew(U_LD, st[sp - 1], 0, 0, 0);
+ break;
+ case 2:
+ if (sp < 2) return -4;
+ vnew(U_ST, st[sp - 1], st[sp - 2], 0, 0);
+ sp -= 2;
+ break;
+ case 3:
+ if (sp < 1) return -4;
+ x = st[sp - 1];
+ if (x < nin) return -4;
+ for (j = 0; j < bn && build[j].d != (unsigned)x; j++);
+ if (j == bn || build[j].op != U_LI) return -4;
+ y = (int)build[j].imm;
+ if (y < 0 || y >= sp - 1) return -4;
+ st[sp - 1] = st[sp - 2 - y];
+ break;
+ case 4:
+ if (sp < 1) return -4;
+ sp--;
+ break;
+ case 5:
+ if (sp < 2) return -4;
+ x = st[sp - 1]; st[sp - 1] = st[sp - 2]; st[sp - 2] = x;
+ break;
+ case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20:
+ if (sp < 2) return -4;
+ x = st[sp - 1]; y = st[sp - 2]; sp -= 2;
+ st[sp++] = vnew(map[k - 13], y, x, 0, 0);
+ break;
+ default:
+ return -4;
+ }
+ if (berr) return berr;
+ }
+ if (sp != nout) return -4;
+ for (i = 0; i < nout; i++) out[i] = st[i];
+ return berr;
+}
+
+static int commit(int slot, int nin, int nout, int *out)
+{
+ Enh *e = &enh[slot];
+ int base = xuse, i;
+ if (berr) return berr;
+ if (e->live && (int)e->at + (int)e->n == xuse) base = e->at;
+ if (base + bn > XPOOL) return -5;
+ if (e->live && (int)e->at + (int)e->n == xuse) xuse = e->at;
+ e->at = (unsigned short)xuse;
+ e->n = (unsigned short)bn;
+ e->nv = (unsigned short)bnv;
+ e->nin = (unsigned char)nin;
+ e->nout = (unsigned char)nout;
+ for (i = 0; i < nout; i++) e->out[i] = (unsigned short)out[i];
+ for (i = 0; i < bn; i++) xops[xuse++] = build[i];
+ e->live = 1;
+ return 0;
+}
+
+static int install(Cell d)
+{
+ Cell slot, kind, nin, nout, pay, len, names;
+ int out[8], r;
+ if (!inmem(d) || !inmem(d + 6)) return -1;
+ slot = m[d]; kind = m[d + 1]; nin = m[d + 2]; nout = m[d + 3];
+ pay = m[d + 4]; len = m[d + 5]; names = m[d + 6];
+ if (kind == 2) return 0;
+ if ((UCell)slot >= NENH || (UCell)nin > 8 || (UCell)nout > 8 || (kind != 0 && kind != 1)) return -1;
+ r = kind == 0 ? parse_recipe(pay, names, (int)nin, (int)nout, out)
+ : lower_vm(pay, len, (int)nin, (int)nout, out);
+ if (r) return r;
+ return commit((int)slot, (int)nin, (int)nout, out);
+}
+
+static int xrun(Enh *e, int s)
+{
+ int i, j;
+ Xop *u;
+ for (i = 0; i < e->nv && i < MAXV; i++) xv[i] = 0;
+ for (j = 0; j < e->nin; j++) xv[j] = m[s + e->nin - 1 - j];
+ for (i = 0; i < e->n; i++) {
+ u = &xops[e->at + i];
+ if (u->op == U_LI) xv[u->d] = u->imm;
+ else if (u->op == U_LD) xv[u->d] = ld(xv[u->a]);
+ else if (u->op == U_ST) { if (inmem(xv[u->a])) m[xv[u->a]] = xv[u->b]; }
+ else xv[u->d] = ev(u->op, xv[u->a], xv[u->b], xv[u->c]);
+ }
+ s = s + e->nin - e->nout;
+ for (j = 0; j < e->nout; j++) m[s + e->nout - 1 - j] = xv[e->out[j]];
+ return s;
+}
+
+/* a run suspends when the port has no input yet but more may come (streaming,
+ see micro_port.h): micro_getc returns MICRO_WAIT, the getc is run again on
+ micro_resume, and the stacks wait where they are */
+static int sus_p, sus_s, sus_r;
+
+static int run_from(int p, int s, int r)
+{
+ int op;
+ Cell w, a, b;
+ while ((UCell)p < mw) {
+ if (s < stack_data_limit() || r > stack_return_limit()) {
+ Cell sp = s, rp = r; int ret = r > stack_return_limit();
+ if (!stack_control || stack_expand(ret, stack_get(ret ? SC_RETURN_TOP : SC_DATA_TOP) - stack_get(ret ? SC_RETURN_LOW : SC_DATA_LOW) + 1, &sp, &rp) < 0) {
+ p = ret ? PC_RETURN_OVERFLOW : PC_DATA_OVERFLOW; break;
+ }
+ s = (int)sp; r = (int)rp;
+ }
+ w = m[p++];
+ if (w & 1) { m[--s] = w >> 1; continue; }
+ a = m[s]; b = m[s + 1];
+ switch ((int)(w >> 1)) {
+ case 1: m[s] = ld(a); break;
+ case 2:
+ if (a == SYSGATE) {
+ /* a call can grow the memory and move m, so the result is stored after the call */
+ Cell result;
+ if (b == 9 && m[a + 1] == 6) result = stack_register(m[a + 2]) ? 0 : -1;
+ else if (b == 9 && (m[a + 1] == 7 || m[a + 1] == 8)) {
+ Cell sp = s + 2, rp = r;
+ result = stack_expand(m[a + 1] == 8, m[a + 2], &sp, &rp);
+ s = (int)sp; r = (int)rp; m[a] = result; break;
+ }
+ else if (b == 9 && m[a + 1] == 9) result = m[a + 2] > 0 && (UCell)m[a + 2] <= mw ? (Cell)mw : -1;
+ else if (b == 9 && m[a + 1] == 4) result = mem_grow_request(m[a + 2]);
+ else if (b == 9 && m[a + 1] == 5) {
+ Cell limit = m[a + 2];
+ if (limit < 0 || (UCell)limit < mw || limit > 0x7fffffff) result = -1;
+ else { memory_limit = (UCell)limit; result = limit; }
+ } else result = micro_syscall(m, b, m[a + 1], m[a + 2], m[a + 3]);
+ m[a] = result;
+ s += 2;
+ break;
+ }
+ if (inmem(a)) m[a] = b;
+ s += 2;
+ break;
+ case 3: m[s] = ld((Cell)s + 1 + a); break;
+ case 4: s++; break;
+ case 5: m[s] = b; m[s + 1] = a; break;
+ case 6: m[r++] = p; p = (int)a; s++; break;
+ case 7: p = (int)a; s++; break;
+ case 8: if (!b) p = (int)a; s += 2; break;
+ case 9: p = (int)m[--r]; break;
+ case 10: {
+ int ch = micro_getc();
+ if (ch == MICRO_WAIT) { sus_p = p - 1; sus_s = s; sus_r = r; return MICRO_SUSPENDED; }
+ m[--s] = (Cell)(signed char)ch;
+ jflag = micro_eof();
+ jknown = 1;
+ break;
+ }
+ case 11:
+ if (!jknown) { jflag = micro_eof(); jknown = 1; }
+ m[--s] = jflag;
+ break;
+ case 12: micro_putc((int)a); s++; break;
+ case 13: m[++s] = b + a; break;
+ case 14: m[++s] = b - a; break;
+ case 15: m[++s] = b * a; break;
+ case 16: m[++s] = b & a; break;
+ case 17: m[++s] = b | a; break;
+ case 18: m[++s] = b ^ a; break;
+ case 19: m[++s] = sar(b, (int)(a & 63)); break;
+ case 20: m[++s] = b < a; break;
+ case 21: m[s] = install(a); break;
+ case 600:
+ if ((UCell)p >= mw) { p = PC_PAST_END; break; }
+ m[--s] = m[p++];
+ break;
+ case 601: m[++s] = (Cell)fop_add((uint64_t)b, (uint64_t)a); break;
+ case 602: m[++s] = (Cell)fop_sub((uint64_t)b, (uint64_t)a); break;
+ case 603: m[++s] = (Cell)fop_mul((uint64_t)b, (uint64_t)a); break;
+ case 604: m[++s] = (Cell)fop_div((uint64_t)b, (uint64_t)a); break;
+ case 605: m[++s] = fop_eq((uint64_t)b, (uint64_t)a); break;
+ case 606: m[++s] = fop_lt((uint64_t)b, (uint64_t)a); break;
+ case 607: m[++s] = fop_le((uint64_t)b, (uint64_t)a); break;
+ case 608: m[s] = (Cell)fop_i2f(a); break;
+ case 609: m[s] = fop_f2i((uint64_t)a); break;
+ case 610: {
+ char buf[400]; int n = fop_format((uint64_t)a, buf), ci;
+ for (ci = 0; ci < n; ci++) micro_putc(buf[ci]);
+ s++;
+ break;
+ }
+ case 618: {
+ char buf[400];
+ int n = fop_format_mode((uint64_t)m[s + 2], a, b, buf), ci;
+ for (ci = 0; ci < n; ci++) micro_putc(buf[ci]);
+ s += 3;
+ break;
+ }
+ case 611:
+ m[s] = inmem(a) ? (Cell)fop_parse_words(m + a, mw - (UCell)a) : 0;
+ break;
+ case 612: m[s] = b_load(a, 1); break;
+ case 613: b_store(a, b, 1); s += 2; break;
+ case 614: m[s] = b_load(a, 2); break;
+ case 615: b_store(a, b, 2); s += 2; break;
+ case 616: m[s] = b_load(a, 4); break;
+ case 617: b_store(a, b, 4); s += 2; break;
+ case 619: { Cell t = s; m[--s] = t; break; }
+ case 620: { /* a native call: there is no native code here, so pop the arguments and answer -1 */
+ int n = 0;
+ Cell sig = (UCell)a < mw - 2 ? m[a + 1] : 0;
+ while (n < 15 && ((sig >> (4 * (n + 1))) & 15)) n++;
+ s += n;
+ m[s] = -1;
+ break; }
+ default:
+ op = (int)(w >> 1);
+ if (op >= XOP0 && op < XOP0 + NENH && enh[op - XOP0].live) s = xrun(&enh[op - XOP0], s);
+ else if (op == 0) p = PC_HALT;
+ else { bad_op = op; p = PC_BAD_OP; }
+ }
+ }
+ if (p == PC_DATA_OVERFLOW) { micro_msg("zy: data stack overflow\n"); return 2; }
+ if (p == PC_RETURN_OVERFLOW) { micro_msg("zy: return stack overflow\n"); return 2; }
+ if (p == PC_PAST_END) { micro_msg("zy: the program counter is past the end of memory\n"); return 2; }
+ if (p == PC_BAD_OP) {
+ char msg[96]; int n = 0, k, neg = bad_op < 0, v = bad_op < 0 ? -bad_op : bad_op;
+ const char *head = "zy: unknown opcode ";
+ while (head[n]) { msg[n] = head[n]; n++; }
+ if (neg) msg[n++] = '-';
+ { char dg[12]; int nd = 0; do { dg[nd++] = (char)('0' + v % 10); v /= 10; } while (v); for (k = nd; k > 0; k--) msg[n++] = dg[k - 1]; }
+ msg[n++] = '\n'; msg[n] = 0;
+ micro_msg(msg);
+ return 2;
+ }
+ return 0;
+}
+
+static int run(int p) { return run_from(p, DS0, RS0); }
+
+/* goes on from a run that suspended for input (MICRO_SUSPENDED), once the port
+ has input for it. the result is as micro_run's */
+int micro_resume(void) { return run_from(sus_p, sus_s, sus_r); }
+
+int micro_run(void)
+{
+ int i;
+ if (!mem_grow_words(memory_initial)) {
+ micro_msg("zy: no memory for the machine\n");
+ return 1;
+ }
+ if ((UCell)GORG + (UCell)GLEN > mw || (UCell)GORG + (UCell)GLEN > (UCell)FBOT) {
+ micro_msg("zy: genome does not fit\n");
+ return 1;
+ }
+ for (i = 0; i < GLEN; i++) m[GORG + i] = genome[i];
+ jknown = 0;
+ return run(GORG);
+}