Files
LithosAnanke/v4/src/manage.c
T
2026-10-19 06:35:46 -04:00

203 lines
8.9 KiB
C

/* 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"
#include "v4/message.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);
}
/* THE KERNEL TELLS A NODE'S GOING (MESH.md 9a, ruled 2026-10-16). Every live node that has a parent, and is
* wired to it, is put a GONE about `gone` on the wire from its parent, as if its parent had sent it: so the
* node believes it, its parent being its centre (7b.4). No node hands a GONE on. A wire with no room for it
* is not waited for: that node is not told (as an answer whose way back is full, 7d.6). */
static void tell_gone(const v4_manage *m, v4_cell gone)
{
unsigned k, port;
v4_cell hd[V4_WIRE_HEADER], text[1];
for (k = 0; k < m->reg_cap; k++) {
const v4_manage_entry *c = &m->reg[k], *p;
if (c->number == 0 || c->parent == 0 || !(p = by_number(m, c->parent))) continue;
for (port = 0; port < V4_PORTS; port++) {
const v4_wire *w = &m->f->place[c->place].wire[port];
if (w->kind != V4_WIRE_NODE || w->node != p->place || !w->rx) continue;
hd[0] = c->number; hd[1] = p->number; hd[2] = V4_MSG_GONE; hd[3] = 16; hd[4] = 0; hd[5] = 0; hd[6] = 4;
text[0] = gone;
(void)v4_wire_put(w->rx, hd, text);
break;
}
}
}
/* 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, k;
v4_cell gone_number = e->number;
v4_fabric_node *was = v4_fabric_remove(m->f, gone_place);
e->number = 0;
/* its children are not removed (as v3) and become Hera's: a number that is not live is no one's
* parent, so a node born later under it inherits nothing (the final review of step 7) */
for (k = 0; k < m->reg_cap; k++) if (m->reg[k].number != 0 && m->reg[k].parent == gone_number) m->reg[k].parent = me->number;
if (was && m->gone) m->gone(m->self, gone_place, was);
tell_gone(m, gone_number);
}
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;
}
}