code wiki / _hdl_build / nx_chip8_page.nx

nx_chip8_page.nx source

↩ module page · 105 lines · 7177 B

1// nx_chip8_page.nx -- R0.4 emit a self-contained PLAYABLE CHIP-8 page. Fork-compiles nx_chip8.nx -> wasm 2// (sovereign pipeline), base64-embeds it (sovereign nx_base64), and authors web_assets/chip8.html: a canvas 3// that loads the wasm, drives reset->load->run_frame, blits the 64x32 framebuffer, and maps a CHIP-8 keypad. 4// The embedded demo program is OUR OWN sovereign CHIP-8 code (a smiley sprite) -> zero IP question. Page is 5// LOCAL until the login wall (R0.1) gates /play. NISHI all the way (forks sovereign sub-ELFs, no .sh). license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_base64.nx" 8 9const CWAT_ELF: *u8 = "/tmp/nx_compile_wat.sov.elf" 10const WATC_ELF: *u8 = "/tmp/nx_wat_compiler.sov.elf" 11const SRC_NX: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/runtime/nx_chip8.nx" 12const WAT_TMP: *u8 = "/tmp/ch8.wat" 13const WASM_OUT: *u8 = "web_assets/ch8.wasm" 14const HTML_OUT: *u8 = "web_assets/chip8.html" 15const CAP: i64 = 262144 16 17func run_elf(elf: *u8, a1: *u8, a2: *u8) -> i64 { 18 let pid: i64 = sys_fork() 19 if pid == 0 { 20 let argv: *i64 = sys_mmap(64) as *i64 21 argv[0] = elf as i64 22 var ai: i64 = 1 23 if (a1 as i64) != 0 { argv[ai] = a1 as i64; ai = ai + 1 } 24 if (a2 as i64) != 0 { argv[ai] = a2 as i64; ai = ai + 1 } 25 argv[ai] = 0 26 let envp: *i64 = sys_mmap(16) as *i64 27 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 28 envp[1] = 0 29 sys_execve(elf, argv, envp) 30 sys_exit(127) 31 } 32 let st: *i64 = sys_mmap(16) as *i64 33 sys_wait4(pid, st, 0) 34 return (st[0] >> 8) & 0xff 35} 36func cat(dst: *u8, off: i64, s: *u8) -> i64 { 37 var o: i64 = off 38 var i: i64 = 0 39 while s[i] != 0 as u8 { dst[o] = s[i]; o = o + 1; i = i + 1 } 40 return o 41} 42 43func main() -> i64 { 44 // 1. build the sovereign wasm 45 run_elf(CWAT_ELF, SRC_NX, WAT_TMP) 46 run_elf(WATC_ELF, WAT_TMP, WASM_OUT) 47 let box: *i64 = sys_mmap(16) as *i64 48 let wbytes: *u8 = sys_read_file(WASM_OUT, box) 49 if (wbytes as i64) == 0 { sys_write(2, "FATAL: no ch8.wasm\n" as *u8, 19); sys_exit(2); return 2 } 50 let wlen: i64 = box[0] 51 let b64: *u8 = sys_mmap(wlen * 2 + 64) 52 let b64len: i64 = b64_encode(wbytes, wlen, b64) 53 54 // 2. author the page 55 let h: *u8 = sys_mmap(CAP) 56 var w: i64 = 0 57 w = cat(h, w, "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'>\n" as *u8) 58 w = cat(h, w, "<title>Nishi CHIP-8 &mdash; sovereign emulator</title>\n" as *u8) 59 w = cat(h, w, "<style>:root{--bg:#0b0f14;--ink:#e6edf3;--mut:#8aa0b4;--acc:#4cc2ff;--line:#243140}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.55 system-ui,Segoe UI,Roboto,sans-serif}\n" as *u8) 60 w = cat(h, w, "header{max-width:720px;margin:0 auto;padding:28px 20px 6px}h1{margin:0;font-size:26px}.sub{color:var(--mut);margin:.5em 0 0;max-width:64ch}main{max-width:720px;margin:0 auto;padding:0 20px 50px}\n" as *u8) 61 w = cat(h, w, "canvas{image-rendering:pixelated;width:640px;max-width:100%;background:#0b0f14;border:1px solid var(--line);border-radius:8px;margin:14px 0;display:block}\n" as *u8) 62 w = cat(h, w, ".btn{background:#1b2733;color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:7px 14px;font:inherit;cursor:pointer}.btn:hover{border-color:var(--acc)}code{color:var(--acc)}.kp{color:var(--mut);font-size:13px}\n" as *u8) 63 w = cat(h, w, "footer{max-width:720px;margin:0 auto;padding:8px 20px 40px;color:var(--mut);font-size:12.5px}</style></head><body>\n" as *u8) 64 w = cat(h, w, "<header><h1>Nishi CHIP-8</h1><p class='sub'>A sovereign CHIP-8 virtual machine &mdash; written from scratch in the Nishi stack, compiled to WebAssembly by the sovereign toolchain (no Emscripten), running right here. CHIP-8 is a public-domain 1977 VM spec; this demo program is our own.</p></header>\n" as *u8) 65 w = cat(h, w, "<main><canvas id='c'></canvas><p><button class='btn' id='reset'>Reset</button> <span class='kp'>keypad: 1234 / QWER / ASDF / ZXCV</span></p></main>\n" as *u8) 66 w = cat(h, w, "<footer>Sovereign Nishi stack &middot; emulator <code>nx_chip8</code> &middot; .nx&rarr;wasm via <code>nx_compile_wat</code>+<code>nx_wat_compiler</code> &middot; verified in <code>nx_wasm_vm</code></footer>\n" as *u8) 67 w = cat(h, w, "<script>\n" as *u8) 68 // embedded program (0x200): A300 601C 610C D018 1208 -> I=0x300, V0=28, V1=12, draw 8 rows, halt 69 w = cat(h, w, "var PROG=[0xA3,0x00,0x60,0x1C,0x61,0x0C,0xD0,0x18,0x12,0x08];\n" as *u8) 70 // smiley sprite at 0x300 (8 rows) 71 w = cat(h, w, "var SPR=[0x3C,0x42,0xA5,0x81,0xA5,0x99,0x42,0x3C];\n" as *u8) 72 w = cat(h, w, "var B='" as *u8) 73 var k: i64 = 0 74 while k < b64len { h[w] = b64[k]; w = w + 1; k = k + 1 } 75 w = cat(h, w, "';\n" as *u8) 76 w = cat(h, w, "var bytes=Uint8Array.from(atob(B),function(c){return c.charCodeAt(0);});\n" as *u8) 77 w = cat(h, w, "WebAssembly.instantiate(bytes).then(function(r){var ex=r.instance.exports;\n" as *u8) 78 w = cat(h, w, "var W=Number(ex.ch8_disp_w()),H=Number(ex.ch8_disp_h()),S=10,off=Number(ex.ch8_disp_off());\n" as *u8) 79 w = cat(h, w, "var cv=document.getElementById('c');cv.width=W*S;cv.height=H*S;var ctx=cv.getContext('2d');\n" as *u8) 80 w = cat(h, w, "function loadAll(){ex.ch8_reset(0n);var i;for(i=0;i<PROG.length;i++)ex.ch8_load(0n,BigInt(0x200+i),BigInt(PROG[i]));for(i=0;i<SPR.length;i++)ex.ch8_load(0n,BigInt(0x300+i),BigInt(SPR[i]));}\n" as *u8) 81 w = cat(h, w, "loadAll();document.getElementById('reset').onclick=loadAll;\n" as *u8) 82 w = cat(h, w, "var KEY={'1':0x1,'2':0x2,'3':0x3,'4':0xC,'q':0x4,'w':0x5,'e':0x6,'r':0xD,'a':0x7,'s':0x8,'d':0x9,'f':0xE,'z':0xA,'x':0x0,'c':0xB,'v':0xF};\n" as *u8) 83 w = cat(h, w, "addEventListener('keydown',function(e){var kk=KEY[e.key];if(kk!==undefined)ex.ch8_key(0n,BigInt(kk),1n);});\n" as *u8) 84 w = cat(h, w, "addEventListener('keyup',function(e){var kk=KEY[e.key];if(kk!==undefined)ex.ch8_key(0n,BigInt(kk),0n);});\n" as *u8) 85 w = cat(h, w, "function frame(){ex.ch8_run_frame(0n,8n);var mem=new Uint8Array(ex.memory.buffer);var x,y;\n" as *u8) 86 w = cat(h, w, "for(y=0;y<H;y++)for(x=0;x<W;x++){var on=mem[off+(y*64+x)*8]!==0;ctx.fillStyle=on?'#9be39b':'#0b0f14';ctx.fillRect(x*S,y*S,S,S);}\n" as *u8) 87 w = cat(h, w, "requestAnimationFrame(frame);}frame();}).catch(function(e){document.body.innerHTML+='<pre style=color:#f99>'+e+'</pre>';});\n" as *u8) 88 w = cat(h, w, "</script></body></html>\n" as *u8) 89 90 let fd: i64 = sys_openat_wr(HTML_OUT, 0x1a4) 91 if fd < 0 { sys_write(2, "FATAL: cannot open chip8.html\n" as *u8, 30); sys_exit(3); return 3 } 92 sys_write(fd, h, w) 93 sys_close(fd) 94 sys_write(1, "[nx_chip8_page] wrote web_assets/chip8.html (wasm bytes=" as *u8, 57) 95 let nb: *u8 = sys_mmap(28) 96 var k2: i64 = 0 97 var m2: i64 = wlen 98 if m2 == 0 { nb[0] = 48 as u8; k2 = 1 } 99 while m2 > 0 { nb[k2] = (48 + (m2 % 10)) as u8; m2 = m2 / 10; k2 = k2 + 1 } 100 var qd: i64 = k2 - 1 101 while qd >= 0 { let o1: *u8 = sys_mmap(1); o1[0] = nb[qd]; sys_write(1, o1, 1); qd = qd - 1 } 102 sys_write(1, ")\n" as *u8, 2) 103 sys_exit(0) 104 return 0 105}