/* manage.c -- what a node asks of whoever holds the fabric. See manage.h. * * Nothing here uses the C library: the bare-metal kernel links this file. */ #include "v4/manage.h" const char *const v4_manage_words[V4_MANAGE_WORDS] = { "NODE-ME", "NODE-BORN", "NODE-WIRE", "NODE-UNWIRE", "NODE-SLEEP", "NODE-WAKE", "NODE-KILL", "NODE-PARITY", "CAPSULE-OPEN", "CAPSULE-CELL", "CAPSULE-LINE", "NODE-CHILD", "NODE-PLACE" }; /* A character of the node's memory: four to a cell, the first lowest, as * the system keeps text (capsules/v4/sys/core.f18). */ static int in_memory(v4_cell baddr) { return baddr >= 0 && (baddr >> 2) < (v4_cell)V4_NODE_WORDS; } static char char_at(const v4_node *n, v4_cell baddr) { return (char)(((v4_ucell)n->mem[baddr >> 2] >> (8 * (unsigned)(baddr & 3))) & 0xFFu); } static void char_put(v4_node *n, v4_cell baddr, char c) { unsigned shift = 8 * (unsigned)(baddr & 3); v4_ucell cell = (v4_ucell)n->mem[baddr >> 2]; cell = (cell & ~((v4_ucell)0xFFu << shift)) | ((v4_ucell)(unsigned char)c << shift); n->mem[baddr >> 2] = (v4_cell)cell; } static unsigned place_of(v4_cell x) { return x < 0 ? ~0u : (unsigned)x; } static v4_manage_entry *by_number(const v4_manage *m, v4_cell number) { unsigned k; if (number < 1) return 0; for (k = 0; k < m->reg_cap; k++) if (m->reg[k].number == number) return &m->reg[k]; return 0; } static v4_manage_entry *by_place(const v4_manage *m, unsigned place) { unsigned k; for (k = 0; k < m->reg_cap; k++) if (m->reg[k].number != 0 && m->reg[k].place == place) return &m->reg[k]; return 0; } /* the place is the asker's own, or that of a node it birthed */ static int mine(const v4_manage *m, const v4_manage_entry *asker, unsigned place) { const v4_manage_entry *e = by_place(m, place); return asker && e && (e == asker || e->parent == asker->number); } /* error 12 on the node that asked: a store to NODE-ERROR raises it (D-18) */ static int refuse(v4_node *n) { v4_node_store(n, n->error_reg, 12); return 1; } int v4_manage_enter(const v4_manage *m, v4_cell number, unsigned place, v4_cell parent) { unsigned k; if (number < 1 || by_number(m, number)) return 0; for (k = 0; k < m->reg_cap; k++) { if (m->reg[k].number == 0) { m->reg[k].number = number; m->reg[k].parent = parent; m->reg[k].place = place; return 1; } } return 0; } int v4_manage_serve(const v4_manage *m, unsigned place, v4_cell request) { v4_fabric_node *at = v4_fabric_node_at(m->f, place); v4_manage_entry *me, *e; v4_node *n; v4_cell a, b, c, d; if (!at || request > V4_REQ_NODE_ME || request < V4_REQ_NODE_PLACE) return 0; n = &at->n; me = by_place(m, place); switch (request) { case V4_REQ_NODE_ME: /* ( -- place ) */ v4_dstack_push(&n->ds, (v4_cell)place); return 1; case V4_REQ_NODE_BORN: { /* ( number -- place | -1 ) */ int p; a = v4_dstack_pop(&n->ds); if (!me || a < 1 || by_number(m, a)) return refuse(n); p = m->born ? m->born(m->self) : -1; if (p >= 0 && !v4_manage_enter(m, a, (unsigned)p, me->number)) { /* no room in the registry: the node goes again */ v4_fabric_node *was = v4_fabric_remove(m->f, (unsigned)p); if (was && m->gone) m->gone(m->self, (unsigned)p, was); p = -1; } v4_dstack_push(&n->ds, (v4_cell)p); return 1; } case V4_REQ_NODE_WIRE: /* ( a pa b pb -- flag ) */ d = v4_dstack_pop(&n->ds); c = v4_dstack_pop(&n->ds); b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds); if (!mine(m, me, place_of(a)) || !mine(m, me, place_of(c))) return refuse(n); v4_dstack_push(&n->ds, v4_fabric_wire(m->f, place_of(a), place_of(b), place_of(c), place_of(d)) ? -1 : 0); return 1; case V4_REQ_NODE_UNWIRE: /* ( a pa -- ) */ b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds); if (!mine(m, me, place_of(a))) return refuse(n); (void)v4_fabric_unwire(m->f, place_of(a), place_of(b)); return 1; case V4_REQ_NODE_SLEEP: /* ( number -- ) */ case V4_REQ_NODE_WAKE: case V4_REQ_NODE_KILL: a = v4_dstack_pop(&n->ds); if (place != m->hera) return refuse(n); e = by_number(m, a); if (!e || e->place == place) return 1; /* not live, or Hera herself: nothing */ if (request == V4_REQ_NODE_SLEEP) v4_fabric_sleep(m->f, e->place); else if (request == V4_REQ_NODE_WAKE) v4_fabric_wake(m->f, e->place); else { unsigned gone_place = e->place; v4_fabric_node *was = v4_fabric_remove(m->f, gone_place); e->number = 0; if (was && m->gone) m->gone(m->self, gone_place, was); } return 1; case V4_REQ_NODE_PARITY: { /* ( place number -- ) */ v4_fabric_node *it; b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds); e = by_place(m, place_of(a)); if (!me || !e || e == me || e->parent != me->number || e->number != b) return refuse(n); it = v4_fabric_node_at(m->f, e->place); if (it && m->parity) m->parity(m->self, e->place, b, &it->n); return 1; } case V4_REQ_CAPSULE_OPEN: { /* ( baddr u -- flag ) */ char name[64]; unsigned i, len; b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds); if (!me) return refuse(n); len = b < 0 ? 0u : (unsigned)b; if (len == 0 || len > sizeof name || !in_memory(a) || !in_memory(a + (v4_cell)len - 1) || !m->open) { v4_dstack_push(&n->ds, 0); return 1; } for (i = 0; i < len; i++) name[i] = char_at(n, a + (v4_cell)i); v4_dstack_push(&n->ds, m->open(m->self, place, name, len) ? -1 : 0); return 1; } case V4_REQ_CAPSULE_CELL: /* ( -- w flag ) */ if (!me) return refuse(n); a = 0; if (m->cell && m->cell(m->self, place, &a)) { v4_dstack_push(&n->ds, a); v4_dstack_push(&n->ds, -1); } else { v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, 0); } return 1; case V4_REQ_CAPSULE_LINE: { /* ( baddr -- u | -1 ) */ char text[V4_MANAGE_LINE]; int len, i; a = v4_dstack_pop(&n->ds); if (!me) return refuse(n); len = m->line ? m->line(m->self, place, text) : -1; if (len > V4_MANAGE_LINE) len = V4_MANAGE_LINE; if (len > 0 && (!in_memory(a) || !in_memory(a + len - 1))) len = -1; for (i = 0; i < len; i++) char_put(n, a + i, text[i]); v4_dstack_push(&n->ds, (v4_cell)len); return 1; } case V4_REQ_NODE_CHILD: { /* ( number i -- child | 0 ) */ unsigned k; v4_cell found = 0; b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds); for (k = 0; k < m->reg_cap && !found; k++) if (m->reg[k].number != 0 && m->reg[k].parent == a && b-- == 0) found = m->reg[k].number; v4_dstack_push(&n->ds, found); return 1; } default: /* V4_REQ_NODE_PLACE ( number -- place | -1 ) */ a = v4_dstack_pop(&n->ds); e = by_number(m, a); v4_dstack_push(&n->ds, e ? (v4_cell)e->place : -1); return 1; } }