const TXBUF = 0; const RXBUF = 32; const TXDATA = 64; const TXHEAD = 65; const TXTAIL = 66; const RXDATA = 67; const RXHEAD = 68; const RXTAIL = 69; const TXBUF_SIZE = 32; const RXBUF_SIZE = 32; const PERIPHS_SIZE = 70; 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 txdata = Atomics.load(this.mem_u8, TXDATA); if (txdata !== 0) this.handle_txdata(); } handle_txdata() { const head = Atomics.load(this.mem_u8, TXHEAD); const tail = Atomics.load(this.mem_u8, TXTAIL); 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); // More data could have been added -- only clear TXDATA if // tail is unchanged. if (tail === Atomics.load(this.mem_u8, TXTAIL)) Atomics.store(this.mem_u8, TXDATA, 0); const str = this.decoder.decode(new Uint8Array(data)); this.output.innerText += str; } } window.addEventListener('DOMContentLoaded', () => { window.emu = new Emulator(); });