code wiki / (root) / nx_emu_probe.nx

nx_emu_probe.nx source

↩ module page · 110 lines · 4654 B

1const PO_MAGIC_1000000: i64 = 1000000 2// nx_emu_probe.nx -- R0.0 FOUNDATION PROOF for the sovereign emulator track. 3// 4// An EMULATOR-SHAPED, array-heavy kernel that (a) runs native for a correctness KAT and (b) compiles 5// .nx -> wat -> wasm through the sovereign pipeline (nx_compile_wat.sov.elf + nx_wat_compiler.sov.elf), 6// to LOCK the reproducible recipe + idiom the CHIP-8 core (R0.3) and later the x86/DOS core (Phase 1) reuse. 7// 8// Base-relative fixed linear-memory layout -- the proven nx_wasmfps / nx_render_core idiom: every buffer 9// lives at a fixed byte offset; base=0 in wasm (linear memory), an mmap region in native. This sidesteps 10// the wasm backend's only real gap (array/address-taken local allocas need a shadow stack) by construction. 11// 12// The three state shapes ANY machine emulator needs are all here: guest MEMORY (bytes, load8/store8), 13// REGISTERS (i64), and a FRAMEBUFFER (i64 RGBA, i64.store on a computed address). license_tier: ORIGINAL 14// 15// genealogy_id: sovereign_emulator_substrate 16// lineage_id: nx_emu_probe_v1 (parents: nx_wasmfps base-relative idiom, nx_render_core caller-buffers) 17 18// ---- fixed linear-memory layout (byte offsets) ---- 19const PO_MEM: i64 = 0 // 4096 guest bytes (the CHIP-8 address space size) 20const PO_REG: i64 = 4096 // 16 V registers, i64 each (128 bytes) 21const PO_FB: i64 = 4224 // 64*32 display cells, i64 RGBA each (16384 bytes); ends at 20608 22 23// ---- guest memory (byte-addressed: exercises wasm i64.load8_u / i64.store8) ---- 24func p_memset_byte(base: i64, addr: i64, v: i64) -> i64 { 25 let mem: *u8 = (base + PO_MEM) as *u8 26 mem[addr] = (v & 0xff) as u8 27 return 0 28} 29func p_memget_byte(base: i64, addr: i64) -> i64 { 30 let mem: *u8 = (base + PO_MEM) as *u8 31 return mem[addr] as i64 32} 33// seed guest memory: mem[i] = (i*7+3) & 0xff (store8 + gep loop) 34func p_seed(base: i64, n: i64) -> i64 { 35 let mem: *u8 = (base + PO_MEM) as *u8 36 var i: i64 = 0 37 while i < n { mem[i] = ((i * 7 + 3) & 0xff) as u8; i = i + 1 } 38 return 0 39} 40// sum guest bytes 0..n (load8 + gep + accumulate) -- a known-answer reduction 41func p_sum(base: i64, n: i64) -> i64 { 42 let mem: *u8 = (base + PO_MEM) as *u8 43 var i: i64 = 0 44 var s: i64 = 0 45 while i < n { s = s + (mem[i] as i64); i = i + 1 } 46 return s 47} 48 49// ---- registers (i64-addressed: exercises wasm i64.load / i64.store on computed addresses) ---- 50func p_reg_get(base: i64, x: i64) -> i64 { 51 let reg: *i64 = (base + PO_REG) as *i64 52 return reg[x] 53} 54func p_reg_set(base: i64, x: i64, v: i64) -> i64 { 55 let reg: *i64 = (base + PO_REG) as *i64 56 reg[x] = v 57 return 0 58} 59 60// ---- ONE fetch-decode-execute step (the emulator inner loop, in miniature) ---- 61// reads a 2-byte big-endian opcode at pc and dispatches a tiny CHIP-8-shaped op set: 62// 6XNN -> Vx = NN ; 7XNN -> Vx += NN ; 8XY4 -> Vx += Vy (all wrapped to a byte) 63// returns the next pc. This is exactly the shape nx_chip8 (R0.3) scales to all 35 opcodes. 64func p_step(base: i64, pc: i64) -> i64 { 65 let mem: *u8 = (base + PO_MEM) as *u8 66 let reg: *i64 = (base + PO_REG) as *i64 67 let hi: i64 = mem[pc] as i64 68 let lo: i64 = mem[pc + 1] as i64 69 let op: i64 = (hi << 8) | lo 70 let top: i64 = (op >> 12) & 0xf 71 let x: i64 = hi & 0xf 72 let y: i64 = (lo >> 4) & 0xf 73 let nn: i64 = lo 74 if top == 6 { reg[x] = nn } 75 if top == 7 { reg[x] = (reg[x] + nn) & 0xff } 76 if top == 8 { reg[x] = (reg[x] + reg[y]) & 0xff } 77 return pc + 2 78} 79 80// ---- framebuffer (i64 RGBA at a computed address: the browser blits this to <canvas>) ---- 81func p_fb_set(base: i64, idx: i64, rgba: i64) -> i64 { 82 let fb: *i64 = (base + PO_FB) as *i64 83 fb[idx] = rgba 84 return 0 85} 86func p_fb_get(base: i64, idx: i64) -> i64 { 87 let fb: *i64 = (base + PO_FB) as *i64 88 return fb[idx] 89} 90func p_fb_off() -> i64 { return PO_FB } 91func p_disp_w() -> i64 { return 64 } 92func p_disp_h() -> i64 { return 32 } 93 94// ---- a single deterministic "do everything" entry (the wasm/VM side calls this) ---- 95// seeds memory, runs a 2-instruction program (6105 7103 -> V1=8), writes a framebuffer cell, 96// and returns a composite number the harness can check: sum + V1*1000000 + (fb[7] low 16 bits). 97func p_probe(base: i64) -> i64 { 98 p_seed(base, 256) 99 let s: i64 = p_sum(base, 256) 100 p_memset_byte(base, 0, 0x61) 101 p_memset_byte(base, 1, 0x05) 102 p_memset_byte(base, 2, 0x71) 103 p_memset_byte(base, 3, 0x03) 104 var pc: i64 = 0 105 pc = p_step(base, pc) 106 pc = p_step(base, pc) 107 let v1: i64 = p_reg_get(base, 1) 108 p_fb_set(base, 7, 0xdeadbeef) 109 return s + v1 * PO_MAGIC_1000000 + (p_fb_get(base, 7) & 0xffff) 110}