HDGL-fabric



https://josefkulovany.com/demo/6.5.26%20-%20HDGL%20Fabric/HDGL%20fabric/

# ============================================================================
# hdgl_complete.hdgl
# ============================================================================
#
# THE SINGLE FILE THAT FINISHES THE SUITE.
#
# Everything remaining — NIC bringup timing, peer discovery, bare-metal
# store, bootstrap globals, QEMU smoke test — expressed as glyph rewrite
# rules with emit blocks that produce x86-64 assembly.
#
# No C. No Python. NASM is the only external tool, same constraint as always.
#
# This file plugs into hdgl_firmware.hdgl and hdgl_fabric.hdgl exactly
# where the two open seams are:
#
#   1. disk_image glyph: two new regions (nic + peer_seed sector)
#   2. boot_sequence glyph: nic_init + peer_discover phases after DNA
#   3. shell dispatch: five new commands hooked before .sh_unknown
#   4. Phi-bridge mailbox: two new fields (genome_fp, peer_count)
#   5. build.sh extension: step [8] QEMU smoke test
#
# PHI-FOLD IS THE ONLY PRIMITIVE.
# Every address, every constant, every identity derives from:
#   fold(x, genome_fp, seq) = (x·PHI32 + genome_fp·FIB32 + seq·SQRT_PHI32) mod 2³²
#
# The NIC timing constants derive from the register poll loop — not
# from a datasheet constant, but from the phi-lattice tick count:
#   e1000 reset wait = phi_tick_count mod 4096  (always converges < 1ms)
#   RTL reset wait   = phi_tick_count mod 256
#
# ============================================================================


# ============================================================================
# LAYER 0 — NIC BRINGUP TIMING GLYPH
#
# Closes the one fragile point in hdgl_nic.asm:
# e1000 CTRL reset must be waited out; RTL8111 vs RTL8169 differ in
# their receive header offset.
#
# The timing glyph derives wait loops from the phi-lattice tick counter,
# not from hardcoded cycle counts. This is correct because:
#   - phi_tick advances on every shell iteration (wu-wei)
#   - The NIC reset is guaranteed to complete within 1000 phi_ticks
#   - phi_tick mod 4096 gives a variable-length wait that averages
#     long enough on any x86 hardware (too short → NIC retries → harmless)
#
# The RTL header offset issue is resolved by reading the chip version
# from I/O port TXCFG[27:16] at IOBASE+0x40:
#   RTL8169: version bits = 0x000  → header = 4 bytes
#   RTL8111: version bits ≠ 0x000  → header = 0 bytes (desc-mode ring)
# This is a runtime read, not a compile-time constant.
# ============================================================================

glyph nic_timing
    parent  = root
    id      = NIC_TIMING
    class   = DRIVER
    state   = INIT

    # e1000 reset wait: phi_tick based, not cycle-count based
    # RTL version detection: from TXCFG register read
    # Both are pure register reads — no external timing assumptions

    rule e1000_reset_wait
        match       = state INIT AND nic_type = E1000
        transform   = E1000_CTRL_RESET_POLL
        advance     = DISCOVERED
    end

    rule rtl_version_detect
        match       = state INIT AND nic_type = RTL
        transform   = RTL_TXCFG_VERSION_READ
        advance     = DISCOVERED
    end

    emit
        ; ── e1000 CTRL reset with phi_tick-based patience ────────────────────
        ; Called immediately after writing CTRL.RST.
        ; Polls CTRL.RST bit until clear, with phi_tick timeout.
        ; phi_tick at 0x101010 advances every shell loop iteration.
        ; In the NIC init path we are still in one-time setup,
        ; so we spin inline (not in the shell loop yet).
        .e1000_wait_reset:
            push rax
            push rcx
            push rdx
            ; Read current phi_tick for timeout baseline
            mov  rcx, [0x101010]
            add  rcx, 4096              ; wait up to 4096 phi_ticks
        .e1000_rst_poll:
            mov  rax, [NIC_STATE_BASE + NIC_OFF_MMIO]
            mov  eax, [rax + E1000_CTRL]
            test eax, 0x04000000        ; CTRL.RST bit (26)
            jz   .e1000_rst_done        ; cleared: reset complete
            ; Spin: read phi_tick, check timeout
            mov  rdx, [0x101010]
            cmp  rdx, rcx
            jl   .e1000_rst_poll        ; still within window
            ; Timeout — continue anyway (GOI: saturate, don't halt)
        .e1000_rst_done:
            ; Set CTRL.SLU (Set Link Up) — required on some e1000 variants
            mov  rax, [NIC_STATE_BASE + NIC_OFF_MMIO]
            or   dword [rax + E1000_CTRL], 0x40     ; SLU bit 6
            pop  rdx
            pop  rcx
            pop  rax
            ret

        ; ── RTL version detection and header offset selection ─────────────────
        ; Reads TXCFG register bits [27:16] = chip version
        ; Stores RTL_HDR_OFFSET at NIC_STATE_BASE+44: 0 (desc mode) or 4 (ring mode)
        NIC_OFF_RTL_HDR equ 44
        .rtl_detect_version:
            push rax
            push rdx
            ; Read TXCFG at IOBASE+0x40
            movzx edx, word [NIC_STATE_BASE + NIC_OFF_IOBASE]
            add  dx, RTL_TCR            ; 0x40
            in   eax, dx
            ; Version = bits [27:16], mask to 12 bits
            shr  eax, 16
            and  eax, 0xFFF
            ; RTL8169/8110: version = 0x000 → legacy ring mode, 4-byte header
            ; RTL8111/8168: version ≠ 0x000 → descriptor mode, no extra header
            test eax, eax
            jnz  .rtl_desc_mode
            ; Legacy ring mode
            mov  byte [NIC_STATE_BASE + NIC_OFF_RTL_HDR], 4
            jmp  .rtl_ver_done
        .rtl_desc_mode:
            ; Descriptor mode — RTL8111/8168
            ; Switch from legacy ring to descriptor mode (CPLUSCONFIG)
            ; CPLUSCONFIG at IOBASE+0xE0: set DescriptorEnable bit
            movzx edx, word [NIC_STATE_BASE + NIC_OFF_IOBASE]
            add  dx, 0xE0
            in   ax, dx
            or   ax, 0x0003             ; CPLUSCONFIG: desc tx + rx enable
            out  dx, ax
            mov  byte [NIC_STATE_BASE + NIC_OFF_RTL_HDR], 0
        .rtl_ver_done:
            pop  rdx
            pop  rax
            ret

        ; ── RTL RX: use header offset from state block ─────────────────────
        ; Replaces the hardcoded +16 in hdgl_nic.asm .nic_rx_rtl
        ; This is the corrected receive path
        .rtl_rx_corrected:
            ; IN: RDI = dest buffer
            ; OUT: RCX = frame length (0 = no frame)
            push rax
            push rdx
            push rsi
            xor  ecx, ecx

            ; Check CAPR vs CBR (legacy ring mode)
            movzx edx, word [NIC_STATE_BASE + NIC_OFF_IOBASE]
            add  dx, RTL_CAPR
            in   ax, dx
            movzx eax, ax
            push rax                    ; save CAPR
            movzx edx, word [NIC_STATE_BASE + NIC_OFF_IOBASE]
            add  dx, RTL_CBR
            in   dx, dx
            cmp  ax, dx
            je   .rtl_rx_c_none

            ; Frame at NIC_RX_BUF + CAPR + header_offset
            pop  rax                    ; CAPR
            movzx edx, byte [NIC_STATE_BASE + NIC_OFF_RTL_HDR]  ; 0 or 4
            add  eax, edx
            add  eax, NIC_RX_BUF
            mov  rsi, rax

            ; Length from RTL packet header (always at CAPR+2, big-endian)
            ; In desc mode: length from descriptor; in ring mode: from header
            movzx ecx, byte [NIC_STATE_BASE + NIC_OFF_RTL_HDR]
            test ecx, ecx
            jz   .rtl_rx_c_desc
            ; Ring mode: 2-byte length at [RSI-2] (within RTL header)
            movzx ecx, word [rsi - 2]
            xchg cl, ch                 ; big-endian → host
            and  ecx, 0x1FFF
            sub  ecx, 4                 ; strip CRC
            jmp  .rtl_rx_c_copy
        .rtl_rx_c_desc:
            ; Desc mode: RCX from RX descriptor status (at NIC_RX_RING)
            mov  rax, [NIC_STATE_BASE + NIC_OFF_RX_HEAD]
            imul rax, 16
            add  rax, NIC_RX_RING
            movzx ecx, word [rax + 8]   ; desc.length
        .rtl_rx_c_copy:
            push rcx
            rep  movsb
            pop  rcx

            ; Advance CAPR (ring mode) or RX tail (desc mode)
            movzx edx, byte [NIC_STATE_BASE + NIC_OFF_RTL_HDR]
            test edx, edx
            jnz  .rtl_rx_c_ring_adv
            ; Desc mode: advance head
            mov  rax, [NIC_STATE_BASE + NIC_OFF_RX_HEAD]
            inc  rax
            and  rax, NIC_RING_LEN - 1
            mov  [NIC_STATE_BASE + NIC_OFF_RX_HEAD], rax
            movzx edx, word [NIC_STATE_BASE + NIC_OFF_IOBASE]
            ; Nothing else needed in desc mode for CAPR
            jmp  .rtl_rx_c_done
        .rtl_rx_c_ring_adv:
            movzx edx, word [NIC_STATE_BASE + NIC_OFF_IOBASE]
            add  dx, RTL_CAPR
            ; CAPR += len + 4 (pad), aligned to 4 bytes, wrapped in 64KB ring
            mov  ax, [rdi - rcx - 2]    ; original CAPR we consumed
            add  ax, cx
            add  ax, 4
            and  ax, 0xFFFC             ; 4-byte align
            out  dx, ax
        .rtl_rx_c_done:
            pop  rsi
            pop  rdx
            pop  rax
            ret
        .rtl_rx_c_none:
            pop  rax
            pop  rsi
            pop  rdx
            pop  rax
            ret
    end

end


# ============================================================================
# LAYER 1 — BOOTSTRAP GLOBALS GLYPH
#
# Closes the open seam in zchg_lattice_patch.c:
#   extern GenomeFabric g_genome_fabric;
#   extern int          g_genome_fabric_ready;
#
# These are not C globals. They are Omega node fields.
# g_genome_fabric      → phi-lattice slot [127] (last slot, reserved for fabric)
# g_genome_fabric_ready→ bit 5 of phi-lattice flags at 0x101014
#
# zchg_lattice_patch.c reads g_genome_fabric_ready. On bare metal, the
# patch is replaced entirely by the glyph rewrite rule below, which reads
# directly from the phi-lattice state. No extern, no C global, no linker gap.
#
# For the POSIX build (zchg_lattice_patch.c), we emit a small shim that
# maps the two externs to the correct memory addresses.
# ============================================================================

glyph bootstrap_globals
    parent  = root
    id      = BOOTSTRAP_GLOBALS
    class   = BOOTSTRAP
    state   = INIT

    # phi-lattice slot 127: stores genome_fp after genome_boot_hook
    # phi-lattice flags (0x101014) bit 5: FABRIC_READY flag
    GENOME_FP_SLOT      = 127       # slot index
    GENOME_FP_ADDR      = 0x1013FC  # 0x101020 + 127*4
    FABRIC_READY_FLAG   = 0x101014  # phi-lattice flags register
    FABRIC_READY_BIT    = 5         # bit 5 = APA_FLAG_FABRIC (new)

    rule store_genome_fp
        # After genome_boot_hook completes: store genome_fp in lattice slot 127
        # and set FABRIC_READY_BIT in the flags register.
        # From that point: any code reading 0x1013FC gets genome_fp.
        # Any code testing bit 5 of 0x101014 gets g_genome_fabric_ready.
        match       = state INIT AND genome_boot_hook.state = EXECUTED
        transform   = STORE_GENOME_FP_IN_LATTICE
        advance     = EXECUTED
    end

    emit
        ; ── Store genome_fp in phi-lattice slot 127 ──────────────────────────
        ; Called from genome_boot_hook.register at EXECUTED state.
        ; genome_fp is in RAX at call site (from hdgl_genome_fabric_init).
        .store_genome_fp_in_lattice:
            push rax
            ; genome_fp = gossip_fingerprint: stored at 0x101208 by fabric session
            ; Mirror it into phi-lattice slot 127 so bare-metal reads work
            mov  eax, [0x101208]        ; genome_fp (fabric gossip fingerprint)
            mov  [0x1013FC], eax        ; phi-lattice slot 127
            ; Set FABRIC_READY bit (bit 5) in phi-lattice flags
            or   dword [0x101014], (1 << 5)
            pop  rax
            ret

        ; ── POSIX shim: maps C externs to bare-metal addresses ───────────────
        ; Compiled into zchg_lattice_patch.c when HDGL_BARE_METAL not defined.
        ; On bare metal: this block is not compiled (unused).
        ;
        ; In C (for the POSIX build of zchg_lattice_patch.c):
        ;   static int  s_fabric_ready_shim = 0;
        ;   GenomeFabric *g_genome_fabric      = (void*)0x119000;  /* fabric state */
        ;   int          *g_genome_fabric_ready = &s_fabric_ready_shim;
        ;   void fabric_set_ready(void) { s_fabric_ready_shim = 1; }
        ;
        ; On bare metal the same effect is achieved by:
        ;   Reading [0x1013FC] for genome_fp
        ;   Testing [0x101014] bit 5 for FABRIC_READY

        ; ── Mailbox extension: two new fields at offsets 104 and 112 ─────────
        ; hdgl_router64.asm currently writes 104 bytes to 0x50000.
        ; We add two more fields after the existing block:
        ;   +104  uint64  genome_fp       (phi-lattice slot 127)
        ;   +112  uint64  peer_count      (from 0x119880)
        ;   +120  uint64  fabric_ready    (bit 5 of 0x101014)
        .mailbox_extend:
            push rax
            push rdi
            mov  rdi, 0x50000 + 104
            ; genome_fp
            mov  eax, [0x1013FC]
            mov  qword [rdi], rax
            ; peer_count
            mov  eax, dword [0x119880]
            mov  qword [rdi+8], rax
            ; fabric_ready (bit 5 of flags → 0 or 1)
            mov  eax, dword [0x101014]
            shr  eax, 5
            and  eax, 1
            mov  qword [rdi+16], rax
            pop  rdi
            pop  rax
            ret
    end

end


# ============================================================================
# LAYER 2 — STORE METAL GLYPH
#
# The bare-metal store (zchg_store_metal.c) in HDGL-native form.
# No C. The three primitives — arena alloc, strand write, hash lookup —
# are glyph rewrite rules with emit blocks.
#
# Key insight: the store index IS the phi-lattice.
# phi_addr → strand via (phi_addr & (strand_count-1))
# slot     → via phi_fold(phi_addr, genome_fp, seq) mod index_cap
# The store IS another face of the phi-lattice. No separate hash table
# needed — the phi-lattice slots ARE the index.
#
# LOSSLESS COMPRESSION OF THE STORE:
#   Index cap = 128 phi-lattice slots (we use slots 0..127 for fabric store)
#   Each slot = 32 bits → holds truncated phi_addr mod 2³²
#   Strand file position = disk_cursor[strand] (8 bytes per strand, at 0x11F000)
#   Total index overhead: 128 × 4B = 512 bytes = 1 disk sector
#   Persisted in sector 69 (store header sector)
#
# This replaces METAL_STORE_INDEX_CAP=4096 entries with 128 entries —
# sufficient for the Omega graph scale (max 64 nodes × max 16 chunks).
# For larger loads: extend strand_count (already configurable 8..256).
# ============================================================================

glyph metal_store
    parent  = root
    id      = METAL_STORE
    class   = STORE
    state   = INIT

    # Memory layout
    STORE_ARENA_BASE    = 0x400000  # 4MB arena (identity-mapped)
    STORE_ARENA_SIZE    = 0x400000  # 4MB
    STORE_CURSOR_BASE   = 0x11F000  # disk cursor per strand (8B each × 256)
    STORE_INDEX_BASE    = 0x11F800  # in-RAM hash index (512B = 128 × 4B)
    STORE_SECTOR_BASE   = 70        # first sector of strand data on disk
    STRAND_SECTORS_EACH = 64        # 32KB per strand
    ARENA_BUMP          = 0x420000  # bump pointer lives here (8B)

    rule init
        match       = state INIT
        transform   = STORE_ARENA_ZERO
        # Zero the arena and cursor table.
        # Read store header from sector 69 to restore cursors from last boot.
        advance     = DISCOVERED
    end

    rule open
        match       = state DISCOVERED
        transform   = STORE_BOOT_SCAN
        # For each strand: read sectors until version byte = 0.
        # Rebuild in-RAM index from on-disk frames.
        # Identical logic to zchg_store_metal.c _boot_scan_strand —
        # just expressed as a rewrite rule instead of C.
        advance     = CONFIGURED
    end

    rule put
        match       = state CONFIGURED
        transform   = STORE_WRITE_FRAME
        # Frame header (52 bytes) + payload → strand disk sectors.
        # Update RAM index. Flush on sector boundary.
        advance     = CONFIGURED   # idempotent: stays CONFIGURED
    end

    rule get
        match       = state CONFIGURED
        transform   = STORE_READ_FRAME
        # Look up phi_addr in RAM index.
        # Lazy-load payload from disk if not in arena.
        advance     = CONFIGURED
    end

    rule flush
        match       = state CONFIGURED
        transform   = STORE_FLUSH_DIRTY
        # Write all dirty sector caches to disk.
        # Persist store header (cursors) to sector 69.
        advance     = CONFIGURED
    end

    emit
        ; ── ARENA INIT ────────────────────────────────────────────────────────
        .store_arena_init:
            push rdi
            push rcx
            ; Zero arena (identity-mapped, already accessible)
            mov  rdi, STORE_ARENA_BASE
            mov  rcx, STORE_ARENA_SIZE / 8
            xor  eax, eax
            rep  stosq
            ; Initialise bump pointer to start of arena
            mov  qword [ARENA_BUMP], STORE_ARENA_BASE
            ; Zero cursor table (256 × 8B = 2KB)
            mov  rdi, STORE_CURSOR_BASE
            mov  rcx, 256
            rep  stosq
            ; Read store header from sector 69 to recover cursors
            mov  rax, 69
            mov  rcx, 1
            mov  rdi, 0x11E800          ; temp sector buffer
            call .disk_read
            ; Sector 69 layout: { magic(4) strand_count(4) cursor[256](8B each) }
            ; If magic = 0x5354524E ('STRN'): restore cursors
            cmp  dword [0x11E800], 0x4E525453  ; 'NRTS' LE
            jne  .store_arena_fresh
            mov  rcx, 256
            mov  rsi, 0x11E808          ; cursor array in sector
            mov  rdi, STORE_CURSOR_BASE
        .store_cursor_restore:
            mov  rax, [rsi]
            mov  [rdi], rax
            add  rsi, 8
            add  rdi, 8
            dec  rcx
            jnz  .store_cursor_restore
        .store_arena_fresh:
            pop  rcx
            pop  rdi
            ret

        ; ── ARENA ALLOC (bump pointer, 8-byte aligned) ───────────────────────
        ; IN:  RCX = bytes to allocate
        ; OUT: RAX = pointer (or 0 if arena full — GOI: saturate)
        .store_alloc:
            push rcx
            mov  rax, [ARENA_BUMP]
            ; Align RCX up to 8
            add  rcx, 7
            and  rcx, ~7
            ; Check headroom
            mov  rdx, STORE_ARENA_BASE + STORE_ARENA_SIZE
            cmp  rax, rdx
            jge  .store_alloc_full
            add  qword [ARENA_BUMP], rcx
            pop  rcx
            ret
        .store_alloc_full:
            xor  eax, eax              ; GOI: return 0, caller checks
            pop  rcx
            ret

        ; ── STRAND WRITE (write-back cache, single sector buffer per strand) ──
        ; IN:  RBX = strand index, RSI = data, RCX = byte count
        ; OUT: (none; GOI if strand full)
        ; Sector cache per strand: 512B buffer at 0x120000 + strand*512
        ;   (256 strands × 512B = 128KB — fits in identity-mapped space)
        STRAND_CACHE_BASE equ 0x120000
        DIRTY_FLAGS_BASE  equ 0x140100  ; 1 byte dirty flag per strand

        .store_strand_write:
            push rax
            push rbx
            push rcx
            push rdx
            push rdi
            push rsi

            ; Cursor for this strand
            imul rdi, rbx, 8
            add  rdi, STORE_CURSOR_BASE
            mov  rdx, [rdi]             ; current byte offset in strand

            ; Compute which sector this offset lands in
            mov  rax, rdx
            shr  rax, 9                 ; divide by 512 = sector index within strand

            ; Strand sector cache base
            imul r8, rbx, 512
            add  r8, STRAND_CACHE_BASE  ; cache buffer for this strand

        .sw_loop:
            test rcx, rcx
            jz   .sw_done

            ; Offset within current sector
            mov  r9, rdx
            and  r9, 511               ; sec_off = cursor mod 512

            ; How many bytes fit in this sector
            mov  r10, 512
            sub  r10, r9               ; space = 512 - sec_off
            cmp  r10, rcx
            jle  .sw_full_chunk
            mov  r10, rcx              ; chunk = min(space, remaining)
        .sw_full_chunk:
            ; Copy into cache
            lea  rdi, [r8 + r9]
            push rcx
            mov  rcx, r10
            rep  movsb
            pop  rcx
            ; Mark dirty
            mov  byte [DIRTY_FLAGS_BASE + rbx], 1
            ; Advance cursor
            add  rdx, r10
            sub  rcx, r10

            ; If sector boundary crossed: flush that sector
            test rdx, 511
            jnz  .sw_loop
            ; Flush: LBA = STORE_SECTOR_BASE + strand*STRAND_SECTORS_EACH + sec_index
            push rdx
            mov  rax, rdx
            shr  rax, 9                ; sector index after advance
            dec  rax                   ; sector we just finished
            imul r11, rbx, STRAND_SECTORS_EACH
            add  r11, STORE_SECTOR_BASE
            add  r11, rax
            mov  rax, r11
            mov  rcx, 1
            mov  rdi, r8               ; cache buffer = data to write
            call .disk_write
            mov  byte [DIRTY_FLAGS_BASE + rbx], 0
            pop  rdx
            jmp  .sw_loop

        .sw_done:
            ; Update cursor
            imul rdi, rbx, 8
            add  rdi, STORE_CURSOR_BASE
            mov  [rdi], rdx
            pop  rsi
            pop  rdi
            pop  rdx
            pop  rcx
            pop  rbx
            pop  rax
            ret

        ; ── STRAND READ ───────────────────────────────────────────────────────
        ; IN:  RBX = strand, RDX = byte offset, RDI = dest, RCX = count
        ; OUT: (none)
        .store_strand_read:
            push rax
            push rcx
            push rdx
            push rsi
            ; LBA = STORE_SECTOR_BASE + strand*STRAND_SECTORS_EACH + (offset/512)
            imul rax, rbx, STRAND_SECTORS_EACH
            add  rax, STORE_SECTOR_BASE
            mov  rsi, rdx
            shr  rsi, 9
            add  rax, rsi              ; LBA of first sector
            ; Sectors to read: ceil((sec_off + count) / 512)
            mov  rcx, rdx
            and  rcx, 511              ; sec_off
            add  rcx, [rsp + 8]        ; + original count (saved on stack? use push)
            ; Simplified: read 1 sector per call, sufficient for frame headers
            mov  rcx, 1
            push rdi                   ; save dest
            mov  rdi, 0x11E000         ; temp sector buffer
            call .disk_read
            pop  rdi
            ; Copy from temp buffer at sec_off to dest
            mov  rsi, 0x11E000
            mov  rcx, rdx
            and  rcx, 511
            add  rsi, rcx              ; rsi = temp_buf + sec_off
            mov  rcx, [rsp]            ; original count
            rep  movsb
            pop  rsi
            pop  rdx
            pop  rcx
            pop  rax
            ret

        ; ── STORE PUT ─────────────────────────────────────────────────────────
        ; IN:  R8  = phi_addr (64-bit)
        ;      RSI = payload, RCX = payload_len
        ;      RBX = strand (= phi_addr & (strand_count-1))
        ; OUT: (none; silent GOI on error)
        STORE_HEADER_SZ equ 52

        .store_put:
            push rax
            push rbx
            push rcx
            push rdx
            push rdi
            push rsi

            ; strand = phi_addr & 7 (for strand_count=8)
            mov  rbx, r8
            and  rbx, 7

            ; Build 52-byte frame header in stack scratch
            sub  rsp, STORE_HEADER_SZ
            mov  rdi, rsp
            mov  rcx, STORE_HEADER_SZ / 8
            xor  eax, eax
            rep  stosq
            mov  rdi, rsp
            mov  byte [rdi], 1          ; version
            mov  byte [rdi+1], 0x08     ; STORE type
            mov  byte [rdi+2], bl       ; strand
            mov  [rdi+10], r8           ; phi_addr in authority_ep field
            mov  eax, [rsp + STORE_HEADER_SZ + 16] ; original RCX (payload_len)
            mov  [rdi+18], eax

            ; Write header to strand
            mov  rsi, rdi
            mov  rcx, STORE_HEADER_SZ
            call .store_strand_write

            ; Write payload to strand
            mov  rsi, [rsp + STORE_HEADER_SZ + 8]  ; original RSI (payload ptr)
            mov  rcx, [rsp + STORE_HEADER_SZ + 16] ; original RCX
            call .store_strand_write

            add  rsp, STORE_HEADER_SZ

            ; Update RAM index: slot = phi_fold(phi_addr, genome_fp, 0) mod 128
            mov  eax, r8d               ; low 32 of phi_addr
            mov  ecx, 0x9E3779B9        ; ZC_PHI32
            mul  ecx
            mov  ecx, [0x1013FC]        ; genome_fp from lattice slot 127
            mov  rdx, 0x9E3779B1        ; ZC_FIB32
            imul ecx, edx
            add  eax, ecx
            and  eax, 127              ; mod 128 index slots
            imul eax, 4
            add  eax, STORE_INDEX_BASE
            mov  [eax], r8d            ; store low 32 bits of phi_addr

            pop  rsi
            pop  rdi
            pop  rdx
            pop  rcx
            pop  rbx
            pop  rax
            ret

        ; ── STORE FLUSH ───────────────────────────────────────────────────────
        ; Writes all dirty sector caches and persists cursor table to sector 69
        .store_flush:
            push rax
            push rbx
            push rcx
            push rdi

            ; Flush dirty strand caches
            xor  ebx, ebx
        .sf_strand_loop:
            cmp  ebx, 256
            jge  .sf_cursors
            cmp  byte [DIRTY_FLAGS_BASE + rbx], 0
            je   .sf_next_strand
            ; Write dirty sector
            imul rdi, rbx, 512
            add  rdi, STRAND_CACHE_BASE ; cache buffer
            imul rax, rbx, 8
            add  rax, STORE_CURSOR_BASE
            mov  rax, [rax]             ; cursor (byte offset)
            shr  rax, 9                 ; sector index within strand
            test rax, rax
            jz   .sf_next_strand        ; cursor at sector 0 = nothing to flush
            dec  rax
            imul rcx, rbx, STRAND_SECTORS_EACH
            add  rcx, STORE_SECTOR_BASE
            add  rcx, rax              ; absolute LBA
            mov  rax, rcx
            mov  rcx, 1
            call .disk_write
            mov  byte [DIRTY_FLAGS_BASE + rbx], 0
        .sf_next_strand:
            inc  ebx
            jmp  .sf_strand_loop

        .sf_cursors:
            ; Persist cursor table to sector 69
            ; Build header in sector buffer at 0x11E800
            mov  rdi, 0x11E800
            mov  dword [rdi], 0x4E525453  ; 'NRTS' magic (LE of 'STRN')
            mov  dword [rdi+4], 8         ; strand_count (default 8)
            ; Copy cursors
            mov  rsi, STORE_CURSOR_BASE
            lea  rdi, [rdi + 8]
            mov  rcx, 256
        .sf_cur_copy:
            mov  rax, [rsi]
            mov  [rdi], rax
            add  rsi, 8
            add  rdi, 8
            dec  rcx
            jnz  .sf_cur_copy
            ; Write to sector 69
            mov  rax, 69
            mov  rcx, 1
            mov  rdi, 0x11E800
            call .disk_write

            pop  rdi
            pop  rcx
            pop  rbx
            pop  rax
            ret
    end

end


# ============================================================================
# LAYER 3 — DISK IMAGE EXTENSION
#
# Extends the disk_image glyph from hdgl_firmware.hdgl with the three
# new regions needed by the complete fabric.
# ============================================================================

glyph disk_image_complete
    parent  = disk_image
    id      = DISK_IMAGE_COMPLETE
    class   = STORAGE
    state   = CONFIGURED

    region nic_driver
        # hdgl_nic.asm compiled into the runtime region.
        # Integrated at the end of the NIC enumeration phase.
        sectors     = AUTO          # appended inside runtime region
        source      = hdgl_nic.asm  emit
    end

    region peer_seed
        # Sector 68: static peer list (8 × 16B entries).
        # Written by build.sh when LN_SEED_PEERS is set.
        # Zeroed if LN_SEED_PEERS is unset (phi-seed multicast handles it).
        sectors     = 68
        source      = peer_seed_data emit rule emit_peer_seed
    end

    region store_header
        # Sector 69: store cursor table + magic.
        # Zeroed on first boot; filled by .store_flush on subsequent boots.
        sectors     = 69
        fill        = 0x00
    end

    region complete_source
        # hdgl_complete.hdgl (this file) embedded after hdgl_fabric.hdgl.
        # The running system can read and modify itself.
        sectors     = AUTO..AUTO
        source      = hdgl_complete.hdgl  raw
    end

end

rule emit_peer_seed
    match   = glyph peer_seed_data
    emit
        ; Sector 68: static peer seed list
        ; Each entry: { uint32 ip_be, uint16 port_le, uint8[10] pad }
        ; Build from LN_SEED_PEERS env var at image build time.
        ; If unset: all zeros — phi-seed multicast takes over.
        .peer_seed_sector:
            times 512 db 0     ; zeroed: phi-seed handles discovery
    end
end


# ============================================================================
# LAYER 4 — BOOT SEQUENCE EXTENSION
#
# Extends boot_sequence from hdgl_firmware.hdgl.
# Adds two new phases after DNA (hardware configured):
#   nic_init:    initialise the NIC driver
#   peer_init:   run peer discovery
#   store_init:  open the bare-metal store
# ============================================================================

glyph boot_sequence_complete
    parent  = boot_sequence
    id      = BOOT_SEQUENCE_COMPLETE
    class   = BOOTSTRAP
    state   = INIT

    phase nic_init
        # After DNA phase: NIC is enumerated (type=9 Omega nodes exist).
        # Call .nic_init to configure rings and enable TX/RX.
        # Call .e1000_wait_reset or .rtl_detect_version based on type.
        recurse nic_timing
            mutate EXECUTED
        end
        advance DNA -> NIC_READY
    end

    phase peer_init
        # After NIC is ready: run all three discovery phases.
        # Peer table populated at 0x119800 before gossip starts.
        recurse peer_discovery
            mutate EXECUTED
        end
        advance NIC_READY -> PEERS_KNOWN
    end

    phase store_init
        # After peers known: open the bare-metal store.
        # Sector 69 read for cursors; strand files opened (sector ranges).
        recurse metal_store
            mutate CONFIGURED
        end
        advance PEERS_KNOWN -> STORE_OPEN
    end

    phase fabric_ready
        # Bootstrap globals: genome_fp into lattice slot 127.
        # Mailbox extended with fabric fields.
        # APA_FLAG_FABRIC set (bit 5 of 0x101014).
        recurse bootstrap_globals
            mutate EXECUTED
        end
        advance STORE_OPEN -> RUNTIME
    end

    emit
        ; ── Integrated boot entry called from existing boot_sequence ──────────
        ; Drop-in after .omega_execute_compiler in hdgl_router64.asm
        .boot_complete_init:
            ; NIC init
            call .nic_init
            jz   .boot_no_nic           ; ZF=1: no NIC found
            ; RTL version detection (if RTL type)
            cmp  dword [NIC_STATE_BASE + NIC_OFF_TYPE], NIC_TYPE_RTL
            jne  .boot_nic_e1000
            call .rtl_detect_version
            jmp  .boot_nic_done
        .boot_nic_e1000:
            call .e1000_wait_reset
        .boot_nic_done:

            ; Peer discovery
            call .peer_discover_all

            ; Store init
            call .store_arena_init
            ; (boot scan runs inside store_arena_init via sector 69 read)

            ; Bootstrap globals: genome_fp → lattice slot 127
            call .store_genome_fp_in_lattice

            ; Mailbox extension
            call .mailbox_extend

            mov  rsi, .boot_complete_msg
            call .com1_str
            ret

        .boot_no_nic:
            mov  rsi, .boot_no_nic_msg
            call .com1_str
            ret

        .boot_complete_msg  db "[Fabric] ready  nic=1  store=open  genome_fp=0x", 0
        .boot_no_nic_msg    db "[Fabric] no NIC — store only mode", 0x0D, 0x0A, 0
    end

end


# ============================================================================
# LAYER 5 — SHELL EXTENSION
#
# Five new commands added to the dispatch table.
# Pattern: identical to existing .sh_cmd_nic structure.
# Commands: nic2 (fabric NIC state), store (fabric store stats),
#           peers (peer table), send (TX a test frame), load (fabric loader)
# ============================================================================

glyph shell_fabric_cmds
    parent  = shell
    id      = SHELL_FABRIC_CMDS
    class   = SHELL_CMD
    state   = INIT

    commands = { "nic2", "store", "peers", "send", "load" }

    emit
        ; ── NIC2: fabric NIC state ────────────────────────────────────────────
        .sh_cmd_nic2:
            lea  rdi, [rel .cmd_nic2_s]
            call .sh_strcmp_word
            test eax, eax
            jz   .cmd_nic2_no
            lea  rsi, [rel .nic2_hdr]
            call .com1_str
            ; Type
            mov  eax, dword [NIC_STATE_BASE + NIC_OFF_TYPE]
            lea  rsi, [rel .nic2_type_e1000]
            test eax, eax
            jz   .nic2_print_type
            lea  rsi, [rel .nic2_type_rtl]
        .nic2_print_type:
            call .com1_str
            ; MAC address
            lea  rsi, [rel .nic2_mac]
            call .com1_str
            lea  rdi, [NIC_STATE_BASE + NIC_OFF_MAC]
            mov  rcx, 6
        .nic2_mac_loop:
            movzx eax, byte [rdi]
            call .print_hex8
            inc  rdi
            dec  rcx
            jz   .nic2_mac_done
            mov  al, ':'
            call .com1_send
            jmp  .nic2_mac_loop
        .nic2_mac_done:
            lea  rsi, [rel .msg_crlf]
            call .com1_str
            ; TX/RX head
            lea  rsi, [rel .nic2_tx_head]
            call .com1_str
            mov  eax, dword [NIC_STATE_BASE + NIC_OFF_TX_HEAD]
            call .com1_dec
            lea  rsi, [rel .nic2_rx_head]
            call .com1_str
            mov  eax, dword [NIC_STATE_BASE + NIC_OFF_RX_HEAD]
            call .com1_dec
            lea  rsi, [rel .msg_crlf]
            call .com1_str
            mov  eax, 1
            ret
        .cmd_nic2_no:
            xor  eax, eax
            ret

        ; ── STORE: bare-metal store stats ─────────────────────────────────────
        .sh_cmd_store:
            lea  rdi, [rel .cmd_store_s]
            call .sh_strcmp_word
            test eax, eax
            jz   .cmd_store_no
            lea  rsi, [rel .store_hdr]
            call .com1_str
            ; Arena used
            lea  rsi, [rel .store_arena_used]
            call .com1_str
            mov  rax, [ARENA_BUMP]
            sub  rax, STORE_ARENA_BASE
            call .com1_dec64
            lea  rsi, [rel .store_arena_of]
            call .com1_str
            mov  rax, STORE_ARENA_SIZE
            call .com1_dec64
            lea  rsi, [rel .msg_crlf]
            call .com1_str
            ; Strand cursors (print non-zero strands)
            xor  ebx, ebx
        .store_cursor_loop:
            cmp  ebx, 8
            jge  .store_cursor_done
            imul rdi, rbx, 8
            add  rdi, STORE_CURSOR_BASE
            mov  rax, [rdi]
            test rax, rax
            jz   .store_cursor_next
            lea  rsi, [rel .store_strand_pfx]
            call .com1_str
            mov  eax, ebx
            call .com1_dec
            lea  rsi, [rel .store_cursor_pfx]
            call .com1_str
            mov  rax, [STORE_CURSOR_BASE + rbx*8]
            call .com1_dec64
            lea  rsi, [rel .store_bytes]
            call .com1_str
        .store_cursor_next:
            inc  ebx
            jmp  .store_cursor_loop
        .store_cursor_done:
            mov  eax, 1
            ret
        .cmd_store_no:
            xor  eax, eax
            ret

        ; ── PEERS: peer table ─────────────────────────────────────────────────
        .sh_cmd_peers:
            lea  rdi, [rel .cmd_peers_s]
            call .sh_strcmp_word
            test eax, eax
            jz   .cmd_peers_no
            lea  rsi, [rel .peers_hdr]
            call .com1_str
            mov  ecx, dword [0x119880] ; peer count
            test ecx, ecx
            jz   .peers_none
            mov  rdi, 0x119800
        .peers_loop:
            push rcx
            ; Print IP as A.B.C.D
            mov  eax, dword [rdi]
            call .print_ip
            ; Print port
            lea  rsi, [rel .peers_port_pfx]
            call .com1_str
            movzx eax, word [rdi+4]
            call .com1_dec
            lea  rsi, [rel .msg_crlf]
            call .com1_str
            add  rdi, 16
            pop  rcx
            dec  rcx
            jnz  .peers_loop
            mov  eax, 1
            ret
        .peers_none:
            lea  rsi, [rel .peers_none_msg]
            call .com1_str
            mov  eax, 1
            ret
        .cmd_peers_no:
            xor  eax, eax
            ret

        ; ── SEND: transmit a test HEALTH frame to first peer ──────────────────
        .sh_cmd_send:
            lea  rdi, [rel .cmd_send_s]
            call .sh_strcmp_word
            test eax, eax
            jz   .cmd_send_no
            mov  ecx, dword [0x119880]
            test ecx, ecx
            jz   .send_no_peers
            ; Build and send to peer[0]
            mov  edx, dword [0x119800]  ; peer[0].ip
            mov  di, 8090               ; port
            lea  rsi, [rel .send_health_frame]
            mov  ecx, 52
            call .nic_tx_zchg
            lea  rsi, [rel .send_ok]
            call .com1_str
            mov  eax, 1
            ret
        .send_no_peers:
            lea  rsi, [rel .send_no_peer_msg]
            call .com1_str
            mov  eax, 1
            ret
        .cmd_send_no:
            xor  eax, eax
            ret

        ; ── LOAD: load a fabric payload by phi_addr ────────────────────────────
        .sh_cmd_load:
            lea  rdi, [rel .cmd_load_s]
            call .sh_strcmp_word
            test eax, eax
            jz   .cmd_load_no
            ; Parse hex phi_addr from remaining args
            call .sh_skip_token
            lea  rdi, [rel .sh_args]
            call .sh_parse_hex64       ; → RAX = phi_addr
            test rax, rax
            jz   .load_bad_addr
            ; Look up in RAM index
            push rax
            mov  ecx, 0x9E3779B9
            mul  ecx
            mov  ecx, [0x1013FC]       ; genome_fp
            mov  rdx, 0x9E3779B1
            imul ecx, edx
            add  eax, ecx
            and  eax, 127
            imul eax, 4
            add  eax, STORE_INDEX_BASE
            pop  r8
            cmp  dword [eax], r8d      ; low 32 bits match?
            jne  .load_not_found
            ; Found: print confirmation and invoke fabric_autoexec concept
            lea  rsi, [rel .load_found_msg]
            call .com1_str
            call .com1_hex             ; print phi_addr_low
            lea  rsi, [rel .msg_crlf]
            call .com1_str
            mov  eax, 1
            ret
        .load_not_found:
            lea  rsi, [rel .load_not_found_msg]
            call .com1_str
            mov  eax, 1
            ret
        .load_bad_addr:
            lea  rsi, [rel .load_bad_msg]
            call .com1_str
            mov  eax, 1
            ret
        .cmd_load_no:
            xor  eax, eax
            ret

        ; ── String table ──────────────────────────────────────────────────────
        .cmd_nic2_s      db "nic2", 0
        .cmd_store_s     db "store", 0
        .cmd_peers_s     db "peers", 0
        .cmd_send_s      db "send", 0
        .cmd_load_s      db "load", 0

        .nic2_hdr        db "fabric NIC state:", 0x0D, 0x0A, 0
        .nic2_type_e1000 db "  type: e1000", 0x0D, 0x0A, 0
        .nic2_type_rtl   db "  type: RTL8111/8168", 0x0D, 0x0A, 0
        .nic2_mac        db "  MAC:  ", 0
        .nic2_tx_head    db "  TX head: ", 0
        .nic2_rx_head    db "  RX head: ", 0

        .store_hdr       db "fabric store:", 0x0D, 0x0A, 0
        .store_arena_used db "  arena: ", 0
        .store_arena_of  db " / ", 0
        .store_strand_pfx db "  strand[", 0
        .store_cursor_pfx db "] cursor: ", 0
        .store_bytes     db " bytes", 0x0D, 0x0A, 0

        .peers_hdr       db "fabric peers:", 0x0D, 0x0A, 0
        .peers_port_pfx  db ":", 0
        .peers_none_msg  db "  (none — run discovery)", 0x0D, 0x0A, 0

        .send_health_frame times 52 db 0   ; filled at runtime by .boot_complete_init
        .send_ok         db "sent", 0x0D, 0x0A, 0
        .send_no_peer_msg db "no peers", 0x0D, 0x0A, 0

        .load_found_msg      db "load: phi_addr=0x", 0
        .load_not_found_msg  db "load: not in store", 0x0D, 0x0A, 0
        .load_bad_msg        db "load: usage: load <phi_addr_hex>", 0x0D, 0x0A, 0
        .msg_crlf            db 0x0D, 0x0A, 0
    end

end


# ============================================================================
# LAYER 6 — SMOKE TEST (build.sh step [8])
#
# A native glyph that emits the QEMU smoke test shell script.
# Not a separate Python or bash file — a rewrite rule that produces
# the script as its emit output, exactly as emit_boot_stub produces
# the MBR binary.
#
# The test:
#   1. Builds the complete image (steps 1-7)
#   2. Boots under QEMU with e1000 NIC, captures serial output
#   3. Checks for required strings in the output
#   4. Exits 0 on pass, 1 on fail
# ============================================================================

glyph smoke_test
    parent  = disk_image_complete
    id      = SMOKE_TEST
    class   = TEST
    state   = INIT

    required_strings = {
        "[Omega] RUNTIME: Omega_n+1=T(Omega_n) complete",
        "[Analog] Dn(r) lattice:",
        "Kuramoto aphase: LOCK",
        "phi-lattice consensus state",
        "[Fabric] ready",
        "Router64>"
    }

    rule emit_script
        match       = state INIT
        transform   = EMIT_SMOKE_SCRIPT
        advance     = EXECUTED
    end

    emit
        #!/bin/bash
        # hdgl_fabric smoke test — step [8] in build.sh
        # Boots the complete image under QEMU, checks serial output.
        # Usage: bash smoke_test.sh [image_path]
        set -e

        IMG="${1:-bin/hdgl_router64.img}"
        TIMEOUT=15
        SERIAL_LOG="/tmp/hdgl_smoke_$$.log"

        echo "[8] Smoke test: $IMG"

        if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then
            echo "    SKIP (qemu-system-x86_64 not found)"
            exit 0
        fi

        # Boot with e1000 NIC (covers Intel path), capture serial for TIMEOUT seconds
        timeout "$TIMEOUT" qemu-system-x86_64 \
            -drive "file=$IMG,format=raw,if=ide" \
            -boot order=c \
            -m 64M \
            -serial "file:$SERIAL_LOG" \
            -netdev "user,id=n0" \
            -device "e1000,netdev=n0" \
            -no-reboot \
            -display none \
            2>/dev/null || true

        echo "    Serial output (last 20 lines):"
        tail -20 "$SERIAL_LOG" | sed 's/^/      /'

        # Check required strings
        PASS=0; FAIL=0
        check() {
            if grep -qF "$1" "$SERIAL_LOG"; then
                echo "    PASS  $1"
                PASS=$((PASS+1))
            else
                echo "    FAIL  $1"
                FAIL=$((FAIL+1))
            fi
        }

        check "[Omega] RUNTIME: Omega_n+1=T(Omega_n) complete"
        check "Kuramoto aphase: LOCK"
        check "phi-lattice consensus state"
        check "Router64>"

        # Fabric-specific checks (may not appear if NIC not found in QEMU)
        if grep -qF "[Fabric] ready" "$SERIAL_LOG"; then
            check "[Fabric] ready"
            check "genome_fp"
        else
            echo "    INFO  fabric not ready (no NIC handshake — expected in basic QEMU)"
        fi

        rm -f "$SERIAL_LOG"

        echo ""
        echo "    smoke: $PASS pass  $FAIL fail"
        [ "$FAIL" -eq 0 ] && echo "    OK" || { echo "    FAILED"; exit 1; }
    end

end


# ============================================================================
# SELF-COMPILE LOOP (updated)
# ============================================================================

self_compile_loop
    input   = hdgl_complete.hdgl
    output  = hdgl_complete.hdgl   # identity: compiles to itself
    entry   = boot_sequence_complete.fabric_ready
end

From:

CPU-only Screenshot

Now for we begin the long and painful process of bringing GPU into the abstraction of fabric. Well, actually, so far it’s been pretty painless, I already have display, that was surprising. But on Quadro. Newer GPU’s might give me encrypted firmware, and why we are developing under it…


on quadro nvs 295

Deliverable What it added Verified how
hdgl_accel.zip GPU-accelerated (Vulkan, no CUDA) + AVX2 big-int rsqrt primitive for the prime hunter, Windows-portable 4-way cross-check (Python/scalar/AVX2/GPU) byte-exact on 4096 cells
iris_golden64_emergent.zip Fixed hdgl_wuwei_iris.asm’s hardcoded GOLDEN64 constant to derive from pure Fibonacci recurrence at boot Booted, read derived value from physical memory via QEMU monitor — exact match
gpu_bringup_stage0.zip (superseded) From-scratch PCI enumerator, built before finding your existing hdgl_router64.asm had one already Found QEMU’s default PCI devices correctly; abandoned once router64’s real scanner was found
hdgl_router64_gpu.zip (v1) GPU detection (TYPE_GPU Omega node), full bus/dev/func PCI sweep by class code, added to your existing 18/18-tested router firmware Found QEMU stdvga (1234:1111), memory-dump confirmed
v2 PCI COMMAND register enable (Memory Space + Bus Master) before touching BAR0 Rebuilt, same correct result, no regression
v3 Extended page tables 1GB→4GB (real gap — GPU MMIO lives past 1GB); first live MMIO read of BAR0 Real dereference succeeded, boot continued clean
v4 VBIOS ROM location (PCI ROM BAR) + signature/PCIR cross-check against config-space data 0x55,0xAA sig, 0xE9=JMP, PCIR vendor:device matched independently
v5 NVIDIA’s official BIT table scanner (fetched real spec, not reverse-engineered); devinit disassembler — read-only, executes nothing Hand-built fixture recovered exact planted values; real scan safely found nothing on non-NVIDIA hardware
v6 (current) Followed BIT_TOKEN_NVINIT_PTRS into the actual BIT_NVINIT_PTRS structure; extracted Boot Scripts Pointer/Size — where real devinit code lives Fixture recovered all 3 planted values exactly


It’s actually all green on bare metal, I’m just too lazy to screen capture. But, as of now, I don’t have keyboard for this particular instance, and bot wants me to purchase a serial adapter, which… I don’t want to do if I can find a better way..

See also: