Files
LithosAnanake/v4/src/manage.c
T
rajamesandClaude Opus 5.5 d154342314 feat(v4): the kernel keeps a registry of nodes and checks who may ask (MESH.md 9a.4)
Any node may birth and wire its own; only Hera sleeps, wakes and kills, by
number.  Requests have fixed numbers, -6 to -18.  NODE-BORN takes the
number; NODE-KILL a number.  test_manage.c: 41 checks at both widths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:58:03 -04:00

175 lines
7.3 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"
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;
}
}