const TXBUF = 0; const RXBUF = 32; const TXHEAD = 64; const TXTAIL = 65; const RXHEAD = 66; const RXTAIL = 67; const TXBUF_SIZE = 32; const RXBUF_SIZE = 32; const PERIPHS_SIZE = 68; const POLL_INTERVAL_MS = 20; class Emulator { constructor() { this.mem = new WebAssembly.Memory({ initial: 1, maximum: 1, shared: true }); this.mem_u8 = new Uint8Array(this.mem.buffer); for (let i = 0; i < PERIPHS_SIZE; ++i) this.mem_u8[i] = 0; this.decoder = new TextDecoder('utf-8'); this.output = document.getElementById('output'); this.input = document.getElementById('input'); this.timer = setInterval(() => this.poll(), POLL_INTERVAL_MS); this.worker = new Worker('boot.js'); this.worker.postMessage(this.mem); } poll() { const txhead = Atomics.load(this.mem_u8, TXHEAD); const txtail = Atomics.load(this.mem_u8, TXTAIL); if (txhead !== txtail) this.handle_txdata(txhead, txtail); } handle_txdata(head, tail) { const data = []; let i = head; do { data.push(this.mem_u8[TXBUF + i]); i = (i + 1) % TXBUF_SIZE; } while (i !== tail); Atomics.store(this.mem_u8, TXHEAD, tail); const str = this.decoder.decode(new Uint8Array(data)); this.output.innerText += str; } } window.addEventListener('DOMContentLoaded', () => { window.emu = new Emulator(); });