diff options
author | polwex <polwex@sortug.com> | 2025-10-05 21:56:51 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-10-05 21:56:51 +0700 |
commit | fcedfddf00b3f994e4f4e40332ac7fc192c63244 (patch) | |
tree | 51d38e62c7bdfcc5f9a5e9435fe820c93cfc9a3d /vere/pkg/noun/nock.c |
claude is gud
Diffstat (limited to 'vere/pkg/noun/nock.c')
-rw-r--r-- | vere/pkg/noun/nock.c | 3268 |
1 files changed, 3268 insertions, 0 deletions
diff --git a/vere/pkg/noun/nock.c b/vere/pkg/noun/nock.c new file mode 100644 index 0000000..8218efa --- /dev/null +++ b/vere/pkg/noun/nock.c @@ -0,0 +1,3268 @@ +/// @file + +#include "nock.h" + +#include "allocate.h" +#include "hashtable.h" +#include "imprison.h" +#include "jets.h" +#include "jets/k.h" +#include "jets/q.h" +#include "manage.h" +#include "options.h" +#include "retrieve.h" +#include "trace.h" +#include "vortex.h" +#include "xtract.h" +#include "zave.h" + + +// define to have each opcode printed as it executes, +// along with some other debugging info +# undef VERBOSE_BYTECODE + +#if 0 +// Retained for debugging purposes. +static u3_noun _n_nock_on(u3_noun bus, u3_noun fol); + +/* _n_hint(): process hint. +*/ +static u3_noun +_n_hint(u3_noun zep, + u3_noun hod, + u3_noun bus, + u3_noun nex) +{ + switch ( zep ) { + default: { + // u3m_p("weird zep", zep); + u3a_lose(zep); + u3a_lose(hod); + + return _n_nock_on(bus, nex); + } + + case c3__hunk: + case c3__lose: + case c3__mean: + case c3__spot: { + u3_noun tac = u3nc(zep, hod); + u3_noun pro; + + u3t_push(tac); +#if 0 + { + static int low_i; + + if ( !low_i ) { + low_i = 1; + if ( 0 == (u3R->pro.nox_d % 65536ULL) ) { + if ( c3__spot == zep ) { + u3l_log("spot %d/%d : %d/%d", + u3h(u3h(u3t(hod))), + u3t(u3h(u3t(hod))), + u3h(u3t(u3t(hod))), + u3t(u3t(u3t(hod)))); + } + } + low_i = 0; + } + } +#endif + pro = _n_nock_on(bus, nex); + u3t_drop(); + + return pro; + } + + case c3__live: { + if ( c3y == u3ud(hod) ) { + u3t_off(noc_o); + u3t_heck(hod); + u3t_on(noc_o); + } else { + u3z(hod); + } + return _n_nock_on(bus, nex); + } + + case c3__slog: { + if ( !(u3C.wag_w & u3o_quiet) ) { + u3t_off(noc_o); + u3t_slog(hod); + u3t_on(noc_o); + } + return _n_nock_on(bus, nex); + } + + case c3__germ: { + u3_noun pro = _n_nock_on(bus, nex); + + if ( c3y == u3r_sing(pro, hod) ) { + u3z(pro); return hod; + } else { + u3z(hod); return pro; + } + } + + case c3__fast: { + u3_noun pro = _n_nock_on(bus, nex); + + u3t_off(noc_o); + u3j_mine(hod, u3k(pro)); + u3t_on(noc_o); + + return pro; + } + + case c3__memo: { + u3z(hod); +#if 0 + return _n_nock_on(bus, nex); +#else + { + u3_noun pro = u3z_find_2(144 + c3__nock, bus, nex); + + if ( pro != u3_none ) { + u3z(bus); u3z(nex); + return pro; + } + pro = _n_nock_on(u3k(bus), u3k(nex)); + + if ( &(u3H->rod_u) != u3R ) { + u3z_save_2(144 + c3__nock, bus, nex, pro); + } + + u3z(bus); u3z(nex); + + return pro; + } +#endif + } + + case c3__sole: { + u3z(hod); + { + u3_noun pro = _n_nock_on(bus, nex); + + // return u3z_uniq(pro); + return pro; + } + } + } +} + +/* _n_nock_on(): produce .*(bus fol). Do not virtualize. +*/ +static u3_noun +_n_nock_on(u3_noun bus, u3_noun fol) +{ + u3_noun hib, gal; + + while ( 1 ) { + hib = u3h(fol); + gal = u3t(fol); + +#ifdef U3_CPU_DEBUG + u3R->pro.nox_d += 1; +#endif + + if ( c3y == u3du(hib) ) { + u3_noun poz, riv; + + poz = _n_nock_on(u3k(bus), u3k(hib)); + riv = _n_nock_on(bus, u3k(gal)); + + u3a_lose(fol); + return u3i_cell(poz, riv); + } + else switch ( hib ) { + default: return u3m_bail(c3__exit); + + case 0: { + if ( c3n == u3ud(gal) ) { + return u3m_bail(c3__exit); + } + else { + u3_noun pro = u3k(u3at(gal, bus)); + + u3a_lose(bus); u3a_lose(fol); + return pro; + } + } + u3_assert(!"not reached"); + + case 1: { + u3_noun pro = u3k(gal); + + u3a_lose(bus); u3a_lose(fol); + return pro; + } + u3_assert(!"not reached"); + + case 2: { + u3_noun nex = _n_nock_on(u3k(bus), u3k(u3t(gal))); + u3_noun seb = _n_nock_on(bus, u3k(u3h(gal))); + + u3a_lose(fol); + bus = seb; + fol = nex; + continue; + } + u3_assert(!"not reached"); + + case 3: { + u3_noun gof, pro; + + gof = _n_nock_on(bus, u3k(gal)); + pro = u3du(gof); + + u3a_lose(gof); u3a_lose(fol); + return pro; + } + u3_assert(!"not reached"); + + case 4: { + u3_noun gof, pro; + + gof = _n_nock_on(bus, u3k(gal)); + pro = u3i_vint(gof); + + u3a_lose(fol); + return pro; + } + u3_assert(!"not reached"); + + case 5: { + u3_noun wim = _n_nock_on(bus, u3k(gal)); + u3_noun pro = u3r_sing(u3h(wim), u3t(wim)); + + u3a_lose(wim); u3a_lose(fol); + return pro; + } + u3_assert(!"not reached"); + + case 6: { + u3_noun b_gal, c_gal, d_gal; + + u3x_trel(gal, &b_gal, &c_gal, &d_gal); + { + u3_noun tys = _n_nock_on(u3k(bus), u3k(b_gal)); + u3_noun nex; + + if ( 0 == tys ) { + nex = u3k(c_gal); + } else if ( 1 == tys ) { + nex = u3k(d_gal); + } else return u3m_bail(c3__exit); + + u3a_lose(fol); + fol = nex; + continue; + } + } + u3_assert(!"not reached"); + + case 7: { + u3_noun b_gal, c_gal; + + u3x_cell(gal, &b_gal, &c_gal); + { + u3_noun bod = _n_nock_on(bus, u3k(b_gal)); + u3_noun nex = u3k(c_gal); + + u3a_lose(fol); + bus = bod; + fol = nex; + continue; + } + } + u3_assert(!"not reached"); + + case 8: { + u3_noun b_gal, c_gal; + + u3x_cell(gal, &b_gal, &c_gal); + { + u3_noun heb = _n_nock_on(u3k(bus), u3k(b_gal)); + u3_noun bod = u3nc(heb, bus); + u3_noun nex = u3k(c_gal); + + u3a_lose(fol); + bus = bod; + fol = nex; + continue; + } + } + u3_assert(!"not reached"); + + case 9: { + u3_noun b_gal, c_gal; + + u3x_cell(gal, &b_gal, &c_gal); + { + u3_noun seb = _n_nock_on(bus, u3k(c_gal)); + u3_noun pro; + + u3t_off(noc_o); + pro = u3j_kick(seb, b_gal); + u3t_on(noc_o); + + if ( u3_none != pro ) { + u3a_lose(fol); + return pro; + } + else { + if ( c3n == u3ud(b_gal) ) { + return u3m_bail(c3__exit); + } + else { + u3_noun nex = u3k(u3at(b_gal, seb)); + + u3a_lose(fol); + bus = seb; + fol = nex; + continue; + } + } + } + } + u3_assert(!"not reached"); + + case 10: { + u3_noun p_gal, q_gal; + + u3x_cell(gal, &p_gal, &q_gal); + { + u3_noun zep, hod, nex; + + if ( c3y == u3du(p_gal) ) { + u3_noun b_gal = u3h(p_gal); + u3_noun c_gal = u3t(p_gal); + u3_noun d_gal = q_gal; + + zep = u3k(b_gal); + hod = _n_nock_on(u3k(bus), u3k(c_gal)); + nex = u3k(d_gal); + } + else { + u3_noun b_gal = p_gal; + u3_noun c_gal = q_gal; + + zep = u3k(b_gal); + hod = u3_nul; + nex = u3k(c_gal); + } + + u3a_lose(fol); + return _n_hint(zep, hod, bus, nex); + } + } + + case 11: { + u3_noun ref = _n_nock_on(u3k(bus), u3k(u3h(gal))); + u3_noun gof = _n_nock_on(bus, u3k(u3t(gal))); + u3_noun val; + + u3t_off(noc_o); + val = u3m_soft_esc(u3k(ref), u3k(gof)); + u3t_on(noc_o); + + if ( !_(u3du(val)) ) { + u3m_bail(u3nt(1, gof, 0)); + } + if ( !_(u3du(u3t(val))) ) { + // + // replace with proper error stack push + // + u3t_push(u3nt(c3__hunk, ref, gof)); + return u3m_bail(c3__exit); + } + else { + u3_noun pro; + + u3z(ref); + u3z(gof); + u3z(fol); + pro = u3k(u3t(u3t(val))); + u3z(val); + + return pro; + } + } + u3_assert(!"not reached"); + } + } +} +#endif + +// Several opcodes "overflow" (from byte to short index) to their successor, so +// order can matter here. +// Note that we use an X macro (https://en.wikipedia.org/wiki/X_Macro) to unify +// the opcode's enum name, string representation, and computed goto into a +// single structure. +#define OPCODES \ + /* non-nock bytecodes */ \ + X(HALT, "halt", &&do_halt), /* 0: terminator, end of bytcode program */ \ + X(BAIL, "bail", &&do_bail), /* 1: deterministic crash */ \ + /* stack manipulation */ \ + X(COPY, "copy", &&do_copy), /* 2 */ \ + X(SWAP, "swap", &&do_swap), /* 3 */ \ + X(TOSS, "toss", &&do_toss), /* 4 */ \ + /* auto-cons */ \ + X(AUTO, "auto", &&do_auto), /* 5: kept */ \ + X(AULT, "ault", &&do_ault), /* 6: lost */ \ + /* general purposes */ \ + X(SNOC, "snoc", &&do_snoc), /* 7: keep */ \ + X(SNOL, "snol", &&do_snol), /* 8: lose */ \ + /* nock 0: head */ \ + X(HEAD, "head", &&do_head), /* 9: keep */ \ + X(HELD, "held", &&do_held), /* 10: lose */ \ + /* nock 0: tail */ \ + X(TAIL, "tail", &&do_tail), /* 11: keep */ \ + X(TALL, "tall", &&do_tall), /* 12: lose */ \ + /* nock 0: fragment (keep) */ \ + X(FABK, "fabk", &&do_fabk), /* 13: c3_y */ \ + X(FASK, "fask", &&do_fask), /* 14: c3_s */ \ + X(FIBK, "fibk", &&do_fibk), /* 15: c3_y */ \ + X(FISK, "fisk", &&do_fisk), /* 16: c3_s */ \ + /* nock 0: fragment (lose) */ \ + X(FABL, "fabl", &&do_fabl), /* 17: c3_y */ \ + X(FASL, "fasl", &&do_fasl), /* 18: c3_s */ \ + X(FIBL, "fibl", &&do_fibl), /* 19: c3_y */ \ + X(FISL, "fisl", &&do_fisl), /* 20: c3_s */ \ + /* nock 1: literal (keep) */ \ + X(LIT0, "lit0", &&do_lit0), /* 21: a literal 0 */ \ + X(LIT1, "lit1", &&do_lit1), /* 22: a literal 1 */ \ + X(LITB, "litb", &&do_litb), /* 23: c3_y */ \ + X(LITS, "lits", &&do_lits), /* 24: c3_s */ \ + X(LIBK, "libk", &&do_libk), /* 25: c3_y */ \ + X(LISK, "lisk", &&do_lisk), /* 26: c3_s */ \ + /* nock 1: literal (lose) */ \ + X(LIL0, "lil0", &&do_lil0), /* 27: a literal 0 */ \ + X(LIL1, "lil1", &&do_lil1), /* 28: a literal 1 */ \ + X(LILB, "lilb", &&do_lilb), /* 29: c3_y */ \ + X(LILS, "lils", &&do_lils), /* 30: c3_s */ \ + X(LIBL, "libl", &&do_libl), /* 31: c3_y */ \ + X(LISL, "lisl", &&do_lisl), /* 32: c3_s */ \ + /* nock 2: nock */ \ + X(NOLK, "nolk", &&do_nolk), /* 33, lost */ \ + X(NOCT, "noct", &&do_noct), /* 34, tail */ \ + X(NOCK, "nock", &&do_nock), /* 35, kept */ \ + /* nock 3 & 4 */ \ + X(DEEP, "deep", &&do_deep), /* 36 */ \ + X(BUMP, "bump", &&do_bump), /* 37 */ \ + /* nock 5: equality */ \ + X(SAM0, "sam0", &&do_sam0), /* 38: test that it is equal to 0 */ \ + X(SAM1, "sam1", &&do_sam1), /* 39: test that it is equal to 1 */ \ + X(SAMB, "samb", &&do_samb), /* 40: test equality for vars size c3_b */ \ + X(SAMS, "sams", &&do_sams), /* 41: test equality for vars size c3_s */ \ + X(SANB, "sanb", &&do_sanb), /* 42: test equality for vars size c3_b */ \ + X(SANS, "sans", &&do_sans), /* 43: test equality for vars size c3_s */ \ + X(SAME, "same", &&do_same), /* 44 */ \ + X(SALM, "salm", &&do_salm), /* 45 */ \ + X(SAMC, "samc", &&do_samc), /* 46 */ \ + /* related to nock 6: unconditional skips */ \ + X(SBIP, "sbip", &&do_sbip), /* 47: c3_b */ \ + X(SIPS, "sips", &&do_sips), /* 48: c3_s */ \ + X(SWIP, "swip", &&do_swip), /* 49: c3_l */ \ + /* related to nock 6: conditional skips */ \ + X(SBIN, "sbin", &&do_sbin), /* 50: c3_b */ \ + X(SINS, "sins", &&do_sins), /* 51: c3_s */ \ + X(SWIN, "swin", &&do_swin), /* 52: c3_l */ \ + /* nock 9 */ \ + X(KICB, "kicb", &&do_kicb), /* 53: c3_b */ \ + X(KICS, "kics", &&do_kics), /* 54: c3_s */ \ + X(TICB, "ticb", &&do_ticb), /* 55: c3_b */ \ + X(TICS, "tics", &&do_tics), /* 56: c3_s */ \ + /* nock 12: scry (only defined in arvo, not in base nock spec) */ \ + X(WILS, "wils", &&do_wils), /* 57 */ \ + X(WISH, "wish", &&do_wish), /* 58 */ \ + /* nock 11: hint processing */ \ + X(BUSH, "bush", &&do_bush), /* 59: c3_b */ \ + X(SUSH, "sush", &&do_sush), /* 60: c3_s */ \ + X(DROP, "drop", &&do_drop), /* 61 */ \ + X(HECK, "heck", &&do_heck), /* 62 */ \ + X(SLOG, "slog", &&do_slog), /* 63 */ \ + /* nock 11: fast (keep) */ \ + X(BAST, "bast", &&do_bast), /* 64: c3_b */ \ + X(SAST, "sast", &&do_sast), /* 65: c3_s */ \ + /* nock 11: fast (lost) */ \ + X(BALT, "balt", &&do_balt), /* 66: c3_b */ \ + X(SALT, "salt", &&do_salt), /* 67: c3_s */ \ + /* nock 11: memo (keep) */ \ + X(SKIB, "skib", &&do_skib), /* 68: c3_b */ \ + X(SKIS, "skis", &&do_skis), /* 69: c3_s */ \ + /* nock 11: memo (lose) */ \ + X(SLIB, "slib", &&do_slib), /* 70: c3_b */ \ + X(SLIS, "slis", &&do_slis), /* 71: c3_s */ \ + X(SAVE, "save", &&do_save), /* 72 */ \ + /* nock 11: before formula */ \ + X(HILB, "hilb", &&do_hilb), /* 73: atomic, byte */ \ + X(HILS, "hils", &&do_hils), /* 74: atomic, short */ \ + X(HINB, "hinb", &&do_hinb), /* 75: arbitrary, byte */ \ + X(HINS, "hins", &&do_hins), /* 76: arbitrary, short */ \ + /* nock 11: after formula */ \ + X(HILK, "hilk", &&do_hilk), /* 77: atomic, keep */ \ + X(HILL, "hill", &&do_hill), /* 78: atomic, lose */ \ + X(HINK, "hink", &&do_hink), /* 79: arbitrary, keep */ \ + X(HINL, "hinl", &&do_hinl), /* 80: arbitrary, lose */ \ + /* nock 10 */ \ + X(MUTH, "muth", &&do_muth), /* 81 */ \ + X(KUTH, "kuth", &&do_kuth), /* 82 */ \ + X(MUTT, "mutt", &&do_mutt), /* 83 */ \ + X(KUTT, "kutt", &&do_kutt), /* 84 */ \ + X(MUSM, "musm", &&do_musm), /* 85 */ \ + X(KUSM, "kusm", &&do_kusm), /* 86 */ \ + X(MUTB, "mutb", &&do_mutb), /* 87: c3_b */ \ + X(MUTS, "muts", &&do_muts), /* 88: c3_s */ \ + X(MITB, "mitb", &&do_mitb), /* 89: c3_b */ \ + X(MITS, "mits", &&do_mits), /* 90: c3_s */ \ + X(KUTB, "kutb", &&do_kutb), /* 91: c3_b */ \ + X(KUTS, "kuts", &&do_kuts), /* 92: c3_s */ \ + X(KITB, "kitb", &&do_kitb), /* 93: c3_b */ \ + X(KITS, "kits", &&do_kits), /* 94: c3_s */ \ + X(LAST, NULL, NULL), /* 95 */ + +// Opcodes. Define X to select the enum name from OPCODES. +#define X(opcode, name, indirect_jump) opcode +enum { OPCODES }; +#undef X + +/* _n_arg(): return the size (in bytes) of an opcode's argument + */ +static inline c3_y +_n_arg(c3_y cod_y) +{ + switch ( cod_y ) { + case FABK: case FABL: case FIBL: case FIBK: + case LILB: case LITB: case LIBL: case LIBK: + case SAMB: case SANB: case SBIP: case SBIN: + case SLIB: case SKIB: case KICB: case TICB: + case BUSH: case BAST: case BALT: + case MUTB: case KUTB: case MITB: case KITB: + case HILB: case HINB: + return sizeof(c3_y); + + case FASK: case FASL: case FISL: case FISK: + case LILS: case LITS: case LISL: case LISK: + case SAMS: case SANS: case SIPS: case SINS: + case SLIS: case SKIS: case KICS: case TICS: + case SUSH: case SAST: case SALT: + case MUTS: case KUTS: case MITS: case KITS: + case HILS: case HINS: + return sizeof(c3_s); + + case SWIP: case SWIN: + return sizeof(c3_l); + + default: + u3_assert( cod_y < LAST ); + return 0; + } +} + + +/* _n_melt(): measure space for list of ops (from _n_comp) */ +static u3_noun +_n_melt(u3_noun ops, c3_w* byc_w, c3_w* cal_w, + c3_w* reg_w, c3_w* lit_w, c3_w* mem_w) +{ + c3_w len_w = u3qb_lent(ops), + i_w = len_w - 1, + a_w; + c3_y cod_y; + c3_y* siz_y = u3a_malloc(len_w); + u3_noun op, sip = u3_nul; + + while ( u3_nul != ops ) { + op = u3h(ops); + if ( c3n == u3du(op) ) { + switch ( op ) { + default: + siz_y[i_w] = 1; + break; + + case BAST: case BALT: + a_w = (*reg_w)++; + if ( a_w <= 0xFF ) { + siz_y[i_w] = 2; + } + else if ( a_w <= 0xFFFF ) { + siz_y[i_w] = 3; + } + else { + fprintf(stderr, "_n_melt(): over 2^16 registration sites.\r\n"); + u3_assert(0); + } + break; + } + } + else { + cod_y = u3h(op); + + switch ( cod_y ) { + default: + siz_y[i_w] = 1 + _n_arg(cod_y); + break; + + case SBIP: case SBIN: { + c3_l tot_l = 0, + sip_l = u3t(op); + c3_w j_w, k_w = i_w; + for ( j_w = 0; j_w < sip_l; ++j_w ) { + tot_l += siz_y[++k_w]; + } + sip = u3nc(tot_l, sip); + siz_y[i_w] = tot_l <= 0xFF ? 2 : tot_l <= 0xFFFF ? 3 : 5; + break; + } + + case SKIB: case SLIB: { + c3_l tot_l = 0, + sip_l = u3h(u3t(u3t(op))); + c3_w j_w, k_w = i_w; + for ( j_w = 0; j_w < sip_l; ++j_w ) { + tot_l += siz_y[++k_w]; + } + sip = u3nc(tot_l, sip); + a_w = (*mem_w)++; + if ( a_w <= 0xFF ) { + siz_y[i_w] = 2; + } + else if ( a_w <= 0xFFFF ) { + siz_y[i_w] = 3; + } + else { + fprintf(stderr, "_n_melt(): over 2^16 memos.\r\n"); + u3_assert(0); + } + break; + } + + case SIPS: case SINS: case SWIP: case SWIN: + case SAST: case SALT: case KICS: case TICS: + case FISK: case FISL: case SUSH: case SANS: + case LISL: case LISK: case SKIS: case SLIS: + case HILS: case HINS: + u3_assert(0); //overflows + break; + + case KICB: case TICB: + a_w = (*cal_w)++; + if ( a_w <= 0xFF ) { + siz_y[i_w] = 2; + } + else if ( a_w <= 0xFFFF ) { + siz_y[i_w] = 3; + } + else { + fprintf(stderr, "_n_melt(): over 2^16 call sites.\r\n"); + u3_assert(0); + } + break; + + case BUSH: case FIBK: case FIBL: + case SANB: case LIBL: case LIBK: + case KITB: case MITB: + case HILB: case HINB: + a_w = (*lit_w)++; + if ( a_w <= 0xFF ) { + siz_y[i_w] = 2; + } + else if ( a_w <= 0xFFFF ) { + siz_y[i_w] = 3; + } + else { + fprintf(stderr, "_n_melt(): over 2^16 literals.\r\n"); + u3_assert(0); + } + break; + } + } + + *(byc_w) += siz_y[i_w--]; + ops = u3t(ops); + } + + u3a_free(siz_y); + return u3kb_flop(sip); +} + +/* _n_prog_dat(): return pointer to program's data segment + */ +static void* +_n_prog_dat(u3n_prog* pog_u) +{ + return ((void*) pog_u) + sizeof(u3n_prog); +} + +/* _n_prog_new(): allocate and set up pointers for u3n_prog + */ +static u3n_prog* +_n_prog_new(c3_w byc_w, c3_w cal_w, + c3_w reg_w, c3_w lit_w, c3_w mem_w) +{ + c3_w cab_w = (sizeof(u3j_site) * cal_w), + reb_w = (sizeof(u3j_rite) * reg_w), + lib_w = (sizeof(u3_noun) * lit_w), + meb_w = (sizeof(u3n_memo) * mem_w), + pad_w = (8 - byc_w % 8) % 8, + pod_w = lit_w % 2, + ped_w = mem_w % 2, + dat_w = byc_w + cab_w + reb_w + lib_w + meb_w + pad_w + + (pod_w * sizeof(u3_noun)) + (ped_w * sizeof(u3n_memo)); + + u3n_prog* pog_u = u3a_malloc(sizeof(u3n_prog) + dat_w); + pog_u->byc_u.own_o = c3y; + pog_u->byc_u.len_w = byc_w; + pog_u->byc_u.ops_y = (c3_y*) _n_prog_dat(pog_u); + + pog_u->lit_u.len_w = lit_w; + pog_u->lit_u.non = (u3_noun*) (pog_u->byc_u.ops_y + pog_u->byc_u.len_w + pad_w); + + pog_u->mem_u.len_w = mem_w; + pog_u->mem_u.sot_u = (u3n_memo*) (pog_u->lit_u.non + pog_u->lit_u.len_w + pod_w); + + pog_u->cal_u.len_w = cal_w; + pog_u->cal_u.sit_u = (u3j_site*) (pog_u->mem_u.sot_u + pog_u->mem_u.len_w + ped_w); + + pog_u->reg_u.len_w = reg_w; + pog_u->reg_u.rit_u = (u3j_rite*) (pog_u->cal_u.sit_u + pog_u->cal_u.len_w); + + return pog_u; +} + +/* _n_prog_old(): as _n_prog_new(), + * but leech off senior program's data segment + */ +static u3n_prog* +_n_prog_old(u3n_prog* sep_u) +{ + c3_w cab_w = sizeof(u3j_site) * sep_u->cal_u.len_w, + reb_w = sizeof(u3j_rite) * sep_u->reg_u.len_w, + lib_w = sizeof(u3_noun) * sep_u->lit_u.len_w, + meb_w = sizeof(u3n_memo) * sep_u->mem_u.len_w, + pod_w = sep_u->lit_u.len_w % 2, + ped_w = sep_u->mem_u.len_w % 2, + dat_w = cab_w + reb_w + lib_w + meb_w + + (pod_w * sizeof(u3_noun)) + (ped_w * sizeof(u3n_memo)); + + u3n_prog* pog_u = u3a_malloc(sizeof(u3n_prog) + dat_w); + pog_u->byc_u.own_o = c3n; + pog_u->byc_u.len_w = sep_u->byc_u.len_w; + pog_u->byc_u.ops_y = sep_u->byc_u.ops_y; + + pog_u->lit_u.len_w = sep_u->lit_u.len_w; + pog_u->lit_u.non = (u3_noun*) _n_prog_dat(pog_u); + + pog_u->mem_u.len_w = sep_u->mem_u.len_w; + pog_u->mem_u.sot_u = (u3n_memo*) (pog_u->lit_u.non + pog_u->lit_u.len_w + pod_w); + + pog_u->cal_u.len_w = sep_u->cal_u.len_w; + pog_u->cal_u.sit_u = (u3j_site*) (pog_u->mem_u.sot_u + pog_u->mem_u.len_w + ped_w); + + pog_u->reg_u.len_w = sep_u->reg_u.len_w; + pog_u->reg_u.rit_u = (u3j_rite*) (pog_u->cal_u.sit_u + pog_u->cal_u.len_w); + + memcpy(pog_u->lit_u.non, sep_u->lit_u.non, dat_w); + return pog_u; +} + +/* _n_prog_asm_inx(): write an index to the bytestream with overflow + */ +static void +_n_prog_asm_inx(c3_y* buf_y, c3_w* i_w, c3_s inx_s, c3_y cod) +{ + if ( inx_s <= 0xFF ) { + buf_y[(*i_w)--] = (c3_y) (inx_s); + buf_y[*i_w] = (c3_y) cod; + } + else { + buf_y[(*i_w)--] = (c3_y) (inx_s >> 8); + buf_y[(*i_w)--] = (c3_y) (inx_s); + // the short-index versions of these opcodes must immediately + // follow the byte-index versions because of this convention + buf_y[(*i_w)] = cod + 1; + } +} + +/* _n_prog_asm(): assemble list of ops (from _n_comp) into u3n_prog + */ +static void +_n_prog_asm(u3_noun ops, u3n_prog* pog_u, u3_noun sip) +{ + u3_noun top = ops; + c3_y* buf_y = pog_u->byc_u.ops_y; + c3_s lit_s = 0, + cal_s = 0, + mem_s = 0, + reg_s = 0; + c3_w i_w = pog_u->byc_u.len_w-1; + + buf_y[i_w] = HALT; + + while ( i_w-- > 0 ) { + u3_noun op = u3h(ops); + if ( c3y == u3ud(op) ) { + switch ( op ) { + default: + buf_y[i_w] = (c3_y) op; + break; + + /* registration site index args */ + case BAST: case BALT: { + _n_prog_asm_inx(buf_y, &i_w, reg_s, op); + u3j_rite* rit_u = &(pog_u->reg_u.rit_u[reg_s++]); + rit_u->own_o = c3n; + rit_u->clu = u3_none; + rit_u->fin_p = 0; + break; + } + } + } + else { + u3_noun cod = u3h(op); + switch ( cod ) { + default: + u3_assert(0); + return; + + /* memo index args */ + case SKIB: case SLIB: { + u3n_memo* mem_u; + c3_l sip_l = u3h(sip); + u3_noun tmp = sip; + sip = u3k(u3t(sip)); + u3z(tmp); + _n_prog_asm_inx(buf_y, &i_w, mem_s, cod); + mem_u = &(pog_u->mem_u.sot_u[mem_s++]); + mem_u->sip_l = sip_l; + // [op_y, cid, mem_w, nef] + mem_u->key = u3k(u3t(u3t(u3t(op)))); + mem_u->cid = u3h(u3t(op)); + break; + } + + /* skips */ + case SBIP: case SBIN: { + c3_l sip_l = u3h(sip); + u3_noun tmp = sip; + sip = u3k(u3t(sip)); + u3z(tmp); + if ( sip_l <= 0xFF ) { + buf_y[i_w--] = (c3_y) sip_l; + buf_y[i_w] = (c3_y) cod; + } + else if ( sip_l <= 0xFFFF ) { + buf_y[i_w--] = (c3_y) (sip_l >> 8); + buf_y[i_w--] = (c3_y) sip_l; + buf_y[i_w] = (c3_y) cod + 1; + } + else { + buf_y[i_w--] = (c3_y) (sip_l >> 24); + buf_y[i_w--] = (c3_y) (sip_l >> 16); + buf_y[i_w--] = (c3_y) (sip_l >> 8); + buf_y[i_w--] = (c3_y) sip_l; + buf_y[i_w] = (c3_y) cod + 2; + } + break; + } + + /* 8-bit direct args */ + case FABK: case FABL: + case LITB: case LILB: + case MUTB: case KUTB: + case SAMB: + buf_y[i_w--] = (c3_y) u3t(op); + buf_y[i_w] = (c3_y) cod; + break; + + /* 16-bit direct args */ + case FASK: case FASL: + case LILS: case LITS: + case MUTS: case KUTS: + case SAMS: case SIPS: case SINS: { + c3_s off_s = u3t(op); + buf_y[i_w--] = (c3_y) (off_s >> 8); + buf_y[i_w--] = (c3_y) off_s; + buf_y[i_w] = (c3_y) cod; + break; + } + + /* 31-bit direct args */ + case SWIP: case SWIN: { + c3_w off_l = u3t(op); + buf_y[i_w--] = (c3_y) (off_l >> 24); + buf_y[i_w--] = (c3_y) (off_l >> 16); + buf_y[i_w--] = (c3_y) (off_l >> 8); + buf_y[i_w--] = (c3_y) off_l; + buf_y[i_w] = (c3_y) cod; + break; + } + + /* literal index args */ + case FIBK: case FIBL: + case LIBK: case LIBL: + case BUSH: case SANB: + case KITB: case MITB: + case HILB: case HINB: + _n_prog_asm_inx(buf_y, &i_w, lit_s, cod); + pog_u->lit_u.non[lit_s++] = u3k(u3t(op)); + break; + + /* call site index args */ + case TICB: case KICB: { + _n_prog_asm_inx(buf_y, &i_w, cal_s, cod); + u3j_site* sit_u = &(pog_u->cal_u.sit_u[cal_s++]); + sit_u->axe = u3k(u3t(op)); + sit_u->pog_p = 0; + sit_u->bat = u3_none; + sit_u->bas = u3_none; + sit_u->loc = u3_none; + sit_u->lab = u3_none; + sit_u->jet_o = c3n; + sit_u->fon_o = c3n; + sit_u->cop_u = NULL; + sit_u->ham_u = NULL; + sit_u->fin_p = 0; + break; + } + } + } + ops = u3t(ops); + } + u3z(top); + // this assert will fail if we overflow a c3_w worth of instructions + u3_assert(u3_nul == ops); + // this is just a sanity check + u3_assert(u3_nul == sip); +} + +/* _n_prog_from_ops(): new program from _n_comp() product + */ +static u3n_prog* +_n_prog_from_ops(u3_noun ops) +{ + u3_noun sip; + u3n_prog* pog_u; + c3_w byc_w = 1, // HALT + cal_w = 0, + reg_w = 0, + lit_w = 0, + mem_w = 0; + + sip = _n_melt(ops, &byc_w, &cal_w, ®_w, &lit_w, &mem_w); + pog_u = _n_prog_new(byc_w, cal_w, reg_w, lit_w, mem_w); + _n_prog_asm(ops, pog_u, sip); + return pog_u; +} + +#if 0 +/* _n_print_stack(): print out the cap stack up to a designated "empty" + * used only for debugging + */ +static void _n_print_stack(u3p(u3_noun) empty) { + c3_w cur_p = u3R->cap_p; + fprintf(stderr, "["); + int first = 1; + while ( cur_p != empty ) { + if ( first ) { + first = 0; + } + else { + fprintf(stderr, " "); + } + if ( c3y == u3a_is_north(u3R) ) { + fprintf(stderr, "%u", *(u3to(u3_noun, cur_p))); + cur_p++; + } + else { + fprintf(stderr, "%u", *(u3to(u3_noun, cur_p-1))); + cur_p--; + } + } + fprintf(stderr, "]\r\n"); +} +#endif + +// Define X to select the opcode string representation from OPCODES. +# define X(opcode, name, indirect_jump) name +static c3_c* opcode_names[] = { OPCODES }; +# undef X + +/* _n_apen(): emit the instructions contained in src to dst + */ +static inline void +_n_apen(u3_noun* dst, u3_noun src) +{ + *dst = u3kb_weld(src, *dst); +} + +/* _n_emit(): emit a single instruction to ops + */ +static inline void +_n_emit(u3_noun *ops, u3_noun op) +{ + *ops = u3nc(op, *ops); +} + +static c3_w _n_comp(u3_noun*, u3_noun, c3_o, c3_o); + +/* _n_bint(): hint-processing helper for _n_comp. + * hif: hint-formula (first part of 11). RETAIN. + * nef: next-formula (second part of 11). RETAIN. + */ +static c3_w +_n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) +{ + c3_w tot_w = 0; + + if ( c3n == u3du(hif) ) { + // compile whitelisted atomic hints to dispatch protocol; + // compute and drop all others; + // + switch ( hif ) { + default: { + return _n_comp(ops, nef, los_o, tel_o); + } + case c3__cash: + case c3__xray: + case c3__meme: + case c3__nara: + case c3__hela: + case c3__bout: { + u3_noun fen = u3_nul; + c3_w nef_w = _n_comp(&fen, nef, los_o, c3n); + // add appropriate hind opcode + ++nef_w; _n_emit(&fen, ( c3y == los_o ) ? HILL : HILK); + // skip over the cleanup opcode + ++nef_w; _n_emit(&fen, u3nc(SBIP, 1)); + + // call hilt_fore + // HILB overflows to HILS + ++tot_w; _n_emit(ops, u3nc(HILB, u3nc(u3k(hif), u3k(nef)))); + // if fore return c3n, skip fen + ++tot_w; _n_emit(ops, u3nc(SBIN, nef_w)); + tot_w += nef_w; _n_apen(ops, fen); + // post-skip cleanup opcode + ++tot_w; _n_emit(ops, ( c3y == los_o ) ? TOSS : SWAP); + } break; + } + } + else { + u3_noun zep, hod; + u3x_cell(hif, &zep, &hod); + + switch ( zep ) { + default: { + // compile whitelisted dynamic hints to dispatch protocol; + // compute and drop all others; + // + switch ( zep ) { + default: { + tot_w += _n_comp(ops, hod, c3n, c3n); + ++tot_w; _n_emit(ops, TOSS); + tot_w += _n_comp(ops, nef, los_o, tel_o); + } break; + case c3__xray: + case c3__meme: + case c3__nara: + case c3__hela: + case c3__spin: + case c3__bout: { + u3_noun fen = u3_nul; + c3_w nef_w = _n_comp(&fen, nef, los_o, c3n); + // add appropriate hind opcode + ++nef_w; _n_emit(&fen, ( c3y == los_o ) ? HINL : HINK); + // skip over the cleanup opcode + ++nef_w; _n_emit(&fen, u3nc(SBIP, 1)); + + // push clue + tot_w += _n_comp(ops, hod, c3n, c3n); + // call hint_fore + // HINB overflows to HINS + ++tot_w; _n_emit(ops, u3nc(HINB, u3nc(u3k(zep), u3k(nef)))); + // if fore return c3n, skip fen + ++tot_w; _n_emit(ops, u3nc(SBIN, nef_w)); + tot_w += nef_w; _n_apen(ops, fen); + // post-skip cleanup opcode + ++tot_w; _n_emit(ops, ( c3y == los_o ) ? TOSS : SWAP); + } break; + } + } break; + + case c3__hunk: + case c3__lose: + case c3__mean: + case c3__spot: + tot_w += _n_comp(ops, hod, c3n, c3n); + ++tot_w; _n_emit(ops, u3nc(BUSH, zep)); // overflows to SUSH + tot_w += _n_comp(ops, nef, los_o, c3n); + ++tot_w; _n_emit(ops, DROP); + break; + + case c3__live: + tot_w += _n_comp(ops, hod, c3n, c3n); + ++tot_w; _n_emit(ops, HECK); + tot_w += _n_comp(ops, nef, los_o, tel_o); + break; + + case c3__slog: + tot_w += _n_comp(ops, hod, c3n, c3n); + ++tot_w; _n_emit(ops, SLOG); + tot_w += _n_comp(ops, nef, los_o, tel_o); + break; + + // germ and sole are unused... + + case c3__fast: + tot_w += _n_comp(ops, hod, c3n, c3n); + ++tot_w; _n_emit(ops, SWAP); + tot_w += _n_comp(ops, nef, los_o, c3n); + // overflows to SALT / SAST + ++tot_w; _n_emit(ops, (c3y == los_o) ? BALT : BAST); + break; + + case c3__memo: { + u3_noun mem = u3_nul; + c3_w mem_w = 0; + c3_y op_y; + + tot_w += _n_comp(ops, hod, c3n, c3n); + ++tot_w; _n_emit(ops, TOSS); + + mem_w += _n_comp(&mem, nef, c3y, c3n); + ++mem_w; _n_emit(&mem, SAVE); + + op_y = (c3y == los_o) ? SLIB : SKIB; // overflows to SLIS / SKIS + u3z_cid cid = u3z_memo_toss; + { + u3_weak con = u3r_skip(hod); + if ( (u3_none != con) && (c3y == u3du(con)) ) { + cid = u3z_memo_keep; + } + } + ++tot_w; _n_emit(ops, u3nq(op_y, cid, mem_w, u3k(nef))); + tot_w += mem_w; _n_apen(ops, mem); + break; + } + } + } + + return tot_w; +} + +static c3_t +_n_formulaic(u3_noun fol) +{ + u3_noun op, ar, a, b, c; + if ( c3n == u3r_cell(fol, &op, &ar) ) { + return 0; + } + if ( c3y == u3du(op) ) { + return _n_formulaic(op) && _n_formulaic(ar); + } + else switch ( op ) { + case 0: + return ( c3y == u3ud(ar) ); + case 1: + return 1; + case 3: + case 4: + return _n_formulaic(ar); + case 2: + case 5: + case 7: + case 8: + case 12: + return (c3y == u3r_cell(ar, &a, &b)) + && _n_formulaic(a) && _n_formulaic(b); + case 6: { + u3_noun lit; + + if ( c3n == u3r_trel(ar, &a, &b, &c) || !_n_formulaic(a) ) { + return 0; + } + + if ( c3n == u3r_safe(a, &lit) || u3_none == lit ) { + return _n_formulaic(b) || _n_formulaic(c); + } + + switch (lit) { + case 0: return _n_formulaic(b); + case 1: return _n_formulaic(c); + default: return 0; + } + } + case 9: + return (c3y == u3r_cell(ar, &a, &b)) + && (c3y == u3ud(a)) + && _n_formulaic(b); + case 10: + if ( c3n == u3r_cell(ar, &a, &b) ) { + return 0; + } + if ( c3n == u3du(a) ) { + return 0; + } + if ( c3n == u3ud(u3h(a)) ) { + return 0; + } + return _n_formulaic(u3t(a)) && _n_formulaic(b); + case 11: + if ( c3n == u3r_cell(ar, &a, &b) ) { + return 0; + } + if ( !_n_formulaic(b) ) { + return 0; + } + if ( c3y == u3ud(a) ) { + return 1; + } + else { + return ( c3y == u3ud(u3h(a)) ) && _n_formulaic(u3t(a)); + } + default: + return 0; + } +} + +/* _n_comp(): compile nock formula to reversed opcode list + * ops is a pointer to a list (to be emitted to) + * fol is the nock formula to compile. RETAIN. + * los_o indicates whether we should remove our + * subject from the stack + * tel_o is yes if this formula is in tail position + * return: number of instructions added to the opcode list + */ +static c3_w +_n_comp(u3_noun* ops, u3_noun fol, c3_o los_o, c3_o tel_o) +{ + c3_y op_y; + c3_w tot_w = 0; + u3_noun cod, arg, hed, tel; + u3x_cell(fol, &cod, &arg); + if ( c3y == u3du(cod) ) { + tot_w += _n_comp(ops, cod, c3n, c3n); + ++tot_w; _n_emit(ops, SWAP); + tot_w += _n_comp(ops, arg, los_o, c3n); + ++tot_w; _n_emit(ops, (c3y == los_o ) ? AULT : AUTO); + } + else switch ( cod ) { + case 0: + if ( c3n == u3ud(arg) ) { + u3m_bail(c3__exit); + return 0; + } + switch ( arg ) { + case 0: + ++tot_w; _n_emit(ops, BAIL); + break; + case 1: + if ( c3n == los_o ) { + ++tot_w; _n_emit(ops, COPY); + } + break; + case 2: + ++tot_w; _n_emit(ops, (c3y == los_o) ? HELD : HEAD); + break; + case 3: + ++tot_w; _n_emit(ops, (c3y == los_o) ? TALL : TAIL); + break; + default: + op_y = (c3y == los_o) + ? (arg <= 0xFF ? FABL : arg <= 0xFFFF ? FASL : FIBL) // overflows to FISL + : (arg <= 0xFF ? FABK : arg <= 0xFFFF ? FASK : FIBK); // overflows to FISK + ++tot_w; _n_emit(ops, u3nc(op_y, u3k(arg))); + break; + } + break; + + case 1: + switch ( arg ) { + case 0: + ++tot_w; _n_emit(ops, (c3y == los_o) ? LIL0 : LIT0); + break; + case 1: + ++tot_w; _n_emit(ops, (c3y == los_o) ? LIL1 : LIT1); + break; + default: + op_y = (c3y == los_o) + ? (arg <= 0xFF ? LILB : arg <= 0xFFFF ? LILS : LIBL) // overflows to LISL + : (arg <= 0xFF ? LITB : arg <= 0xFFFF ? LITS : LIBK); // overflows to LISK + ++tot_w; _n_emit(ops, u3nc(op_y, u3k(arg))); + break; + } + break; + + case 2: + u3x_cell(arg, &hed, &tel); + tot_w += _n_comp(ops, hed, c3n, c3n); + ++tot_w; _n_emit(ops, SWAP); + tot_w += _n_comp(ops, tel, los_o, c3n); + /* things in tail position replace (so, lose) top of stack, + * so NOCT "loses" and there is no non-losing version */ + op_y = (c3y == tel_o) ? NOCT + : ((c3y == los_o) ? NOLK : NOCK); + ++tot_w; _n_emit(ops, op_y); + break; + + case 3: + tot_w += _n_comp(ops, arg, los_o, c3n); + ++tot_w; _n_emit(ops, DEEP); + break; + + case 4: + tot_w += _n_comp(ops, arg, los_o, c3n); + ++tot_w; _n_emit(ops, BUMP); + break; + + case 5: { + u3x_cell(arg, &hed, &tel); + + if ( c3n == u3du(hed) ) { + u3m_bail(c3__exit); + return 0; + } + else { + c3_t hec_t, tec_t; + hec_t = (1 == u3h(hed)); + if ( c3n == u3du(tel) ) { + u3m_bail(c3__exit); + break; + } + else { + tec_t = (1 == u3h(tel)); + } + if ( hec_t && tec_t ) { + if ( c3y == u3r_sing(u3t(hed), u3t(tel)) ) { + ++tot_w; _n_emit(ops, (c3y == los_o) ? LIL0 : LIT0); + } + else { + ++tot_w; _n_emit(ops, (c3y == los_o) ? LIL1 : LIT1); + } + } + else if ( !hec_t && !tec_t ) { + tot_w += _n_comp(ops, hed, c3n, c3n); + ++tot_w; _n_emit(ops, SWAP); + tot_w += _n_comp(ops, tel, los_o, c3n); + ++tot_w; _n_emit(ops, (c3y == los_o) ? SALM : SAME); + } + else { + tot_w += _n_comp(ops, (hec_t ? tel : hed), los_o, c3n); + u3_noun lit = u3t(hec_t ? hed : tel); + switch ( lit ) { + case 0: + ++tot_w; _n_emit(ops, SAM0); + break; + case 1: + ++tot_w; _n_emit(ops, SAM1); + break; + default: + // overflows to SANS + op_y = lit <= 0xFF ? SAMB : lit <= 0xFFFF ? SAMS : SANB; + ++tot_w; _n_emit(ops, u3nc(op_y, u3k(lit))); + } + } + } + break; + } + + case 6: { + u3_noun mid, lit; + u3x_trel(arg, &hed, &mid, &tel); + if ( c3y == u3r_safe(hed, &lit) && u3_none != lit ) { + switch ( lit ) { + case 0: + tot_w += _n_comp(ops, mid, los_o, tel_o); + break; + + case 1: + tot_w += _n_comp(ops, tel, los_o, tel_o); + break; + + default: + ++tot_w; _n_emit(ops, BAIL); + break; + } + } + else { + u3_noun yep = u3_nul, + nop = u3_nul; + c3_w yep_w, nop_w; + c3_t yep_t, nop_t; + + tot_w += _n_comp(ops, hed, c3n, c3n); + yep_t = _n_formulaic(mid); + nop_t = _n_formulaic(tel); + + if ( !yep_t && !nop_t ) { + u3m_bail(c3__exit); + break; + } + + if ( yep_t ) { + yep_w = _n_comp(&yep, mid, los_o, tel_o); + } + else { + yep_w = 1; _n_emit(&yep, BAIL); + } + + if ( nop_t ) { + nop_w = _n_comp(&nop, tel, los_o, tel_o); + } + else { + nop_w = 1; _n_emit(&nop, BAIL); + } + + // SBIP and SBIN get sized during assembly + ++yep_w; _n_emit(&yep, u3nc(SBIP, nop_w)); + ++tot_w; _n_emit(ops, u3nc(SBIN, yep_w)); + tot_w += yep_w; _n_apen(ops, yep); + tot_w += nop_w; _n_apen(ops, nop); + } + break; + } + + case 7: + u3x_cell(arg, &hed, &tel); + tot_w += _n_comp(ops, hed, los_o, c3n); + tot_w += _n_comp(ops, tel, c3y, tel_o); + break; + + case 8: + u3x_cell(arg, &hed, &tel); + tot_w += _n_comp(ops, hed, c3n, c3n); + ++tot_w; _n_emit(ops, (c3y == los_o) ? SNOL : SNOC); + tot_w += _n_comp(ops, tel, c3y, tel_o); + break; + + case 9: + u3x_cell(arg, &hed, &tel); + if ( (1 == hed) || (3 == u3qc_cap(u3x_atom(hed))) ) { + u3_noun mac = u3nq(7, u3k(tel), 2, u3nt(u3nc(0, 1), 0, u3k(hed))); + tot_w += _n_comp(ops, mac, los_o, tel_o); + u3z(mac); + } + else { + tot_w += _n_comp(ops, tel, (c3y == tel_o ? c3y : los_o), c3n); + op_y = (c3y == tel_o) ? TICB : KICB; // overflows to TICS/KICS + ++tot_w; _n_emit(ops, u3nc(op_y, u3k(hed))); + } + break; + + case 10: { + u3_noun axe, nef; + u3x_cell(arg, &hed, &tel); + u3x_cell(hed, &axe, &nef); + tot_w += _n_comp(ops, tel, c3n, c3n); + ++tot_w; _n_emit(ops, SWAP); + tot_w += _n_comp(ops, nef, los_o, c3n); + + ++tot_w; + switch ( axe ) { + case 2: + _n_emit(ops, (c3y == los_o) ? MUTH : KUTH); + break; + + case 3: + _n_emit(ops, (c3y == los_o) ? MUTT : KUTT); + break; + + case u3x_sam: + _n_emit(ops, (c3y == los_o) ? MUSM : KUSM); + break; + + default: + op_y = (c3y == los_o) + ? (axe <= 0xFF) ? MUTB : (axe <= 0xFFFF) ? MUTS : MITB // overflows to MITS + : (axe <= 0xFF) ? KUTB : (axe <= 0xFFFF) ? KUTS : KITB; // overflows to KITS + _n_emit(ops, u3nc(op_y, u3k(axe))); + break; + } + break; + } + + case 11: + u3x_cell(arg, &hed, &tel); + tot_w += _n_bint(ops, hed, tel, los_o, tel_o); + break; + + case 12: + u3x_cell(arg, &hed, &tel); + tot_w += _n_comp(ops, hed, c3n, c3n); + ++tot_w; _n_emit(ops, SWAP); + tot_w += _n_comp(ops, tel, los_o, c3n); + ++tot_w; _n_emit(ops, (c3y == los_o) ? WILS : WISH); + break; + + default: + u3m_bail(c3__exit); + return 0; + } + return tot_w; +} + +/* _n_push(): push a noun onto the stack. RETAIN + * mov: -1 north, 1 south + * off: 0 north, -1 south + */ +static inline void +_n_push(c3_ys mov, c3_ys off, u3_noun a) +{ + u3R->cap_p += mov; + + // XX switch to u3a_push() + // +#ifndef U3_GUARD_PAGE + if ( 0 == off ) { + if( !(u3R->cap_p > u3R->hat_p) ) { + u3m_bail(c3__meme); + } + } + else { + if( !(u3R->cap_p < u3R->hat_p) ) { + u3m_bail(c3__meme); + } + } +#endif + + u3_noun* p = u3to(u3_noun, u3R->cap_p + off); + *p = a; +} + +/* _n_peek(): pointer to noun at top of stack + * off: 0 north, -1 south + */ +static inline u3_noun* +_n_peek(c3_ys off) +{ + return u3to(u3_noun, u3R->cap_p + off); +} + +/* _n_peet(): address of the next-to-top of stack + * mov: -1 north, 1 south + * off: 0 north, -1 south + */ +static inline u3_noun* +_n_peet(c3_ys mov, c3_ys off) +{ + return u3to(u3_noun, (u3R->cap_p - mov) + off); +} + +/* _n_pop(): pop a noun from the cap stack + * mov: -1 north, 1 south + */ +static inline void +_n_pop(c3_ys mov) +{ + u3R->cap_p -= mov; +} + +/* _n_pep(): pop and return noun from the cap stack + * mov: -1 north, 1 south + * off: 0 north, -1 south + */ +static inline u3_noun +_n_pep(c3_ys mov, c3_ys off) +{ + u3_noun r = *(_n_peek(off)); + _n_pop(mov); + return r; +} + +/* _n_toss(): pep and lose + */ +static inline void +_n_toss(c3_ys mov, c3_ys off) +{ + u3z(_n_pep(mov, off)); +} + +/* _n_resh(): read a c3_s from the bytecode stream + */ +static inline c3_s +_n_resh(c3_y* buf, c3_w* ip_w) +{ + c3_y les = buf[(*ip_w)++]; + c3_y mos = buf[(*ip_w)++]; + return les | (mos << 8); +} + +/* _n_rewo(): read a c3_w from the bytecode stream. + */ +static inline c3_w +_n_rewo(c3_y* buf, c3_w* ip_w) +{ + c3_y one = buf[(*ip_w)++], + two = buf[(*ip_w)++], + tre = buf[(*ip_w)++], + qua = buf[(*ip_w)++]; + return one | (two << 8) | (tre << 16) | (qua << 24); +} + +/* _n_swap(): swap two items on the top of the stack, return pointer to top + */ +static inline u3_noun* +_n_swap(c3_ys mov, c3_ys off) +{ + u3_noun* top = _n_peek(off); + u3_noun* up = _n_peet(mov, off); + u3_noun tmp = *up; + *up = *top; + *top = tmp; + return top; +} + +#ifdef VERBOSE_BYTECODE +/* _n_print_byc(): print bytecode. used for debugging. + */ +static void +_n_print_byc(c3_y* pog, c3_w her_w) +{ + c3_w ip_w = 0; + if ( her_w == 0 ) { + fprintf(stderr, "begin: {"); + } + else { + fprintf(stderr, "resume: {"); + } + int first = 1; + while ( pog[ip_w] ) { + if ( first ) { + first = 0; + } + else if (ip_w == her_w) { + fprintf(stderr, " [*]"); + } + else { + fprintf(stderr, " "); + } + switch ( _n_arg(pog[ip_w]) ) { + case 0: + fprintf(stderr, "%s", opcode_names[pog[ip_w++]]); + break; + + case 1: + fprintf(stderr, "[%s ", opcode_names[pog[ip_w++]]); + fprintf(stderr, "%u]", pog[ip_w++]); + break; + + case 2: + fprintf(stderr, "[%s ", opcode_names[pog[ip_w++]]); + fprintf(stderr, "%u]", _n_resh(pog, &ip_w)); + break; + + case 4: + fprintf(stderr, "[%s", opcode_names[pog[ip_w++]]); + fprintf(stderr, "%u]", _n_rewo(pog, &ip_w)); + break; + default: + u3_assert(0); + break; + } + } + fprintf(stderr, " halt}\r\n"); +} +#endif + +/* _n_bite(): compile a nock formula to bytecode. RETAIN. + */ +static inline u3n_prog* +_n_bite(u3_noun fol) { + u3_noun ops = u3_nul; + _n_comp(&ops, fol, c3y, c3y); + return _n_prog_from_ops(ops); +} + +static inline c3_w +_cn_of_prog(u3n_prog *pog_u) +{ + u3_post pog_p = u3of(u3n_prog, pog_u); + return pog_p >> u3a_vits; +} + +static inline u3n_prog* +_cn_to_prog(c3_w pog_w) +{ + u3_post pog_p = pog_w << u3a_vits; + return u3to(u3n_prog, pog_p); +} + +/* _n_find(): return prog for given formula with prefix (u3_nul for none). + * RETAIN. + */ +static u3n_prog* +_n_find(u3_noun pre, u3_noun fol) +{ + u3_noun key = u3nc(u3k(pre), u3k(fol)); + u3_weak pog = u3h_git(u3R->byc.har_p, key); + if ( u3_none != pog ) { + u3z(key); + return _cn_to_prog(pog); + } + else if ( u3R != &u3H->rod_u ) { + u3a_road* rod_u = u3R; + while ( rod_u->par_p ) { + rod_u = u3to(u3a_road, rod_u->par_p); + pog = u3h_git(rod_u->byc.har_p, key); + if ( u3_none != pog ) { + c3_w i_w; + u3n_prog* old = _n_prog_old(_cn_to_prog(pog)); + for ( i_w = 0; i_w < old->reg_u.len_w; ++i_w ) { + u3j_rite* rit_u = &(old->reg_u.rit_u[i_w]); + rit_u->own_o = c3n; + } + for ( i_w = 0; i_w < old->cal_u.len_w; ++i_w ) { + u3j_site* sit_u = &(old->cal_u.sit_u[i_w]); + sit_u->bat = u3_none; + sit_u->pog_p = 0; + sit_u->fon_o = c3n; + } + u3h_put(u3R->byc.har_p, key, _cn_of_prog(old)); + u3z(key); + return old; + } + } + } + + { + u3n_prog* gop = _n_bite(fol); + u3h_put(u3R->byc.har_p, key, _cn_of_prog(gop)); + u3z(key); + return gop; + } +} + +/* u3n_find(): return prog for given formula, + * split by key (u3_nul for no key). RETAIN. + */ +u3p(u3n_prog) +u3n_find(u3_noun key, u3_noun fol) +{ + u3p(u3n_prog) pog_p; + u3t_on(noc_o); + pog_p = u3of(u3n_prog, _n_find(key, fol)); + u3t_off(noc_o); + return pog_p; +} + +/* _cn_prog_free(): free memory retained by program pog_u +*/ +static void +_cn_prog_free(u3n_prog* pog_u) +{ + c3_w dex_w; + for (dex_w = 0; dex_w < pog_u->lit_u.len_w; ++dex_w) { + u3z(pog_u->lit_u.non[dex_w]); + } + for (dex_w = 0; dex_w < pog_u->mem_u.len_w; ++dex_w) { + u3z(pog_u->mem_u.sot_u[dex_w].key); + } + for (dex_w = 0; dex_w < pog_u->cal_u.len_w; ++dex_w) { + u3j_site_lose(&(pog_u->cal_u.sit_u[dex_w])); + } + for (dex_w = 0; dex_w < pog_u->reg_u.len_w; ++dex_w) { + u3j_rite_lose(&(pog_u->reg_u.rit_u[dex_w])); + } + u3a_free(pog_u); +} + +/* _cn_intlen(): find the number of characters num_w would take to print. +** num_w: an int we want to later serialize to a string +*/ +c3_w +_cn_intlen(c3_w num_w) +{ + c3_w len_w=0; + while(num_w){ + num_w/=10; + len_w++; + } + return len_w; +} + +/* _cn_is_indexed(): return true if bop_w is an opcodes that uses pog_u->lit_u.non +** bop_w: opcode (assumed 0-94) +*/ +c3_b +_cn_is_indexed(c3_w bop_w) +{ + switch (bop_w) { + case FIBK: case FISK: + case FIBL: case FISL: + case LIBK: case LISK: + case LIBL: case LISL: + case BUSH: case SUSH: + case SANB: case SANS: + case KITB: case KITS: + case MITB: case MITS: + case HILB: case HILS: + case HINB: case HINS: + return 1; + default: + return 0; + } +} + +/* _cn_pog_to_num(): read a bytecode from the steam and advance the index +** par_w: c3_w: can be 0, 2, 4 +** pog_y: c3_y*: a bytecode stream +** ip_w: c3_w: an index into pog +*/ +#define _cn_pog_to_num(par_w, pog_y, ip_w) (\ + par_w == 4 ? _n_rewo(pog_y, &ip_w): \ + par_w == 2 ? _n_resh(pog_y, &ip_w): \ + pog_y[ip_w++]) + +/* _cn_etch_bytecode(): render a nock program as string of bytecodes +** fol: a nock formula to compile and render +** returns: a u3i_string noun of the rendered bytecode +*/ +u3_noun +_cn_etch_bytecode(u3_noun fol) { + u3n_prog* pog_u = _n_bite(fol); + c3_y* pog_y = pog_u->byc_u.ops_y; + c3_w len_w = pog_u->byc_u.len_w; + c3_w ip_w=0, num_w=0, bop_w=0, dex_w=0; + c3_w len_c = 2; // closing "}", null terminator + // set par_w (parameter flag) to an invalid value, + // so we can break imeadately if needed + c3_w par_w = 5; + // lets count the chars in this string + while ( ip_w < len_w ) { + par_w = _n_arg(pog_y[ip_w]); + bop_w = pog_y[ip_w++]; // move ip_w for reading a opcode name + dex_w = _cn_is_indexed(bop_w); // is this an indexed bytecode argument + len_c += 5; // a leading space, and opcode name + if (par_w > 0) { // if pair: "[bytecode arg]" else "bytecode" + len_c += 3; // "[", space between opcode & arg, "]" + if ( dex_w ) len_c += 2; // 'i:' + len_c += _cn_intlen( // length of the bytecode argument + _cn_pog_to_num(par_w, pog_y, ip_w) + ); + } + } + // reset so we can loop again + ip_w=0, num_w=0, bop_w=0, dex_w=0, par_w=5; + // init our string, and give it a trailing null + c3_c str_c[len_c]; + str_c[0] = 0; + // lets print this string + while ( ip_w < len_w ) { + par_w = _n_arg(pog_y[ip_w]); + bop_w = pog_y[ip_w++]; // move ip_w for reading a opcode name + dex_w = _cn_is_indexed(bop_w); // is this an indexed bytecode argument + strcat(str_c, " "); // leading space + if (par_w > 0) strcat(str_c, "["); // add "[" if the opcode pairs + strncat(str_c, opcode_names[bop_w], 4); // add the opcode name + if (par_w > 0) { // finish the pair + strcat(str_c, " "); // add the space between byt and arg + if ( dex_w ) strcat(str_c, "i:"); // indexed args are labeled as "index of arg" + num_w = _cn_pog_to_num(par_w, pog_y, ip_w); // the bytecode argument + if (num_w == 0) { // + strcat(str_c, "0"); // handle a literal zero + } // + else { // + c3_w x = 0; // + for (x = _cn_intlen(num_w); x > 0; x--) { // + strcat(str_c, "_"); // prefill the buffer + } // + c3_w f = strlen(str_c)-1; // get the index of the last prefill + while (num_w > 0) { // stringify number in LSB order + str_c[f--] = (num_w%10)+'0'; // .. stringify the tail of num into tail of buf + num_w /= 10; // .. turncate num by one digit + } // + } // + strcat(str_c, "]"); // add the closing brace + } + } + // replace the first leading space and append the last char to the string + str_c[0] = '{'; + strcat(str_c, "}"); + _cn_prog_free(pog_u); + return u3i_string(str_c); +} + + +/* _n_hilt_fore(): literal (atomic) dynamic hint, before formula evaluation. +** hin: [hint-atom, formula]. TRANSFER +** bus: subject. RETAIN +** out: token for _n_hilt_hind(); convention: +** [hint-atom] or [hint-atom data], ~ if unused. +** +** any hints herein must be whitelisted in _n_burn(). +*/ +static c3_o +_n_hilt_fore(u3_noun hin, u3_noun bus, u3_noun* out) +{ + u3_noun tag, fol; + u3x_cell(hin, &tag, &fol); + + switch ( tag ) { + case c3__cash: { + u3_atom har = u3i_word(u3h_count(u3R->cax.har_p)); + u3h_discount(u3R->cax.har_p); + u3_atom per = u3i_word(u3h_count(u3R->cax.per_p)); + u3h_discount(u3R->cax.per_p); + *out = u3i_cell(tag, u3i_cell(har, per)); + } break; + + case c3__bout: { + u3_atom now = u3i_chub(u3t_trace_time()); + *out = u3i_cell(tag, now); + } break; + + case c3__nara : { + u3t_slog_nara(0); + *out = u3_nul; + } break; + + case c3__hela : { + u3t_slog_hela(0); + *out = u3_nul; + } break; + + case c3__xray : { + u3t_slog(u3nc(0, _cn_etch_bytecode(fol))); + *out = u3_nul; + } break; + + case c3__meme : { + u3t_slog(u3nc(0, u3t_etch_meme(0))); + *out = u3_nul; + } break; + + default: { + *out = u3_nul; + } break; + } + + u3z(hin); + return c3y; +} + +/* _n_hilt_hind(): literal (atomic) dynamic hint, after formula evaluation. +** tok: token from _n_hilt_fore(). TRANSFER +** pro: product of formula evaluation. RETAIN +*/ +static void +_n_hilt_hind(u3_noun tok, u3_noun pro) +{ + u3_noun p_tok, q_tok, r_tok; + if ( (c3y == u3r_cell(tok, &p_tok, &q_tok)) && (c3__bout == p_tok) ) { + u3_atom delta = u3ka_sub(u3i_chub(u3t_trace_time()), u3k(q_tok)); + c3_c str_c[64]; + u3a_print_time(str_c, "took", u3r_chub(0, delta)); + u3t_slog(u3nc(0, u3i_string(str_c))); + u3z(delta); + } + else if ( (c3y == u3r_trel(tok, &p_tok, &q_tok, &r_tok)) && + (c3__cash == p_tok) ) { + c3_c str_c[4096]; + + u3_atom har = u3i_word(u3h_count(u3R->cax.har_p)); + u3h_discount(u3R->cax.har_p); + u3_atom har_delta = u3ka_sub(har, u3k(q_tok)); + u3a_print_memory_str(str_c, "ephemeral cache", + u3r_word(0, har_delta)); + u3t_slog(u3nc(0, u3i_string(str_c))); + + u3_atom per = u3i_word(u3h_count(u3R->cax.per_p)); + u3h_discount(u3R->cax.per_p); + u3_atom per_delta = u3ka_sub(per, u3k(r_tok)); + u3a_print_memory_str(str_c, "persistent cache", + u3r_word(0, per_delta)); + u3t_slog(u3nc(0, u3i_string(str_c))); + + u3z(har_delta); + u3z(per_delta); + } + else { + u3_assert( u3_nul == tok ); + } + + u3z(tok); +} + +/* _n_hint_fore(): arbitrary dynamic hint, before formula evaluation +** hin: [hint-atom, formula]. TRANSFER +** bus: subject. RETAIN +** clu: product of the hint-formula. TRANSFER +** also, token for _n_hilt_hind(); convention: +** [hint-atom] or [hint-atom data], ~ if unused. +** +** any hints herein must be whitelisted in _n_burn(). +*/ +static c3_o +_n_hint_fore(u3_cell hin, u3_noun bus, u3_noun* clu) +{ + u3_noun tag, fol; + u3x_cell(hin, &tag, &fol); + + switch ( tag ) { + case c3__bout: { + u3_atom now = u3i_chub(u3t_trace_time()); + *clu = u3nt(u3k(tag), *clu, now); + } break; + + case c3__spin: { + u3t_sstack_push(*clu); + *clu = c3__spin; + } break; + + case c3__nara: { + u3_noun pri, tan; + if ( c3y == u3r_cell(*clu, &pri, &tan) ) { + c3_l pri_l = c3y == u3a_is_cat(pri) ? pri : 0; + u3t_slog_cap(pri_l, u3i_string("trace of"), u3k(tan)); + u3t_slog_nara(pri_l); + } + u3z(*clu); + *clu = u3_nul; + } break; + + case c3__hela: { + u3_noun pri, tan; + if ( c3y == u3r_cell(*clu, &pri, &tan) ) { + c3_l pri_l = c3y == u3a_is_cat(pri) ? pri : 0; + u3t_slog_cap(pri_l, u3i_string("trace of"), u3k(tan)); + u3t_slog_hela(pri_l); + } + u3z(*clu); + *clu = u3_nul; + } break; + + case c3__xray : { + u3_noun pri, tan; + if ( c3y == u3r_cell(*clu, &pri, &tan) ) { + c3_l pri_l = c3y == u3a_is_cat(pri) ? pri : 0; + u3t_slog_cap(pri_l, u3k(tan), _cn_etch_bytecode(fol)); + } + u3z(*clu); + *clu = u3_nul; + } break; + + case c3__meme : { + u3_noun pri, tan; + if ( c3y == u3r_cell(*clu, &pri, &tan) ) { + c3_l mod_l = c3y == u3a_is_cat(pri) ? pri : 0; + // replace with better str fmt + u3t_slog_cap(1, u3k(tan), u3t_etch_meme(mod_l)); + } + u3z(*clu); + *clu = u3_nul; + } break; + + default: { + u3z(*clu); + *clu = u3_nul; + } break; + } + + u3z(hin); + return c3y; +} + +/* _n_hint_hind(): arbitrary dynamic hint, after formula evaluation. +** tok: token from _n_hint_fore(). TRANSFER +** pro: product of formula evaluation. RETAIN +*/ +static void +_n_hint_hind(u3_noun tok, u3_noun pro) +{ + u3_noun p_tok, q_tok, r_tok; + if ( c3__spin == tok ) { + u3t_sstack_pop(); + } + else if ( (c3y == u3r_trel(tok, &p_tok, &q_tok, &r_tok)) && (c3__bout == p_tok) ) { + // get the microseconds elapsed + u3_atom delta = u3ka_sub(u3i_chub(u3t_trace_time()), u3k(r_tok)); + + // unpack q_tok to get the priority integer and the tank + // p_q_tok is the priority, q_q_tok is the tank we will work with + u3_noun p_q_tok, q_q_tok; + u3_assert(c3y == u3r_cell(q_tok, &p_q_tok, &q_q_tok)); + + // format the timing report + c3_c str_c[64]; + u3a_print_time(str_c, "took", u3r_chub(0, delta)); + + // join the timing report with the original tank from q_q_tok like so: + // "q_q_tok: report" + // prepend the priority to form a cell of the same shape q_tok + // send this to ut3_slog so that it can be logged out + c3_l pri_l = c3y == u3a_is_cat(p_q_tok) ? p_q_tok : 0; + u3t_slog_cap(pri_l, u3k(q_q_tok), u3i_string(str_c)); + u3z(delta); + } + else { + u3_assert( u3_nul == tok ); + } + + u3z(tok); +} + +/* _n_kick(): stop tracing noc and kick a u3j_site. + */ +static u3_weak +_n_kick(u3_noun cor, u3j_site* sit_u) +{ + u3_weak pro; + u3t_off(noc_o); + pro = u3j_site_kick(cor, sit_u); + u3t_on(noc_o); + return pro; +} + +/* _n_kale(): bail(exit) if not cell + */ +static inline u3_noun +_n_kale(u3_noun a) +{ + if ( c3n == u3du(a) ) { + u3m_bail(c3__exit); + } + return a; +} + +typedef struct __attribute__((__packed__)) { + u3n_prog* pog_u; + c3_w ip_w; +} burnframe; + +/* _n_burn(): pog: program + * bus: subject (TRANSFER) + * mov: -1 north, 1 south + * off: 0 north, -1 south + */ +static u3_noun +_n_burn(u3n_prog* pog_u, u3_noun bus, c3_ys mov, c3_ys off) +{ + + // Opcode jump table. Define X to select the opcode computed goto from + // OPCODES. +# define X(opcode, name, indirect_jump) indirect_jump + static void* lab[] = { OPCODES }; +# undef X + + u3j_site* sit_u; + u3j_rite* rit_u; + u3n_memo* mem_u; + c3_y *pog = pog_u->byc_u.ops_y; + c3_w sip_w, ip_w = 0; + u3_noun* top; + u3_noun x, o; + u3p(void) empty; + burnframe* fam; + + empty = u3R->cap_p; + _n_push(mov, off, bus); + +#ifdef U3_CPU_DEBUG + u3R->pro.nox_d += 1; +#endif +#ifdef VERBOSE_BYTECODE + #define BURN() fprintf(stderr, "%s ", opcode_names[pog[ip_w]]); goto *lab[pog[ip_w++]] +#else + #define BURN() goto *lab[pog[ip_w++]] +#endif + BURN(); + { + do_halt: // [product ...burnframes...] + x = _n_pep(mov, off); +#ifdef VERBOSE_BYTECODE + fprintf(stderr, "return\r\n"); +#endif + if ( empty == u3R->cap_p ) { + return x; + } + else { + fam = u3to(burnframe, u3R->cap_p) + off; + pog_u = fam->pog_u; + pog = pog_u->byc_u.ops_y; + ip_w = fam->ip_w; + + u3R->cap_p = u3of(burnframe, fam - (mov+off)); + _n_push(mov, off, x); +#ifdef VERBOSE_BYTECODE + _n_print_byc(pog, ip_w); +#endif + BURN(); + } + + do_bail: + u3m_bail(c3__exit); + return u3_none; + + do_copy: + top = _n_peek(off); + _n_push(mov, off, u3k(*top)); + BURN(); + + do_swap: + _n_swap(mov, off); + BURN(); + + do_toss: + _n_toss(mov, off); + BURN(); + + do_auto: // [tel bus hed] + x = _n_pep(mov, off); // [bus hed] + top = _n_swap(mov, off); // [hed bus] + *top = u3nc(*top, x); // [pro bus] + BURN(); + + do_ault: // [tel hed] + x = _n_pep(mov, off); // [hed] + top = _n_peek(off); + *top = u3nc(*top, x); // [pro] + BURN(); + + do_snoc: // [hed tel] + x = _n_pep(mov, off); + top = _n_peek(off); + _n_push(mov, off, u3nc(x, u3k(*top))); + BURN(); + + do_snol: + x = _n_pep(mov, off); + top = _n_peek(off); + *top = u3nc(x, *top); + BURN(); + + do_head: + top = _n_peek(off); + _n_push(mov, off, u3k(u3h(_n_kale(*top)))); + BURN(); + + do_held: + top = _n_peek(off); + o = _n_kale(*top); + *top = u3k(u3h(o)); + u3z(o); + BURN(); + + do_tail: + top = _n_peek(off); + _n_push(mov, off, u3k(u3t(_n_kale(*top)))); + BURN(); + + do_tall: + top = _n_peek(off); + o = _n_kale(*top); + *top = u3k(u3t(o)); + u3z(o); + BURN(); + + do_fisk: + x = pog_u->lit_u.non[_n_resh(pog, &ip_w)]; + goto frag_in; + + do_fibk: + x = pog_u->lit_u.non[pog[ip_w++]]; + goto frag_in; + + do_fask: + x = _n_resh(pog, &ip_w); + goto frag_in; + + do_fabk: + x = pog[ip_w++]; + frag_in: + top = _n_peek(off); + _n_push(mov, off, u3k(u3x_at(x, *top))); + BURN(); + + do_fisl: + x = pog_u->lit_u.non[_n_resh(pog, &ip_w)]; + goto flag_in; + + do_fibl: + x = pog_u->lit_u.non[pog[ip_w++]]; + goto flag_in; + + do_fasl: + x = _n_resh(pog, &ip_w); + goto flag_in; + + do_fabl: + x = pog[ip_w++]; + flag_in: + top = _n_peek(off); + o = *top; + *top = u3k(u3x_at(x, o)); + u3z(o); + BURN(); + + do_lit0: + _n_push(mov, off, 0); + BURN(); + + do_lit1: + _n_push(mov, off, 1); + BURN(); + + do_litb: + _n_push(mov, off, pog[ip_w++]); + BURN(); + + do_lits: + _n_push(mov, off, _n_resh(pog, &ip_w)); + BURN(); + + do_libk: + _n_push(mov, off, u3k(pog_u->lit_u.non[pog[ip_w++]])); + BURN(); + + do_lisk: + _n_push(mov, off, u3k(pog_u->lit_u.non[_n_resh(pog, &ip_w)])); + BURN(); + + do_lil1: + x = 1; + goto lil_in; + + do_lilb: + x = pog[ip_w++]; + goto lil_in; + + do_lils: + x = _n_resh(pog, &ip_w); + goto lil_in; + + do_libl: + x = u3k(pog_u->lit_u.non[pog[ip_w++]]); + goto lil_in; + + do_lisl: + x = u3k(pog_u->lit_u.non[_n_resh(pog, &ip_w)]); + goto lil_in; + + do_lil0: + x = 0; + lil_in: + top = _n_peek(off); + u3z(*top); + *top = x; + BURN(); + + do_noct: // [fol bus] + o = _n_pep(mov, off); // [bus] + goto nock_out; + + do_nolk: // [fol bus] + o = _n_pep(mov, off); // [bus] + goto nock_in; + + do_nock: // [fol old bus] + o = _n_pep(mov, off); // [old bus] + _n_swap(mov, off); // [bus old] + nock_in: + x = _n_pep(mov, off); + fam = u3to(burnframe, u3R->cap_p) + off + mov; + u3R->cap_p = u3of(burnframe, fam - off); + fam->ip_w = ip_w; + fam->pog_u = pog_u; + _n_push(mov, off, x); + nock_out: + pog_u = _n_find(u3_nul, o); + pog = pog_u->byc_u.ops_y; + ip_w = 0; +#ifdef U3_CPU_DEBUG + u3R->pro.nox_d += 1; +#endif +#ifdef VERBOSE_BYTECODE + fprintf(stderr, "\r\nnock jump: %u\r\n", o); + _n_print_byc(pog, ip_w); +#endif + u3z(o); + BURN(); + + do_deep: + top = _n_peek(off); + o = *top; + *top = u3du(o); + u3z(o); + BURN(); + + do_bump: + top = _n_peek(off); + *top = u3i_vint(*top); + BURN(); + + do_sam0: + top = _n_peek(off); + if ( *top == 0 ) { + *top = c3y; + } + else { + u3z(*top); + *top = c3n; + } + BURN(); + + do_sam1: + top = _n_peek(off); + if ( *top == 1 ) { + *top = c3y; + } + else { + u3z(*top); + *top = c3n; + } + BURN(); + + do_samb: + top = _n_peek(off); + if ( *top == pog[ip_w++] ) { + *top = c3y; + } + else { + u3z(*top); + *top = c3n; + } + BURN(); + + do_sams: + top = _n_peek(off); + if ( *top == _n_resh(pog, &ip_w) ) { + *top = c3y; + } + else { + u3z(*top); + *top = c3n; + } + BURN(); + + do_sans: + x = pog_u->lit_u.non[_n_resh(pog, &ip_w)]; + goto samn_in; + do_sanb: + x = pog_u->lit_u.non[pog[ip_w++]]; + samn_in: + top = _n_peek(off); + o = *top; + *top = u3r_sing(o, x); + u3z(o); + BURN(); + + do_same: + x = _n_pep(mov, off); + _n_swap(mov, off); + goto same_in; + + do_salm: + x = _n_pep(mov, off); + goto same_in; + + same_in: + top = _n_peek(off); + o = *top; + *top = u3r_sing(x, o); + u3z(o); + u3z(x); + BURN(); + + do_samc: + top = _n_peek(off); + o = *top; + *top = u3r_sing(u3h(o), u3t(o)); + u3z(o); + BURN(); + + do_sbip: + sip_w = pog[ip_w++]; + ip_w += sip_w; + BURN(); + + do_sips: + sip_w = _n_resh(pog, &ip_w); + ip_w += sip_w; + BURN(); + + do_swip: + sip_w = _n_rewo(pog, &ip_w); + ip_w += sip_w; + BURN(); + + do_swin: + sip_w = _n_rewo(pog, &ip_w); + goto skin_in; + + do_sins: + sip_w = _n_resh(pog, &ip_w); + goto skin_in; + + do_sbin: + sip_w = pog[ip_w++]; + skin_in: + x = _n_pep(mov, off); + if ( c3n == x ) { + ip_w += sip_w; + } + else if ( c3y != x ) { + u3m_bail(c3__exit); + return u3_none; + } + BURN(); + + do_kics: + x = _n_resh(pog, &ip_w); + goto kick_in; + + do_kicb: + x = pog[ip_w++]; + kick_in: + sit_u = &(pog_u->cal_u.sit_u[x]); + top = _n_peek(off); + o = *top; + *top = _n_kick(o, sit_u); + if ( u3_none == *top ) { + _n_pop(mov); + + fam = u3to(burnframe, u3R->cap_p) + off + mov; + u3R->cap_p = u3of(burnframe, fam - off); + fam->ip_w = ip_w; + fam->pog_u = pog_u; + + pog_u = u3to(u3n_prog, sit_u->pog_p); + pog = pog_u->byc_u.ops_y; + ip_w = 0; +#ifdef U3_CPU_DEBUG + u3R->pro.nox_d += 1; +#endif +#ifdef VERBOSE_BYTECODE + fprintf(stderr, "\r\nhead kick jump: %u, sp: %p\r\n", u3r_at(sit_u->axe, cor), top); + _n_print_byc(pog, ip_w); +#endif + _n_push(mov, off, o); + } +#ifdef VERBOSE_BYTECODE + else { + fprintf(stderr, "head jet\r\n"); + } +#endif + BURN(); + + do_tics: + x = _n_resh(pog, &ip_w); + goto tick_in; + + do_ticb: + x = pog[ip_w++]; + tick_in: + sit_u = &(pog_u->cal_u.sit_u[x]); + top = _n_peek(off); + o = *top; + *top = _n_kick(o, sit_u); + if ( u3_none == *top ) { + *top = o; + pog_u = u3to(u3n_prog, sit_u->pog_p); + pog = pog_u->byc_u.ops_y; + ip_w = 0; +#ifdef U3_CPU_DEBUG + u3R->pro.nox_d += 1; +#endif +#ifdef VERBOSE_BYTECODE + fprintf(stderr, "\r\ntail kick jump: %u, sp: %p\r\n", u3x_at(sit_u->axe, o);, top); + _n_print_byc(pog, ip_w); +#endif + } +#ifdef VERBOSE_BYTECODE + else { + fprintf(stderr, "tail jet\r\n"); + } +#endif + BURN(); + + do_wils: // [gof ref] + o = _n_pep(mov,off); // [ref] + top = _n_peek(off); + goto wish_in; + + do_wish: // [gof bus ref] + o = _n_pep(mov,off); // [bus ref] + top = _n_swap(mov, off); // [ref bus] + wish_in: + u3t_off(noc_o); + x = u3m_soft_esc(u3k(*top), u3k(o)); + u3t_on(noc_o); + + if ( c3n == u3du(x) ) { + u3m_bail(u3nc(1, o)); + return u3_none; + } + else if ( c3n == u3du(u3t(x)) ) { + u3t_push(u3nt(c3__hunk, *top, o)); + u3m_bail(c3__exit); + return u3_none; + } + else { + u3z(o); + u3z(*top); + *top = u3k(u3t(u3t(x))); + u3z(x); + BURN(); + } + + do_sush: + x = _n_resh(pog, &ip_w); + goto cush_in; + + do_bush: + x = pog[ip_w++]; + cush_in: + x = u3k(pog_u->lit_u.non[x]); + o = _n_pep(mov, off); + u3t_push(u3nc(x, o)); + BURN(); + + do_drop: + u3t_drop(); + BURN(); + + do_heck: + x = _n_pep(mov, off); + if ( c3y == u3ud(x) ) { + u3t_off(noc_o); + u3t_heck(x); + u3t_on(noc_o); + } + else { + u3z(x); + } + BURN(); + + do_slog: + x = _n_pep(mov, off); + if ( !(u3C.wag_w & u3o_quiet) ) { + u3t_off(noc_o); + u3t_slog(x); + u3t_on(noc_o); + } + else { + u3z(x); + } + BURN(); + + + do_sast: + x = _n_resh(pog, &ip_w); + goto fast_in; + + do_bast: + x = pog[ip_w++]; + goto fast_in; + + do_salt: + x = _n_resh(pog, &ip_w); + goto falt_in; + do_balt: + x = pog[ip_w++]; + falt_in: // [pro clu] + o = _n_pep(mov, off); // [clu] + top = _n_peek(off); + goto fast_out; + + fast_in: // [pro bus clu] + o = _n_pep(mov, off); // [bus clu] + top = _n_swap(mov, off); // [clu bus] + fast_out: + rit_u = &(pog_u->reg_u.rit_u[x]); + u3t_off(noc_o); + u3j_rite_mine(rit_u, *top, u3k(o)); + u3t_on(noc_o); + *top = o; + BURN(); + + do_skis: + x = _n_resh(pog, &ip_w); + goto skim_in; + + do_skib: + x = pog[ip_w++]; + skim_in: + mem_u = &(pog_u->mem_u.sot_u[x]); + top = _n_peek(off); + x = u3k(*top); + goto skim_out; + + do_slis: + x = _n_resh(pog, &ip_w); + goto slim_in; + + do_slib: + x = pog[ip_w++]; + slim_in: + mem_u = &(pog_u->mem_u.sot_u[x]); + x = _n_pep(mov, off); + skim_out: + o = u3k(mem_u->key); + x = u3nc(x, o); + o = u3z_find_m(mem_u->cid, 144 + c3__nock, x); + if ( u3_none == o ) { + _n_push(mov, off, u3nc(mem_u->cid, x)); + _n_push(mov, off, u3k(u3h(x))); + } + else { + ip_w += mem_u->sip_l; + _n_push(mov, off, o); + u3z(x); + } + BURN(); + + do_save: + x = _n_pep(mov, off); // product + top = _n_peek(off); + o = *top; + if ( ( u3z_memo_toss == u3h(o) ) + ? ( &(u3H->rod_u) != u3R ) + : ( 0 == u3R->ski.gul ) ) { // prevents userspace from persistence + u3z_save_m(u3h(o), 144 + c3__nock, u3t(o), x); + } + // XX can we still print? + // else if ( u3z_memo_keep == u3h(o) ) { + // fprintf(stderr, "\r\nnock: userspace can't save to persistent cache\r\n"); + // } + *top = x; + u3z(o); + BURN(); + + do_hilb: + x = pog[ip_w++]; + goto hilt_fore_in; + + do_hils: + x = _n_resh(pog, &ip_w); + hilt_fore_in: + x = u3k(pog_u->lit_u.non[x]); + top = _n_peek(off); // bus + x = _n_hilt_fore(x, *top, &o); + _n_push(mov, off, o); + _n_swap(mov, off); // bus + _n_push(mov, off, x); // shortcircuit if c3n + BURN(); + + do_hinb: + x = pog[ip_w++]; + goto hint_fore_in; + + do_hins: + x = _n_resh(pog, &ip_w); + hint_fore_in: // [clu bus] + x = u3k(pog_u->lit_u.non[x]); + o = _n_pep(mov, off); // [bus] + top = _n_peek(off); + x = _n_hint_fore(x, *top, &o); + _n_push(mov, off, o); // [tok bus] + _n_swap(mov, off); // [bus tok] + _n_push(mov, off, x); // [kip bus tok] + BURN(); + + do_hilk: // [pro bus tok] + x = _n_pep(mov, off); // [bus tok] + _n_swap(mov, off); // [tok bus] + o = _n_pep(mov, off); // [bus] + _n_push(mov, off, x); // [pro bus] + _n_hilt_hind(o, x); + BURN(); + + do_hill: // [pro tok] + top = _n_swap(mov, off); // [tok pro] + o = _n_pep(mov, off); // [pro] + top = _n_peek(off); + _n_hilt_hind(o, *top); + BURN(); + + do_hink: // [pro bus tok] + x = _n_pep(mov, off); // [bus tok] + _n_swap(mov, off); // [tok bus] + o = _n_pep(mov, off); // [bus] + _n_push(mov, off, x); // [pro bus] + _n_hint_hind(o, x); + BURN(); + + do_hinl: // [pro tok] + top = _n_swap(mov, off); // [tok pro] + o = _n_pep(mov, off); // [pro] + top = _n_peek(off); + _n_hint_hind(o, *top); + BURN(); + + do_kuth: + x = _n_pep(mov, off); + top = _n_swap(mov, off); + goto muth_in; + do_muth: + x = _n_pep(mov, off); + top = _n_peek(off); + muth_in: + o = *top; + *top = u3nc(x, u3k(u3t(o))); + u3z(o); + BURN(); + + do_kutt: + x = _n_pep(mov, off); + top = _n_swap(mov, off); + goto mutt_in; + do_mutt: + x = _n_pep(mov, off); + top = _n_peek(off); + mutt_in: + o = *top; + *top = u3nc(u3k(u3h(o)), x); + u3z(o); + BURN(); + + do_kusm: + x = _n_pep(mov, off); + top = _n_swap(mov, off); + goto musm_in; + do_musm: + x = _n_pep(mov, off); + top = _n_peek(off); + musm_in: + o = *top; + *top = u3nt(u3k(u3h(o)), x, u3k(u3t(u3t(o)))); + u3z(o); + BURN(); + + do_kitb: + x = pog_u->lit_u.non[pog[ip_w++]]; + goto kut_in; + + do_kits: + x = pog_u->lit_u.non[_n_resh(pog, &ip_w)]; + goto kut_in; + + do_kuts: + x = _n_resh(pog, &ip_w); + goto kut_in; + + do_kutb: + x = pog[ip_w++]; + kut_in: + o = _n_pep(mov, off); + top = _n_swap(mov, off); + goto edit_in; + + do_mitb: + x = pog_u->lit_u.non[pog[ip_w++]]; + goto mut_in; + + do_mits: + x = pog_u->lit_u.non[_n_resh(pog, &ip_w)]; + goto mut_in; + + do_muts: + x = _n_resh(pog, &ip_w); + goto mut_in; + + do_mutb: + x = pog[ip_w++]; + mut_in: + o = _n_pep(mov, off); + top = _n_peek(off); + edit_in: + *top = u3i_edit(*top, x, o); + BURN(); + } +} + +/* _n_burn_out(): execute u3n_prog with bus as subject. + */ +static u3_noun +_n_burn_out(u3_noun bus, u3n_prog* pog_u) +{ + c3_ys mov, off; + if ( c3y == u3a_is_north(u3R) ) { + mov = -1; + off = 0; + } + else { + mov = 1; + off = -1; + } + return _n_burn(pog_u, bus, mov, off); +} + +/* u3n_burn(): execute u3n_prog with bus as subject. + */ +u3_noun +u3n_burn(u3p(u3n_prog) pog_p, u3_noun bus) +{ + u3_noun pro; + u3t_on(noc_o); + pro = _n_burn_out(bus, u3to(u3n_prog, pog_p)); + u3t_off(noc_o); + return pro; +} + +/* _n_burn_on(): produce .*(bus fol) with bytecode interpreter + */ +static u3_noun +_n_burn_on(u3_noun bus, u3_noun fol) +{ + u3n_prog* pog_u = _n_find(u3_nul, fol); + + u3z(fol); + return _n_burn_out(bus, pog_u); +} + +/* u3n_nock_on(): produce .*(bus fol). Do not virtualize. +*/ +u3_noun +u3n_nock_on(u3_noun bus, u3_noun fol) +{ + u3_noun pro; + + u3t_on(noc_o); +#if 0 + pro = _n_nock_on(bus, fol); +#else + pro = _n_burn_on(bus, fol); +#endif + u3t_off(noc_o); + + return pro; +} + +/* _cn_take_prog_dat(): take references from junior u3n_prog. +*/ +static void +_cn_take_prog_dat(u3n_prog* dst_u, u3n_prog* src_u) +{ + c3_w i_w; + + for ( i_w = 0; i_w < src_u->lit_u.len_w; ++i_w ) { + dst_u->lit_u.non[i_w] = u3a_take(src_u->lit_u.non[i_w]); + } + + for ( i_w = 0; i_w < src_u->mem_u.len_w; ++i_w ) { + u3n_memo* emo_u = &(src_u->mem_u.sot_u[i_w]); + u3n_memo* ome_u = &(dst_u->mem_u.sot_u[i_w]); + ome_u->sip_l = emo_u->sip_l; + ome_u->key = u3a_take(emo_u->key); + ome_u->cid = emo_u->cid; + } + + for ( i_w = 0; i_w < src_u->cal_u.len_w; ++i_w ) { + u3j_site_take(&(dst_u->cal_u.sit_u[i_w]), + &(src_u->cal_u.sit_u[i_w])); + } + + for ( i_w = 0; i_w < src_u->reg_u.len_w; ++i_w ) { + u3j_rite_take(&(dst_u->reg_u.rit_u[i_w]), + &(src_u->reg_u.rit_u[i_w])); + } +} + +/* _cn_take_prog_cb(): u3h_take_with cb for taking junior u3n_prog's. +*/ +static u3p(u3n_prog) +_cn_take_prog_cb(c3_w pog_w) +{ + u3n_prog* pog_u = _cn_to_prog(pog_w); + u3n_prog* gop_u; + + if ( c3y == pog_u->byc_u.own_o ) { + c3_w pad_w = (8 - pog_u->byc_u.len_w % 8) % 8; + gop_u = _n_prog_new(pog_u->byc_u.len_w, + pog_u->cal_u.len_w, + pog_u->reg_u.len_w, + pog_u->lit_u.len_w, + pog_u->mem_u.len_w); + memcpy(gop_u->byc_u.ops_y, pog_u->byc_u.ops_y, pog_u->byc_u.len_w + pad_w); + } + else { + gop_u = _n_prog_old(pog_u); + } + + _cn_take_prog_dat(gop_u, pog_u); + // _n_prog_take_dat(gop_u, pog_u, c3n); + + return _cn_of_prog(gop_u); +} + +/* u3n_take(): copy junior bytecode state. +*/ +u3p(u3h_root) +u3n_take(u3p(u3h_root) har_p) +{ + return u3h_take_with(har_p, _cn_take_prog_cb); +} + +/* _cn_merge_prog_dat(): copy references from src_u u3n_prog to dst_u. +*/ +static void +_cn_merge_prog_dat(u3n_prog* dst_u, u3n_prog* src_u) +{ + c3_w i_w; + + for ( i_w = 0; i_w < src_u->lit_u.len_w; ++i_w ) { + u3z(dst_u->lit_u.non[i_w]); + dst_u->lit_u.non[i_w] = src_u->lit_u.non[i_w]; + } + + for ( i_w = 0; i_w < src_u->mem_u.len_w; ++i_w ) { + u3n_memo* emo_u = &(dst_u->mem_u.sot_u[i_w]); + u3n_memo* ome_u = &(src_u->mem_u.sot_u[i_w]); + u3z(emo_u->key); + emo_u->sip_l = ome_u->sip_l; + emo_u->key = ome_u->key; + emo_u->cid = ome_u->cid; + } + + for ( i_w = 0; i_w < src_u->cal_u.len_w; ++i_w ) { + u3j_site_merge(&(dst_u->cal_u.sit_u[i_w]), + &(src_u->cal_u.sit_u[i_w])); + } + + for ( i_w = 0; i_w < src_u->reg_u.len_w; ++i_w ) { + u3j_rite_merge(&(dst_u->reg_u.rit_u[i_w]), + &(src_u->reg_u.rit_u[i_w])); + } +} + +/* _cn_merge_prog_cb(): u3h_walk_with cb for integrating taken u3n_prog's. +*/ +static void +_cn_merge_prog_cb(u3_noun kev, void* wit) +{ + u3p(u3h_root) har_p = *(u3p(u3h_root)*)wit; + u3n_prog* pog_u; + u3_weak got; + u3_noun key; + c3_w pog_w; + u3x_cell(kev, &key, &pog_w); + + pog_u = _cn_to_prog(pog_w); + got = u3h_git(har_p, key); + + if ( u3_none != got ) { + u3n_prog* sep_u = _cn_to_prog(got); + _cn_merge_prog_dat(sep_u, pog_u); + u3a_free(pog_u); + pog_u = sep_u; + } + + u3h_put(har_p, key, _cn_of_prog(pog_u)); +} + +/* u3n_reap(): promote bytecode state. +*/ +void +u3n_reap(u3p(u3h_root) har_p) +{ + u3h_walk_with(har_p, _cn_merge_prog_cb, &u3R->byc.har_p); + // NB *not* u3n_free, _cn_merge_prog_cb() transfers u3n_prog's + u3h_free(har_p); +} + +/* _n_ream(): ream program call sites +*/ +void +_n_ream(u3_noun kev) +{ + u3n_prog* pog_u = _cn_to_prog(u3t(kev)); + + c3_w pad_w = (8 - pog_u->byc_u.len_w % 8) % 8; + c3_w pod_w = pog_u->lit_u.len_w % 2; + c3_w ped_w = pog_u->mem_u.len_w % 2; + // fix up pointers for loom portability + pog_u->byc_u.ops_y = (c3_y*) _n_prog_dat(pog_u); + pog_u->lit_u.non = (u3_noun*) (pog_u->byc_u.ops_y + pog_u->byc_u.len_w + pad_w); + pog_u->mem_u.sot_u = (u3n_memo*) (pog_u->lit_u.non + pog_u->lit_u.len_w + pod_w); + pog_u->cal_u.sit_u = (u3j_site*) (pog_u->mem_u.sot_u + pog_u->mem_u.len_w + ped_w); + pog_u->reg_u.rit_u = (u3j_rite*) (pog_u->cal_u.sit_u + pog_u->cal_u.len_w); + + for ( c3_w i_w = 0; i_w < pog_u->cal_u.len_w; ++i_w ) { + u3j_site_ream(&(pog_u->cal_u.sit_u[i_w])); + } +} + +/* u3n_ream(): refresh after restoring from checkpoint. +*/ +void +u3n_ream() +{ + u3_assert(u3R == &(u3H->rod_u)); + u3h_walk(u3R->byc.har_p, _n_ream); +} + +/* _n_prog_mark(): mark program for gc. +*/ +static c3_w +_n_prog_mark(u3n_prog* pog_u) +{ + c3_w i_w, tot_w = u3a_mark_mptr(pog_u); + + for ( i_w = 0; i_w < pog_u->lit_u.len_w; ++i_w ) { + tot_w += u3a_mark_noun(pog_u->lit_u.non[i_w]); + } + + for ( i_w = 0; i_w < pog_u->mem_u.len_w; ++i_w ) { + tot_w += u3a_mark_noun(pog_u->mem_u.sot_u[i_w].key); + } + + for ( i_w = 0; i_w < pog_u->cal_u.len_w; ++i_w ) { + tot_w += u3j_site_mark(&(pog_u->cal_u.sit_u[i_w])); + } + + for ( i_w = 0; i_w < pog_u->reg_u.len_w; ++i_w ) { + tot_w += u3j_rite_mark(&(pog_u->reg_u.rit_u[i_w])); + } + + return tot_w; +} + +/* _n_bam(): u3h_walk_with helper for u3n_mark + */ +static void +_n_bam(u3_noun kev, void* dat) +{ + u3n_prog* pog = _cn_to_prog(u3t(kev)); + c3_w* bam_w = dat; + + *bam_w += _n_prog_mark(pog); +} + +/* u3n_mark(): mark the bytecode cache for gc. + */ +u3m_quac* +u3n_mark() +{ + u3m_quac** qua_u = c3_malloc(sizeof(*qua_u) * 3); + + qua_u[0] = c3_calloc(sizeof(*qua_u[0])); + qua_u[0]->nam_c = strdup("bytecode programs"); + + u3p(u3h_root) har_p = u3R->byc.har_p; + u3h_walk_with(har_p, _n_bam, &qua_u[0]->siz_w); + qua_u[0]->siz_w = qua_u[0]->siz_w * 4; + + qua_u[1] = c3_calloc(sizeof(*qua_u[1])); + qua_u[1]->nam_c = strdup("bytecode cache"); + qua_u[1]->siz_w = u3h_mark(har_p) * 4; + + qua_u[2] = NULL; + + u3m_quac* tot_u = c3_malloc(sizeof(*tot_u)); + tot_u->nam_c = strdup("total nock stuff"); + tot_u->siz_w = qua_u[0]->siz_w + qua_u[1]->siz_w; + tot_u->qua_u = qua_u; + + return tot_u; +} + +/* u3n_reclaim(): clear ad-hoc persistent caches to reclaim memory. +*/ +void +u3n_reclaim(void) +{ + // clear the bytecode cache + // + // We can't just u3h_free() -- the value is a post to a u3n_prog. + // Note that the hank cache *must* also be freed (in u3j_reclaim()) + // + u3n_free(); + u3R->byc.har_p = u3h_new(); +} + +/* u3n_rewrite_compact(): rewrite the bytecode cache for compaction. + * + * NB: u3R->byc.har_p *must* be cleared (currently via u3n_reclaim above), + * since it contains things that look like nouns but aren't. + * Specifically, it contains "cells" where the tail is a + * pointer to a u3a_malloc'ed block that contains loom pointers. + * + * You should be able to walk this with u3h_walk and rewrite the + * pointers, but you need to be careful to handle that u3a_malloc + * pointers can't be turned into a box by stepping back two words. You + * must step back one word to get the padding, step then step back that + * many more words (plus one?). + */ +void +u3n_rewrite_compact() +{ + u3h_relocate(&(u3R->byc.har_p)); +} + + +/* _n_feb(): u3h_walk helper for u3n_free + */ +static void +_n_feb(u3_noun kev) +{ + _cn_prog_free(_cn_to_prog(u3t(kev))); +} + +/* u3n_free(): free bytecode cache + */ +void +u3n_free() +{ + u3p(u3h_root) har_p = u3R->byc.har_p; + u3h_walk(har_p, _n_feb); + u3h_free(har_p); +} + +/* u3n_kick_on(): fire `gat` without changing the sample. +*/ +u3_noun +u3n_kick_on(u3_noun gat) +{ + return u3j_kink(gat, 2); +} + +c3_w exc_w; + +/* u3n_slam_on(): produce (gat sam). +*/ +u3_noun +u3n_slam_on(u3_noun gat, u3_noun sam) +{ + u3_noun cor = u3nc(u3k(u3h(gat)), u3nc(sam, u3k(u3t(u3t(gat))))); + +#if 0 + if ( &u3H->rod_u == u3R ) { + if ( exc_w == 1 ) { + u3_assert(0); + } + exc_w++; + } +#endif + u3z(gat); + return u3n_kick_on(cor); +} + +/* u3n_nock_et(): produce .*(bus fol), as ++toon, in namespace. +*/ +u3_noun +u3n_nock_et(u3_noun gul, u3_noun bus, u3_noun fol) +{ + return u3m_soft_run(gul, u3n_nock_on, bus, fol); +} + +/* u3n_slam_et(): produce (gat sam), as ++toon, in namespace. +*/ +u3_noun +u3n_slam_et(u3_noun gul, u3_noun gat, u3_noun sam) +{ + return u3m_soft_run(gul, u3n_slam_on, gat, sam); +} + +/* u3n_nock_an(): as nock_et(), but with the scry handler that always blocks. +*/ +u3_noun +u3n_nock_an(u3_noun bus, u3_noun fol) +{ + u3_noun gul = u3nt(u3nc(1, 0), u3nc(0, 0), 0); // |~(^ ~) + return u3n_nock_et(gul, bus, fol); +} + + |